我们前面在试各种功能的时候都是在admin下把数据硬插在table里,但是这明显不符合我们的日常使用环境,博客里最常用的就是添加新的文章了,所以这里我们就看一看文章的添加是怎么实现的。
富文本编辑器看一看博客园在添加文章的页面,新的文章是怎么添加进去的呢?
这个图片里的文本编辑器就是一个富文本编辑器(富文本编辑器,Rich Text Editor, 简称 RTE, 是一种可内嵌于浏览器,所见即所得的文本编辑器。)因为我们在编辑文本的时候包含了各种各样的样式,其实真实情况下我们是写了一个html的代码,这个代码点击上面的图标是可以显示出来的,只不过富文本编辑器是一种所见即所得的效果,更利于我们的编辑。但是最终生成的还是一堆html类型的字符串。
kindeditor
在新文章的页面中我们就使用了KindEditor这个富文本编辑器,点击官网查看,下载以后放在static文件夹中,就可以直接调用了,在HTML页面中就是定义一个textarea的标签,然后导入Kindeditor,把他绑定给这个textarea
<textarea id="editor_id" name="content" style="width:700px;height:300px;"> <strong>HTML内容</strong> </textarea> <script charset="utf-8" src="/editor/kindeditor.js"></script> <script charset="utf-8" src="/editor/lang/zh-CN.js"></script> <script> KindEditor.ready(function(K) { window.editor = K.create('#editor_id'); }); </script>
至于create函数中还可以添加好多参数,可以直接在官网上查到。
官网上的文档说的很清楚,如何在页面中加载KindEditor,这里就不说了。但是下面的一个用法是要着重讲一下的:图片的上传。
KindEditor中上传图片默认的KindEditor是有两个图片上传的按钮的,但是上传以后是没有任何行为的(我们也没有告诉他上传到服务器的哪里?怎么上传)!所以我么就要给K.create()中添加下面几个参数
<script> KindEditor.ready(function(K){ window.editor = K.create('#editor_id',{ uploadJson:'/upload/', }) </script>
就是指定了上传图片时候对应的url请求。然后上传一下会发现有下面的错误
很明显,发生了跨站请求错误,所以要添加csrf参数,这个参数的添加在一个新的配置参数中
1 uploadJson:'/upload/', 2 extraFileUploadParams:{ 3 csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val() 4 }, 5 filePostName:"upload_img" //指定上传文件的key
但是一定要记得在前面加上{%csrf_token%}才行啊,要不是jQuery取不到这个csrf的值。
最后一行的filePostName的作用我们下面马上会说明。
既然上传图片是用到一个url,所以我们要在路由总设置好这个url和它对应的视图函数
1 def upload(request): 2 print(request.FILES) 3 obj = request.FILES.get('upload_img') 4 path = os.path.join(settings.MEDIA_ROOT,'img/',obj.name) 5 print(obj.name) 6 7 with open(path,'wb') as f: 8 for line in obj: 9 f.write(line) 10 return HttpResponse('OK')
我们在settings.py中定义了一个静态文件的上传路径(/media/)为了管理的方便,我们在这个路径下新建一个img/文件夹来存放上传的图片文件。所以path就是项目的路径加上img后面是文件名。而request.FILE.get的值就是刚刚我们在模板中定义的filePostName
最后用常规的文件句柄把数据写入文件就行了。
上传完毕,就有下面的效果
不要看标题上的上传错误,那个是定义出来的显示内容。主体内的OK是我们在视图中通过HttpResponse返回的字符串。然后在看看media文件夹内,是不是有了我们新上传的图片!
上传图片的显示
在常规操作中,我们一点上传图片的时候在文本编辑框中会出现我们上传的图片,这个操作就是需要我们视图中加一个返回的字典
1 def upload(request): 2 obj = request.FILES.get('upload_img') 3 path = os.path.join(settings.MEDIA_ROOT,'img/',obj.name) 4 5 with open(path,'wb') as f: 6 for line in obj: 7 f.write(line) 8 9 ret = { 10 'error':0, 11 'url':'/media/img/'+obj.name, 12 } 13 14 return HttpResponse(json.dumps(ret))
我们返回了一个字典,里面是一个状态字和一个url,这个url就是我们保存图片的路径,一定要注意,这个路径不是绝对路径!这样就完成了上传图片的显示
富文本编辑器里内容的保存
新的文章保存,有一个字段比较重要:desc,我们在访问博客园的时候会看到有个这样的文章简介,就是标题下面那个
这个简介就是把文章的内容截取了一个固定的长度,我们可以先用切片的方法来试一下
1 def new_article(request,username): 2 if request.method == 'POST': 3 title = request.POST.get('title') 4 content = request.POST.get('article_content') 5 6 user = request.user 7 8 desc = content[:150] 9 10 models.Article.objects.create(user=user,desc=desc) 11 print(title,content) 12 13 return render(request,'new_article.html')
结果会是这种显示效果
这是因为我们从页面的富文本编辑器传过来的字符串是一堆HTML形式的代码,字符串在做切片的时候是按照整个HTML文件作为一个大字符串来切片的,图示中的效果还算好,有些时候可能由于文章中的一些特定的标签在切片的时候被切掉,导致整个页面的效果崩溃。这就要用到一个新的库——BeautifulSoup。我们可以先通过下面的案例看一看这个beautifulsoup的作用是什么
from bs4 import BeautifulSoup s = "<div>BeautifulSoup测试</div>" soup = BeautifulSoup(s,'html.parser') print('soup:\n',soup) print('soup.text:\n',soup.text) ##########输出########## soup: <div>BeautifulSoup测试</div> soup.text: BeautifulSoup测试
我们可以通过soup.text获取到各种标签之间的字符串,就是页面上显示的内容。所以就可以把上面的那段视图代码修改成下面的方式
1 from bs4 import BeautifulSoup 2 def new_article(request,username): 3 if request.method == 'POST': 4 title = request.POST.get('title') 5 content = request.POST.get('article_content') 6 7 soup = BeautifulSoup(content,'html.parser') 8 user = request.user 9 10 article_obj = models.Article.objects.create(user=user,desc=soup.text[0:150],title=title) 11 models.ArticleDetail.objects.create(content = content,article = article_obj) 12 print(title,content) 13 14 return render(request,'new_article.html')
简易的XSS攻击预防
XSS攻击
我们在前面提到过XSS攻击,这里可以再演示一下:如果我们新写的文章是这样的:
注意,添加的字符串类型选择的是html,那么我们在打开这个文章页面的时候就会有个弹框弹出来
这还是最简单的alert攻击,如果加上了死循环什么的浏览器就崩溃了。
看一下百度百科对XSS攻击的定义:
XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容
简单的XSS防御
有一个最简单暴力的方法就是对整个添加的字符串进行正则搜索,把定义的关键字率除掉,就可以了。但是BeautifulSoup给我们提供了一个功能,对指定的标签类型进行过滤
from bs4 import BeautifulSoup s = "<div>BeautifulSoup测试</div> \ <a href='http://127.0.0.1:8000'>a标签</a> \ <script> \ alert('XSS攻击测试')\ </script>" soup = BeautifulSoup(s,'html.parser') for tag in soup.find_all(): if tag.name in ['script','a']: tag.decompose() print(soup) ##########输出值########## <div>BeautifulSoup测试</div>
可以看一下,我们可以用find_all的方式来拿到所有的tag,如果这个tag的样式是我们指定的(script和a标签),直接删掉。不是的就保留下来。
也就是我们直接定义一个非法的标签列表就行了。
但是还是有一点问题,像博客园一样,我们是可以插入程序段,里面包含了script的类型,那么用这个方式就被过滤掉了,留个悬念,以后再讲!