使用 gin web 框架,在 moose-go 基础之上开发
安装
go get -u github.com/dgrijalva/jwt-go
封装 JWT
生成 JWT
type JwtUtil struct{}
var verifyKey = []byte("moose-go")
type CustomClaims struct {
*jwt.StandardClaims
*model.UserInfo
}
func GeneratorJwt(userInfo *model.UserInfo) (string, error) {
claims := &CustomClaims{
&jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Minute * 60 * 24).Unix(),
IssuedAt: time.Now().Unix(),
},
userInfo,
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(verifyKey)
}
- jwt.StandardClaims 负载 jwt 信息
- ExpiresAt 过期时间
- IssuedAt 签发时间
- 创建 CustomClaims 结构体,用来封装 jwt 信息
- jwt.NewWithClaims 创建 jwt
- jwt.SigningMethodHS256 使用 SigningMethodHS256 签名
- claims 负载信息
- token.SignedString 转换成 str 字符串
解析 JWT
func ParseJwt(tokenStr string) *jwt.Token {
token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return verifyKey, nil
})
if token.Valid {
log.Println("You look nice today")
return token
} else if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
log.Printf("%v", ve)
panic(api.JwtValidationErr)
} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
log.Printf("%v", ve)
panic(api.JwtExpiresErr)
} else {
panic(api.JwtExpiresErr)
}
} else {
panic(api.JwtExpiresErr)
}
}
- jwt.Parse 解析 jwt,需要保持签名一直 verifyKey
- token.Valid 返回 bool 值,true 解析成功,false 解析失败
- 解析失败会返回对应错误信息
从 Header 中解析、获取 token
func ParseBearerToken(token string) string {
tokens := strings.Split(token, " ")
if len(tokens) != 2 || !strings.EqualFold("Bearer", tokens[0]) {
panic(api.JwtValidationErr)
}
return tokens[1]
}
Gin 中使用
在中间件中使用
auth_required.go
package middleware
import (
"log"
"moose-go/api"
"moose-go/util"
"github.com/gin-gonic/gin"
)
var anonymous = []string{
"/api/v1/account/register",
"/api/v1/account/login",
"/api/v1/qrcode/get",
"/api/v1/qrcode/ask",
"/api/v1/sms/send",
"/api/v1/user/info",
}
func AuthRequired() gin.HandlerFunc {
return func(c *gin.Context) {
path := c.Request.URL.Path
if util.In(path, anonymous) {
c.Next()
return
}
bearerToken := c.GetHeader("Authorization")
log.Println("auth check token... ", bearerToken)
if bearerToken == "" {
panic(api.JwtValidationErr)
}
// Bearer xxxx
token := util.ParseBearerToken(bearerToken)
util.ParseJwt(token)
c.Next()
}
}
-
anonymous 可以匿名访问,不需要登录的接口
-
path := c.Request.URL.Path 获取访问路由
-
if util.In(path, anonymous) 在匿名访问中,直接放行 c.Next()
-
bearerToken := c.GetHeader("Authorization") 从头部获取 token
-
解析 token,校验 token
token := util.ParseBearerToken(bearerToken) util.ParseJwt(token)
登录接口
- 在登录校验通过之后,生成 jwt token 返回
- 把用户 id 封装到 jwt 负载中
- 通过解析 jwt 获取 userId,通过 userId 获取用户信息
userId := string(userResult[0]["user_id"])
return createToken(&model.UserInfo{UserId: userId})
// cerate jwt tokn
func createToken(userInfo *model.UserInfo) string {
token, err := util.GeneratorJwt(userInfo)
if err != nil {
panic(api.JwtGeneratorErr)
}
return token
}
获取用户信息
func (uc *UserService) GetUserByToken(header string) *model.UserInfo {
token := util.ParseBearerToken(header)
jwtToken := util.ParseJwt(token)
data, err := json.Marshal(jwtToken.Claims)
if err != nil {
panic(api.QueryUserFailErr)
}
var claims jwt.MapClaims
err = json.Unmarshal(data, &claims)
if err != nil {
panic(api.QueryUserFailErr)
}
userId, ok := claims["userId"]
if userId == "" || !ok {
panic(api.QueryUserFailErr)
}
。。。。
}