美多后台准备和登录

美多后台准备

  • 后台仓库的创建

    • gitee上新建仓库meiduo_admin
    • 给meiduo_admin添加dev分支
    • 复制仓库的SSH
    • 在本地终端中对gitee上新建的仓库进行克隆git clone SSH
    • 在项目目录下,将dev分支克隆到本地git checkout -b dev origin/dev
    • 在本地创建项目分支并切换到项目分支git checkout -b 项目分支名
    • 将后台项目文件夹复制到项目目录下,再将项目文件推送到gitee
      git add ./
      git cimmit -m '说明信息'
      git push
      
  • 前端运行

    • 在前端项目文件下,执行npm run dev,开启前端服务,前端为8080端口 或者在vs code 中打开项目,运行npm run dev
    • 在浏览器进行前端访问
  • 后端运行

    • 在Pycharm中打开后台项目并配置Django虚拟环境
    • 在version control中添加git
    ctrl + k:commit
    ctrl + shift + k:push
    
    • 在终端中导入项目数据库mysql -uroot -p < *.sql
    • 在pycharm中连接数据库
    • pycharm中运行后台项目,后台端口为8000
    • 编辑运行,runserver
    • 开启redis服务
    • 在浏览器进行后端访问

美多后台登录

  • 后台项目架构
    -开发模式:前后端分离
    • 前端框架:VUE
    • 后端框架:Django REST framework
    • 功能部分:管理员登陆,数据统计,用户管理,商品管理,订单管理,权限管理
    • 主要技术:JWT用户认证,CORS跨域
  • 项目搭建:在原有项目基础上创建一个meiduo_admin的子应用,在子应用中完成后台的所有功能
    • python manage.py startapp meiduo_admin,注意manage.py所在的相对路径, 将apps右击指定为导包路径
    • 将子应用和rest_framework注册到配置文件中
  • 登录
    在后台登录时,前端与后端服务的域名不同,需要先解决跨域问题,登录后的状态保持采用jwt
    • 跨域CORS
      -前端与后端分处不同的域名,涉及到跨域访问数据的问题,因为浏览器的同源策略,默认不支持两个不同域间相互访问数据,如果需要在两个域名间相互传递数据,就要为后端添加跨域访问的支持
      • 只用CORS解决后端对跨域访问的支持,使用django-cors-headers扩展
      • 安装pip install django-cors-headers
        -添加应用,在配置文件中注册'corsheaders'
      • 中间层设置,添加在第一位,'corsheaders.middleware.CorsMiddleware'
      • 添加白名单
        #添加白名单
        # CORS
        CORS_ORIGIN_WHITELIST = (
            'http://127.0.0.1:8080',
            'http://localhost:8080',
            'http://www.meiduo.site:8080',
            'http://127.0.0.1:8000'
        )
        CORS_ALLOW_CREDENTIALS = True  # 允许携带cookie
        
    • JWT
      • 在用户注册或登录后,为了记录用户的登录状态,创建用户身份认证的凭证。这里不再使用Session认证机制,而使用Json Web Token认证机制

      • 浏览器在访问后端时,先发送options询问请求,进行跨域验证,后端允许访问并返回200,浏览器再发送post请求,与后端进行数据访问

      • session认证的三个缺点

        • session存储在Redis中,随着认证用户的增多,服务器开销增大
        • 扩展性问题:用户认证记录被保存在一台服务器内存中,再次访问,必须请求这台服务器,才能被授权,用户状态保持可能会失效,限制了负载均衡
        • CSRF:浏览器只要发送请求,就会携带cookie,基于cookie用来用户识别,一旦被截获,用户很容易受到跨站请求伪造的攻击
      • 基于token的认证机制,针对session认证的三个缺点

        • token存储在浏览器的storage中,对后端服务器开销无影响
        • 扩展性:解密token的的代码相同,不影响服务器扩展
        • 安全:同域名的前端js代码才能提取token
      • JWT构成,三部分信息构成,三段信息文本由.连接构成JWT字符串

        • 第一部分为头部(header),含声明类型(jwt)和加密算法(sha256)

          {
            'typ': 'JWT',
            'alg': 'HS256'
          }
          再对头部进行base64加密,构成第一部分
          
        • 第二部分为载荷(payload),存放有效信息,有效信息包含三部分

          • 标准中注册的声明

            iss: jwt签发者
            sub: jwt所面向的用户
            aud: 接收jwt的一方
            exp: jwt的过期时间,这个过期时间必须要大于签发时间
            nbf: 定义在什么时间之前,该jwt都是不可用的.
            iat: jwt的签发时间
            jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避    重放攻击。
            
          • 公共的声明:公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密

          • 私有的声明:私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息

          {
            "sub": "1234567890",
            "name": "John Doe",
            "admin": true
          }
          再对其进行base64加密,构成第二部分
          
        • 第三部分为签证(signature),这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分
          -secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了

          • header (base64后的)
          • payload (base64后的)
          • secret
        • 服务器验证token的流程


          jwt.png
      • 总结

        • 因为json通用,JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用
        • 有了payload部分,JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息
        • 便于传输,jwt的构成非常简单,字节占用很小,非常便于传输的
        • 不需要在服务端保存会话信息, 所以它易于应用的扩展
        • 安全相关
          • 不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分
          • 保护好secret私钥,该私钥非常重要
          • 如果可以,请使用https协议
      • JWT使用

        • 安装配置
          pip install djangorestframework-jwt
          
          REST_FRAMEWORK = {
              'DEFAULT_AUTHENTICATION_CLASSES': (
                  'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
                  'rest_framework.authentication.SessionAuthentication',
                  'rest_framework.authentication.BasicAuthentication',
              ),
          }
          JWT_AUTH = {          
              'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
          }   #指明token的有效期
          
        • 账号登录
          • 业务说明:验证用户名和密码,验证成功后,为用户签发JWT,前端将签发的JWT保存下来

          • 后端接口设计

            • 请求方式和路径:POST meiduo_admin/authorizations/
            • 请求参数:JSON或表单(username和pwd)
            • 返回数据:JSON(username,id和token)
          • 后端实现,重写jwt_response_payload_handler方法,因为默认的返回值只有token

            #总路由配置
            url('^meiduo_admin/', include('meiduo_admin.urls')),
            #子路由配置
            url(r'^authorizations/$', obtain_jwt_token)
            #重写jwt_response_payload_handler方法
            def jwt_response_payload_handler(token, user=None, request=None):
                #自定义jwt认证成功返回数据
                return {
                    'id': user.id,
                    'username': user.username,
                    'token': token
                }
            #配置重写的方法
            JWT_AUTH = {
                'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
                'JWT_RESPONSE_PAYLOAD_HANDLER': 'meiduo_admin.utils.jwt_response_payload_handler',
            }
            #对于管理员用户和普通用户登录进行路径判断
            from django.contrib.auth.backends import ModelBackend
            from django.http import HttpRequest
            import re
            from users.models import User
            
            
            class MeiduoModelBackend(ModelBackend):
                def authenticate(self, request, username=None, password=None, **kwargs):
                    if request is None:   #JWT中未返回request,用来判断是管理员用户
                        try:
                            user = User.objects.get(username=username, is_staff=True)
                        except:
                            return None
                        # 判断密码
                        if user.check_password(password):
                            return user
            
                    else:
                        # 变量username的值,可以是用户名,也可以是手机号,需要判断,再查询
                        try:
                            user = User.objects.get(username=username)
                        except:
                            # 如果未查到数据,则返回None,用于后续判断
                            try:
                                user = User.objects.get(mobile=username)
                            except:
                                return None
                                # return None
                        # 判断密码
                        if user.check_password(password):
                            return user
                        else:
                            return None
            
          • 前端保存token,浏览器的本地提供sessionStorage(浏览器关闭即失效)和 localStorage(长期有效) 两种

你可能感兴趣的:(美多后台准备和登录)