对JWT中的核心代码,逐步解析学习
package jwt
import (
"errors"
"fmt"
"time"
"github.com/dgrijalva/jwt-go"
)
//注意下方的User其实应该定义在module数据模型里 这里定义支付辅助显示一下下
type User struct {
name string
Password string
juese string
shouji string
}
//验证与解密当中的密匙设置的复杂一点,
//viper.GetString会设置这个值(32byte长度)
var AppSecret = "guwen-0208"
//发行网站发行人的意思,
//我的备注:这个值会被viper.GetString重写
var AppIss = "github.com/libragen/felix"
//自定义payload结构体,不建议直接使用 dgrijalva/jwt-go `jwt.StandardClaims`结构体.因为他的payload包含的用户信息太少.
//我的备注:自己的结构体结合jwt框架里的结构体,形成新的结构体来存放信息
type userStdClaims struct {
jwt.StandardClaims
*User
}
//实现 `type Claims interface` 的 `Valid() error` 方法,自定义校验内容,
//我的备注:自定义一个函数验证差事,发行人错误等等情况,是代码简洁
func (c userStdClaims) Valid() (err error) {
/*
特别注意,下面的c.VerifyExpiresAt方法和c.VerifyIssuer方法都是jwt框架里
jwt.StandardClaims结构体的方法,继承到了userStdClaims上面
*/
//检查一下超时没有,超时就返回错误
if c.VerifyExpiresAt(time.Now().Unix(), true) == false {
return errors.New("token is expired")
}
//检查一下发行方正确,令牌的发行方错误
if !c.VerifyIssuer(AppIss, true) {
return errors.New("token's issuer is wrong")
}
//检查用户ID是否有效,invalid user in jwt
if c.User.Id < 1 {
return errors.New("invalid user in jwt")
}
return
}
//登录生成token令牌,其中m是数据模型当中定义的用户结构体,d是超时时间使用time时间即可,比如一年这样写time.Hour*24*365
func jwtGenerateToken(m *User,d time.Duration) (string, error) {
//清空密码,防止密码泄露
m.Password = ""
//设置超时时间,当前时间+超时时间
expireTime := time.Now().Add(d)
//设置jwt当中类似用户对象StandardClaims
stdClaims := jwt.StandardClaims{
ExpiresAt: expireTime.Unix(),//过期时间,int64类型
IssuedAt: time.Now().Unix(),//发现人时间,int64类型
Id: fmt.Sprintf("%d", m.Id),//用户ID字符串类型
Issuer: AppIss,//发行人,字符串类型
}
//实例我们定义的userStdClaims这个结合我们和框架的结构体
uClaims := userStdClaims{
StandardClaims: stdClaims,
User: m,
}
//通过加密方式jwt.SigningMethodHS256加密用户结构体uClaims
token := jwt.NewWithClaims(jwt.SigningMethodHS256, uClaims)
// Sign and get the complete encoded token as a string using the secret
// 使用密钥签名并以字符串形式获取完整的编码令牌
tokenString, err := token.SignedString([]byte(AppSecret))
return tokenString, err
}
//解析用户登录当中携带的token信息,解密并返回用户对象User,接受参数字符串形式的token
//JwtParseUser 解析payload的内容,得到用户信息
func JwtParseUser(tokenString string) (*User, error) {
if tokenString == "" {
//返回错误,在授权持有者中未发现令牌
return nil, errors.New("no token is found in Authorization Bearer")
}
//设置一个空的userStdClaims接受返回来token中携带的信息
claims := userStdClaims{}
//
//解析token,函数接受三个参数
//第一个,未解密的token字符串
//第二个,之前定义的空的userStdClaims对象来接受返回信息
//第三个一个函数,我们称之为keyFunc函数,将接收解析后的令牌,并返回用于验证的密钥
_, err := jwt.ParseWithClaims(tokenString, &claims, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHS256); !ok {
/* 加密方式错误
意想不到的签名方法,说明token中加密方式和咱们之前加密的方式有出入
Header通常由两部分组成:令牌的类型,即JWT。和常用的散列算法,如HMAC SHA256或RSA
{
"alg": "HS256",
"typ": "JWT"
}
咱们生成的时候使用的SHA256加密方式即jwt.SigningMethodHS256,正常是不会出这个错误的
*/
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
//返回加密当初设置的秘钥
return []byte(AppSecret), nil
})
//解析未发生错误
if err != nil {
return nil, err
}
return claims.User, err
}