基于自定义表编写认证类、django-jwt源码分析、权限介绍、simpleui的使用

扩写auth的user表

[1 基于自定义表编写认证类](#1 基于自定义表编写认证类)
[1.1 认证类](#1.1 认证类)
[1.2 登录接口](#1.2 登录接口)
[1.3 路由](#1.3 路由)
[1.4 视图类](#1.4 视图类)

[2 django-jwt源码分析](#2 django-jwt源码分析)
[2.1 签发](#2.1 签发)
[2.2 认证](#2.2 认证)

[3 权限介绍](#3 权限介绍)

[4 simpleui的使用](#4 simpleui的使用)
[4.1 安装simpleui](#4.1 安装simpleui)
[4.2 修改django后台模块默认的模板](#4.2 修改django后台模块默认的模板)
[4.3 字符集及时区设置](#4.3 字符集及时区设置)

扩写auth的user表范

python 复制代码
# 1 基于自定义的用户表,签发token
	-1 前端(postman,web,app,小程序)---》发送http请求,携带用户的用户名密码---》到后端
    -2 后端request.data 取出前端传入的数据--》字典---》取出用户名,密码
    -3 拿着用户名密码去数据库查询,有没有这个人
    -4 如果有,说明 登录成功
    -5 签发token:1 通过当前用户得到payload(自己生成荷载)  2 通过荷载得到token
    -6 返回给前端{code,msg,token,username,icon...}
    -7 如果查不到,就返回用户名密码错误
    
    
# 2 基于auth的user表,签发token,多方式登录
	-1 扩写auth的user表
    	-自己写一个表,继承AbstractUser:username,password,is_superuser
        	-password:加密的---》如何加密的
            -同样的明文---》加密得到的密文是不一样的
            -AuthUser.objects.create_user(username=xx,password=123)
            -AuthUser.objects.create_superuser(username=xx,password=123)
            -pbkdf2_sha256$260000$e3ZRrKYrR0kSEMNAKPOJl4$2dzB06dDZEe4ZSTy7DtWGqvCwY
            加密方式        有效时间  盐                     加密后的结果
            
            -自定义密码加密---》同样的密码,加密结果不一样
        -项目一开始,就要处理完,再迁移,如果先迁移,就会出问题
        -配置文件中配置
        
    -2 多方式登录
    	-前端接收 用户名,手机号,邮箱的这个字段是同一个
        	{username:用户名/手机号/邮箱,password:密码}
        -正则匹配---》如果是手机号,就查 手机号+密码,如果是邮箱 邮箱+密码
        -check_password
        -签发token
        ------所有逻辑都写在视图类中了-----
        
        
        -逻辑写到序列化类中
        	-如果继承了ModelSerializer,必须重写username
            -重写了全局钩子:在全局钩子中完成校验,如果校验不过,抛异常
        	-写了获取用户方法
            -写了签发token方法
            -把token和用户名放到了self.context中了
      

1 基于自定义表编写认证类

python 复制代码
### 补充翻译函数,只要做了国际化,就会显示当前国家的语言
from django.utils.translation import gettext_lazy as _
msg = _('Signature has expired.') # _是个函数的别名,这个函数是翻译函数,只要做了国际化,它就是中文

1.1 认证类

python 复制代码
from rest_framework_jwt.authentication import JSONWebTokenAuthentication

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
import jwt
from rest_framework_jwt.settings import api_settings
from .models import User

jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
from django.utils.translation import ugettext as _

class JWTLoginAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # 1 取出用户传入的token----》带在哪里?后端规定的---》规定放在请求头 token=asfas.asfdas.asdfa
        token = request.META.get('HTTP_TOKEN')
        # 2 验证token-->django-jwt 提供了一个校验的方法---》 jwt_decode_handler 通过传入token传,校验,校验通过返回payload,校验失败抛异常
        try:
            payload = jwt_decode_handler(token)
            # 通过payload 得到当前用户
            # 认证类,配好后,只要有认证的,都会走这里---》每次走这里都会查询一次数据库
            # 做个优化?
            # 1 自己根据payload数据,创建一个用户,有的参数没传,会使用默认值,跟我真实用户还是有区别的
            # user=User(id=payload.get('user_id'),username=payload.get('username'))
            # 2 直接返回用户id,后续 的request.user 都是用户id,如果真正要用的时候,再查
            user = User.objects.get(pk=payload.get('user_id'))
        except jwt.ExpiredSignature as e:
            # msg = _('Signature has expired.') # _是个函数的别名,这个函数是翻译函数,只要做了国际化,它就是中文
            msg = '签名过期'
            raise AuthenticationFailed(msg)
        except jwt.DecodeError:
            msg = 'token错误'
            raise AuthenticationFailed(msg)
        except jwt.InvalidTokenError:
            raise AuthenticationFailed('不合法的token')
        except Exception:
            raise AuthenticationFailed('未知错误,请联系系统管理员')

        return (user, token)  # 后续的request.user 都是用户id,如果真正要用的时候,再查

    
    
 '''
 每次访问需要登录的接口,都会去查数据库
 所以咱们可以做优化
 	1 自己生成一个user对象
 	2 直接返回user_id,以后用的时候,再查数据库
 
 '''

1.2 登录接口

python 复制代码
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
from rest_framework.viewsets import ViewSet
from rest_framework.decorators import action
# ### ### ### ### ##1 自己定义的用户表,签发token# ### ### ### ### ### ##
class UserView(ViewSet):
    @action(methods=['POST'],detail=False)
    def login(self, request):
        username = request.data.get('username')
        password = request.data.get('password')
        user = User.objects.filter(username=username, password=password).first()
        if user:
            # 签发token
            # 1 通过user生成payload---》jwt 提供一个方法(username),传入user,返回payload
            payload = jwt_payload_handler(user)
            # payload={'username':'asdfasdf','exp':1694401763}
            # 2 生成token---》jwt提供了方法,把payload放入--》token
            token = jwt_encode_handler(payload)
            return Response({'code': 101, 'msg': '登录成功', 'token': token, 'username': user.username})
        else:
            return Response({'code': 101, 'msg': '用户名或密码错误'})

1.3 路由

python 复制代码
router.register('user', UserView, 'user') # 127.0.0.1:8000/user/login  的post请求
router.register('books', BooView, 'books') # 127.0.0.1:8000/user/login  的post请求

1.4 视图类

python 复制代码
from rest_framework.viewsets import GenericViewSet

# 登录后才能访问
from .auth import JWTLoginAuthentication
class BooView(GenericViewSet):
    authentication_classes = [JWTLoginAuthentication]
    def list(self, request):
        return Response("你看到我了")

2 django-jwt源码分析

2.1 签发

python 复制代码
# 1 入口是:obtain_jwt_token---》from rest_framework_jwt.views import obtain_jwt_token
# 2 obtain_jwt_token本质是:ObtainJSONWebToken.as_view()
# 3 看ObtainJSONWebToken视图类
	-前端post请求,提交了用户名密码,就能签发token
    -去ObtainJSONWebToken中找---》post
    
# 4 发现没有
    class ObtainJSONWebToken(JSONWebTokenAPIView):
        serializer_class = JSONWebTokenSerializer  #就是它
        
# 5 去父类中找JSONWebTokenAPIView
	
class JSONWebTokenAPIView(APIView):
    def post(self, request, *args, **kwargs):
        #serializer = self.get_serializer(data=request.data) # 配置的序列化类
        serializer =JSONWebTokenSerializer(data=request.data)
        if serializer.is_valid():  # 字段自己,局部,全局
            user = serializer.object.get('user')
            token = serializer.object.get('token')
            # 定制返回格式--》如果咱们写了,走自己的
            response_data = jwt_response_payload_handler(token, user, request)
            response = Response(response_data)
            return response
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    
 # 6 校验用户名密码和生成token,写在了序列化类的--》全局钩子validate中--》JSONWebTokenSerializer

class JSONWebTokenSerializer(Serializer):
    def validate(self, attrs):
        credentials = {
            'username': attrs.get("username"),
            'password': attrs.get('password')
        }
        if all(credentials.values()): # credentials字典的value必须不能为空
            # authenticate django的auth,通过用户名和明文密码校验用户是否存的函数:authenticate
            # User.objects.filter(username='lqz',password='123').first()
            # user = authenticate(username='lqz',password='123')
            user = authenticate(**credentials)
            if user: # 登录成功,前token
                if not user.is_active: # 判断用户是否锁定
                    msg = _('User account is disabled.')
                    raise serializers.ValidationError(msg)

                payload = jwt_payload_handler(user)

                return {
                    'token': jwt_encode_handler(payload),
                    'user': user
                }
            else:
                msg = _('Unable to log in with provided credentials.')
                raise serializers.ValidationError(msg)
        else:
            msg = _('Must include "{username_field}" and "password".')
            msg = msg.format(username_field=self.username_field)
            raise serializers.ValidationError(msg)
            
            
            
 # 总结
	1 前端携带用户名密码到后端---》执行后端你的post方法---》后端生成一个序列化类的对象---》执行校验---》走了全局钩子--》全局钩子中通过用户名密码获取用户(如果获取不到抛异常)---》获取到后签发token---》签发完返回----》在视图类中---》取出来,返回给前端

2.2 认证

python 复制代码
# 1 从哪开始看---》认证类JSONWebTokenAuthentication
# 2 JSONWebTokenAuthentication 的 
class JSONWebTokenAuthentication(BaseJSONWebTokenAuthentication):
    def get_jwt_value(self, request):
        auth = get_authorization_header(request).split()
        auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower()

        if not auth:
            if api_settings.JWT_AUTH_COOKIE:
                return request.COOKIES.get(api_settings.JWT_AUTH_COOKIE)
            return None

        if smart_text(auth[0].lower()) != auth_header_prefix:
            return None

        if len(auth) == 1:
            msg = _('Invalid Authorization header. No credentials provided.')
            raise exceptions.AuthenticationFailed(msg)
        elif len(auth) > 2:
            msg = _('Invalid Authorization header. Credentials string '
                    'should not contain spaces.')
            raise exceptions.AuthenticationFailed(msg)

        return auth[1]
    
    
# 2 父类中BaseJSONWebTokenAuthentication的authenticate
    def authenticate(self, request):
        # 拿出前端传入的token,可能前端没传,就是None
        jwt_value = self.get_jwt_value(request)
        if jwt_value is None: # None就不管了,认证类继续往后走,相当于没有认证,于是咱们才需要权限类
            return None

        try:
            payload = jwt_decode_handler(jwt_value)
        except jwt.ExpiredSignature:
            msg = _('Signature has expired.')
            raise exceptions.AuthenticationFailed(msg)
        except jwt.DecodeError:
            msg = _('Error decoding signature.')
            raise exceptions.AuthenticationFailed(msg)
        except jwt.InvalidTokenError:
            raise exceptions.AuthenticationFailed()

        user = self.authenticate_credentials(payload)

        return (user, jwt_value)
    
    
    
 # 总结:逻辑,跟咱们自定义的一模一样
	-获取token复杂一点,有个前端

3 权限介绍

python 复制代码
# 所有项目都会有权限控制
# https://www.cnblogs.com/liuqingzheng/articles/16972464.html


#ACL(Access Control List,访问控制列表)---》针对互联网用户,多半是这个
	将用户与权限对接(多对多)
       张三   [发视频,点赞,评论]
       李四   [看视频]
    
    
    
# RBAC(Role-Based Access Control,基于角色的访问控制)--》公司内部项目
	将用户与角色对接,然后角色与对象的权限对接。
    django的admin+auth就是使用了这套认证机制
    
# ABAC(Attribute-Based Access Control,基于属性的访问控制)
    ABAC(Attribute-Based Access Control,基于属性的访问控制),又称为PBAC(Policy-Based Access Control,基于策略的访问控制),CBAC(Claims-Based Access Control,基于声明的访问控制)。

    传统的ACL、RBAC的架构是{subject,action,object},
    而ABAC的架构是{subject,action,object,contextual}且为他们添加了parameter(参数)。

    subject属性:比如用户的年龄、部门、角色、威望、积分等主题属性。

    action属性:比如查看、读取、编辑、删除等行为属性。

    object属性:比如银行账户、文章、评论等对象或资源属性。

    contextual属性:比如时段、IP位置、天气等环境属性
    
    
    
# python写公司内部项目比较多,使用rbac控制居多
	RBAC  是基于角色的访问控制(Role-Based Access Control )在 RBAC  中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。
    
    
    
# rbac如何设计
	-用户表---》一堆用户:张三,李四
    -角色表(group组)---》用户的角色:后勤部,开发部,总裁办。。。
    -权限表-----》放了一堆权限:发电脑,提交代码,删除代码,发工资
    -用户和角色多对多:一个用户属于多个角色,一个角色属于多个用户
    -角色 和权限多对多:一个角色有多个权限,一个权限属于多个角色
    --------rabc----通过5张表可以实现
    -django为了更细粒度划分---》多了一张表,用户和权限多对多
    

    
#演示django的rbac权限控制

4 simpleui的使用

4.1安装simpleui

python 复制代码
# 指定清华源安装simpleui
pip install django-simpleui -i https://pypi.tuna.tsinghua.edu.cn/simple

# 公司内部,做公司内的项目需要使用这套权限控制
## 方案一:使用django-admin写
# 有的公司,不怎么写前端,直接使用django的admin,快速写出一套具有权限管理的系统
# django admin的界面不好看:第三方美化--》simpleui

## 方案二:自己写,前端使用vue,后端使用django,做公司内部的项目
	-第三方开源的权限控制 项目
    	-python界:django-vue-admin   7期学长写的
        -java界:若依
        -go界:gin-vue-admin

        
#  django admin的美化: simpleui
	

4.2 修改django后台模块默认的模板

python 复制代码
# 修改project的setting文件,在INSTALLED_APPS 首行引入simple应用
  INSTALLED_APPS = [
      'simpleui',
  ]

4.3 字符集及时区设置

python 复制代码
# 修改project的setting文件
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = False # 这里务必调整为False,否则时区设置无效
相关推荐
柏油3 分钟前
MySQL InnoDB 行锁
数据库·后端·mysql
咖啡调调。5 分钟前
使用Django框架表单
后端·python·django
BO_S__9 分钟前
python调用ffmpeg对截取视频片段,可批量处理
python·ffmpeg·音视频
白泽talk10 分钟前
2个小时1w字| React & Golang 全栈微服务实战
前端·后端·微服务
摆烂工程师19 分钟前
全网最详细的5分钟快速申请一个国际 “edu教育邮箱” 的保姆级教程!
前端·后端·程序员
一只叫煤球的猫34 分钟前
你真的会用 return 吗?—— 11个值得借鉴的 return 写法
java·后端·代码规范
就叫飞六吧36 分钟前
如何判断你的PyTorch是GPU版还是CPU版?
人工智能·pytorch·python
Asthenia04121 小时前
HTTP调用超时与重试问题分析
后端
颇有几分姿色1 小时前
Spring Boot 读取配置文件的几种方式
java·spring boot·后端
AntBlack1 小时前
别说了别说了 ,Trae 已经在不停优化迭代了
前端·人工智能·后端