go get -u github.com/dgrijalva/jwt-go
"github.com/dgrijalva/jwt-go"
// JwtClaims 创建自己的Claims
type JwtClaims struct {
*jwt.StandardClaims
//用户编号
UID string
Username string
}
// 登录的参数
type UserLogin struct {
UserName string `json:"user_name"` //用户名
Password string `json:"password"` //密码,对明文密码进行sha256
}
var (
//盐
secret = []byte("wondersafebox") // 后续加密增加盐增加复杂度
TokenExpired error = errors.New("Token is expired") // token错误类型提炼
TokenNotValidYet error = errors.New("Token not active yet") // token错误类型提炼
TokenMalformed error = errors.New("That's not even a token") // token错误类型提炼
TokenInvalid error = errors.New("Couldn't handle this token:") // token错误类型提炼
)
// CreateJwtToken 生成一个jwttoken
func CreateJwtToken(id string, username string) (string, error) {
// 定义过期时间,7天后过期
expireToken := time.Now().Add(time.Hour * 24 * 7).Unix()
claims := JwtClaims{
&jwt.StandardClaims{
NotBefore: int64(time.Now().Unix() - 1000), // token信息生效时间
ExpiresAt: int64(expireToken), // 过期时间
Issuer: "wonders", // 发布者
},
id,
username,
}
// 对自定义claims加密,jwt.SigningMethodHS256是加密算法得到第二部分
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// 给这个token盐加密 第三部分,得到一个完整的三段的加密
signedToken, err := token.SignedString(secret)
if err != nil {
return "", err
}
return signedToken, nil
}
在登录验证时,首先校验完用户名和密码是否匹配,匹配生成后调用工具包jwt中的CreateJwtToken方法生成token返回给前端
// 用户登录
func UserLogin(c *gin.Context) {
appG := app.Gin{C: c}
//1.解析footprint Body参数
userLoginBody := new(models.UserLogin)
if err := c.ShouldBindJSON(userLoginBody); err != nil {
appG.Response(models.ErrorArgs, "参数错误", err.Error())
return
}
// 一些用户名、密码校验业务代码
... ...
//password := sha256.Sum256([]byte(userLoginBody.Password)) // sha256加密
//passwordStr:=hex.EncodeToString(password[:]) // 转为字符串
// 参数Password(前端已经做sha256加密)与查询到的用户密码做比较是否一直
if userData["password"] != userLoginBody.Password {
appG.Response(models.ErrorFormatPassword, "用户密码错误", err.Error())
return
}
// 登录成功生成token
token, err := jwt.CreateJwtToken(userData["account_id"].(string), userData["user_name"].(string))
if err != nil {
appG.Response(models.ErrorToken, "生成token失败", err.Error())
return
}
// 登录成功返回前端生成的token
appG.Response(models.Success, "登录成功", token)
}
前端后续接口需要携带返回的token在请求头中,便于后续访问
ParseJwtToken方法与CreateJwtToken方法可一起放在自建的工具包jwt中便于调用
// ParseJwtToken 解析token得到是自己创建的Claims
func ParseJwtToken(jwtToken string) (*JwtClaims, error) {
var jwtclaim = &JwtClaims{}
_, err := jwt.ParseWithClaims(jwtToken, jwtclaim, func(*jwt.Token) (interface{}, error) {
//得到盐
return secret, nil
})
if err != nil {
if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
return nil, TokenMalformed
} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
// Token is expired
return nil, TokenExpired
} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
return nil, TokenNotValidYet
} else {
return nil, TokenInvalid
}
}
}
return jwtclaim, nil
}
在众多接口都需要验证前端token的情况下,我们可以写一个中间件,在请求访问之前先统一对请求头中的token值进行验证,成功之后再进行后续请求响应,反正直接返回请求头错误,拒绝访问
// 校验token中间件
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 从请求头中获取token
tokeString := c.GetHeader("token")
fmt.Println(tokeString, "当前token")
if tokeString == "" {
c.JSON(http.StatusOK, gin.H{
"code": 1005,
"message": "必须传递token",
})
c.Abort()
return
}
claims, err := jwt.ParseJwtToken(tokeString)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 1005,
"message": "token解析错误",
})
c.Abort()
return
}
// 从token中解析出来的数据挂载到上下文上,方便后面的控制器使用
c.Set("userId", claims.UID)
c.Set("userName", claims.Username)
c.Next()
}
}
在路由中使用如下,先登录校验生成token,再在需要验证token的接口前进行验证token
参考博文:
gin框架使用jwt对前端用户身份认证
Golang Gin框架使用JWT实现简单身份验证
golang之JWT实现