【go商城】gin+mysql实现token登陆校验

Cookie,Session,Token这些用于认证,鉴权的名词相信大家都很熟悉了,网上都有大量的文章讲解,这里我们主要针对本商城通过mysql进行存储的token鉴权进行讲解。

这只是作为一个学习项目使用Mysql进行存储鉴权信息,一般企业项目中的单点登录都会使用redis进行存储

什么是token

访问资源接口(API)时所需要的资源凭证
简单 token 的组成: uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)

特点

服务端无状态化、可扩展性好
支持移动端设备
安全
支持跨程序调用

token 的身份验证流程:
转自https://juejin.cn/post/6844904034181070861
token的解释,转自https://juejin.cn/post/6844904034181070861

商城中token存储

新蜂商城项目中存在两个类型的用户:

  1. 后台管理员
  2. 商城用户

其实现逻辑是一致的所以我们调一个讲解即可,这里我们使用后台管理员的登陆进行讲解

我们使用了两张数据库表对用户的登录进行管理

tb_newbee_mall_admin_user & tb_newbee_mall_admin_user_token

我们通过两张表的admin_user_id将 用户和token进行了关联,如果用户登陆状态是有效的,则tb_newbee_mall_admin_user_token中就会存在一条记录,否则就是用户没有登陆。
在这里插入图片描述

在这里插入图片描述

gin实现接口鉴权

管理员登录的时候,校验用户名和密码,通过的话则将生成的token存入数据库中用于后续的鉴权

// AdminLogin 管理员登陆
func (m *ManageAdminUserService) AdminLogin(params manageReq.MallAdminLoginParam) (err error, mallAdminUser manage.MallAdminUser, adminToken manage.MallAdminUserToken) {
	err = global.GVA_DB.Where("login_user_name=? AND login_password=?", params.UserName, params.PasswordMd5).First(&mallAdminUser).Error
	if mallAdminUser != (manage.MallAdminUser{}) {
		token := getNewToken(time.Now().UnixNano()/1e6, int(mallAdminUser.AdminUserId))
		global.GVA_DB.Where("admin_user_id", mallAdminUser.AdminUserId).First(&adminToken)
		nowDate := time.Now()
		// 48小时过期
		expireTime, _ := time.ParseDuration("48h")
		expireDate := nowDate.Add(expireTime)
		// 没有token新增,有token 则更新
		if adminToken == (manage.MallAdminUserToken{}) {
			adminToken.AdminUserId = mallAdminUser.AdminUserId
			adminToken.Token = token
			adminToken.UpdateTime = nowDate
			adminToken.ExpireTime = expireDate
			if err = global.GVA_DB.Create(&adminToken).Error; err != nil {
				return
			}
		} else {
			adminToken.Token = token
			adminToken.UpdateTime = nowDate
			adminToken.ExpireTime = expireDate
			if err = global.GVA_DB.Save(&adminToken).Error; err != nil {
				return
			}
		}
	}
	return err, mallAdminUser, adminToken

}

然后我们定义一个拦截器,让需要鉴权的接口进行token的校验

// 拦截器
func AdminJWTAuth() gin.HandlerFunc {
	return func(c *gin.Context) {
		token := c.Request.Header.Get("token")
		if token == "" {
			response.FailWithDetailed(nil, "未登录或非法访问", c)
			c.Abort()
			return
		}
		err, mallAdminUserToken := manageAdminUserTokenService.ExistAdminToken(token)
		if err != nil {
			response.FailWithDetailed(nil, "未登录或非法访问", c)
			c.Abort()
			return
		}
		if time.Now().After(mallAdminUserToken.ExpireTime) {
			response.FailWithDetailed(nil, "授权已过期", c)
			manageAdminUserTokenService.DeleteMallAdminUserToken(token)
			c.Abort()
			return
		}
		c.Next()
	}

}

最后在我们需要使用鉴权的接口处注册上这个拦截器的中间件,不需要鉴权的接口就不使用拦截器

func (r *ManageAdminUserRouter) InitManageAdminUserRouter(Router *gin.RouterGroup) {
	mallAdminUserRouter := Router.Group("v1").Use(middleware.AdminJWTAuth())
	mallAdminUserWithoutRouter := Router.Group("v1")
	var mallAdminUserApi = v1.ApiGroupApp.ManageApiGroup.ManageAdminUserApi
	{
		mallAdminUserRouter.POST("createMallAdminUser", mallAdminUserApi.CreateAdminUser) // 新建MallAdminUser
		mallAdminUserRouter.PUT("adminUser/name", mallAdminUserApi.UpdateAdminUserName)   // 更新MallAdminUser
		mallAdminUserRouter.PUT("adminUser/password", mallAdminUserApi.UpdateAdminUserPassword)
......
	}
	{
		mallAdminUserWithoutRouter.POST("adminUser/login", mallAdminUserApi.AdminLogin) //管理员登陆
	}
}

因为这里我们没有使用到更高级别的权限管理,go中对于权限管理有casbin这个库,有兴趣的话可以了解一下,这里不做过多的讲解。

效果演示

启动我们后管系统的vue项目,登陆后发现tb_newbee_mall_admin_user_token 表中新增了一条token的数据,同时前端的请求头中也会新增一个token的字段,用于存储鉴权信息。
在这里插入图片描述

总结

如果你对这一节的内容感兴趣可以访问https://github.com/newbee-ltd/newbee-mall-api-go/ 下载源代码,后续一章我会介绍跨域相关的内容。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是使用 Golang web 框架 GinMySQL 数据库实现登录注册的示例代码: ```go package main import ( "database/sql" "fmt" "log" "github.com/gin-gonic/gin" _ "github.com/go-sql-driver/mysql" ) type User struct { ID uint `json:"id" gorm:"primary_key"` Username string `json:"username"` Password string `json:"password"` } func main() { // 连接 MySQL 数据库 db, err := sql.Open("mysql", "root:password@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local") if err != nil { log.Fatal(err) } defer db.Close() // 初始化 Gin 引擎 r := gin.Default() // 注册路由 r.POST("/register", func(c *gin.Context) { var user User if err := c.ShouldBindJSON(&user); err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } if user.Username == "" || user.Password == "" { c.JSON(400, gin.H{"error": "username and password cannot be empty"}) return } // 查询用户名是否已经存在 var count int if err := db.QueryRow("SELECT COUNT(*) FROM users WHERE username = ?", user.Username).Scan(&count); err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } if count > 0 { c.JSON(400, gin.H{"error": "username already exists"}) return } // 插入新用户信息 result, err := db.Exec("INSERT INTO users (username, password) VALUES (?, ?)", user.Username, user.Password) if err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } // 返回新用户的 ID id, _ := result.LastInsertId() c.JSON(200, gin.H{"id": id}) }) r.POST("/login", func(c *gin.Context) { var user User if err := c.ShouldBindJSON(&user); err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } if user.Username == "" || user.Password == "" { c.JSON(400, gin.H{"error": "username and password cannot be empty"}) return } // 查询用户信息 var u User if err := db.QueryRow("SELECT id, username, password FROM users WHERE username = ?", user.Username).Scan(&u.ID, &u.Username, &u.Password); err != nil { c.JSON(400, gin.H{"error": "invalid username or password"}) return } // 检查密码是否正确 if u.Password != user.Password { c.JSON(400, gin.H{"error": "invalid username or password"}) return } c.JSON(200, gin.H{"id": u.ID, "username": u.Username}) }) // 启动服务 if err := r.Run(":8080"); err != nil { log.Fatal(err) } } ``` 在上面的代码中,我们定义了一个 `User` 结构体来表示用户信息,包含 ID、用户名和密码。首先,我们连接了 MySQL 数据库,并使用 Gin 框架注册了两个路由:`/register` 和 `/login`。`/register` 路由用于新用户注册,会检查用户名是否已经存在,如果不存在则将用户信息插入到数据库中,并返回新用户的 ID。`/login` 路由用于用户登录,会查询用户名是否存在,并检查密码是否正确,如果正确则返回用户信息。 你需要注意修改上面代码中数据库连接的用户名和密码,并且需要创建一个名为 `users` 的表,包含 `id`、`username` 和 `password` 三个字段。 你可以将以上代码保存为 `main.go` 文件,然后使用以下命令在本地启动服务器: ``` go run main.go ``` 然后在浏览器或者使用 curl 工具测试 `/register` 和 `/login` 接口,例如: ``` curl -X POST -H "Content-Type: application/json" -d '{"username": "test", "password": "123456"}' http://localhost:8080/register curl -X POST -H "Content-Type: application/json" -d '{"username": "test", "password": "123456"}' http://localhost:8080/login ``` 以上命令中的 `test` 和 `123456` 分别为用户名和密码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值