权限和jwt(json.web.token)

一、权限

权限概念:

在实际开发中,项目中都有后台运营站点,运营站点里面会存在多个管理员,
那么不同的管理员会具备不同的任务和能力,那么要实现这样的管理员功能,那么就需要了解权限机制了。
在开发中, 一般现在流行的权限机制有2种: RBAC[基于角色(分组)的权限认证]和Auth认证[授权认证机制]
我们使用的django框架内部集成的Auth模块实际上就是基于RBAC开发出来的权限认证机制。

  • RBAC: 用户表 | 角色表 | 权限表
  • Auth: 规则表 | 用户表 | 用户明细表

在开发中,实现RBAC权限机制,一般有2种不同的设计方式:分别是3表和5表,django扩展出6表
基于角色的权限访问控制(Role-Based Access Control) 

三表、五表、六表模型如下: 

其实我们通过跨表查询完全能够实现查询,但是跨的表太多,会耗费数据库的资源,影响代码的执行效率,因此需要这种多表关系。  

二、认证的发展(cookie,session,jwt)

基础的cookie认证:

 

 利用集群redis的认证:

jwt认证:

三、jwt

3.1、简介

1、组成: 
header.payload.signature     头.载荷.签名

2、距离:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Im93ZW4iLCJleHAiOjE1NTgzMDM1NDR9.4j5QypLwufjpqoScwUB9LYiuhYcTw1y4dPrvnv7DUyo

3:介绍:

  • header:一般存放如何处理token的方式:加密的算法、是否有签名等,头是固定的。
  • payload:数据的主体部分:用户信息、发行者、过期时间等
  • signature:签名:将header、payload再结合密码盐整体处理一下,以便下次认证。

 3.2、工作原理

1)  jwt = base64(头部).base64(载荷).hash256(base64(头部).base(载荷).密钥)
2)  base64是可逆的算法、hash256是不可逆的算法
3)  密钥是固定的字符串,保存在服务器(django的在配置文件中,有一个SCRECT_KEY)

 3.3、drf-jwt

官网:https://github.com/jpadilla/django-rest-framework-jwt

安装:pip install djangorestframework-jwt

使用:

from django.urls import path
# 返回token
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
    path('login/', obtain_jwt_token),
]

我在路由里配了这个,jwt就会签发token。可以用postman测试接口。

上面只是自己返回了一个token,如果我们想按照restful规范,那就需要自定义返回的接口数据,需要重写jwt_response_payload_handler()方法

首先在配置文件中配置,自定义认证结果就是你的认证方法所在的文件:

import datetime
JWT_AUTH = {
    # 过期时间
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
    # 自定义认证结果:见下方序列化user和自定义response
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'user.utils.jwt_response_payload_handler',  
}

 在返回数据时,需要序列化数据:

from rest_framework import serializers
from .models import User
class UserModelSerializer(serializers.ModelSerializer):
    """用户信息序列化器"""
    class Meta:
        model = User
        fields = ["username", "mobile"]

再来看自定义签发token的返回结果:

from .serializers import UserModelSerializer
# rest_framework_jwt中的jwt_response_payload_handler用于返回token的,我们可以重写这个方法自定义序列化返回的信息
def jwt_response_payload_handler(token, user=None, request=None):
    return {
        'token': token,
        # 序列化后的数据
        'user': UserModelSerializer(user).data
    }
    # restful 规范
    # return {
    #     'status': 0,
    #     'msg': 'OK',
    #     'data': {
    #         'token': token,
    #         'username': user.username
    #     }
    # }

现在已经可以返回token了,我们的某些功能需要登录后才能访问,那么就该做一个登录的认证。当用户访问的时候,判断用户的token是否有,是否合法,是否过期等。

# 由于源码的认证有缺陷,因此需要我们自定义
# -*- coding: utf-8 -*-
# @Author  : Sunhaojie
# @Time    : 2019/7/18 20:51
import jwt
from rest_framework_jwt.settings import api_settings
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework_jwt.authentication import get_authorization_header
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication

class JSONWebTokenAuthentication(BaseJSONWebTokenAuthentication):
    def authenticate(self, request):
        # 采用drf获取token字段  HTTP_AUTHORIZATION - Authorization
        token = get_authorization_header(request)
        # 自定义从请求头的某个字段得到认证的token
        # token = request.META.get('HTTP_TOKEN', b'')
        if not token:
            raise AuthenticationFailed('Authorization字段是必需的')
        # 可以添加反爬措施,原功能是token有前缀
        jwt_value_list = token.split()
        if len(jwt_value_list) != 2:
            raise AuthenticationFailed('传值长了或短了')
        # JWT_AUTH_HEADER_PREFIX在dev.py中配置了,所以找自己的
        if jwt_value_list[0].lower().decode() != api_settings.JWT_AUTH_HEADER_PREFIX.lower():
            raise AuthenticationFailed('没头,认证失败')
        token = jwt_value_list[1]
        # drf-jwt认证校验算法
        try:
            payload = jwt_decode_handler(token)
        except jwt.ExpiredSignature:
            raise AuthenticationFailed('签名过期,认证失败')
        except jwt.InvalidTokenError:
            raise AuthenticationFailed('非法用户')
        user = self.authenticate_credentials(payload)
        # 将认证结果丢给该drf:通过认证的request.user拿到用户对象,request.auth拿到token,是bytes类型
        return user, token

 在配置文件可以配置全局启用;

REST_FRAMEWORK = {
    # 认证模块
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'user.authentications.JSONWebTokenAuthentication',
    ),
}

在要访问的视图类可以局部启用或禁用:

# 局部禁用
authentication_classes = []

# 局部启用
from user.authentications import JSONWebTokenAuthentication
authentication_classes = [JSONWebTokenAuthentication]

有些时候,我们会有用户名,邮箱,手机号登录的需求。可以使用正则匹配,继承ModelBackend类,重写authenticate方法,在内部返回user,交给前面的jwt去签发token

# 有时候需要 用户名/邮箱/电话号码 都可以登录,就该用正则进行匹配
from django.contrib.auth.backends import ModelBackend
from .models import User
import re
class JWTModelBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        """
        :param request:
        :param username: 前台传入的用户名
        :param password: 前台传入的密码
        :param kwargs:
        :return:
        """
        try:
            if re.match(r'^1[3-9]\d{9}$', username):
                user = User.objects.get(mobile=username)
            elif re.match(r'.*@.*', username):
                user = User.objects.get(email=username)
            else:
                user = User.objects.get(username=username)
        except User.DoesNotExist:
            return None  # 认证失败就返回None即可,jwt就无法删除token
        # 用户存在,密码校验通过,是活着的用户 is_active字段为1
        if user and user.check_password(password) and self.user_can_authenticate(user):
            return user  # 认证通过返回用户,交给jwt生成token

将我们的文件位置配置到settings配置文件中:

AUTHENTICATION_BACKENDS = ['user.utils.JWTModelBackend']

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值