首页 > 其他分享 >Gin中使用jwt-go实现JWT鉴权登陆

Gin中使用jwt-go实现JWT鉴权登陆

时间:2023-12-23 23:33:51浏览次数:32  
标签:return 请求 JWT utils jwt token go

在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 方案还是要看项目的具体需求。

标签:return,请求,JWT,utils,jwt,token,go
From: https://www.cnblogs.com/beatle-go/p/17923846.html

相关文章

  • Go EASY游戏框架 之 RPC Guide 03
    1Overvieweasy解决服务端通信问题,同样使用了RPC技术。easy使用的ETCD+GRPC,直接将它们打包组合在了一起。随着服务发现的成熟,稳定,简单,若是不用,甚至你也并不需要RPC来分解你的架构。GRPC有默认resovler解决服务发现的方案,只需要完成resolver,watch等,可以轻易实现,RPC的负载均衡。只......
  • 深入浅出Go泛型
    众所周知,Go是一门静态类型的语言。静态类型也就意味着在使用Go语言编程时,所有的变量、函数参数都需要指定具体的类型,同时在编译阶段编译器也会对指定的数据类型进行校验。这也意味着一个函数的输入参数和返回参数都必须要和具体的类型强相关,不能被不同类型的数据结构所复用。而泛......
  • 无涯教程-PL/SQL - GOTO函数
    PL/SQL编程语言中的GOTO语句提供了从GOTO到同一子程序中带标签的语句的无条件跳转。注意-在任何编程语言中均不建议使用GOTO语句,因为它会使跟踪程序的控制流变得困难,从而使程序难以理解且难以修改。GOTO-语法PL/SQL中的GOTO语句的语法如下-GOTOlabel;....<<label>>......
  • Go 语言中 Panic 和 os.Exit 的区别
    Go语言中Panic和os.Exit的区别原创 源自开发者 源自开发者 2023-12-2307:30 发表于广东 听全文源自开发者专注于提供关于Go语言的实用教程、案例分析、最新趋势,以及云原生技术的深度解析和实践经验分享。50篇原创内容公众号在Go语言的错误......
  • Python MongoDB 聚合管道操作符及使用
    ​ 1、$match筛选文档,类似于SQL的WHERE子句。可以使用$match来选择满足特定条件的文档。使用示例:PythonMongoDB聚合管道操作符及使用-CJavaPy2、$group分组和汇总数据,类似于SQL的GROUPBY子句。你可以使用$group来对文档进行分组,并进行聚合操作,如计算总和、......
  • 2023-12-23:用go语言,一支n个士兵的军队正在趁夜色逃亡,途中遇到一条湍急的大河 敌军在T
    2023-12-23:用go语言,一支n个士兵的军队正在趁夜色逃亡,途中遇到一条湍急的大河敌军在T的时长后到达河面,没到过对岸的士兵都会被消灭现在军队只找到了1只小船,这船最多能同时坐上2个士兵。当1个士兵划船过河,用时为a[i]当2个士兵坐船同时划船过河时,用时为max(a[j],a[i])两士兵中用时最......
  • 2023-12-23:用go语言,一支n个士兵的军队正在趁夜色逃亡,途中遇到一条湍急的大河 敌军在T
    2023-12-23:用go语言,一支n个士兵的军队正在趁夜色逃亡,途中遇到一条湍急的大河敌军在T的时长后到达河面,没到过对岸的士兵都会被消灭现在军队只找到了1只小船,这船最多能同时坐上2个士兵。当1个士兵划船过河,用时为a[i]当2个士兵坐船同时划船过河时,用时为max(a[j],a[i])两士......
  • 关于Secure Hash Algorithm加密算法
    一、概述SHA(SecureHashAlgorithm)加密算法是一种广泛应用的密码散列函数,由美国国家安全局(NSA)设计,用于保障数据的安全性和完整性。SHA算法经历了多个版本的更新,目前主要应用于各种网络安全和数据加密领域。SHA在线加密|一个覆盖广泛主题工具的高效在线平台(amd794.com)http......
  • go1-base
    一.demo1单包程序运行packagemain//注意要有src目录?//F:\ProgramFiles\go\goprojects\src\project1\main包\main.goimport"fmt"funcmain(){ s1:="[1]建议换行符号'\\r\\n'windows='\\n'linux='\\r\\n'\n\r&quo......
  • Go 泛型之类型参数
    Go泛型之类型参数一、Go的泛型与其他主流编程语言的泛型差异Go泛型和其他支持泛型的主流编程语言之间的泛型设计与实现存在差异一样,Go的泛型与其他主流编程语言的泛型也是不同的。我们先看一下Go泛型设计方案已经明确不支持的若干特性,比如:不支持泛型特化(specialization),即......