table of Contents
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 toHS256
encryption, and then to thehs256
ciphertext 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_segment
three parts - First portion
header_segment
for decrypting base64url giveheader
- A second portion
payload_segment
for decrypting base64url givepayload
- A third portion
crypto_segment
for decrypting base64url givesignature
- The third part of
signature
the 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_input
result and encrypts obtainedsignature
ciphertext comparison.
- Splicing the first two ciphertext, namely:
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:
- Requests execution sequence of the method serializer_class ObtainJSONWebToken time to the class,
- User authentication is successful will perform jwt_payload_handler user object as a parameter function,
- In this function, users will be inside the id, user name, and time-out into a payload of the dictionary,
- Then the payload as a parameter to perform jwt_encode_handler function,
- In the encode process will be base64 internal encryption algorithm and the encryption type comprising,
- The payload carried base64 encryption,
- After stitching together the first two paragraphs were hs256 encryption, and then base64 encryption, and then stitching together the three sections