昨日回顾
基于自定义用户表签发token
1、前端(postman、web、appp、小程序)发送http请求,携带用户名和密码,通过中间件到达后端
2、后端request.data取出用户名和密码
3、拿着用户名和密码去数据库中查询,有没有
4、如果有就说明登陆成功
5、签发token:通过当前用户得到payload(自动生成荷载),通过荷载得到token
6、返回给前端(自定义)
7、查不到,就返回错误信息
基于auth的user表,签发token,多方式登录
1、扩写auth的user表:在配置文件中注册项目名.表名
2、在models文件中自己写一个表,继承Abstrcactuser
3、多方式登录
前端接收 用户名,手机号,邮箱的这个字段是同一个
{username:用户名/手机号/邮箱,password:密码}
正则匹配,如果是手机号,就查 手机号+密码,如果是邮箱 邮箱+密码
check_password
签发token
所有逻辑都写在视图类中
逻辑写到序列化类中
如果继承了ModelSerializer,必须重写username
重写了全局钩子:在全局钩子中完成校验,如果校验不过,抛异常
写了获取用户方法
写了签发token方法
把token和用户名放到了self.context中
基于自定义表编写认证类
补充
翻译函数 只要做国际化处理,就会显示当前国家的语言 from django.utils.translation import gettext_lazy as _ msg = _('Signature has expired.') _是翻译函数的别名,只要做国际化处理,就会显示哪个国家的语言
认证类
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,以后用的时候,再查数据库
登录接口
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': '用户名或密码错误'})
路由
outer.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请求
视图类
from rest_framework.viewsets import GenericViewSet # 登录后才能访问 from .auth import JWTLoginAuthentication class BooView(GenericViewSet): authentication_classes = [JWTLoginAuthentication]#局部认证登录 def list(self, request): return Response("你看到我了")
django-jwt源码分析
签发源码分析
# 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)
签发总结
前端携带用户名密码到后端,执行后端你的post方法,后端生成一个序列化类的对象,执行校验,走了全局钩子,全局钩子中通过用户名密码获取用户(如果获取不到抛异常),获取到后签发token,签发完返回,在视图类中,取出来,返回给前端
认证源码分析
# 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)
权限介绍
# 所有项目都会有权限控制 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权限控制
simpleui的使用
公司内部,做公司内的项目需要使用这套权限控制 方案一:使用django-admin写 有的公司,不怎么写前端,直接使用django的admin,快速写出一套具有权限管理的系统 django admin的界面不好看:第三方美化》》》simpleui 方案二:自己写,前端使用vue,后端使用django,做公司内部的项目 第三方开源的权限控制 项目 python界:django-vue-admin 7期学长写的 java界:若依 go界:gin-vue-admin django admin的美化: simpleui
标签:username,jwt,token,user,day12,msg,payload,drf From: https://www.cnblogs.com/shanghaipudong/p/17696251.html