RBAC认证规则和JWT认证方式
RBAC介绍
RBAC是一种认证规则 RBAC:Role-Based Access Control –>基于角色的访问控制
公司内部系统:crm,客户管理系统;oa系统,自动化运维项目
原理:权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便
Django的auth组件采用的规则及时RBAC
实现RBAC:
- user表:存用户信息
- Permission表:存权限:发工资、招人
- Role/group表:存角色:市场、销售、运营、开发
- 用户和角色,多对多的中间表
- 权限和角色,多得多的中间表
- 用户和权限,多对多的中间表
Django中auth表:
- auth_group 角色表
- auth_user 用户表
- auth_permission 权限表
- auth_user_groups 用户和角色多对多中间表
- auth_group_permissions 角色和权限多对多中间表
- auth_user_user_permissions 用户和权限多对多中间表
Django有后台管理admin,自带RBAC,有的公司基于admin做二次开发(xadmin,simple-ui)
有的公司自己基于前后端分离实现RBAC:https://github.com/liqianglog/django-vue-admin
前后端混合的后台前端模板 :X-admin:lay-ui+jq实现的纯静态页面(html,css,js),帮助我们快速开发后台管理系统
智慧大屏:https://gitee.com/kevin_chou/dataVIS.git
JWT
JWT介绍
jwt是什么:Json web token (JWT),一种前后端的认证方式
JWT的构成:头.荷载.签名
头(header):认证方式,加密方式,公司名字...
{
\'typ\': \'JWT\',
\'alg\': \'HS256\'
}
荷载(payload):用户信息,过期时间,签发时间...
{
"userid": "2",
"name": "John Doe",
"exp": 1214356
}
签名:将头和荷载通过某种方式加密得到的一串字符
签发和校验
签发:用户登录成功,构造token的头和荷载,使用设置好的加密方式对头和荷载进行加密,得到签名token,将三个部分进行拼接(使用base64编码)
校验:用户携带token,来到后端,把头和荷载再使用同样的加密方式加密,得到新的签名,比较新签名和旧签名是否一致,如果一致,说明该token可以信任,解析出当前用户,继续往后走
token例子:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
base64编码
### 编码
import base64
import json
dic={\'name\':\'lqz\',\'id\':1}
user_info_str=json.dumps(dic)
# res=base64.b64encode(bytes(user_info_str,encoding=\'utf-8\'))
res=base64.b64encode(user_info_str.encode(\'utf-8\'))
print(res) # eyJuYW1lIjogImxxeiIsICJpZCI6IDF9
### 解码
res=base64.b64decode(\'TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ=\'.encode(\'utf-8\'))
print(res)
### 注意:base64长度是4的倍数,如果不足,需要用 = 补齐
JWT快速使用
# 签发和验证
# 使用第三方:
-djangorestframework-jwt:好久不维护,还能用
-djangorestframework-simplejwt:最新的,https://django-rest-framework-simplejwt.readthedocs.io/en/latest/getting_started.html
签发
# 签发,只需要再路由中加入,向这个地址发送post请求,携带用户名密码,就可以签发token
# 内部使用的是,auth 的user表
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
path(\'login/\',obtain_jwt_token)
]
# 只需要再前端向 /login/地址,发送post请求,携带用户名密码,就可以签发token
# 认证
认证
# 需要在视图类中配置上认证类和权限类两个
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated
class BookView(ViewSetMixin,ListAPIView):
authentication_classes = [JSONWebTokenAuthentication,]
permission_classes = [IsAuthenticated,]
### 前端访问,需要在请求头中加入,如果不携带,或者篡改了,就认证不通过
Authorization:jwt eyJ0eXAiOiJKV1QiLChbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyLCJ1c2VybmFtZSI6InJvb3QiLCJleHAiOjE2MjU3Mjk3MDAsImVtYWlsIjoiM0BxcS5jb20ifQ.V6X_4wdBvrrRSPOuUf8z5gENB1kJyGimxiLBEjbiQGY
自定制返回格式
# 自己写一个返回的函数,模板使用rest_framework_jwt.utils.jwt_response_payload_handler
def jwt_response_payload_handler(token, user=None, request=None):
return {
\'code\':100,
\'msg\':\'登录成功!\',
\'user\': user.username,
\'token\': token,
}
# 配置文件中
JWT_AUTH = {
# token的过期时间
\'JWT_EXPIRATION_DELTA\': datetime.timedelta(days=7),
# 更改使用的返回函数
\'JWT_RESPONSE_PAYLOAD_HANDLER\':
\'app01.utils.jwt_response_payload_handler\',
}
djangorestframework-jwt模块obtain_jwt_token源码分析
# obtain_jwt_token-->ObtainJSONWebToken-->JSONWebTokenAPIView的post方法
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
user = serializer.object.get(\'user\') or request.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)
# .is_valid()触发了validate(self, attrs)方法
# obtain_jwt_token-->ObtainJSONWebToken-->JSONWebTokenSerializer序列化类-->validate(self, attrs)钩子函数
def validate(self, attrs):
credentials = {
self.username_field: attrs.get(self.username_field),
\'password\': attrs.get(\'password\')
}
if all(credentials.values()):
# 根据用户名密码去auth的user表校验,是否存在
user = authenticate(**credentials)
if user:
if not user.is_active:
msg = _(\'User account is disabled.\')
raise serializers.ValidationError(msg)
payload = jwt_payload_handler(user)
# 生成payload
return {
\'token\': jwt_encode_handler(payload),# 通过payload生成token
\'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)