官方
:http://getblimp.github.io/django-rest-framework-jwt/python
他是个第三方的开源项目git
安装
:pip install djangorestframework-jwt
github
使用自带
设定好的jwtweb
from django.urls import path from rest_framework_jwt.views import obtain_jwt_token urlpatterns = [ path('login/', obtain_jwt_token), ] ''' path('login/', obtain_jwt_token)其实至关于path('login/', ObtainJSONWebToken.as_view()) 由于咱们之间进源码能够看到 obtain_jwt_token = ObtainJSONWebToken.as_view() #得到 refresh_jwt_token = RefreshJSONWebToken.as_view() #刷新 verify_jwt_token = VerifyJSONWebToken.as_view() #验证 '''
测试接口:post请求ajax
""" postman发生post请求 接口:http://api.luffy.cn:8000/user/login/ 数据: { "username":"admin", "password":"admin" } """
""" jwt:json web tokens 采用json格式在web上传输的 认证字符串 jwt字符串:头.载荷.签名 头:公司基本信息、项目组基本信息、常规加密算法名 载荷:用户信息、过时时间 签名:头、载荷、秘钥 {头信息字典,采用base64加密算法}.{载荷信息字典,采用base64加密算法}.{头加密串、载荷加密串、服务器秘钥,采用hs256加密算法} base64是可逆加密 hash256是不可逆加密 咱们通常只会将帐号信息,过时时间放载荷里面,通常把密码什么重要信息丢签名里面 """
""" 系统:session认证 rest_framework.authentication.SessionAuthentication ajax请求经过认证: cookie中要携带 sessionid、csrftoken,请求头中要携带 x-csrftoken 第三方:jwt认证 rest_framework_jwt.authentication.JSONWebTokenAuthentication ajax请求经过认证: 请求头中要携带 authorization,值为 jwt空格token 自定义:基于jwt、其它 1)自定义认证类,继承BaseAuthentication(或其子类),重写authenticate 2)authenticate中完成 拿到认证标识 auth 反解析出用户 user 前两步操做失败 返回None => 游客 前两步操做成功 返回user,auth => 登陆用户 注:若是在某个分支抛出异常,直接定义失败 => 非法用户 """
""" 系统: 1)AllowAny:容许全部用户,校验方法直接返回True 2)IsAuthenticated:只容许登陆用户 必须request.user和request.user.is_authenticated都经过 3)IsAuthenticatedOrReadOnly:游客只读,登陆用户无限制 get、option、head 请求无限制 前台请求必须校验 request.user和request.user.is_authenticated 4)IsAdminUser:是不是后台用户 校验 request.user和request.user.is_staff is_staff(能够登陆后台管理系统的用户) 自定义:基于auth的Group与Permission表 1)自定义权限类,继承BasePermission,重写has_permission 2)has_permission中完成 拿到登陆用户 user <= request.user 校验user的分组或是权限 前两步操做失败 返回False => 无权限 前两步操做成功 返回True => 有权限 """
""" 系统: 1)AnonRateThrottle:对同一IP游客的限制 2)UserRateThrottle:对同一IP登陆用户的限制 必须在settings.py中 'DEFAULT_THROTTLE_RATES': { 'user': '10/min', # 登陆的用户一分钟能够访问10次 'anon': '3/min', # 游客一分钟能够访问3次 } 在视图类中: class TempAPIView(APIView): ... throttle_classes = [AnonRateThrottle, UserRateThrottle] 自定义:基于auth的Group与Permission表 1)自定义频率类,继承SimpleRateThrottle,重写get_cache_key,明确scope SimpleRateThrottle已经帮咱们实现了 allow_request、wait 2)scope与settings.py的DEFAULT_THROTTLE_RATES配合使用 3)get_cache_key中完成 拿到限制信息 ident <= request中获取 没有限制信息 返回None => 不限制 有限制信息 返回限制信息字符串 => 有限制 """
其实就是在jwt的源码基础上进行相关的修改
算法
最简单的修改django
from rest_framework.exceptions import AuthenticationFailed import jwt from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication from rest_framework_jwt.authentication import jwt_decode_handler from rest_framework.authentication import BaseAuthentication def authenticate(self, request): auth = 从request中获得 user = 从auth中获得 if not user: return None return user, auth
若是咱们自定制了一个权限咱们进行全局设置必须本身在setting把这个函数加进去
json
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ # django默认session校验:校验规则 游客 及 登陆用户 # 'rest_framework.authentication.SessionAuthentication', # 'rest_framework.authentication.BasicAuthentication', # 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', ], }
咱们作局部设置就在咱们自定义的类中添加
api
authentication_classes = [咱们自定义认证函数的对象]
也是改源码
服务器
""" 系统: 1)AllowAny:容许全部用户,校验方法直接返回True 2)IsAuthenticated:只容许登陆用户 必须request.user和request.user.is_authenticated都经过 3)IsAuthenticatedOrReadOnly:游客只读,登陆用户无限制 get、option、head 请求无限制 前台请求必须校验 request.user和request.user.is_authenticated 4)IsAdminUser:是不是后台用户 校验 request.user和request.user.is_staff is_staff(能够登陆后台管理系统的用户) 自定义:基于auth的Group与Permission表 1)自定义权限类,继承BasePermission,重写has_permission 2)has_permission中完成 拿到登陆用户 user <= request.user 校验user的分组或是权限 前两步操做失败 返回False => 无权限 前两步操做成功 返回True => 有权限 """
#根据用户分组信息设置相关权限 from rest_framework.permissions import BasePermission class AdminPermission(BasePermission): # 继承BasePermission,重写has_permission def has_permission(self, request, view): # 有权限,返回True # 无权限,返回False user = request.user if not user: return False # 用户是 管理员 分组 (管理员分组是Group表中的一条自定义记录) if not user.groups.filter(name='管理员'): return False # 登陆的用户必须是自定义管理员分组成员 return True
若是咱们自定制了一个权限全局设置咱们必须本身在setting把这个函数加进去
自定义类的路径api.authentications.JWTAuthentication
#自定义一个权限 from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed import jwt from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication from rest_framework_jwt.authentication import jwt_decode_handler class JWTAuthentication(BaseJSONWebTokenAuthentication): # 自定义认证类,重写authenticate方法 def authenticate(self, request): # 认证经过,返回user,auth # 认证失败,返回None auth = request.META.get('HTTP_AUTH') # 前台用auth携带token if not auth: return None try: payload = jwt_decode_handler(auth) # 出现jwt解析异常,直接抛出异常,表明非法用户,也能够返回None,做为游客处理 except jwt.ExpiredSignature: raise AuthenticationFailed('token已过时') except: raise AuthenticationFailed('token非法') user = self.authenticate_credentials(payload) return (user, auth)
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ # '咱们自定义权限函数的路径', 'api.authentications.JWTAuthentication', ], 'DEFAULT_PERMISSION_CLASSES': [ # 'rest_framework.permissions.AllowAny', # 全局配置:一站式网站(全部操做都须要登陆后才能访问) # 'rest_framework.permissions.IsAuthenticated', ], }
咱们作局部设置就在咱们自定义的类中添加
permission_classes = [咱们自定义认证函数的对象]
""" 系统: 1)AnonRateThrottle:对同一IP游客的限制 2)UserRateThrottle:对同一IP登陆用户的限制 必须在settings.py中 REST_FRAMEWORK = { 'DEFAULT_THROTTLE_RATES': { 'user': '10/min', # 登陆的用户一分钟能够访问10次 'anon': '3/min', # 游客一分钟能够访问3次 } } 在视图类中: class TempAPIView(APIView): ... throttle_classes = [AnonRateThrottle, UserRateThrottle] 自定义:基于auth的Group与Permission表 1)自定义频率类,继承SimpleRateThrottle,重写get_cache_key,明确scope SimpleRateThrottle已经帮咱们实现了 allow_request、wait 2)scope与settings.py的DEFAULT_THROTTLE_RATES配合使用 3)get_cache_key中完成 拿到限制信息 ident <= request中获取 没有限制信息 返回None => 不限制 有限制信息 返回限制信息字符串 => 有限制 """
自定义频率类:一分钟一个手机号只容许访问一次接口
from rest_framework.throttling import SimpleRateThrottle class ThreeMinRateThrottle(SimpleRateThrottle): scope = 'sms' def get_cache_key(self, request, view): # 对手机号频率限制 ident = request.data.get('mobile') if not ident: # 为发现限制条件,返回None表明不进行频率限制 return None return self.cache_format % { 'scope': self.scope, 'ident': ident } # settings.py REST_FRAMEWORK = { 'DEFAULT_THROTTLE_RATES': { 'user': '10/min', # 登陆的用户一分钟能够访问10次 若是是 'anon': '3/min', # 游客一分钟能够访问3次 'sms': '1/min', #是咱们自定义的,默认只提供user以及anon } }
在视图层 class UserListAPIView(ListAPIView): throttle_classes = [咱们自定义的方法路径]
源码里关于时间的一段代码
def parse_rate(self, rate): """ Given the request rate string, return a two tuple of: <allowed number of requests>, <period of time in seconds> """ if rate is None: return (None, None) num, period = rate.split('/') num_requests = int(num) duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] return (num_requests, duration)
这里咱们能够看出来是先/进行字符串切分而后取第一个字母
全部咱们这边不必定用min表明分,只要开头是m便可
import datetime JWT_AUTH = { 'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=30000),#d到期时间 'JWT_AUTH_HEADER_PREFIX': 'TOKEN', #咱们传参数的时候开头自定义内容,注意点这里必须与下面的token中以宫格隔开 }
源码中为
USER_SETTINGS = getattr(settings, 'JWT_AUTH', None) #他是经过JWT_AUTH这个名字 ...... 'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300), 'JWT_AUTH_HEADER_PREFIX': 'JWT', 系统默认以jwt开头
源码在rest_framework_jwt.seriallizers.py中
JSONWebTokenSerializer类
payload = jwt_payload_handler(user) return { 'token': jwt_encode_handler(payload), 'user': user }
咱们若是自定义有几个关键点把握就行了一个是jwt_payload_handler
的方法
一个是 user对象
全部若是咱们要在登入的时候抛出token
import re from utils.response import APIResponse from rest_framework_jwt.serializers import jwt_encode_handler, jwt_payload_handler class LoginJWTAPIView(APIView): authentication_classes = () permission_classes = () def post(self, request, *args, **kwargs): # username可能携带的不止是用户名,可能仍是用户的其它惟一标识 手机号 邮箱 username = request.data.get('username') password = request.data.get('password') # 若是username匹配上手机号正则 => 多是手机登陆 if re.match(r'1[3-9][0-9]{9}', username): try: # 手动经过 user 签发 jwt-token user = models.User.objects.get(mobile=username) except: return APIResponse(1, '该手机未注册') # 邮箱登陆 等 # 帐号登陆 else: try: # 手动经过 user 签发 jwt-token user = models.User.objects.get(username=username) except: return APIResponse(1, '该帐号未注册') # 得到用户后,校验密码并签发token if not user.check_password(password): return APIResponse(1, '密码错误') payload = jwt_payload_handler(user) token = jwt_encode_handler(payload) return APIResponse(0, 'ok', results={ 'username': user.username, 'mobile': user.mobile, 'token': token })
# settings.py # jwt配置 import datetime JWT_AUTH = { 'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=30000), 'JWT_AUTH_HEADER_PREFIX': 'TOKEN', }