10.16 simpleui 集成监控大屏、restframework-jwt执行流程分析、jwt_xxx_handler
simpleui 集成监控大屏、restframework-jwt执行流程分析、jwt_xxx_handler
上节回顾
# 代码正常执行--->可以写一些类和方法--->正常代码在某个位置就会执行到我们写的类,方法
- 面向切面编程(AOP: 是oop的补充), spring框架使用了aop理念
- 装饰器:也是aop的实践
# 排序和过滤的源码分析
- 查询所有接口--->list--->queryset=self.filter_queryset(self.get_queryset())--->GenericAPIView的filter_queryset----》把配置在视图类上的所有过滤类一个个执行了一下返回了qs,把返回的qs序列化了
# jwt的认证类
- 写一个认证类,取出token,使用restframework-jwt提供的验证方法即可
- 拿到了payload--->要不要从数据库查出用户取决于需求
# ACL:访问控制列表
# RBAC:基于角色的访问控制
# RBAC+ACL:django的6个表
# ABAC:基于角色的访问控制+属性控制
# django的admin严格符合人ABC--->基于admin二次开发--->内部的管理系统
# 第三方美化:simpleui
# 分离版的rbac :vue+django
# simlieui的使用
一、simpleui 集成监控大屏
# 从gitee上找到开源的前端页面--->集成到项目中
https://gitee.com/lvyeyou/DaShuJuZhiDaPingZhanShi?_from=gitee_search
# 本质就是前后端混合的项目
二、restframework-jwt执行流程分析
# 签发的token,有过期额时间,过期时间是?
# 答:配置一般设为7天
自定义设置token的时间:
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
}
# 双token认证
- 用户正在app或者应用中操作,token突然过期,此时用户不得返回登录界面,重新进行一次登录,这种体验性不好,于是引入双token校验机制
- 实现原理:首次登录时服务端返回两个token,accessToken和refreshToken,accessToken过期时间比较短,refreshToken时间比较长,且每次使用后会刷新,每次刷新后的refreshToken都是不同
-- refreshToken假设7天,accessToken过期时间5分钟
-- 正常使用accessToken即可,如果accessToken过期了,重新发送请求,携带refreshToken过来,能正常返回,并且这次响应中又带了accessToken
# django中顶格写的代码,都会执行
1、签发
签发流程:本质就是登录接口--->【校验用户是否正确,如果正确签发token】写到了序列化类中,如果不正确返回错误
- obtain_jwt_token:核心代码--->ObtainJSONWebToken.as_view()
- ObtainJSONWebToken:视图类,实现了登录功能
class ObtainJSONWebToken(JSONWebTokenAPIView):
serializer_class = JSONWebTokenSerializer
ObtainJSONWebToken继承了JSONWebTokenAPIView
class JSONWebTokenAPIView(APIView):
# 局部禁用掉权限和认证
permission_classes = ()
authentication_classes = ()
def get_serializer_context(self):
return {
'request': self.request,
'view': self,
}
def get_serializer_class(self):
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__)
return self.serializer_class
def get_serializer(self, *args, **kwargs):
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
return serializer_class(*args, **kwargs)
# 登录就是post请求,执行post方法
def post(self, request, *args, **kwargs):
# JSONWebTokenSerializer实例化得到一个序列化类的对象,传入前端的值
serializer = self.get_serializer(data=request.data)
# 校验前端传入的数据是否合法
if serializer.is_valid():
# 字段自己的规则、局部钩子,全局钩子(序列化类的validate方法)
# 获取当前登录用户和签发token是在序列化类中完成的【以前是在视图类中完成的】
# 序列化类里面返回的return值传给了serializer.object
# 从序列化类对象中取出了当前登录用户
user = serializer.object.get('user') or request.user
# 从序列化类对象中取出了token
token = serializer.object.get('token')
# 自定义过:返回格式
response_data = jwt_response_payload_handler(token, user, request)
response = Response(response_data)
if api_settings.JWT_AUTH_COOKIE:
expiration = (datetime.utcnow() +
api_settings.JWT_EXPIRATION_DELTA)
response.set_cookie(api_settings.JWT_AUTH_COOKIE,
token,
expires=expiration,
httponly=True)
return response
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
self.get_serializer(data=request.data)的get_serializer方法来自于JSONWebTokenSerializer类:
class JSONWebTokenSerializer(Serializer):
def validate(self, attrs): # attrs校验后的数据
credentials = {
self.username_field: attrs.get(self.username_field),
'password': attrs.get('password')
}
if all(credentials.values()):
# auth的校验用户名和密码是否正确
user = authenticate(**credentials)
if user:
if not user.is_active:
msg = _('User account is disabled.')
raise serializers.ValidationError(msg)
# 通过用户获得 payload:{}
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)
2、认证
class JSONWebTokenAuthentication(BaseJSONWebTokenAuthentication):
www_authenticate_realm = 'api'
def get_jwt_value(self, request):
# 获取用户传进来的token
# get_authorization_header(request)根据请求头中HTTP_AUTHORIZATION,取出token
# jwt token码
# auth=['jwt','真正的token']
auth = get_authorization_header(request).split()
auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower()
if not auth: # 如果token不存在,先去cookie里面找下,不在直接返回None
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
# 判断auth切分后的列表的大小
if len(auth) == 1: # 如果auth列表只一个值说明少了
msg = _('Invalid Authorization header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2: # 如果auth列表只一个值说明多了
msg = _('Invalid Authorization header. Credentials string '
'should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
return auth[1] # 返回token
def authenticate_header(self, request):
return '{0} realm="{1}"'.format(api_settings.JWT_AUTH_HEADER_PREFIX, self.www_authenticate_realm)
父类中:BaseJSONWebTokenAuthentication--->authenticate--->class BaseJSONWebTokenAuthentication(BaseAuthentication);
我们需要重写的是authenticate,去父类中找。JSONWebTokenAuthentication继承了BaseJSONWebTokenAuthentication:
class BaseJSONWebTokenAuthentication(BaseAuthentication):
def authenticate(self, request):
# jwt_value前端传入的token
jwt_value = self.get_jwt_value(request)
# 判断前端有没有传入token
if jwt_value is None:
return None # 没有带token,认证类也能过,但是权限类会根据是否user判断能否通过权限类,所以使用jwt提供的认证类,必须配合权限类
try:
# jwt_decode_handler去BaseJSONWebTokenAuthentication找
payload = jwt_decode_handler(jwt_value) # 验证token,token合法,返回payload
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) # 通过payload得到当前登录用户
return (user, jwt_value) # 后期的request.user就是当前登录用户
注意:只要带了token,request.user就有值,如果没有带token,会继续往后走
get_jwt_value里面的get_authorization_header(request)(JSONWebTokenAuthentication继承BaseJSONWebTokenAuthentication)
def get_authorization_header(request):
auth = request.META.get('HTTP_AUTHORIZATION', b'')
if isinstance(auth, str):
# Work around django test client oddness
auth = auth.encode(HTTP_HEADER_ENCODING)
return auth
3、jwt_encode_handler:
def jwt_encode_handler(payload):
key = api_settings.JWT_PRIVATE_KEY or jwt_get_secret_key(payload)
return jwt.encode(
payload,
key,
api_settings.JWT_ALGORITHM
).decode('utf-8')
def jwt_get_secret_key(payload=None):
if api_settings.JWT_GET_USER_SECRET_KEY:
User = get_user_model() # 获取模型中的user类
user = User.objects.get(pk=payload.get('user_id')) # 根据表以及payload的用户id返回相匹配的对象
key = str(api_settings.JWT_GET_USER_SECRET_KEY(user)) # 获取用户的密钥
return key
return api_settings.JWT_SECRET_KEY
4、jwt_decode_handler:
def jwt_decode_handler(token):
options = {
'verify_exp': api_settings.JWT_VERIFY_EXPIRATION,
}
# get user from token, BEFORE verification, to get user secret key
unverified_payload = jwt.decode(token, None, False) # 将获取的token解码
secret_key = jwt_get_secret_key(unverified_payload) # 调用jwt_get_secret_key函数得到密钥
return jwt.decode(
token,
api_settings.JWT_PUBLIC_KEY or secret_key,
api_settings.JWT_VERIFY,
options=options,
leeway=api_settings.JWT_LEEWAY,
audience=api_settings.JWT_AUDIENCE,
issuer=api_settings.JWT_ISSUER,
algorithms=[api_settings.JWT_ALGORITHM]
)
5、jwt_payload_handler:
def jwt_payload_handler(user):
username_field = get_username_field()
username = get_username(user) # 获取用户名
warnings.warn(
'The following fields will be removed in the future: '
'`email` and `user_id`. ',
DeprecationWarning
)
payload = {
'user_id': user.pk, # 用户的id
'username': username, # 用户名
# 'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
'exp': datetime.utcnow() + api_settings.JWT_EXPIRATION_DELTA # 当前时间加上300秒,设置的是过期的时间
}
if hasattr(user, 'email'):
payload['email'] = user.email # 如果用户有email添加到payload中
if isinstance(user.pk, uuid.UUID):
payload['user_id'] = str(user.pk) # 如果用户id是uuid加密后,添加到payload中
payload[username_field] = username
# Include original issued at time for a brand new token,
# to allow token refresh
if api_settings.JWT_ALLOW_REFRESH:
payload['orig_iat'] = timegm(
datetime.utcnow().utctimetuple()
)
if api_settings.JWT_AUDIENCE is not None:
payload['aud'] = api_settings.JWT_AUDIENCE
if api_settings.JWT_ISSUER is not None:
payload['iss'] = api_settings.JWT_ISSUER
return payload
三、drf回顾
# djangorestframework
- 视图:执行流程多了东西
-- 两个视图基类
-- 5个视图扩张类
-- 9个视图子类
-- 视图集:ViewSetMinxin,ModelViewSet,ReadOnlyModelViewSet
- 路由
-- 三种写法
--- path('user/', views.UserView.as_view())
--- 自动生成
--- action装饰器
- 序列化类
-- 字段类,字段属性(read_only, write_only)
-- serializer:每个字段都要写
-- ModelSerializer:跟表有映射关系
--- model,fields,extra_kwargs
- 局部钩子,全局钩子
- save和update
- 自定义序列化的字段:两种方式
- 认证,权限,频率
-- 认证类
-- 权限类:ACL,RABC
-- 频率类
-- 局部使用和全局使用
- 过滤和排序
-- 配置排序和过滤即可
-- 内置的
-- 第三方
-- 自定义
- 全局异常
-- 写函数,配置一下
- 接口文档
-- md,word,第三方...
- jwt:
-- 签发和认证

浙公网安备 33010602011771号