Gin中使用jwt-go实现JWT鉴权登陆
在Go语言中,JWT(JSON Web Token)鉴权可以使用第三方库来实现,比如jwt-go。
库的介绍和使用可见文档:jwt package - github.com/golang-jwt/jwt/v5 - Go Packages
创建JWT令牌
在服务器中,可以使用以下代码创建JWT令牌
package middleware
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"mybili/serializer"
"mybili/utils"
"net/http"
"os"
"time"
)
type MyClaims struct {
Username string `json:"user_name"`
//Password string `json:"password"`
jwt.RegisteredClaims
}
// 生成token
func SetToken(username string) (string, int) {
SetClaims := MyClaims{
Username: username,
//Password: password,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), //有效时间
IssuedAt: jwt.NewNumericDate(time.Now()), //签发时间
NotBefore: jwt.NewNumericDate(time.Now()), //生效时间
Issuer: os.Getenv("JWT_ISSUER"), //签发人
Subject: "somebody", //主题
ID: "1", //JWT ID用于标识该JWT
Audience: []string{"somebody_else"}, //用户
},
}
//使用指定的加密方式和声明类型创建新令牌
tokenStruct := jwt.NewWithClaims(jwt.SigningMethodHS256, SetClaims)
//获得完整的、签名的令牌
token, err := tokenStruct.SignedString([]byte(os.Getenv("JWT_KEY")))
if err != nil {
utils.Logger.Errorf("err:%v", err.Error())
return "", utils.TOKEN_CREATE_FAILED
}
return token, utils.SUCCESS
}
jwt.RegisteredClaims是对标准注册声明的封装,可以单独使用它,也可以把它嵌入到自定义类型中,以提供标准验证功能。
jwt.NewWithClaims使用指定的签名方法和声明创建一个新令牌。
创建了一个名为MyClaims 的结构体,用于定义JWT负载。除了标准验证选项外,我们也可以自定义Payload有效载荷字段,例如将用户名Username放到payload中。
JWT验证
验证JWT可以使用如下代码实现:
// 验证token
func CheckToken(token string) (*MyClaims, int) {
//解析、验证并返回token。
tokenObj, err := jwt.ParseWithClaims(token, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(os.Getenv("JWT_KEY")), nil
})
if err != nil {
utils.Logger.Errorf("err:%v", err.Error())
return nil, utils.ERROR
}
if claims, ok := tokenObj.Claims.(*MyClaims); ok && tokenObj.Valid {
fmt.Printf("%v %v\n", claims.Username, claims.RegisteredClaims)
return claims, utils.SUCCESS
} else {
return nil, utils.ERROR
}
}
jwt.ParseWithClaims是NewParser().ParseWithClaims()的快捷方式,第一个参数是token的string值,第二个参数是之后需要把解析的数据放入的地方,第三个参数将被Parse方法用作回调函数,以提供用于验证的键。函数接收已解析但未验证的令牌。
解析结果输出结果如下:
Username:mjiarong
RegisteredClaims:{mybili somebody [somebody_else] 2023-10-02 20:25:30 +0800 CST 2023-10-01 20:25:30 +0800 CST 2023-10-01 20:25:30 +0800 CST 1}
JWT中间件
在中间件中服务端会获取用户token,基于jwt校验是否合法,合法放行,否则拒绝。
// jwt中间件
func JwtToken() gin.HandlerFunc {
return func(c *gin.Context) {
tokenHeader := c.Request.Header.Get("Authorization")
code := utils.SUCCESS
if tokenHeader == "" {
code = utils.TOKEN_NOT_EXIST
c.JSON(http.StatusOK, serializer.CheckToken(
code,
utils.GetErrMsg(code)))
c.Abort()
return
}
key, tCode := CheckToken(tokenHeader)
if tCode == utils.ERROR {
code = utils.TOKEN_WRONG
c.JSON(http.StatusOK, serializer.CheckToken(
code,
utils.GetErrMsg(code)))
c.Abort()
return
}
//判断token是否过期
if time.Now().Unix() > key.ExpiresAt.Unix() {
code = utils.TOKEN_RUNTIME
c.JSON(http.StatusOK, serializer.CheckToken(
code,
utils.GetErrMsg(code)))
c.Abort()
return
}
c.Set("username", key.Username)
c.Next()
}
}
前端JWT登录鉴权的实现
一般情况下,客户端在登录后得到了一个JWT方式的token。那这个token放哪里呢?最好把JWT放在HTTP请求的Header Authorization,格式是Authorization: Bearer jwtStr。那是否意味着前端的每个请求都附带后端返回的token吗?显然是否定的,只需要把需要鉴权登陆的请求头中带上token就行了。对此可以使用axios的请求拦截器来对请求进行过滤,以下是某个项目中的一个请求拦截器的实现:
import axios from "axios";
let http = axios.create({
baseURL: process.env.VUE_APP_BASE_API, //配置默认的地址
withCredentials: true, //将会默认携带认证给后端
timeout: 1000 * 10, //请求超时设置,如果超过了10秒,那么就会进入reject
});
http.interceptors.request.use(
//axios的请求拦截器,它可以拦截所有的请求,为所有的请求添加逻辑
//拦截了请求后,如果不放行,那么所有的请求会一直被拦截,因此需要return不需要拦截的请求。
function(config) {
let postWhiteList = [
"/user/login",
"/user/register",
]; //将不需要拦截的请求拿出来
let getWhiteList = [
"/rank/daily",
"/comment",
"/upload/token",
"/upload/credentials",
"/comment",
];
if (config.method === 'post' && postWhiteList.includes(config.url)) {
//如果当前的请求地址中,包含在不需要拦截请求地址中,那么就放行
return config;
} else if (config.method === 'get' && (getWhiteList.includes(config.url) || config.url.includes("/video"))) {
return config;
} else {
//如果是需要被拦截的请求
let token = window.sessionStorage.getItem("token") || ""; //将登录成功后,在后端中返回来的token从本地存储取出来
config.headers["Authorization"] = token; //给需要拦截的请求中请求头添加token。config.headers["authorization"]是一个固定的写法
return config; //添加后,放行
}
}
);
对此,前后端中对JWT的实现就基本完成了。
总结
JWT作为一种安全、简单的认证方式,被广泛应用于Web开发中。JWT不仅可以用于用户认证,还可以用于数据传输、API认证等场景。在实际应用中,应该注意JWT的有效期,以及签名密钥的安全保护。任何技术都不是完美的,JWT 也有很多缺陷,具体是选择 JWT 还是 Session 方案还是要看项目的具体需求。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」