基于gin的golang web开发:实现用户登录

前文分别介绍过了Restygin-jwt两个包,Resty是一个HTTP和REST客户端,gin-jwt是一个实现了JWT的Gin中间件。本文将使用这两个包来实现一个简单的用户登录功能。

环境准备

实现登录功能之前要提前准备一个用于查询用户是否存在的服务。访问服务http://127.0.0.1:18081/users?username=root时返回用户root的相关信息

{
    "total": 1,
    "data": [
        {
            "id": 1,
            "username": "root",
            "password": "CGUx1FN++xS+4wNDFeN6DA==",
            "nickname": "超级管理员",
            "mobile": "13323232323"
        }
    ]
}

返回结果中password字段AES加密后的结果。当参数username传入其他字符串时返回null

{
    "total": 0,
    "data": null
}

好了准备工作到此结束,下面来看一下如何实现登录功能。

实现认证

首先实现调用查询用户服务的方法。

func FindUser(userName string) (user sysUser.SysUser, err error) {
	client := resty.New().SetRetryCount(3)
	resp, err := client.R().
		SetQueryParams(map[string]string{
			"username": userName,
		}).
		SetResult(&PagedUser{}).
		Get("http://127.0.0.1:18081/users")
	if err != nil {
		log.Panicln("FindUser err: ", err.Error())
	}
	response := resp.Result().(*PagedUser)
	if response.Total == 1 {
		user = response.Data[0]
		return
	}
	err = errors.New("用户不存在")
	return
}

这里我们创建了一个Resty客户端,并设置了3次重试,依照服务的要求传入username参数,然后通过Total值判断用户是否存在,用户存在的话返回用户信息,否则返回错误。

接下来我们实现有关jwt验证的部分。

var identityKey = "id"

type login struct {
	Username string `form:"username" json:"username" binding:"required"`
	Password string `form:"password" json:"password" binding:"required"`
}

type User struct {
	Id       int
	UserName string
	NickName string
}

func JwtMiddleware() (authMiddleware *jwt.GinJWTMiddleware, err error) {
	authMiddleware, err = jwt.New(&jwt.GinJWTMiddleware{
		Realm:       "test zone",
		Key:         []byte("secret key"),
		Timeout:     time.Hour,
		MaxRefresh:  time.Hour,
		IdentityKey: identityKey,
		PayloadFunc: func(data interface{}) jwt.MapClaims {
			if v, ok := data.(*User); ok {
				return jwt.MapClaims{
					identityKey: v.UserName,
				}
			}
			return jwt.MapClaims{}
		},
		IdentityHandler: func(c *gin.Context) interface{} {
			claims := jwt.ExtractClaims(c)
			return &User{
				UserName: claims[identityKey].(string),
			}
		},
		Authenticator: func(c *gin.Context) (interface{}, error) {
			var loginVals login
			if err := c.ShouldBind(&loginVals); err != nil {
				return "", jwt.ErrMissingLoginValues
			}
			userID := loginVals.Username
			password := loginVals.Password

			user, err := http_service.FindUser(userID)
			if err != nil {
				return nil, jwt.ErrFailedAuthentication
			}

			encrypt := utils.PasswordEncrypt(password, userID)
			if encrypt != user.Password.String {
				return nil, jwt.ErrFailedAuthentication
			}

			return &User{
				Id:       user.Id,
				UserName: user.Username.String,
				NickName: user.Nickname.String,
			}, nil
		},
		Authorizator: func(data interface{}, c *gin.Context) bool {
			if v, ok := data.(*User); ok && v.UserName == "admin" {
				return true
			}

			return false
		},
		Unauthorized: func(c *gin.Context, code int, message string) {
			c.JSON(code, gin.H{
				"code":    code,
				"message": message,
			})
		},
		TokenLookup: "header: Authorization, query: token, cookie: jwt_middleware",
		TokenHeadName: "Bearer",
		TimeFunc: time.Now,
	})
	return
}

以上代码在基于gin的golang web开发:认证利器jwt一文中有详细的解释,我们重点来看一下用户验证的部分:Authenticator

方法ShouldBind对参数进行模型绑定,不熟悉模型绑定的话可以查看前文基于gin的golang web开发:模型绑定。然后调用FindUser方法检查用户是否存在,如果用户存在的话还需要验证一下用户密码是否正确。全部验证通过返回User结构体,进入gin-jwt的后续流程。

最后一步在Gin中增加用户登录路由。

func main() {
	r := gin.Default()

	authMiddleware, err := JwtMiddleware()
	if err != nil {
		log.Fatal("JWT Error:" + err.Error())
	}

	errInit := authMiddleware.MiddlewareInit()

	if errInit != nil {
		log.Fatal("authMiddleware.MiddlewareInit() Error:" + errInit.Error())
	}

	r.POST("/login", authMiddleware.LoginHandler)

	r.Run(":8090")
}

大功告成,现在调用/login接口,并提供正确的用户名和密码,不出意外的话会得到一个成功的JSON,里面包含了验证通过的JWT。

文章出处:基于gin的golang web开发:实现用户登录

posted @ 2020-12-11 15:34  陈宏博  阅读(3497)  评论(0编辑  收藏  举报