使用jwt時,使用本身的用戶類校驗

壞境:
web端使用django的auth_user表進行用戶登錄認證
因爲某種緣由app端需使用新用戶表進行用戶登錄
都採用jwt 驗證前端

基於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

登錄生成jwt token

  • Header 頭部

頭部包含了兩部分,token 類型和採用的加密算法api

{
  "alg": "HS256",
  "typ": "JWT"
}

它會使用 Base64 編碼組成 JWT 結構的第一部分,若是你使用Node.js,能夠用Node.js的包base64url來獲得這個字符串。app

Base64是一種編碼,也就是說,它是能夠被翻譯回原來的樣子來的。它並非一種加密過程。ide

  • Payload 負載

這部分就是咱們存放信息的地方了,你能夠把用戶 ID 等信息放在這裏,JWT 規範裏面對這部分有進行了比較詳細的介紹,經常使用的由 iss(簽發者),exp(過時時間),sub(面向的用戶),aud(接收方),iat(簽發時間)。編碼

{
    "iss": "lion1ou JWT",
    "iat": 1441593502,
    "exp": 1441594722,
    "aud": "www.example.com",
    "sub": "lion1ou@163.com"
}

一樣的,它會使用 Base64 編碼組成 JWT 結構的第二部分加密

  • Signature 簽名

前面兩部分都是使用 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}
相關文章
相關標籤/搜索