这是我参与「第三届青训营 -后端场」笔记创作活动的第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")
}