上一篇文章《Go语言MVC模式Web开发实战(七)集成jwt框架且进行有效验证》我们在web项目中集成了jwt的验证。之前的做法是吧jwt的秘钥信息存放在客户端的,让客户端每次传递过来。但是在实际的业务中,由于jwt的秘钥存放在客户端,因此安全问题无法避免,比如为了安全考虑,当前用户是机器人,需要封禁掉当前用户,并且立刻强制用户退出。此时我们就很难做到。所以正常的做法是我们把jwt的秘钥存放在服务端,然后给客户端返回一个token,每次进来之后我们使用token去redis中获取jwt的信息,再进行解密使用。当需要封禁用户的时候,直接把token给删除掉即可。这样用户直接退出了。是不是很方便了?下面我们来演示一下。
1、集成redis等相关框架
首先安装框架,执行如下的命令:
go get -u github.com/go-redis/redis/v8 go get -u github.com/google/uuid
2、在config中添加redis的集成
在config.go里面我们集成redis代码,完整的代码示例如下:
package config import ( "context" "fmt" "github.com/BurntSushi/toml" "github.com/go-redis/redis/v8" "gorm.io/driver/mysql" "gorm.io/gorm" "log" ) var DB *gorm.DB type DatabaseConfig struct { Database struct { User string `toml:"user"` Password string `toml:"password"` Host string `toml:"host"` Port string `toml:"port"` DBName string `toml:"dbname"` } } var Rdb *redis.Client func ConnectRedis() { // 初始化Redis客户端 Rdb = redis.NewClient(&redis.Options{ Addr: "192.168.1.249:6379", // Redis服务器地址和端口 Password: "123456", // 密码,如果没有设置则留空 DB: 0, // 使用默认数据库 }) ctx := context.Background() _, err := Rdb.Ping(ctx).Result() if err != nil { panic("无法连接到redis") } log.Println("redis连接成功") } func ConnectDatabase() { var config DatabaseConfig if _, err := toml.DecodeFile("./config/config.toml", &config); err != nil { panic(err) } dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", config.Database.User, config.Database.Password, config.Database.Host, config.Database.Port, config.Database.DBName) fmt.Println(dsn) var err error DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { panic("Failed to connect to database") } }
上面我们在config.go中集成了redis,那么在main.go里面,启动的时候我们需要去执行这里的redis连接代码,添加上:
config.ConnectRedis()
具体示例图如下:
3、更改登录接口
接下来我们需要更改登录接口,需要生成uuid作为key,jwt秘钥作为value存储到redis中,所以这里需要修改userController.go里面的login方法,完整代码示例如下:
package controllers import ( "awesomeProject/config" "awesomeProject/models" "awesomeProject/request" "awesomeProject/response" "awesomeProject/utils" "context" "github.com/gin-gonic/gin" "github.com/google/uuid" "log" "net/http" "strings" "time" ) func RegisterUser(c *gin.Context) { var user models.User if err := c.ShouldBindJSON(&user); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } log.Println("请求了接口register接口") config.DB.Create(&user) c.JSON(http.StatusCreated, user) } func LoginUser(c *gin.Context) { loginRequest := request.LoginReuqest{} if err := c.ShouldBindJSON(&loginRequest); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } token, err := utils.GenToken(loginRequest.Username) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } tokenString := strings.Replace(uuid.New().String(), "-", "", -1) errr := config.Rdb.Set(context.Background(), "token_"+tokenString, token, time.Minute*2).Err() if errr != nil { panic(errr) } rs := response.BaseResponse{ Code: http.StatusOK, Msg: "请求成功", Data: tokenString, } c.JSON(http.StatusCreated, rs) }
4、更改中间件的jwt获取部分
接下来用户在request header中传递的Authorization值就是md5值了,所以需要在中间件里面通过这里的md5值从redis中获取到jwt秘钥然后再进行解密,所以这里需要修改middleware包下面的jwt.go文件,修改后的代码示例如下:
package middleware import ( "awesomeProject/config" "awesomeProject/utils" "context" "github.com/gin-gonic/gin" "log" "net/http" ) func AuthenticateJWT() gin.HandlerFunc { return func(c *gin.Context) { tokenString := c.GetHeader("Authorization") if tokenString == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header required"}) c.Abort() return } jwtString, err := config.Rdb.Get(context.Background(), "token_"+tokenString).Result() if err != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()}) c.Abort() return } chain, err := utils.ParseToken(jwtString) if err != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()}) c.Abort() return } log.Println("获取到当前登录用户的用户名是:", chain.Username) c.Set("username", chain.Username) c.Next() } }
5、测试
接下来我们把项目启动起来,进行下测试,首先请求login接口:
可以看到返回的是一串md5值,不再是jwt秘钥了,接下来我们请求listgoods接口
可以看到获取到了jwt秘钥并且成功进行解密了。
以上就是使用redis的案例,同时我们对jwt的安全性做了改造。
最后附上本案例的源码,登录后即可下载。
还没有评论,来说两句吧...