视频链接
JWT与Session的差异 相同点是,它们都是存储用户信息;然而,Session是在服务器端的,而JWT是在客户端的。
Session方式存储用户信息的最大问题在于要占用大量服务器内存,增加服务器的开销。
而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。
Session的状态是存储在服务器端,客户端只有session id;而Token的状态是存储在客户端。
playload
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分
标准中注册的声明
公共的声明
私有的声明
标准中注册的声明 (建议但不强制使用) :
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
# -*- coding: utf8 -*-
from typing import Optional
import datetime
import jwt
from werkzeug.local import LocalProxy
from flask import current_app, request, has_app_context, _app_ctx_stack
from . import exceptions
import os
registered_claims = {'iss', 'sub', 'aud', 'exp', 'nbf', 'iat', 'jti'}
class _AuthObject:
def __init__(self, **kwargs):
for key, val in kwargs.items():
setattr(self, key, val)
def __getattr__(self, item):
return None
def __repr__(self):
return str(self.__dict__)
def encode_token(iss: Optional[str] = None,
expire: Optional[datetime.timedelta] = None,
**kwargs):
"""
encode jwt token
:param iss:
:param expire: timedelta object, set the expire time of jwt
:param kwargs:
:return:
"""
try:
header = {'algorithm': 'HS256', 'type': 'JWT'}
payload = {
'iat': datetime.datetime.utcnow(),
'iss': iss or 'website.com',
}
# update public claim names
payload.update(**kwargs)
# if set jwt expire time, update exp claim
if expire:
payload['exp'] = payload['iat'] + expire
# gen jwt token
token = jwt.encode(payload, os.getenv('SECRET_KEY'), headers=header)
return token
except Exception:
raise exceptions.Internal(message='无效token')
def decode_token(token, verify_exp: bool = False):
"""
decode jwt token
:param token:
:param verify_exp:
:return:
"""
try:
tmp = {}
payload = jwt.decode(token, os.getenv('SECRET_KEY'), options={
'verify_exp': verify_exp})
# get the public claim names
for field in payload.keys():
if field in registered_claims:
continue
tmp[field] = payload[field]
return tmp
except jwt.ExpiredSignatureError:
raise exceptions.Unauthenticated(message='token已过期')
except jwt.InvalidTokenError:
raise exceptions.Unauthenticated(message='无效token')
def _get_auth():
if not has_app_context():
raise RuntimeError(
'No application found. Either work inside a view function or push'
' an application context.'
)
if not hasattr(_app_ctx_stack.top, 'auth'):
# get and validate auth filed in request header
auth_header = request.headers.get('Authorization')
if not auth_header:
raise exceptions.InvalidArgument(message="请求头错误")
auth_attr = auth_header.split(' ')
if not auth_attr or auth_attr[0] != 'JWT' or len(auth_attr) != 2:
raise exceptions.InvalidArgument(message="token格式错误")
payload = decode_token(auth_attr[1])
# set auth data to app context
_app_ctx_stack.top.auth = _AuthObject(**payload)
return getattr(_app_ctx_stack.top, 'auth')
# global variable
current_auth = LocalProxy(lambda: _get_auth())
package main
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
//自定义一个字符串
var jwtkey = []byte("www.topgoer.com")
var str string
type Claims struct {
UserId uint
jwt.StandardClaims
}
func main() {
r := gin.Default()
r.GET("/set", setting)
r.GET("/get", getting)
//监听端口默认为8080
r.Run(":8080")
}
//颁发token
func setting(ctx *gin.Context) {
expireTime := time.Now().Add(7 * 24 * time.Hour)
claims := &Claims{
UserId: 2,
StandardClaims: jwt.StandardClaims{
ExpiresAt: expireTime.Unix(), //过期时间
IssuedAt: time.Now().Unix(),
Issuer: "127.0.0.1", // 签名颁发者
Subject: "user token", //签名主题
},
}
fmt.Println(claims)
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
fmt.Println(token)
tokenString, err := token.SignedString(jwtkey)
fmt.Println(tokenString)
if err != nil {
fmt.Println(err)
}
str = tokenString
ctx.JSON(200, gin.H{"token": tokenString})
}
//解析token
func getting(ctx *gin.Context) {
tokenString := ctx.GetHeader("Authorization")
//vcalidate token formate
if tokenString == "" {
ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "权限不足"})
ctx.Abort()
return
}
token, claims, err := ParseToken(tokenString)
if err != nil || !token.Valid {
ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "权限不足"})
ctx.Abort()
return
}
fmt.Println(111)
fmt.Println(claims.UserId)
}
func ParseToken(tokenString string) (*jwt.Token, *Claims, error) {
Claims := &Claims{}
token, err := jwt.ParseWithClaims(tokenString, Claims, func(token *jwt.Token) (i interface{}, err error) {
return jwtkey, nil
})
fmt.Println(token, Claims)
return token, Claims, err
}
参考
因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。
它不需要在服务端保存会话信息, 所以它易于应用的扩展