186 lines
4.4 KiB
Go
186 lines
4.4 KiB
Go
|
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)
|
|||
|
}
|