JWT身份认证原理 
 
0x01 认证原理 
JWT,Json Web Token。用户登陆成功后,服务端返回一个 jwt 的 token,服务端不储存,客户端储存。此后客户端每次请求,都带上 token。服务端会 jwt校验 token 的有效性。优点显而易见,是不需要在服务端储存 token,服务端只需要做校验有效性的工作即可。
JWT 格式: 
点分三段的字符串:
第一段:HEADER 
base64url 加密的密文,可解密。
jwt 头部,包含说明第三段不可解密的编码算法名 HS256、字段数据类型标识 JWT。
第二段:PAYLOAD 
base64url 加密的密文,可解密。
jwt 数据,包含自定义的数据。
第三段:VERIFY SIGNATURE 
HS256 加密的密文,无法解密
校验签名,第一段密文.第二段密文“盐”进行 HS256 加密,生成密文,再做base64url 加密。
0x02 代码实例 
为了实现用户登录之后的身份认证和授权管理,采用 JWT 认证。
pip install pyjwt
颁发 token 
release.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import  jwtimport  datetimefrom  django.conf import  settingsdef  create_token (payload,timeout=30  ):    """      创建token的部分     payload 外部传入payload,即用户信息在外部写,传入这里输出token即可     timeout 超时时间,默认30s     返回值:token     """          headers = {         "alg" :"HS256" ,         "typ" :"jwt"      }          payload['exp' ] = datetime.datetime.utcnow() + datetime.timedelta(seconds=timeout)     token = jwt.encode(payload=payload,key=settings.SECRET_KEY,algorithm="HS256" ,headers=headers)     return  token 
认证 token 
auth.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from  django.conf import  settingsfrom  rest_framework.authentication import  BaseAuthenticationfrom  rest_framework.exceptions import  AuthenticationFailedimport  jwtfrom  jwt import  exceptionsclass  JwtAuthentication (BaseAuthentication ):    def  authenticate (self, request ):         token = request.META.get("HTTP_AAMS_TOKEN" )         try :             payload = jwt.decode(token, settings.SECRET_KEY, algorithms="HS256" )         except  exceptions.ExpiredSignatureError:             raise  AuthenticationFailed({"code" : 400 , "msg" : "token已失效" })         except  jwt.DecodeError:             raise  AuthenticationFailed({"code" : 400 , "msg" : "token认证失败" })         except  jwt.InvalidTokenError:             raise  AuthenticationFailed({"code" : 400 , "msg" : "token无效" })                                             return  (payload, token) 
配置在全局 
若是要哪个请求需要认证才能进行后续业务,那么:
views.py
1 2 3 4 5 from  myserver.mytoken.auth import  JwtAuthenticationclass  aView (...):	authentication_class = [JwtAuthentication,]     def  get (self,request ):         .. 
因为基本上所有的请求,都是要认证再进行业务的。所以可以配置在
settings.py
1 2 3 4 5 6 7 SECRET_KEY = "xxxxxxxxxxxx自己设置即可xxxxxxxxxxxxx" 	 REST_FRAMEWORK = [     "DEFAULT_AUTHENTICATION_CLASS" :[         "myserver.mytoken.JwtAuthentication" ,     ] ] 
注意,要在登陆视图函数取消认证配置:
1 2 3 4 class  LoginView (...):    authentication_class = []           def  get (... ): 
login视图函数 
views.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 from  django.views.decorators.csrf import  ensure_csrf_cookiefrom  rest_framework import  statusfrom  rest_framework.response import  Responsefrom  rest_framework.views import  APIViewfrom  .mytoken.release import  create_tokenfrom  .models import  Major,Userfrom  .serializers import  CourseSerializerclass  Login (APIView ):    authentication_classes = []     def  get (self,request ):         return  Response({"code" :400 , "msg" :"后端没有login的get" },status=status.HTTP_200_OK)     def  post (self,request ):         username = request.POST.get("username" )         password = request.POST.get("password" )         exists = User.objects.filter (username=username,password=password).exists()         if  exists:             token = create_token({"username" :username})             res = {                 "code" :200 ,                 "msg" :"登陆成功!" ,                 "data" :{                     "aams-token" :token,                 }             }             return  Response(res,status=status.HTTP_200_OK)         return  Response({"code" :400 , "msg" :"登陆失败!" },status=status.HTTP_400_BAD_REQUEST) 
若想一天不失效,可以:
1 token = create_token({"username" :username},timeout=60 *60 *24 )     
 
0x03 认证测试 
正如前面所说,我们采用JWT认证,那么测试登录和认证十分重要。
在第一次登录成功之后,应该由后端颁发一个token(我的系统命名为aams-token)。此后,每次网络请求必须带上这个aams-token作为请求头,后端会在请求头里取到aams-token,由中间件进行认证拦截,认证成功则会放行,否则请求失败。
登录测试 
首先看一下能否在登录成功后得到一个正确的token:
认证测试 
除登录API,随便选择一个API,发起请求,分别看在没有token和带有token的情况下,会收到什么响应:
注:以下所有API的response都是自行设计的code和msg。
为了方便测试,我只采用了少量的code(同时也尽量贴合状态码的原意)和msg。
code 状态码:200 处理成功;400 处理失败;301 重定向;202 接收处理但等待创建。
msg 状态信息:解释每种情况下的具体信息。
 
情况1:不带token 
如下图,可见code状态码为400,msg状态信息为token认证失败。
情况2:带失效的token 
如下图,可见code状态码为400,msg状态信息为token已失效。
情况3:带有效的token 
如下图,可见code状态码为200,msg状态信息为获取成功。