壞境:
web端使用django的auth_user表進行用戶登錄認證
因爲某種緣由app端需使用新用戶表進行用戶登錄
都採用jwt 驗證前端
class JSONWebTokenAuthentication(BaseJSONWebTokenAuthentication): www_authenticate_realm = 'api' def get_jwt_value(self, request): auth = get_authorization_header(request).split() auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower() if not auth or smart_text(auth[0].lower()) != auth_header_prefix: return None if len(auth) == 1: msg = _('Invalid Authorization header. No credentials provided.') raise exceptions.AuthenticationFailed(msg) elif len(auth) > 2: msg = _('Invalid Authorization header. Credentials string ' 'should not contain spaces.') raise exceptions.AuthenticationFailed(msg) return auth[1] def authenticate_header(self, request): return 'JWT realm="{0}"'.format(self.www_authenticate_realm)
執行順序:python
1.先執行BaseJSONWebTokenAuthentication的authenticate方法 def authenticate(self, request): jwt_value = self.get_jwt_value(request) .... return (user, jwt_value) 2.執行JSONWebTokenAuthentication的get_jwt_value方法 def get_jwt_value(self, request): #split空格,咱們傳的jwt格式,以空格分隔 """ jwt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImRlbW9jaGluYV9jbiIsInVzZXJfaWQiOjE1MzIsImVtYWlsIjoiMTYzdkBxcS5jbyIsImV4cCI6MTU3MjIzMDI2NX0.f7sQ8FSAmkiatiIbJ2qVgkdCC9bOj_nU0kfr3LuHwdE """ auth = get_authorization_header(request).split() auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower() #auth[0] jwt是否等於auth_header_prefix jwt if not auth or smart_text(auth[0].lower()) != auth_header_prefix: return None if len(auth) == 1: msg = _('Invalid Authorization header. No credentials provided.') raise exceptions.AuthenticationFailed(msg) elif len(auth) > 2: msg = _('Invalid Authorization header. Credentials string ' 'should not contain spaces.') raise exceptions.AuthenticationFailed(msg) return auth[1]#返回的是token 3.get_authorization_header(request)方法 def get_authorization_header(request): auth = request.META.get('HTTP_AUTHORIZATION', b'') if isinstance(auth, type('')): auth = auth.encode(HTTP_HEADER_ENCODING) return auth 4.回到BaseJSONWebTokenAuthentication的authenticate方法 def authenticate(self, request): #返回jwt token jwt_value = self.get_jwt_value(request) if jwt_value is None: return None try: #反解jwt token payload = jwt_decode_handler(jwt_value) #超時錯誤 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) return (user, jwt_value) 5. def authenticate_credentials(self, payload): #獲取默認的用戶表 User = utils.get_user_model() #經過jwt token獲取用戶id 生成jwt token的時候封裝進去的 user_id = jwt_get_user_id_from_payload(payload) if user_id is not None: try: user = User.objects.get(pk=user_id, is_active=True) except User.DoesNotExist: msg = _('Invalid signature.') raise exceptions.AuthenticationFailed(msg) else: msg = _('Invalid payload.') raise exceptions.AuthenticationFailed(msg) return user
web端登錄使用User表, app端使用CourseUser表web
由於判斷用戶是否存在,是在authenticate_credentials()方法中實現的算法
故重寫authenticate_credentials()方法,在CourseUser表中查找django
class TokenAuthentication(JSONWebTokenAuthentication): def authenticate(self, request): credentials = super(TokenAuthentication, self).authenticate(request) if not credentials: msg = "Authentication credenttials were not provided" raise exceptions.AuthenticationFailed(msg) request.user, jwt = credentials return credentials def authenticate_credentials(self, payload): user_id = jwt_get_user_id_from_payload(payload) if user_id is not None: try: # user = User.objects.get(pk=user_id, is_active=True) or Userinfo.objects.get(pk=user_id) user = CourseUser.objects.get(pk=user_id) except : msg = 'Invalid signature.' raise exceptions.AuthenticationFailed(msg) else: msg = 'Invalid payload.' raise exceptions.AuthenticationFailed(msg) return user
頭部包含了兩部分,token 類型和採用的加密算法api
{ "alg": "HS256", "typ": "JWT" }
它會使用 Base64 編碼組成 JWT 結構的第一部分,若是你使用Node.js,能夠用Node.js的包base64url來獲得這個字符串。app
Base64是一種編碼,也就是說,它是能夠被翻譯回原來的樣子來的。它並非一種加密過程。ide
這部分就是咱們存放信息的地方了,你能夠把用戶 ID 等信息放在這裏,JWT 規範裏面對這部分有進行了比較詳細的介紹,經常使用的由 iss(簽發者),exp(過時時間),sub(面向的用戶),aud(接收方),iat(簽發時間)。編碼
{ "iss": "lion1ou JWT", "iat": 1441593502, "exp": 1441594722, "aud": "www.example.com", "sub": "lion1ou@163.com" }
一樣的,它會使用 Base64 編碼組成 JWT 結構的第二部分加密
前面兩部分都是使用 Base64 進行編碼的,即前端能夠解開知道里面的信息。Signature 須要使用編碼後的 header 和 payload 以及咱們提供的一個密鑰,而後使用 header 中指定的簽名算法(HS256)進行簽名。簽名的做用是保證 JWT 沒有被篡改過。
詳情請查看這篇文章 https://www.jianshu.com/p/180a870a308a
def get_jwt(user): jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER #頭部 user_id #exp 過時時間 payload = { 'user_id': user.pk, 'exp': datetime.datetime.utcnow() + datetime.timedelta(days=365) } #生成token token = jwt_encode_handler(payload) return {"token": token, "user_id": user.pk}