郑统京 发表于 2022-11-13 00:31:08

八、后台搭建、添加文章、防止XSS攻击、上传图片、修改头

1、后台管理搭建思路
所有文件夹都可以根据功能再细化,你还可以继续创建文件夹分类处理
    templates文件夹
      backend文件夹
      应用1文件夹
      应用2文件夹①backend.base.html前端页面,很多地方都要用到这个,所以做成基类,其他页面直接继承<nav class="navbar navbar-inverse"...><!--导航条--><div class="container-fluid">
    <div class="row"><div class="col-md-2"><!--左侧侧边栏--><div class="col-md-10"><!--右侧公共编辑部分-->   <ul class="nav nav-tabs" role="tablist">   <li role="presentation" class="active"><a href="#home" 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="#files" aria-controls="files" role="tab" data-toggle="tab">文件</a></li>
   <li role="presentation"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">设置</a></li>
   </ul><div role="tabpanel" class="tab-pane active" id="home"> <!--文章区域-->
    {% block article %}
   
    {% endblock %}②backend.html部分{% extends 'backend/backend_base.html' %}
{% block article %}
{#接受当前用户的所有的文章#}
{#    标题 发布状态 评论数 阅读数 操作 #}
    <table class="table table-hover table-striped ">
      <thead>
            <tr>
                <th>标题</th>
                <th>评论数</th>
                <th>点赞数</th>
                <th>操作</th>
                <th>操作</th>
            </tr>
      </thead>
      <tbody>
      {% for article in page_queryset %} //分页器传过来的参数
            <tr>
                <td><a href="/{{ request.user.username }}/article/{{ article.pk }}">{{ article.title }}</a></td>
                <td>{{ article.comment_num }}</td>
                <td>{{ article.up_num }}</td>
                <td><a href="">编辑</a></td>
                <td><a href="">删除</a></td>
            </tr>
      {% endfor %}

      </tbody>
    </table>
    <div class="pull-right">{{ page_obj.page_html|safe }}</div>
{% endblock %}③后端代码from utils.mypage import Pagination
@login_required
def backend(request):
    article_list = models.Article.objects.filter(blog=request.user.blog)
    page_obj = Pagination(current_page=request.GET.get('page',1),all_count=article_list.count(),per_page_num=5)
    page_queryset = article_list
    return render(request,'backend/backend.html',locals())2、添加文章、富文本编辑器使用、截取固定字符、阻止CSS攻击思路实现:①添加文章
1、文章简介
不能直接切取
    应该先想办法获取到当前页面的内容之后在截取150个文本字符
②XSS脚本攻击
    针对用户直接编写的html代码网址
    针对用户直接编写的script标签 我们需要处理
      1、注释掉标签内部的内容
      2、直接将script删除
[如何解决?
    我们自己解决的话
      针对1 后端通过正则表达式筛选
      针对2 首先需要确定及获取script标签
    这两步都繁琐 有没有人帮我们处理一下呢
      模块 bs4模块
            专门用来处理html页面的
            该模块主要用于爬虫程序
    下载千万别下错了
# beautifulsoup模块使用
      soup = BeautifulSoup(content,'html.parser')
      tags = soup.find_all()
      # 获取所有标签
      for tag in tags:
            # print(tag.name) 获取页面所有标签
            # 可以针对与 获取的script标签进行删除
            if tag.name == 'script':
                # 删除标签
                tag.decomposed()
      # 文章简介
      # 1、先简单暴力的直接切去content 150个字符
      desc = soup.text
前端综合代码{% extends 'backend/backend_base.html' %}
{% block article %}
    <h3>添加文章</h3>
    {#    直接利用form表单提交数据#}
    <form action="" method="post">
      {% csrf_token %}
      <p>标题</p>
      <div>
            <input type="text" name="title" class="form-control">
      </div>
      <p>内容</p>
      <div>
            <textarea name="content" id="id_content" cols="80" rows="10"></textarea>
      </div>
      <p>分类</p>
      <div>
            {% for category in category_list %}
                <input type="radio" value="{{ category.pk }}" name="category">{{ category.name }}
            {% endfor %}

      </div>
      <p>标签</p>
      <div>
            {% for tag in tag_list %}
                <input type="checkbox" value="{{ tag.pk }}" name="tag">{{ tag.name }}
            {% endfor %}
      </div>
      <input type="submit" class="btn btn-danger">
    </form>
{% endblock %}

{% block js %}
{% load static %}
    <script charset="utf-8" src="{% static 'kindeditor/kindeditor-all-min.js' %}"></script>
    <script>
      KindEditor.ready(function (K) {
            window.editor = K.create('#id_content', {
                width: '100%',
                height: '600px',
                resizeType:1,
                uploadJson : '/upload_image/',// 上传图片的后端提交路径
                extraFileUploadParams : {
                        'csrfmiddlewaretoken':'{{ csrf_token }}'
                },
            });
      });
    </script>
{% endblock %}后端综合代码from bs4 import BeautifulSoup
@login_required
def add_article(request):
    if request.method =='POST':
      title = request.POST.get('title')
      content = request.POST.get('content')
      category_id = request.POST.get('category')
      tag_id_list = request.POST.getlist('tag')
      # 模块使用
      soup = BeautifulSoup(content,'html.parser')
      tags = soup.find_all()
      # 获取所有标签
      for tag in tags:
            # print(tag.name) 获取页面所有标签
            # 可以针对与 获取的script标签进行删除
            if tag.name == 'script':
                # 删除标签
                tag.decompose()
      # 文章简介
      # 1、先简单暴力的直接切去content 150个字符
      desc = soup.text[0:150] # 相当于内部用正则把文本筛选出来了
      article_obj=models.Article.objects.create(
            title=title,
            content=str(soup),# bs4处理后的结果放进来
            desc=desc,
            category_id=category_id,
            blog=request.user.blog
      )
      # 文章和标签关系表是我们自己创建的 没法使用add,set,remove,clear方法
      # 自己去操作关系表 一次性可能需要创建多天数据批量插入bulk_create()
      article_obj_list = []
      for i in tag_id_list:
            # 先生成一个对象
            tag_article_obj=models.Article2Tag(article=article_obj, tag_id=i) # i 代表for循环出来的每个tag对象
            article_obj_list.append(tag_article_obj)
      # 批量插入数据
      models.Article2Tag.objects.bulk_create(article_obj_list)
      # 跳转到后台文章展示页
      return redirect('/backend/')
    category_list = models.Category.objects.filter(blog=request.user.blog)
    tag_list = models.Tag.objects.filter(blog=request.user.blog)
    return render(request,'backend/add_article.html',locals())三、上传图片后端代码import os
from BBS14 import settings

def upload_imge(request):
    """
      //成功时
      {
                "error" : 0,
                "url" : "http://www.example.com/path/to/file.ext"
      }
      //失败时
      {
                "error" : 1,
                "message" : "错误信息"
      }
    :param request:
    :return:
    """
    back_dic = {'error': 0, }# 先提前定义返回给编辑器的数据格式
    # 用户写文章上传的头像 也算是静态资源 也应该放到media文件夹下
    if request.method == 'POST':
      # 获取用户上传的图片对象
      # 不是自己写的,那么打印看一下 print(request.FILES) 看到imgFile
      file_obj=request.FILES.get('imgFile')
      # 手动拼接存储文件的路径
      file_dir = os.path.join(settings.BASE_DIR,'media','article_img')
      # 优化操作 先判断文件夹是否存在 不存在 自动创建
      if not os.path.isdir(file_dir):
            os.mkdir(file_dir)# 创建一层目录结构
      # 拼接图片的完整路径
      file_path = os.path.join(file_dir,file_obj.name)
      with open(file_path,'wb')as f:
            for line in file_obj:
                f.write(line)
      # 为什么不直接把放上面处理好的file_dir呢? 以为file_dir路径是/BBS14/media,我们没有开设这个接口
      back_dic['url']='/media/article_img/%s'%file_obj.name
    return JsonResponse(back_dic)四、修改头像①前端set_avatar代码,直接把注册时候的代码拷过来{% extends 'base.html' %}
{% block content %}
    <h3 class="text-center">修改头像</h3>
    <form action="" method="post" enctype="multipart/form-data">
      {% csrf_token %}
      <p>
            原头像:
            <img src="/media/{{ request.user.avatar }}" alt="">
      </p>
      <p>

            <label for="myfile">新头像:
                  {% load static %}
            <img src="{% static 'img/default.png' %}" alt="" width="100" style="padding-left: 20px" id="myimg">
                </label>
            <input type="file" id="myfile" name="avatar" style="display: none">
      </p>
      <input type="submit" class="btn btn-info">
    </form>
{% endblock %}

{% block js %}
    <script>
    $("#myfile").change(function () {
      // 文件阅读器对象
      // 1 先生成一个文件阅读器对象
      let myFileReaderObj = new FileReader();
      // 2 获取用户上传的头像文件
      let fileObj = $(this)[0].files[0];
      // 3 将文件对象交给阅读器对象读取
      myFileReaderObj.readAsDataURL(fileObj)// 异步操作IO操作
      // 4 利用文件阅读器将文件展示到前端页面修改src属性
      // 等待文件阅读器加载完毕之后再执行
      myFileReaderObj.onload = function(){
             $('#myimg').attr('src',myFileReaderObj.result)
      }
    })
    </script>
{% endblock %}
②后端代码@login_required
def set_avatar(request):
    if request.method =='POST':
      file_obj = request.FILES.get('avatar')
      # models.UserInfo.objects.filter(pk=request.user.pk).update(avatar=file_obj) # 不会自动加(avatar/文件名)前缀所以上传时候少了前缀就不显示头像
      # 1、自己手动加前缀
      # 2、换一种更新方式
      user_obj = request.user
      user_obj.avatar =file_obj
      user_obj.save()
      return redirect('/home/')
    blog= request.user.blog
    username = request.user.username
    return render(request,'set_avatar.html',locals())



页: [1]
查看完整版本: 八、后台搭建、添加文章、防止XSS攻击、上传图片、修改头