本文使用Go Gin框架搭建的webserver示例,其中使用jwt进行登录验证,至于cookies session jwt token的区别,优缺点不再本位讨论范围内。
简单总结:使用jwt更方便,具有一定程度安全性,
具体使用Gin搭建webserver提供API服务部分就不赘述了, 可以参考其他博客介绍。完整代码在这里,
在标准的Claims信息之外添加一些自定义信息
// 载荷,可添加自己需要的一些信息
type CustomClaims struct {
UserId int64 `json:"userId"`
UserName string `json:"userName"`
RoleId string `json:"roleId"`
jwt.StandardClaims
}
类似于java中过滤器
func JWTAuth() gin.HandlerFunc {
return func(c *gin.Context) {
//过滤是否验证token, login结构直接放行,这里为了简单起见,直接判断路径中是否带login,携带login直接放行
if strings.Contains(c.Request.RequestURI, "login") {
return
}
token := c.Request.Header.Get("token")
if token == "" {
c.JSON(http.StatusOK, gin.H{
"status": -1,
"msg": "请求未携带token,无权限访问",
})
c.Abort()
return
}
klog.Infof("gotten token:%s", token)
j := NewJWT()
// parse token, get the user and role info
claims, err := j.ParseToken(token)
if err != nil {
if err == util.TokenExpired {
c.JSON(http.StatusOK, gin.H{
"status": -1,
"msg": "授权已过期",
})
c.Abort()
return
}
c.JSON(http.StatusOK, gin.H{
"status": -1,
"msg": err.Error(),
})
c.Abort()
return
}
// 继续交由下一个路由处理,并将解析出的信息传递下去
c.Set(util.Gin_Context_Key, claims)
}
}
/login接口不需要验证登录, 其他接口需要验证登录的token
func ConfigRouter(router *gin.Engine) {
userController := mycontroller.NewUserController()
//配置middleware
router.Use(myjwt.JWTAuth())
router.POST("/login", userController.Login)
router.GET("/users", userController.GetAllUsers)
router.GET("/usersfind", userController.FindUsers)
router.GET("/users/:userId", userController.GetOneUser)
router.PUT("/users", userController.CreateOneUser)
router.POST("/users/:userId", userController.UpdateOneUser)
router.DELETE("/users/:userId", userController.DeleteOneUser)
}
生成token
func generateToken(c *gin.Context, user mydomain.User, roleId string, expiredTimeByMinute int64) {
j := &myjwt.JWT{
[]byte(util.SignKey),
}
claims := myjwt.CustomClaims{
user.UserId,
user.UserName,
roleId,
jwtgo.StandardClaims{
NotBefore: int64(time.Now().Unix() - 1000), // 签名生效时间
ExpiresAt: int64(time.Now().Unix() + expiredTimeByMinute*60), // 过期时间 一小时
Issuer: "ginjwtdemo", //签名的发行者
},
}
token, err := j.CreateToken(claims)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"status": -1,
"msg": err.Error(),
})
return
}
data := mydomain.LoginResp{
Token: token,
}
c.JSON(http.StatusOK, gin.H{
"status": 0,
"msg": "登录成功!",
"data": data,
})
return
}
解析token
func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
return j.SigningKey, nil
})
if err != nil {
if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
return nil, util.TokenMalformed
} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
// Token is expired
return nil, util.TokenExpired
} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
return nil, util.TokenNotValidYet
} else {
return nil, util.TokenInvalid
}
}
}
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
return claims, nil
}
return nil, util.TokenInvalid
}