官网地址:http://kindeditor.net/demo.php
下载地址:
- 官网下载:http://kindeditor.net/down.php
- 本地下载:http://files.cnblogs.com/files/wupeiqi/kindeditor_a5.zip
什么是KindEdtor?
KindEditor是一套开源的HTML可视化编辑器,主要用于让用户在网站上获得所见即所得编辑效果,兼容IE,Firefox,Chrome,Safari,Opera等主流浏览器。
当我们下载后解压后会发现,里面存在很多文件夹,那么这些文件夹有什么意义?
文件夹
├── asp asp示例 ├── asp.net asp.net示例 ├── attached 空文件夹,放置关联文件attached ├── examples HTML示例 ├── jsp java示例 ├── kindeditor-all-min.js 全部JS(压缩) ├── kindeditor-all.js 全部JS(未压缩) ├── kindeditor-min.js 仅KindEditor JS(压缩) ├── kindeditor.js 仅KindEditor JS(未压缩) ├── lang 支持语言 ├── license.txt License ├── php PHP示例 ├── plugins KindEditor内部使用的插件 └── themes KindEditor主题
下面举个简单的例子来看看KindEditor的简单使用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>提交的文章内容</h1> <div class="article"></div> <h1>请输入内容</h1> <div class="edit"> <textarea name="content"></textarea> </div> <script src="/static/js/jquery-1.12.4.js"></script> <script src="/static/plugins/kindeditor/kindeditor-all.js"></script> <script> $(function () { KindEditor.create('textarea[name="content"])',{ allowImageUpload: true, allowImageRemote: true, allowFlashUpload: false, allowFileManager:true, filePostName: 'fafafa', extraFileUploadParams : { csrfmiddlewaretoken : "{{ csrf_token }}" }, uploadJson:'/upload_file.html', fileManagerJson:'/manager_file.html' }) }) </script> </body> </html>
这里我们需要注意的是在JS代码中,{allowImageUpload:true}表示参数,具体详细参数的使用方法详见 http://kindeditor.net/docs/option.html
在众多参数中,大部分都是起到在前端装饰的作用,而以下4个参数可以与后端交互UploadJson,fileManageJson,extraFileUploadParams,filePostName
以其中两个为例简单介绍以下上传文件
上传文件
前端代码如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>提交的文章内容</h1> <div class="article"></div> <h1>请输入内容</h1> <div class="edit"> <textarea name="content"></textarea> </div> <script src="/static/js/jquery-1.12.4.js"></script> <script src="/static/plugins/kindeditor/kindeditor-all.js"></script> <script> $(function () { KindEditor.create('textarea[name="content"])',{ allowImageUpload: true, allowImageRemote: true, allowFlashUpload: false, allowFileManager:true, filePostName: 'fafafa', extraFileUploadParams : { csrfmiddlewaretoken : "{{ csrf_token }}" }, uploadJson:'/upload_file.html', fileManagerJson:'/manager_file.html' }) }) </script> </body> </html>
1 def upload_file(request): 2 import os 3 import json 4 dir = request.GET.get('dir') 5 if dir == 'image': 6 pass 7 obj = request.FILES.get('fafafa') 8 file_path = os.path.join('static/imgs', obj.name) 9 with open(file_path, 'wb') as f: 10 for chunk in obj.chunks(): 11 f.write(chunk) 12 ret = { 13 'error': 0, 14 'url': 'http://127.0.0.1:8000/' + file_path, 15 'message': '错误了...' 16 } 17 return HttpResponse(json.dumps(ret))
def manager_file(request):
import os
import time
import json
from EdmureBlog.settings import BASE_DIR
dic = {}
root_path = os.path.join(BASE_DIR, 'static/')
static_root_path = '/static/'
# 要访问的路径
request_path = request.GET.get('path')
# 如果是根目录,则为空
print(request_path, '++++++++++++++')
if request_path:
abs_current_dir_path = os.path.join(root_path, request_path)
# request_path=css/ ""
# move_up_dir_path=css
#
move_up_dir_path = os.path.dirname(request_path.rstrip('/'))
dic['moveup_dir_path'] = move_up_dir_path + '/' if move_up_dir_path else move_up_dir_path
else:
# 根目录无上一级
abs_current_dir_path = root_path
dic['moveup_dir_path'] = ''
dic['current_dir_path'] = request_path
dic['current_url'] = os.path.join(static_root_path, request_path)
file_list = []
print(os.listdir(abs_current_dir_path))
for item in os.listdir(abs_current_dir_path):
# item每一个文件名
abs_item_path = os.path.join(abs_current_dir_path, item)
a, exts = os.path.splitext(item)
is_dir = os.path.isdir(abs_item_path) # 得到的是bool值,有为True,没有我False
print('is_dir', is_dir)
if is_dir:
temp = {
'is_dir': True,
'has_file': True,
'filesize': 0,
'dir_path': '',
'is_photo': False,
'filetype': '',
'filename': item,
'datetime': time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(os.path.getctime(abs_item_path)))
}
else:
temp = {
'is_dir': False,
'has_file': False,
'filesize': os.stat(abs_item_path).st_size,
'dir_path': '',
'is_photo': True if exts.lower() in ['.jpg', '.png', '.jpeg'] else False,
'filetype': exts.lower().strip('.'),
'filename': item,
'datetime': time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(os.path.getctime(abs_item_path)))
}
file_list.append(temp)
dic['file_list'] = file_list
return HttpResponse(json.dumps(dic))
这虽然我们提供了便捷,但懂技术的人使用时,利用JS代码攻击你写的内容,比如在你发布的博客中评论添加<script>alert(OK)</script>,这样会导致在别人访问你的博客时,每次刷新都会弹出OK,这就是所谓的XSS攻击,当然我们也有办法解决它。
XSS攻击解决方法
解决的原理很简单,就是我们先定义一份白名单,就是允许的HTML标签存在后台,这样我们在访问时,首先遍历出提交到后台的HTML标签与白名单进行匹配,讲不存在白名单中的HTML标签取出,删除内容或者删除标签即可
首先我们在遍历HTML标签时需要安装beautifulsoup4
pip3 install beautifulsoup4
from bs4 import BeautifulSoup class XSSFilter(object): __instance = None def __init__(self): # XSS白名单 self.valid_tags = { "font": ['color', 'size', 'face', 'style'], 'b': [], 'div': [], "span": [], "table": [ 'border', 'cellspacing', 'cellpadding' ], 'th': [ 'colspan', 'rowspan' ], 'td': [ 'colspan', 'rowspan' ], "a": ['href', 'target', 'name'], "img": ['src', 'alt', 'title'], 'p': [ 'align' ], "pre": ['class'], "hr": ['class'], 'strong': [] } @classmethod def instance(cls): if not cls.__instance: obj = cls() cls.__instance = obj return cls.__instance def process(self, content): soup = BeautifulSoup(content, 'lxml') # 遍历所有HTML标签 for tag in soup.find_all(recursive=True): # 判断标签名是否在白名单中 if tag.name not in self.valid_tags: tag.hidden = True if tag.name not in ['html', 'body']: tag.hidden = True tag.clear() continue # 当前标签的所有属性白名单 attr_rules = self.valid_tags[tag.name] keys = list(tag.attrs.keys()) for key in keys: if key not in attr_rules: del tag[key] return soup.renderContents() if __name__ == '__main__': html = """<p class="title"> <b>The Dormouse's story</b> </p> <p class="story"> <div name='root'> Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister c1" style='color:red;background-color:green;' id="link1"><!-- Elsie --></a> <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and <a href="http://example.com/tillie" class="sister" id="link3">Tilffffffffffffflie</a>; and they lived at the bottom of a well. <script>alert(OK)</script> </div> </p> <p class="story">...</p>""" v = XSSFilter.instance().process(html) print(v)
在上面的例子中,我们会发现每当有用户访问时,都会创建一个对象,而且每个对象都是一样的,这样用户访问量多了,就会增加内存的使用量,所以这是没有必要的。在我们了解到的类方法中,有一种单例模式就很好的解决了这一问题,不管多少个用户访问,只会创建一个对象,而且后面的用户访问都会使用这一对象。
from bs4 import BeautifulSoup class XSSFilter(object): __instance = None def __init__(self): # XSS白名单 self.valid_tags = { "font": ['color', 'size', 'face', 'style'], 'b': [], 'div': [], "span": [], "table": [ 'border', 'cellspacing', 'cellpadding' ], 'th': [ 'colspan', 'rowspan' ], 'td': [ 'colspan', 'rowspan' ], "a": ['href', 'target', 'name'], "img": ['src', 'alt', 'title'], 'p': [ 'align' ], "pre": ['class'], "hr": ['class'], 'strong': [] } def __new__(cls, *args, **kwargs): """ 单例模式 :param cls: :param args: :param kwargs: :return: """ if not cls.__instance: obj = object.__new__(cls, *args, **kwargs) cls.__instance = obj return cls.__instance def process(self, content): soup = BeautifulSoup(content, 'lxml') # 遍历所有HTML标签 for tag in soup.find_all(recursive=True): # 判断标签名是否在白名单中 if tag.name not in self.valid_tags: tag.hidden = True if tag.name not in ['html', 'body']: tag.hidden = True tag.clear() continue # 当前标签的所有属性白名单 attr_rules = self.valid_tags[tag.name] keys = list(tag.attrs.keys()) for key in keys: if key not in attr_rules: del tag[key] return soup.renderContents() if __name__ == '__main__': html = """<p class="title"> <b>The Dormouse's story</b> </p> <p class="story"> <div name='root'> Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister c1" style='color:red;background-color:green;' id="link1"><!-- Elsie --></a> <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and <a href="http://example.com/tillie" class="sister" id="link3">Tilffffffffffffflie</a>; and they lived at the bottom of a well. <script>alert(OK)</script> </div> </p> <p class="story">...</p>""" obj = XSSFilter() v = obj.process(html) print(v)