本文章分为上下两章,在上篇文章里我们介绍了API的概念、快速构建服务、使用 gin 构建 API 接口、JWT。在下篇文章里我们将使用上篇文章所讲述的技术实现用户认证功能API。
0 场景假设
假设用户输入账号和密码通过User/Login
接口登录账号,再通过User/Do
接口获取自己的Id
、姓名
、账号
、密码
等字段。
1 定义model
以下我们定义一个User结构体用于存放用户信息。
model/user.go
type User struct {
Id int
Name string
Account string
Password string
}
2 实现json的生成解析校验
用户输入账号和密码通过User/Login
接口登录账号需要服务端生成token
返回给客户端进行保存,同时服务端也应该要保存token
对应的用户。
登录成功后,用户想要通过User/Do
接口获取自己的Id
、姓名
、账号
、密码
等字段应该要发送token
给服务端,服务端通过解析校验token
获取到对应的用户再将用户信息返回给客户端。
在此之前我们先要声明以下变量:
var secretKey = []byte("your-secret-key")
:存储秘钥var tokenStore = make(map[string]model.User)
:存储token
以下三个函数都在common/jwt.go中
2.1 json生成
根据传来的用户以及秘钥进行加密,再建立秘钥与用户的映射关系
func GenerateToken(user model.User) (string, error) {
claims := jwt.MapClaims{
"id": user.Id,
"name": user.Name,
"account": user.Account,
"exp": time.Now().Add(time.Hour * 24).Unix(), // 过期时间设置为一天
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString(secretKey)
if err != nil {
return "", err
}
tokenStore[tokenString] = user
return tokenString, nil
}
2.2 解析token
通过用户传来的token,查找用户。
func ParseToken(tokenString string) (model.User, error) {
var user model.User
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return secretKey, nil
})
if err != nil {
return user, err
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
id := int(claims["id"].(float64))
name := claims["name"].(string)
account := claims["account"].(string)
pwd:=claims["password"].(string)
user.Id = id
user.Name = name
user.Account = account
user.Password=pwd
return user, nil
}
return user, fmt.Errorf("invalid token")
}
2.3 校验token
解析用户的请求并获取校验token。
func JWTLoginMiddleware() func(c *gin.Context) model.User {
return func(c *gin.Context) model.User {
loginHeader := c.Request.Header.Get("Login")
if loginHeader == "" {
c.JSON(http.StatusOK, gin.H{
"code": 2003,
"msg": "请求头中login为空",
})
c.Abort()
return model.User{Id: -1}
}
parts := strings.SplitN(loginHeader, " ", 2)
if !(len(parts) == 2 && parts[0] == "Bearer") {
c.JSON(http.StatusOK, gin.H{
"code": 2004,
"loginHeader": loginHeader,
"msg": "请求头中login格式有误",
})
c.Abort()
return model.User{Id: -1}
}
mc, err := ParseToken(parts[1])
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 2005,
"parts[1]": parts[1],
"msg": "无效的Token",
})
c.Abort()
return model.User{Id: -1}
}
c.Next()
return mc
}
}
3 实现controller
在controller中进行处理Handler
3.1 实现Login
假设数据库中有2667
和2222
这两个账户,他们的Id
格式为账号+密码
、名字
格式为user+账号
。Login的实现如下。
controller/Login.go
func Login(c *gin.Context) {
// 用户发送用户名和密码过来
user := model.LoginReq{}
err := c.ShouldBind(&user)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 2001,
"msg": "无效的参数",
})
return
}
// 校验用户名和密码是否正确
if user.Account == "2667" && user.Password == "1234" ||
user.Account == "2222" && user.Password == "1234" {
id,_:=strconv.Atoi(user.Account+user.Password)
// 生成Token
tokenString, _ := common.GenerateToken(model.User{
Id: id ,
Name: "user" + user.Account,
Account: user.Account,
Password: user.Password,
})
c.JSON(http.StatusOK, gin.H{
"code": 2000,
"msg": "success",
"token": tokenString,
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 2002,
"msg": "鉴权失败",
})
return
}
3.2 实现Do
用户通过User/Do
接口查询信息,Do的实现如下:
controller/Do.go
func Do(c *gin.Context) {
mc := common.JWTLoginMiddleware()(c)
c.JSON(http.StatusOK, mc)
}
4 实现router
定义两个接口/User/Login
和/User/Do
实现登录和查询信息,端口设置为8080。
router.go
// 初始化一个http服务对象
var Engine = gin.Default()
func RegisterHandlers() {
Engine.POST("/User/Login", controller.Login)
Engine.POST("/User/Do", controller.Do)
err := Engine.Run(":8080")
if err != nil {
return
}
}
5 调用
main.go
func main() {
RegisterHandlers()
}
6 测试
6.1 apifox介绍
Apifox 是接口管理、开发、测试全流程集成工具,定位 Postman + Swagger + Mock + JMeter。通过一套系统、一份数据,解决多个系统之间的数据同步问题。只要定义好接口文档,接口调试、数据 Mock、接口测试就可以直接使用,无需再次定义;接口文档和接口开发调试使用同一个工具,接口调试完成后即可保证和接口文档定义完全一致。高效、及时、准确!
6.2 登录测试
- 输入以下正确信息得到的结果:
- 错误账号:
- 错误密码:
6.3 Do接口测试
- 输入图1token的结果:
- 错误token: