基於gin的golang web開發:實現用戶登錄
前文分別介紹過了Resty和gin-jwt兩個包,Resty
是一個HTTP和REST客戶端,gin-jwt
是一個實現了JWT的Gin中間件。本文將使用這兩個包來實現一個簡單的用戶登錄功能。
環境準備
實現登錄功能之前要提前準備一個用於查詢用戶是否存在的服務。訪問服務//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("//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開發:實現用戶登錄