报障系统之基础建设
通过Django admin填充基本数据:
#admin.py from django.contrib import admin from app01 import models admin.site.register(models.Article) admin.site.register(models.Article2Tag) admin.site.register(models.ArticleDetail) admin.site.register(models.Blog) admin.site.register(models.Category) admin.site.register(models.Comment) admin.site.register(models.Tag) admin.site.register(models.UpDown) admin.site.register(models.UserFans) admin.site.register(models.UserInfo) #创建用户: python manage.py createsuperuser #访问: 127.0.0.1:8000/admin/ #models.py from django.db import models class UserInfo(models.Model): """ 用户表 """ nid = models.BigAutoField(primary_key=True) username = models.CharField(verbose_name='用户名', max_length=32, unique=True) password = models.CharField(verbose_name='密码', max_length=64) nickname = models.CharField(verbose_name='昵称', max_length=32) email = models.EmailField(verbose_name='邮箱', unique=True) avatar = models.ImageField(verbose_name='头像',upload_to='static/images') create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) fans = models.ManyToManyField(verbose_name='粉丝们', to='UserInfo', through='UserFans', related_name='f', through_fields=('user', 'follower')) def __str__(self): return self.nickname class Blog(models.Model): """ 博客信息 """ nid = models.BigAutoField(primary_key=True) title = models.CharField(verbose_name='个人博客标题', max_length=64) site = models.CharField(verbose_name='个人博客后缀', max_length=32, unique=True) theme = models.CharField(verbose_name='博客主题', max_length=32) user = models.OneToOneField(to='UserInfo', to_field='nid') def __str__(self): return self.title class UserFans(models.Model): """ 互粉关系表 """ user = models.ForeignKey(verbose_name='博主', to='UserInfo', to_field='nid', related_name='users') follower = models.ForeignKey(verbose_name='粉丝', to='UserInfo', to_field='nid', related_name='followers') class Meta: unique_together = [ ('user', 'follower'), ] class Category(models.Model): """ 博主个人文章分类表 """ nid = models.AutoField(primary_key=True) title = models.CharField(verbose_name='分类标题', max_length=32) blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid') def __str__(self): return "%s-%s" %(self.blog.title,self.title) class ArticleDetail(models.Model): """ 文章详细表 """ content = models.TextField(verbose_name='文章内容', ) article = models.OneToOneField(verbose_name='所属文章', to='Article', to_field='nid') class UpDown(models.Model): """ 文章顶或踩 """ article = models.ForeignKey(verbose_name='文章', to='Article', to_field='nid') user = models.ForeignKey(verbose_name='赞或踩用户', to='UserInfo', to_field='nid') up = models.BooleanField(verbose_name='是否赞') class Meta: unique_together = [ ('article', 'user'), ] class Comment(models.Model): """ 评论表 """ nid = models.BigAutoField(primary_key=True) content = models.CharField(verbose_name='评论内容', max_length=255) create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) reply = models.ForeignKey(verbose_name='回复评论', to='self', related_name='back', null=True) article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid') user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid') class Tag(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(verbose_name='标签名称', max_length=32) blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid') def __str__(self): return "%s-%s" %(self.blog.title,self.title) class Article(models.Model): nid = models.BigAutoField(primary_key=True) title = models.CharField(verbose_name='文章标题', max_length=128) summary = models.CharField(verbose_name='文章简介', max_length=255) read_count = models.IntegerField(default=0) comment_count = models.IntegerField(default=0) up_count = models.IntegerField(default=0) down_count = models.IntegerField(default=0) create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid') category = models.ForeignKey(verbose_name='文章类型', to='Category', to_field='nid', null=True) type_choices = [ (1, "Python"), (2, "Linux"), (3, "OpenStack"), (4, "GoLang"), ] # 网站分类 article_type_id = models.IntegerField(choices=type_choices, default=None) tags = models.ManyToManyField( to="Tag", through='Article2Tag', through_fields=('article', 'tag'), ) def __str__(self): return "%s-%s" %(self.blog.title,self.title) class Article2Tag(models.Model): article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid') tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='nid') class Meta: unique_together = [ ('article', 'tag'), ]
报障系统之个人博客主页:
#urls.py url(r'^(\w+)/$', views.home), #views.py def home(request,site): """ 访问个人博客主页 http://127.0.0.1:8000/fangshaowei/ :param request: 请求相关信息 :param site: 个人博客后缀,如: www.xxx.com/xxxxx/ :return: """ blog = models.Blog.objects.filter(site=site).first() if not blog: return redirect('/') # 按照:分类,标签,时间 # 分类 category_list = models.Article.objects.filter(blog=blog).values('category_id','category__title').annotate(ct=Count('nid')) # 标签 tag_list = models.Article2Tag.objects.filter(article__blog=blog).values('tag_id','tag__title').annotate(ct=Count('id')) # 时间 date_list = models.Article.objects.filter(blog=blog).extra(select={'ctime':"strftime('%%Y-%%m',create_time)"}).values('ctime').annotate(ct=Count('nid')) # select xxx as x article_list = models.Article.objects.all() return render( request, 'home.html', { 'blog':blog, 'category_list':category_list, 'tag_list':tag_list, 'date_list':date_list, 'article_list':article_list, } ) #home.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <link rel="stylesheet" href="/static/css/theme/{{ blog.theme }}.css" /> </head> <body> <div class="c1"> <div class="c11">{{ blog.user.nickname }}</div> <div class="c12">{{ blog.title }}</div> </div> <div class="c2"> <h3>分类</h3> <ul> {% for item in category_list %} <li> <a href="/{{ blog.site }}/category/{{ item.category_id }}/">{{ item.category__title }}({{ item.ct }})</a> </li> {% endfor %} </ul> </div> <div class="c3"> <h3>标签</h3> <ul> {% for item in tag_list %} <li> <a href="/{{ blog.site }}/tag/{{ item.tag_id }}/">{{ item.tag__title }}({{ item.ct }})</a> </li> {% endfor %} </ul> </div> <div> <h3>时间</h3> <ul> {% for item in date_list %} <li> <a href="/{{ blog.site }}/date/{{ item.ctime }}/">{{ item.ctime }}({{ item.ct }})</a> </li> {% endfor %} </ul> </div> <div class="c4"> {% for row in article_list %} <div> <a href="/{{ blog.site }}/{{ row.nid }}.html">{{ row.title }}</a> <div>{{ row.summary }}</div> </div> {% endfor %} </div> </body> </html>
后台管理
组合筛选:
#URL #urls.py # url(r'^screen-(?P<article_type_id>\d+)-(?P<category_id>\d+)-(?P<article2tag__tag_id>\d+).html$', views.screen), url(r'^screen-(?P<article_type_id>\d+)-(?P<category_id>\d+)-(?P<tags__nid>\d+).html$', views.screen), #视图函数 #views.py def screen(request,**kwargs): # print(kwargs) condition = {} for k,v in kwargs.items(): kwargs[k] = int(v) if v != '0': condition[k] = v print(condition) #大分类 type_list = models.Article.type_choices #个人分类 catagory_list = models.Category.objects.filter(blog_id=1) #个人标签 tag_list = models.Tag.objects.filter(blog_id=1) #进行筛选 condition['blog_id']=1 article_list = models.Article.objects.filter(**condition) return render(request,'screen.html',{ 'type_list':type_list, 'catagory_list':catagory_list, 'tag_list':tag_list, 'article_list':article_list, 'kwargs':kwargs, }) #模板语言 #screen.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .condition a{ display: inline-block; padding: 5px; } .condition a.active{ background-color: #0a386a; color: white; } </style> </head> <body> <h3>筛选</h3> <div class="condition"> 大大分类: {% if kwargs.article_type_id == 0 %} <a class="active" href="/screen-0-{{ kwargs.category_id }}-{{ kwargs.tags__nid }}.html">全部</a> {% else %} <a href="/screen-0-{{ kwargs.category_id }}-{{ kwargs.tags__nid }}.html">全部</a> {% endif %} {% for row in type_list %} {% if row.0 == kwargs.article_type_id %} <a class="active" href="/screen-{{ row.0 }}-{{ kwargs.category_id }}-{{ kwargs.tags__nid }}.html">{{ row.1 }}</a> {% else %} <a href="/screen-{{ row.0 }}-{{ kwargs.category_id }}-{{ kwargs.tags__nid }}.html">{{ row.1 }}</a> {% endif %} {% endfor %} </div> <div class="condition"> 个人分类: <a href="#">全部</a> {% for row in catagory_list %} <a href="{{ row.nid }}">{{ row.title }}</a> {% endfor %} </div> <div class="condition"> 个人标签: <a href="#">全部</a> {% for row in tag_list %} <a href="{{ row.nid }}">{{ row.title }}</a> {% endfor %} </div> <h3>结果</h3> {% for row in article_list %} <div> <h4><a href="#">{{ row.title }}</a></h4> <div>{{ row.summary }}</div> </div> {% endfor %} </body> </html>
KindEditor上传图片:
参考文档:KindEditor
#URL路由 #urls.py url(r'^upload_img.html$', views.upload_img), #视图函数 #views.py def upload_img(request): import os # print(request.POST, request.FILES) # upload_type = request.GET.get('dir') # 根据上传得文件类型控制上传得文件目录 file_obj = request.FILES.get('imgFile') file_path = os.path.join('static/img',file_obj.name) with open(file_path,'wb') as f: for chunk in file_obj.chunks(): f.write(chunk) dic = { 'error':0, 'url':'/'+file_path, 'message':'错误了...' } import json return HttpResponse(json.dumps(dic)) #模板语言 #editor.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="POST" action="/editor.html" novalidate> {#如果不加novalidate,{{ obj.content }}会报错#} <p> 文章标题 {{ obj.title }} </p> {% csrf_token %} <div> <div>文章内容</div> <div> {{ obj.content }} </div> </div> <input type="submit" value="提交"> </form> <script src="/static/kindeditor-4.1.10/kindeditor-all.js"></script> <script> KindEditor.create("#i1",{ width: "700px", height: "300px", uploadJson: '/upload_img.html', extraFileUploadParams: { "csrfmiddlewaretoken":"{{ csrf_token }}" {# 需要添加CSRF验证#} } }) </script> </body> </html>
补充:
文件上传其实内部就是iframe+form 伪Ajax操作
input type='file' name='imgFile' 提交
可以通过filePostName 更改默认name属性:
filePostName: 'fafafa'
BeautifulSoup模块基本使用:
安装: pip3 install beautifulsoup4 导入模块: from bs4 import BeautifulSoup valid_tag = [] #只能设置标签名的白名单 valid_tag = {} #既能加标签名又能加标签的属性的白名单 tag.name 获取标签名 soup.find() #查找第一个标签 soup.find_all() #查找所有的p标签 tag.clear() #清除标签中的内容 tag.decompose() #清空标签中的内容并且删除标签 decode() soup对象转换成字符串 encode() soup对象转换成字节 #示例: content = """ <p id='i1' a='123' b='999'> <script>alert(123)</script> </p> <p id='i2'> <div> <p>asfjldjf</p> </div> <img id='i3' src="/static/img\lang.jpg" alt="" /> </p> """ from bs4 import BeautifulSoup soup = BeautifulSoup(content,'html.parser') #设置名叫valid_tag的白名单: # valid_tag = ['p','img','div'] #只能放置标签名,列表形式 valid_tag = { #既能加标签名又能加标签的属性,字典形式。 'p':['class','id'], 'img':['src'], 'div':['class'] } # v=soup.find(name='p',attrs={'id':'i2'}) #查找第一个p标签,并且id是i2 # print(v) # tag = soup.find(name='p') #查找第一个p标签,生成的是对象的形式,可以通过“.”形式继续查找 # sc=tag.find('script') # print(sc) # v=soup.find_all(name='p') #查找所有的p标签 # print(v) # tags = soup.find_all() #查找所有的标签 # for tag in tags: #tag.name是标签名 # if tag.name not in valid_tag: #如果标签名不在白名单中则情况标签中的内容 # tag.clear() tags = soup.find_all() for tag in tags: if tag.name not in valid_tag: tag.decompose() #删除不再白名单中的标签 if tag.attrs: for k in list(tag.attrs.keys()): #{id:'i1',a=123,b=999} if k not in valid_tag[tag.name]: del tag.attrs[k] content_str=soup.decode() #去掉特殊标签后,拿到它的字符串 print(content_str) #打印过滤后的标签字符串
基于KindEditor和BeautifuSoup实现防止XSS攻击:
基于KindEditor和BeautifuSoup实现防止XSS攻击 #URL路由系统 #urls.py url(r'^editor.html$', views.editor), #可视化编辑器 url(r'^see.html$', views.see), #查看可视化编辑器生成的样式 url(r'^upload_img.html$', views.upload_img), #上传图片 #视图函数 #views.py CONTENT = "" from app01.forms import ArticleForm def editor(request): if request.method=="GET": obj = ArticleForm() return render(request,'editor.html',{'obj':obj}) else: obj = ArticleForm(request.POST) if obj.is_valid(): content = obj.cleaned_data['content'] global CONTENT CONTENT = content print(content) return HttpResponse("...") def see(request): return render(request,'see.html',{'con':CONTENT}) def upload_img(request): import os # print(request.POST, request.FILES) # upload_type = request.GET.get('dir') # 根据上传得文件类型控制上传得文件目录 file_obj = request.FILES.get('imgFile') file_path = os.path.join('static/img',file_obj.name) with open(file_path,'wb') as f: for chunk in file_obj.chunks(): f.write(chunk) dic = { 'error':0, 'url':'/'+file_path, 'message':'错误了...' } import json return HttpResponse(json.dumps(dic)) #模板语言 #editor.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="POST" action="/editor.html" novalidate> {# 如果不加novalidate,{{ obj.content }}会报错#} <p> 文章标题 {# <input type="text" name="title">#} {{ obj.title }} </p> {# <p>#} {# 选择分类#} {# <select name="" id="">#} {##} {# </select>#} {# </p>#} {# <p>#} {# 选择标签#} {# <input type="checkbox">#} {# <input type="checkbox">#} {# <input type="checkbox">#} {# <input type="checkbox">#} {# </p>#} {% csrf_token %} <div> <div>文章内容</div> <div> {# <textarea name="content" id="i1" cols="30" rows="10"></textarea>#} {{ obj.content }} </div> </div> <input type="submit" value="提交"> </form> <script src="/static/kindeditor-4.1.10/kindeditor-all.js"></script> <script> KindEditor.create("#i1",{ width: "700px", height: "300px", {# items: [ 'source', '|', 'undo', 'redo', '|', 'preview', 'print', 'template', 'code', 'cut', 'copy', 'paste',],#} {# noDisableItems: ['undo','redo'],#} {# designMode: false,#} {# resizeType:1,#} uploadJson: '/upload_img.html', extraFileUploadParams: { "csrfmiddlewaretoken":"{{ csrf_token }}" {# 需要添加CSRF验证#} } }) </script> </body> </html> #Form组件 #forms.py from django.forms import Form from django.forms import fields from django.forms import widgets from django.core.exceptions import ValidationError class ArticleForm(Form): title = fields.CharField(max_length=64) content = fields.CharField( widget=widgets.Textarea(attrs={'id':'i1'}) ) def clean_content(self): from bs4 import BeautifulSoup valid_tag = { 'p': ['class', 'id'], 'img': ['src'], 'div': ['class'] } old=self.cleaned_data['content'] soup = BeautifulSoup(old, 'html.parser') tags = soup.find_all() for tag in tags: if tag.name not in valid_tag: tag.decompose() # 删除不再白名单中的标签 if tag.attrs: for k in list(tag.attrs.keys()): # {id:'i1',a=123,b=999} if k not in valid_tag[tag.name]: del tag.attrs[k] content_str = soup.decode() return content_str
补充:
防止XSS攻击为什么不用黑名单,要用白名单?
使用白名单过滤html标签比黑名单操作起来更简单,把攻击锁定在自己可控制范围内。
参考文档:http://www.cnblogs.com/wupeiqi/articles/6283017.html
总结:报障系统
量化内网运维人员工作量 1. 分角色 2. 三类用户: 普通用户 运维人员 总监 3. 知识库(必须会) 4. 权限(公共组件) *****