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)
|
||
}
|