jwt学习与思考|青训营笔记

154 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第1篇笔记

用户鉴权的三种方式:

1.Session存储

用户输入用户名和密码后,服务端将用户信息保存在session中,将sessionID返回给用户,之后用户的每次请求的cookie都会携带sessionID用于用户的认证与权限识别。

缺点:session都存放在单个服务器上,一旦项目规模变大,扩展到分布式系统中就会出现session不共享的问题。

2. redis存储

将用户的信息存放在redis中

key: token(唯一的标识UUID)
value: 用户信息
服务端将token传给用户,用户之后的请求都会携带token,服务端通过token在redis中查找该用户进行鉴权。

缺点:还是需要在服务端存放数据,一旦redis挂了,就芭比Q了。

3.jwt认证

将用户信息存放在客户端,没服务器啥事了,yyds,下面主要介绍一下jwt。

JWT

全称:JSON Web Token
为了可以通过用户传过来的Token认出是哪个小可爱来访问咱小破站,那么Token就得存些识别信息,例如:

{
  "user_id":1
  "user_name":榜一大哥
  ...
}

但是直接这样明文相互传输,很可能用户要对数据进行篡改,导致小破站认错人,这时候就需要对数据进行一个加密传输(签名)。 因此jwt的数据结构就是:

  • header
  • payload
  • signature

1.header

header部分保存了一些jwt的元数据

{
  "alg": "HS256",(签名算法)
  "typ": "JWT"(令牌类型)
}

2.payload

payload部分保存了真正的用户信息还有些jwt自带的一些信息(比如:过期时间,jwt编号等等)

3.signature

signature部分就是用一个流弊的算法把上面两玩意加个密,算法就决定是(HMAC SHA256)你了,jwt默认的。但是在加密之前还得把上面那两玩意转换成字符串,用base64UrlEncode吧,所以签名就是下面的结构了:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

go实现Token代码:

payload的结构:

type StandardClaims struct {
   Audience  string `json:"aud,omitempty"`
   ExpiresAt int64  `json:"exp,omitempty"`
   Id        string `json:"jti,omitempty"`
   IssuedAt  int64  `json:"iat,omitempty"`
   Issuer    string `json:"iss,omitempty"`
   NotBefore int64  `json:"nbf,omitempty"`
   Subject   string `json:"sub,omitempty"`
}
type MyClaims struct {
   UserId   uint   `json:"userId"`
   Username string `json:"username"`
   jwt.StandardClaims
}

生成Token:

func GenToken(userId uint, username string) (string, error) {
   c := MyClaims{
      userId,
      username,
      jwt.StandardClaims{
         ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(),
         Issuer:    "userFunction",
      },
   }
   token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
   return token.SignedString(MySecret)
}

验证Token:

func ParseToken(tokenString string) (*MyClaims, error) {
   token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
      return MySecret, nil
   })
   if err != nil {
      return nil, err
   }

   if claims, ok := token.Claims.(*MyClaims); ok && token.Valid {
      return claims, err
   }
   //token失效
   return nil, errors.New("invalid token")
}