08 jwt source code analysis

08 jwt source code analysis

JSON Web Tokens, is a development of the industry standard RFC 7519 , to secure representation of the statement between the two sides. Currently, jwt widely used in the certification of the user's system, especially now around the end of the separation project.

1. jwt certification process

In project development, in accordance with generally authentication process shown above, namely: after the user has logged in, the user's browser to the server returns a token, after the user's browser to go to carry the token sending request to the server, the server legality verification token of the legitimate user to look at the data, otherwise, return some error messages.

Traditional token way and jwt What's the difference in certification?

  • Traditional token way

    After the user logs in successfully, the server generates a random token to the user and stored in a token server (database or cache), the need to carry token come back later when the user visits, after the service receives the token, to the database or cache verify whether the token timeout is legitimate.

  • jwt way

    After the user logs in successfully, the server generates a random token by jwt to the user (the server without having to retain the token), required to carry token come back later when the user visits, after the service receives the token, token to check whether a timeout by jwt, whether legitimate.

Create a token 2.jwt

2.1 Principle

  • jwt generation token format, namely: the .three segments of the string concatenation composition.

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
  • Generated rules are as follows:

    • HEADER first stage portion, and the fixed token type contains an algorithm, which base64url json for encryption, which is the first segment of the token.
    {
      "alg": "HS256",
      "typ": "JWT"
    }
    • PAYLOAD part of the second paragraph, contains some data, which were base64url json encryption, which is the token of the second paragraph
    {
      "sub": "1234567890",
      "name": "John Doe",
      "iat": 1516239022
      ...
    }
    • SIGNATURE third segment portion, the first two of the base ciphertext by .stitching together, then subjected to HS256encryption, and then to the hs256ciphertext base64url encrypted token third segment of the finally obtained.
    base64url(
        HMACSHA256(
          base64UrlEncode(header) + "." + base64UrlEncode(payload),
          your-256-bit-secret (秘钥加盐)
        )
    )
    • The last three paragraphs string by .stitching together to generate the token jwt.

    • Note : base64url do first base64 encryption is encrypted, and then -replace +and _substitute /.

2.2 jwt verification token

  • Generally after successful authentication, the jwt generated token is returned to the user after the user access token when once again the need to carry, this time jwt need to be token 超时and 合法性checksum.
  • After obtaining the token, we will verify the following steps:
  • The token is divided into header_segment, payload_segment, crypto_segmentthree parts
  • First portion header_segmentfor decrypting base64url giveheader
  • A second portion payload_segmentfor decrypting base64url givepayload
  • A third portion crypto_segmentfor decrypting base64url givesignature
  • The third part of signaturethe partial data validity check
    • Splicing the first two ciphertext, namely:signing_input
    • Acquiring an encryption algorithm from the first paragraph of plain text, the default:HS256
    • + Salt using an algorithm signing_inputresult and encrypts obtained signatureciphertext comparison.

3. jwt use

  • installation

    pip3 install djangorestframework-jwt
  • setting profiles

    import datetime
    JWT_AUTH = {
        "JWT_EXPIRATION_DELTA":datetime.timedelta(minutes=10)
    }
  • app registered

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'api.apps.ApiConfig',
        'rest_framework',
        'rest_framework_jwt'
    ]
  • User login

    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    from api import models
    
    class LoginView(APIView):
        """
        登录接口
        """
        def post(self,request,*args,**kwargs):
    
            # 基于jwt的认证
            # 1.去数据库获取用户信息
            from rest_framework_jwt.settings import api_settings
            jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
            jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
    
            user = models.UserInfo.objects.filter(**request.data).first()
            if not user:
                return Response({'code':1000,'error':'用户名或密码错误'})
    
            payload = jwt_payload_handler(user)
            token = jwt_encode_handler(payload)
            return Response({'code':1001,'data':token})
  • User Authentication

    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    # from rest_framework.throttling import AnonRateThrottle,BaseThrottle
    
    
    class ArticleView(APIView):
        def get(self,request,*args,**kwargs):
            # 获取用户提交的token,进行一步一步校验
            import jwt
            from rest_framework import exceptions
            from rest_framework_jwt.settings import api_settings
            jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
    
            jwt_value = request.query_params.get('token')
            try:
                payload = jwt_decode_handler(jwt_value)
            except jwt.ExpiredSignature:
                msg = '签名已过期'
                raise exceptions.AuthenticationFailed(msg)
            except jwt.DecodeError:
                msg = '认证失败'
                raise exceptions.AuthenticationFailed(msg)
            except jwt.InvalidTokenError:
                raise exceptions.AuthenticationFailed()
            print(payload)
    
            return Response('文章列表')

4. source code analysis

  • First, from the route looks

    from rest_framework_jwt.views import obtain_jwt_token
    urlpatterns = [
        url(r'^login/', account.LoginView.as_view()),
        url(r'^jwt/login/',obtain_jwt_token), # ObtainJSONWebToken.as_view()
    
        url(r'^article/', article.ArticleView.as_view()),
    ]
    
    # obtain_jwt_token = ObtainJSONWebToken.as_view()
  • ObtainJSONWebToken类

    class ObtainJSONWebToken(JSONWebTokenAPIView):
        serializer_class = JSONWebTokenSerializer
  • JSONWebTokenSerializer class, user authentication

    from rest_framework_jwt.settings import api_settings
    jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
    jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
    
    class JSONWebTokenSerializer(Serializer):
        def validate(self, attrs):
            credentials = {
                self.username_field: attrs.get(self.username_field),
                'password': attrs.get('password')
            }
    
            if all(credentials.values()):
                user = authenticate(**credentials)
    
                if user:
                    payload = jwt_payload_handler(user)
                    return {
                        'token': jwt_encode_handler(payload),
                        'user': user
                    }
  • After successful user authentication will perform jwt_payload_handler user object as a parameter function

    Internal function takes the user id, user name, and put a payload timeout dictionary

    def jwt_payload_handler(user):
        username_field = get_username_field()
        username = get_username(user)
    
        payload = {
            'user_id': user.pk,
            'username': username,
            'exp': datetime.utcnow() + api_settings.JWT_EXPIRATION_DELTA
        }
    
        return payload
  • Payload will function as a parameter to perform 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')
  • It will encode method comprising inner and encryption type encryption algorithm base64

    def encode(self,
               payload,  # type: Union[Dict, bytes]
               key,  # type: str
               algorithm='HS256',  # type: str
               headers=None,  # type: Optional[Dict]
               json_encoder=None  # type: Optional[Callable]
              ):
    
        json_payload = json.dumps(
            payload,
            separators=(',', ':'),
            cls=json_encoder
        ).encode('utf-8')
    
        return super(PyJWT, self).encode(
            json_payload, key, algorithm, headers, json_encoder
        )
  • Perform super (). Encode () method

    After stitching together the first two paragraphs were hs256 encryption, and then base64 encryption, and then stitching together the three sections

    def encode(self,
               payload,  # type: Union[Dict, bytes]
               key,  # type: str
               algorithm='HS256',  # type: str
               headers=None,  # type: Optional[Dict]
               json_encoder=None  # type: Optional[Callable]
              ):
        segments = []
    
        # Header
        header = {'typ': self.header_typ, 'alg': algorithm}
    
        json_header = force_bytes(
            json.dumps(
                header,
                separators=(',', ':'),
                cls=json_encoder
            )
        )
    
        segments.append(base64url_encode(json_header))
        segments.append(base64url_encode(payload))
    
        # Segments
        signing_input = b'.'.join(segments)
        alg_obj = self._algorithms[algorithm]
        key = alg_obj.prepare_key(key)
        signature = alg_obj.sign(signing_input, key)
    
        segments.append(base64url_encode(signature))
    
        return b'.'.join(segments)
  • Next user request comes in, to verify

    class BaseJSONWebTokenAuthentication(BaseAuthentication):
    
        def authenticate(self, request):
            jwt_value = self.get_jwt_value(request)
            if jwt_value is None:
                return None
    
            try:
                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)

to sum up:

  1. Requests execution sequence of the method serializer_class ObtainJSONWebToken time to the class,
  2. User authentication is successful will perform jwt_payload_handler user object as a parameter function,
  3. In this function, users will be inside the id, user name, and time-out into a payload of the dictionary,
  4. Then the payload as a parameter to perform jwt_encode_handler function,
  5. In the encode process will be base64 internal encryption algorithm and the encryption type comprising,
  6. The payload carried base64 encryption,
  7. After stitching together the first two paragraphs were hs256 encryption, and then base64 encryption, and then stitching together the three sections

Guess you like

Origin www.cnblogs.com/liubing8/p/11945844.html
Recommended