当前位置: 首页 > news >正文

Gin + Ant Design Pro JWT认证

文章目录

  • 一:介绍
  • 二:Gin JWT 后台
    • 1. `Claims `定义
    • 2. 创建和解析Token
    • 3. Gin中间件编写
    • 4. 辅助函数
  • 三:Ant Design Pro JWT认证
  • 四:Gin中间件和使用示范

一:介绍

  • JWT现在比较流行的认证方式,微服务中使用特别常见。
  • JWT标准格式

在HTTP请求添加名为Authorization的header,形式如下: (token前面Bearer 是标准前缀字符串)
Authorization: Bearer

  • Go JWT 结构体Claims,可以理解为需要保存的信息,加密后会存储在token中,解密后会从token自动解析出来

配合ant design pro前端实现需要解决以下问题:

  • JWT TOKEN需要存储哪些字段以及定义,如何创建和解析
  • 前端发送请求时如何自动添加JWT认证信息
  • JWT TOKEN过期后,前后端如何配合自动刷新

二:Gin JWT 后台

使用库github.com/golang-jwt/jwt/v5

1. Claims 定义

jwt.RegisteredClaimsgithub.com/golang-jwt/jwt/v5预设的标准claims,后面用到其ExpiresAt过期时间字段,UserClaims 为自定义存储的字段,最终加密到token中的是jwtClaims结构

type UserClaims struct {
// 可以添加字段存储其他信息UserID   uint   `json:"userId"`RoleID   uint   `json:"roleId"`UserName string `json:"username"`
}type jwtClaims struct {UserClaimsjwt.RegisteredClaims
}

2. 创建和解析Token

expireHoursrefreshHours分别是设置过期时间,以及距离过期时间多久返回新token,后续在gin中间件中判断,方式是在Header中返回x-refresh-tokennewToken

type JWT struct {secret       []byteexpireHours  time.TimerefreshHours time.Duration
}// j := NewJWT("密钥", 2, 1)
// token过期时间是2小时, 距离过期时间还有1小时时在`Header`中返回`x-refresh-token`:`newToken`
func NewJWT(secret string, expireHours int, refreshHours int) *JWT {return &JWT{secret:       []byte(secret),expireHours:  time.Now().Add(time.Duration(expireHours) * time.Hour),refreshHours: time.Hour * time.Duration(refreshHours),}
}func (j *JWT) CreateToken(u UserClaims) (string, error) {token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwtClaims{UserClaims: u,RegisteredClaims: jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(j.expireHours),IssuedAt:  jwt.NewNumericDate(time.Now()),},})return token.SignedString(j.secret)
}func (j *JWT) ParseToken(tokenString string) (*jwtClaims, error) {token, err := jwt.ParseWithClaims(tokenString, &jwtClaims{}, func(token *jwt.Token) (interface{}, error) {return j.secret, nil})if err != nil {return nil, err}if claims, ok := token.Claims.(*jwtClaims); ok && token.Valid {return claims, nil} else {return nil, errors.New("Token无效")}
}

3. Gin中间件编写

type BaseResponse struct {Success      bool   `json:"success"`Data         any    `json:"data"`Message      string `json:"message"`ErrorMessage string `json:"errorMessage"`
}func OkWithData(c *gin.Context, data any) {c.JSON(200, BaseResponse{Success: true, Data: data})
}func OkWithMsg(c *gin.Context, msg string) {c.JSON(200, BaseResponse{Success: true, Message: msg})
}func Fail(c *gin.Context, errMsg string) {c.JSON(200, BaseResponse{Success: false, ErrorMessage: errMsg})
}
// 使用如下:
// // token过期时间是2小时, 距离过期时间还有1小时时在`Header`中返回`x-refresh-token`:`newToken`
// j := NewJWT("密钥", 2, 1)
// r.USE(JWTAuth(j))
func JWTAuth(j *JWT) gin.HandlerFunc {return func(c *gin.Context) {token := c.Request.Header.Get("Authorization")token = strings.TrimPrefix(token, "Bearer ")claims, err := j.ParseToken(token)if err != nil {response.Fail(c, "Token错误: "+err.Error())c.Abort()return}if time.Since(claims.ExpiresAt.Local()) < j.refreshHours {newToken, err := j.CreateToken(claims.UserClaims)if err != nil {response.Fail(c, "Token刷新错误: "+err.Error())c.Abort()return}c.Header("x-refresh-token", newToken)}c.Set("claims", claims)c.Next()}}

4. 辅助函数

gin中直接调用该函数可以方便的获取到存储的机构体信息

func GetUserToken(c *gin.Context) *UserClaims {claims, _ := c.Get("claims")if claims == nil {return &UserClaims{}}return &claims.(*jwtClaims).UserClaims
}

三:Ant Design Pro JWT认证

处理登录请求后返回jwt token使用localStorage.setItem('jwt', token)存储起来
app.tsx的文件request定义中添加拦截器,实现方法:

  • 请求前,在header中添加头·Authorization·: 'Bearer ’ + localStorage.getItem(‘jwt’) || ‘’
  • 请求后,检测header中是否存在x-refresh-token,存在的话更新token,目的是防止token过期自动刷新
    实现如下:
function setToken(token: string) {localStorage.setItem('jwt', token);
}function getToken(): string {return localStorage.getItem('jwt') || '';
}export const request = {...errorConfig,// 请求前拦截器requestInterceptors: [(url: string, options: RequestConfig) => {const authHeader = { Authorization: 'Bearer ' + getToken() };return {url: `${url}`,options: { ...options, interceptors: true, headers: authHeader },};},],// 请求后拦截器responseInterceptors: [(response: Response, options: RequestConfig) => {if (response.headers.has('x-refresh-token')) {setToken(response.headers.get('x-refresh-token') || '');}return response;},],
};

四:Gin中间件和使用示范

jwt_middleware.go

package middlewareimport ("errors""strings""time""github.com/gin-gonic/gin""github.com/golang-jwt/jwt/v5"
)// 可以添加字段存储其他信息
type UserClaims struct {UserID   uint   `json:"userId"`RoleID   uint   `json:"roleId"`UserName string `json:"username"`
}type jwtClaims struct {UserClaimsjwt.RegisteredClaims
}type JWT struct {secret       []byteexpireHours  time.TimerefreshHours time.Duration
}// j := NewJWT("密钥", 2, 1)
// token过期时间是2小时, 距离过期时间还有1小时时在`Header`中返回`x-refresh-token`:`newToken`
func NewJWT(secret string, expireHours int, refreshHours int) *JWT {return &JWT{secret:       []byte(secret),expireHours:  time.Now().Add(time.Duration(expireHours) * time.Hour),refreshHours: time.Hour * time.Duration(refreshHours),}
}func (j *JWT) CreateToken(u UserClaims) (string, error) {token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwtClaims{UserClaims: u,RegisteredClaims: jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(j.expireHours),IssuedAt:  jwt.NewNumericDate(time.Now()),},})return token.SignedString(j.secret)
}func (j *JWT) ParseToken(tokenString string) (*jwtClaims, error) {token, err := jwt.ParseWithClaims(tokenString, &jwtClaims{}, func(token *jwt.Token) (interface{}, error) {return j.secret, nil})if err != nil {return nil, err}if claims, ok := token.Claims.(*jwtClaims); ok && token.Valid {return claims, nil} else {return nil, errors.New("Token无效")}
}// 使用如下:
// // token过期时间是2小时, 距离过期时间还有1小时时在`Header`中返回`x-refresh-token`:`newToken`
// j := NewJWT("密钥", 2, 1)
// r.USE(JWTAuth(j))
func JWTAuth(j *JWT) gin.HandlerFunc {return func(c *gin.Context) {token := c.Request.Header.Get("Authorization")token = strings.TrimPrefix(token, "Bearer ")claims, err := j.ParseToken(token)if err != nil {response.Fail(c, "Token错误: "+err.Error())c.Abort()return}if time.Since(claims.ExpiresAt.Local()) < j.refreshHours {newToken, err := j.CreateToken(claims.UserClaims)if err != nil {response.Fail(c, "Token刷新错误: "+err.Error())c.Abort()return}c.Header("x-refresh-token", newToken)}c.Set("claims", claims)c.Next()}}func GetUserToken(c *gin.Context) *UserClaims {claims, _ := c.Get("claims")if claims == nil {return &UserClaims{}}return &claims.(*jwtClaims).UserClaims
}type BaseResponse struct {Success      bool   `json:"success"`Data         any    `json:"data"`Message      string `json:"message"`ErrorMessage string `json:"errorMessage"`
}func OkWithData(c *gin.Context, data any) {c.JSON(200, BaseResponse{Success: true, Data: data})
}func OkWithMsg(c *gin.Context, msg string) {c.JSON(200, BaseResponse{Success: true, Message: msg})
}func Fail(c *gin.Context, errMsg string) {c.JSON(200, BaseResponse{Success: false, ErrorMessage: errMsg})
}

main.go

package middlewareimport ("fmt""github.com/gin-gonic/gin"
)func main() {j := NewJWT("密钥", 2, 1)r := gin.Default()r.POST("/login", func(ctx *gin.Context) {// 处理登录逻辑返回token, 前端需讲token存储到localstoragetoken, err := j.CreateToken(UserClaims{UserID: 1, RoleID: 1, UserName: "Leo"})if err != nil {response.Fail(ctx, err.Error())return}response.OkWithData(ctx, gin.H{"token": token})})auth := r.Group("/api")auth.Use(JWTAuth(j))auth.GET("/test", func(ctx *gin.Context) {u := GetUserToken(ctx)response.OkWithMsg(ctx, fmt.Sprintf("用户ID:%d角色ID:%d用户名:%s", u.UserID, u.RoleID, u.UserName))})
}
http://www.lryc.cn/news/199123.html

相关文章:

  • canvas实现图片标注,绘制区域
  • SELECT COUNT(*) 会造成全表扫描吗?
  • python考前复习(90题)
  • 根据SpringBoot Guides完成进行示例学习(详细步骤)
  • waf、yakit和ssh免密登录
  • 【AIGC核心技术剖析】大型语言和视觉助手——LLaVA(论文+源码)
  • IBM的WAS简介与基本使用手册
  • Deno 快速入门
  • 【计算机网络笔记】OSI参考模型基本概念
  • ConnectTimeout和ReadTimeout所代表的意义
  • 使用Python计算平面多边形间最短距离,数据需要从excel表格中导入
  • 华为数通方向HCIP-DataCom H12-831题库(多选题:1-20)
  • CCC数字钥匙设计【NFC】--通过NFC进行车主配对Phase3
  • 开源OA协同办公系统,集成Flowable流程引擎 可拖拽创建个性表单
  • 为什么嵌入通常优于TF-IDF:探索NLP的力量
  • oracle-AWR报告生成方法
  • 笙默考试管理系统-MyExamTest----codemirror(37)
  • 【Unity3D编辑器拓展】Unity3D的IMGUI、GUI、GUILayout、EditorGUI、EditorGUILayout、OnGUI【全面总结】
  • 11. 机器学习 - 评价指标2
  • Nginx的代理和负载均衡
  • Oracle发布支持Vscode的Java插件
  • 互联网Java工程师面试题·Java 总结篇·第九弹
  • SpringCloud学习笔记-gateway网关自定义全局过滤器
  • 数字图像处理实验记录四(图像的空间域增强-平滑处理)
  • 怎么使用LightPicture开源搭建图片管理系统并远程访问?【搭建私人图床】
  • pytorch_神经网络构建4
  • 外骨骼机器人和人形机器人概览
  • Java面试题:链表-反转链表
  • el-upload实现上传文件夹
  • 京东数据平台(京东数据分析)2023年9月京东冰箱行业品牌销售排行榜!