Golang/Gin-JWT身份验证
前言
如果我们编写的api不需要验证就可以随意调用,显然是不安全的。我们需要对用户进行身份验证,根据用户身份提供服务。这时候JWT就派上用场了。
作者水平有限,有任何问题欢迎在文章下方交流!
JWT验证简介
JSON Web Token(JWT)是一个开放的标准(RFC 7519),它定义了一个紧凑且自包含的方式,用于在各方之间作为JSON对象安全地传输信息。
JWT验证流程
1、前端使用账户密码等信息向服务器发送POST请求;
2、服务器使用私钥secret加密账户密码等信息,生成一个token,并返回给前端;
3、前端使用该token附带在请求参数中,向服务器发送各种请求;
4、服务器验证前端请求时附带的token,验证成功,则对前端请求进行处理。
JWT的组成
jwt的token由3部分组成:
1、头部(header)
2、载荷(payload)
3、签证(signature)
jwt解密网址:https://jwt.io/
头部
JWT的头部采用base64对称加密,包含两个信息:
{
"alg": "HS256", //声明加密算法
"typ": "JWT" //声明类型,为JWT
}
载荷
载荷是存放有效信息的地方。
标准中注册的声明**(建议但不强制使用)** :
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
其他信息可自行添加,但是不建议存放敏感信息,因为base64是对称解密的,意味着只要拿到token就可以解密出载荷中存放的信息。
签证
JWT的第三部分是签证信息,这个签证信息由三部分组成:
- header (base64加密后的)
- payload (base64加密后的)
- secret
这个部分需要base64加密后的header和base64加密后的payload使用.
连接组成的字符串,然后通过header中声明的加密方式进行加盐secret
组合加密,然后就构成了jwt的第三部分。
注意:密钥secret是存储在服务端的,jwt的token的签发生成也是在服务端的,secret是用来进行token的签发和验证,所以,它就是你服务端的私钥,切忌泄露!一旦其他人得知这个secret,那就意味着他可以自己随意签发jwt了,十分危险。
JWT-hello world
package main
// jwt身份验证demo
import (
"fmt"
jwt "github.com/dgrijalva/jwt-go"
"time"
)
// 设置jwt密钥secret
var jwtSecret=[]byte("123")
type Claims struct {
UserID string `json:"userid"`
jwt.StandardClaims
}
// GenerateToken 生成token的函数
func GenerateToken(userid string)(string,error){
nowTime :=time.Now()
expireTime:=nowTime.Add(3*time.Hour)
claims:=Claims{
userid, // 自行添加的信息
jwt.StandardClaims{
ExpiresAt: expireTime.Unix(), // 设置token过期时间
Issuer: "gin-blog", // 设置jwt签发者
},
}
// 生成token
tokenClaims:=jwt.NewWithClaims(jwt.SigningMethodHS256,claims)
token,err:=tokenClaims.SignedString(jwtSecret)
return token,err
}
// ParseToken 验证token的函数
func ParseToken(token string)(*Claims,error){
// 对token的密钥进行验证
tokenClaims,err:=jwt.ParseWithClaims(token,&Claims{},func(token *jwt.Token)(interface{},error){
return jwtSecret,nil
})
// 判断token是否过期
if tokenClaims!=nil{
if claims,ok:=tokenClaims.Claims.(*Claims);ok && tokenClaims.Valid{
return claims,nil
}
}
return nil,err
}
func main() {
// 生成一个token
token,_:=GenerateToken("12")
fmt.Println("生成的token:",token)
// 验证并解密token
claim,err:=ParseToken(token)
if err!=nil {
fmt.Println("解析token出现错误:",err)
}else if time.Now().Unix() > claim.ExpiresAt {
fmt.Println("时间超时")
}else {
fmt.Println("userid:",claim.UserID)
}
}
JWT在实际项目中的使用
1、token的签发一般是在用户登录时进行,用户请求登录成功后就给前端签发一个token,用于后续请求其他接口的身份验证。
2、其他接口的JWT验证可以编写成一个中间件,以下以Gin框架中间件为例:
func JWT() gin.HandlerFunc {
return func(context *gin.Context) {
// 获取token
token := context.Query("token")
if token == "" {
context.JSON(http.StatusOK, gin.H{
"isSuccess": false,
"mesg": "没有携带token",
})
context.Abort()
return
} else {
claims, err := ParseToken(token)
if err != nil {
context.JSON(http.StatusOK, gin.H{
"isSuccess": false,
"mesg": "token验证失败",
})
context.Abort()
return
} else if time.Now().Unix() > claims.ExpiresAt {
context.JSON(http.StatusOK, gin.H{
"isSuccess": false,
"mesg": "token已过期",
})
context.Abort()
return
}
}
}
}