BBS(仿博客园系统)项目05(后台管理功能实现:文章添加、富文本编辑器使用、xss攻击、BeautifulSoup4模块、富文本编辑器上传图片、修改头像)

摘要

  • 布局框架搭建
  • 随笔添加
  • 后台管理富文本编辑器KindEditor
  • xss攻击
  • 文章简介的截取,BeautifulSoup4模块
  • 富文本编辑器上传图片
  • 头像修改

一、后台管理框架布局搭建

 后台管理布局框架分析:导航条、左侧功能区、右侧主要功能显示和实现区

实现:

导航条:使用bootstrap模板:JavaScript>>导航条

左侧:使用bootstrap模板:组件>>列表组

右侧:使用bootstrap模板:JavaScript>>标签页

新建后台管理路由(注意放在站点路由上面)

url(r^backend/, views.backend),

views.py视图函数:

@login_requireddef backend(request): user_obj = request.user article_list = models.Article.objects.filter(blog=user_obj.blog) return render(request, backend/backend.html, locals())

渲染后台管理页面(单独在templates文件夹下创建后台管理文件夹backend,然后在此文件夹下新建backend.html)

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>后台管理</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"> <script src="/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script> <style> #top_left { font-weight: bold; font-size: 24px; color: black; } #top_title { color: blue; font-size: 18px; font-weight: bold; font-family: "微软雅黑 Light"; } </style> {% block css %} {% endblock %}</head><body>{#后台管理导航条#}<nav class="navbar navbar-default"> <div class="container-fluid"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-5" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="/home/" id="top_left">博客园后台管理</a> <p class="navbar-text" id="top_title">{{ request.user.blog.blog_title }}</p> </div> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-5"> <p class="navbar-text navbar-right"> <a href="/{{ request.user.username }}/" class="navbar-link">{{ request.user.username }}</a> </p> </div> </div></nav>{# <a class="navbar-brand" href="#" id="top_left">博客园后台管理</a>#}{##}{# <li><a id="top_title" href="">{{ request.user.blog.blog_title }}</a></li>#}{# <li><a id="top_right" href="#">{{ request.user.username }}</a></li>#}<div class="container-fluid"> <div class="row"> {# 后台管理左侧快捷入口占用2个栅格#} {# 左侧信息展示:需要参数:tag_list category_list date_list user_obj#} <div class="col-md-2 col-sm-2 col-xs-3"> <div class="list-group"> <li class="list-group-item list-group-item-info">操作</li> <a href="/add_article/" class="list-group-item">添加新随笔</a> <a href="#" class="list-group-item">草稿箱</a> <a href="#" class="list-group-item">添加新文章</a> <a href="#" class="list-group-item">其它...</a> </div> <hr> <div class="list-group"> <li class="list-group-item list-group-item-warning">分类</li> <a href="#" class="list-group-item">编辑分类</a> <a href="#" class="list-group-item">所有分类</a> <a href="#" class="list-group-item">其它...</a> </div> </div> {# 后台管理右侧管理主要内容展示区占用10个栅格#} {# 右侧文章列表需要参数:article_list#} <div class="col-md-10 col-sm-10 col-xs-9"> {% block content %} <div> <!-- Nav tabs --> <ul class="nav nav-tabs" role="tablist"> <li role="presentation" class="active"><a href="#article" aria-controls="home" role="tab" data-toggle="tab">随笔</a></li> <li role="presentation"><a href="#profile" aria-controls="profile" role="tab" data-toggle="tab">文章</a> </li> <li role="presentation"><a href="#messages" aria-controls="messages" role="tab" data-toggle="tab">评论</a></li> <li role="presentation"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">设置</a></li> </ul> <!-- Tab panes --> <div class="tab-content"> <div role="tabpanel" class="tab-pane active" id="article"> <table class="table table-hover table-striped"> <thead> <tr> <th>标题</th> <th class="text-center">发布时间</th> <th class="text-center">评论数</th> <th class="text-center">点赞数</th> <th>操作</th> <th>操作</th> </tr> </thead> <tbody> {% for article in article_list %} <tr> <td><a href="/{{ article.blog.userinfo.username }}/article/{{ article.pk }}/">{{ article.title }}</a></td> <td class="text-center">{{ article.create_time|date:‘Y-m-d‘ }}</td> <td class="text-center">{{ article.comment_num }}</td> <td class="text-center">{{ article.up_num }}</td> <td><a href="">编辑</a></td> <td><a href="">删除</a></td> </tr> {% endfor %} </tbody> </table> </div> <div role="tabpanel" class="tab-pane" id="profile">...</div> <div role="tabpanel" class="tab-pane" id="messages">...</div> <div role="tabpanel" class="tab-pane" id="settings">...</div> </div> </div> {% endblock %} </div> </div></div>{% block js %}{% endblock %}</body></html>

大致效果:

二、添加新随笔

 先来一顿操作:

建路由:

url(r^add_article/, views.add_article),

渲染页面:继承后台管理主页面

{% extends ‘backend/backend.html‘ %}{% block css %} <style> #title { margin-top: 5px; background-color: rgba(35,206,235,0.2); height: 22px; border-top: 1px gray solid; border-bottom: 1px gray dashed; } </style>{% endblock %}{% block content %}<div><p id="title">添加随笔</p><p>标题 <input type="text" class="form-control"></p></div>

视图函数:

def add_article(request): return render(request, backend/add_article.html)

然后就可以在上面的基础上继续进行:

富文本编辑器KindEditor的使用

下载该插件后,解压,将其文件夹复制到static文件夹下

 

使用KindEditor需要借助textarea输入框,所以需要在添加随笔处添加一个textarea框

BeautifulSoup4模块

①表单在提交后,后端需要对文章进行截取一部分作为文章的摘要desc,如果直接对提交的内容content进行切片的话得不到文章的文字内容,因为我们通过富文本编辑器提交的内容实际上都是一行行html代码,同时如果这段代码中存在js脚本的话,有可能会存在xss脚本攻击,这里就需要对随笔内容进行过滤

②先参考一下博客园处理xss攻击的方式:

在HTML源码编辑器中写入<script>alert(123)</script>,更新后再打开

可以看出博客园的内部将此段代码给加了代码,让js脚本没法生效,同时将随笔保存再看发现这段代码被认为删除了。

所以:

博客园处理xss攻击方法:将敏感标签自动添加内容,让其失效,在后端处理时直接过滤掉敏感标签,将其删除。

引入一个模块BeautifulSoup4:可以过滤js脚本和拿到content内的文本内容,再进行切分。

先安装模块pip3 install beautifulsoup4# 这里强调一点:一定要下官方推荐的beautifulsoup4版本,因为beautifulsoup3版本已经停止开发

 

富文本编辑器上传图片

通过富文本编辑器上传图片需要再开一个路由进行处理上传的图片

url(r^upload_img/, views.upload_img),

 视图函数upload_img:

def upload_img(request): if request.method == POST: # 前端富文本编辑器上传的图片key值叫imgFile,拿到文件对象 img_obj = request.FILES.get(imgFile) print(img_obj.name,type(img_obj.name)) # 手动拼接文件存放路径,该文件应该存入media文件夹 path = os.path.join(settings.BASE_DIR, media, article_img) # 判断当前路径是否存在,如果不不存在,则创建文件夹 if not os.path.exists(path): os.mkdir(path) # 可以通过img_obj.name,拿到这个文件对象的文件名,然后在拼接文件的路径,用于保存写入 file_path = os.path.join(path, img_obj.name) with open(file_path, wb) as f: for line in img_obj: f.write(line) # 这里需要注意富文本编辑器上传图片接收的响应数据格式规定是以下这种形式: """ //成功时 { "error" : 0, "url" : "http://www.example.com/path/to/file.ext" } //失败时 { "error" : 1, "message" : "错误信息" } """ back_dic = { error: 0, # 这个url就是前端可以通过路由直接访问到的文件路径,这样做的目的是编辑上传一个图片,编辑器肯定需要 # 拿到该图片渲染到页面上。 url: /media/article_img/%s % img_obj.name } return JsonResponse(back_dic)

三、修改头像:

 

相关文章