美多后台准备
-
后台仓库的创建
- 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的流程
-
-
总结
- 因为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(长期有效) 两种
- 安装配置
- 跨域CORS