package jwt import ( "errors" "fmt" "strconv" "time" "biblio.bing89.com/auth" "biblio.bing89.com/auth/jwt/store" "biblio.bing89.com/code" "biblio.bing89.com/utils/ginhelper" "github.com/gin-gonic/gin" ) type manager struct { store store.ClaimStore term time.Duration signedSecret string } func NewManager(store store.ClaimStore, term time.Duration, secret string) auth.AuthManager { return &manager{ store: store, term: term, signedSecret: secret, } } func (m *manager) Login(identifyFunc auth.IdentifyFunc, data *auth.AuthenticationRequest) (*auth.AuthenticationResponse, error) { // if _, err := m.IsLogined(data.Username); err == nil { // return nil, fmt.Errorf("user is already logined") // } resp, err := identifyFunc(data) if err != nil { return nil, err } tokenClaim, err := m.IsLogined(resp.UID) if err == nil && tokenClaim != nil { //已登录 //刷新过期时间 err = m.Expire(tokenClaim.UserID, m.term) tokenString, err1 := tokenClaim.String(m.signedSecret) if err != nil || tokenString == "" { return nil, fmt.Errorf("get token string failed:%v", err1) } resp.Token = tokenString if err != nil { return resp, fmt.Errorf("update token expiration failed:%v", err) } return resp, nil } //未登录 tokenClaim, err = NewTokenClaim(data.Username, m.term, resp.UID) if err != nil { return nil, fmt.Errorf("generate token failed:%v", err) } tokenString, err := tokenClaim.String(m.signedSecret) if err != nil { return nil, fmt.Errorf("get token string failed:%v", err) } //存储登录状态 err = m.StoreLoginState(resp.UID, tokenClaim) if err != nil { return nil, fmt.Errorf("StoreLoginState failed:%v", err) } resp.Token = tokenString return resp, nil } func (m *manager) Logout(data *auth.AuthenticationRequest) error { //解析token,确认有logout权限 claim, err := ParseTokenClaim(data.Token, m.signedSecret) if err != nil { return err } if c, err := m.IsLogined(claim.UserID); err == nil { err = m.DeleteToken(c) return err } return fmt.Errorf("user not login or token invalid") } func (m *manager) Refresh(data *auth.AuthenticationRequest) (string, error) { cliam, err := ParseTokenClaim(data.Token, m.signedSecret) if err != nil { return "", err } //已过期 if cliam.Valid() != nil { return "", fmt.Errorf("cannot use an invalid token to refresh: %v", err) } //找不到登陆状态 if _, err := m.IsLogined(cliam.UserID); err != nil { return "", fmt.Errorf("token invalid, user maybe not login") } //重置有效期 cliam.ExpiresAt = time.Now().Add(m.term).Unix() err = m.StoreLoginState(cliam.UserID, cliam) if err != nil { return "", err } tokenStr, err := cliam.String(m.signedSecret) return tokenStr, err } //中间件 //ERR_NOT_AUTH func (m *manager) MiddleWare() gin.HandlerFunc { return func(c *gin.Context) { tokenString := c.GetHeader("Token") if tokenString == "" { ginhelper.Error(c, code.CodeNoAuth, errors.New("token is empty")) return } tokenClaim, err := ParseTokenClaim(tokenString, m.signedSecret) if err != nil { ginhelper.Error(c, code.CodeNoAuth,err) return } if err := m.TokenValid(tokenClaim.UserID, tokenString); err != nil { ginhelper.Error(c, code.CodeNoAuth, err) return } c.Set("uid", strconv.FormatUint(tokenClaim.UserID, 10)) c.Next() } } //存储登录状态 func (m *manager) StoreLoginState(user uint64, claim *TokenClaim) error { //储存登录状态 str, err := claim.String(m.signedSecret) if err != nil { return err } return m.store.Set(user, str, m.term) } //获取token //是否已登录 func (m *manager) IsLogined(user uint64) (*TokenClaim, error) { token, err := m.store.Get(user) if err != nil { return nil, err } return ParseTokenClaim(token, m.signedSecret) } //删除token func (m *manager) DeleteToken(claim *TokenClaim) error { return m.store.Delete(claim.UserID) } func (m *manager) Close() error { return m.store.Close() } func (m *manager) TokenValid(uid uint64, token string) error { t, err := m.store.Get(uid) if err != nil { return fmt.Errorf("get token failed: %v", err) } if t != token { return fmt.Errorf("token is invalid") } return nil } func (m *manager) ExpireAt(uid uint64, t time.Time) error { err := m.store.ExpireAt(uid, t) return err } func (m *manager) Expire(uid uint64, t time.Duration) error { err := m.store.Expire(uid, t) return err } func (m *manager) TTL(uid uint64) (time.Duration, error) { return m.store.TTL(uid) }