micro 使用 jwt 做統(tǒng)一的token 驗證冰抢,看代碼就好了
package token_jwt
import (
"encoding/json"
"errors"
"github.com/dgrijalva/jwt-go"
"github.com/micro/cli"
"github.com/micro/go-micro/config"
"github.com/micro/go-micro/config/source/etcd"
"github.com/micro/go-micro/v2/logger"
"github.com/micro/micro/plugin"
"net/http"
"strings"
)
var (
TokenExpired = errors.New("Token is expired")
TokenNotValidYet = errors.New("Token not active yet")
TokenMalformed = errors.New("That's not even a token")
TokenInvalid = errors.New("Couldn't handle this token:")
)
type Token struct {
Name string
PrivateKey []byte
conf config.Config
UnAuthPath []string
}
type JwtPayloadInfo struct {
UserName string
UUID string
ID uint
NickName string
AuthorityId string
jwt.StandardClaims
}
//定義一些參數,可以通過啟動micro 的時候傳參
func (l *Token) Flags() []cli.Flag {
return []cli.Flag{cli.StringFlag{
Name: "token_path",
Usage: "token私鑰在etcd中的路徑",
EnvVar: "TOKEN_PATH",
}}
}
func (l *Token) Commands() []cli.Command {
return nil
}
//處理程序 會在每次請求的時候調用
func (l *Token) Handler() plugin.Handler {
return func(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
//處理token 的邏輯
if Contains(l.UnAuthPath,r.URL.Path)!=-1 {
logger.Info("allow pass url :",r.URL.Path)
handler.ServeHTTP(w, r)
return
}
authStr := r.Header.Get("Authorization")
logger.Infof("authStr:%s",authStr)
payload, err := l.Decode(authStr)
if err != nil {
logger.Info("auth fail:",err)
w.WriteHeader(http.StatusUnauthorized)
return
}
bs,_:=json.Marshal(payload)
logger.Infof("payload info= %s", string(bs))
r.Header.Add("claims",string(bs))
handler.ServeHTTP(w, r)
return
})
}
}
//初始化數據,程序啟動的時候會調用這個方法炸站,可以在這里初始化一些參數
func (l *Token) Init(ctx *cli.Context) error {
//從配置加載 公鑰
//加載公鑰蔚万,放行鏈接等,存放到實例里面
address:=ctx.String("registry_address")
token_path:=ctx.String("token_path")
source := etcd.NewSource(
etcd.WithAddress(address),
)
l.conf=config.NewConfig()
err:=l.conf.Load(source)
if err!=nil {
logger.Fatal(err)
}
value:=l.conf.Get(strings.Split(token_path,"/")...).Bytes()
logger.Debug(l.conf.Map())
if err!=nil {
logger.Fatal(err)
}
l.PrivateKey=value
logger.Info("JWT privateKey:", string(value))
//初始化放行地址
l.UnAuthPath=make([]string,0)
l.UnAuthPath=append(l.UnAuthPath,"/admin/base/login")
l.enableAutoUpdate(strings.Split(token_path,"/")...)
return nil
}
//配置更新的方法
func (l *Token) enableAutoUpdate(path ...string) {
go func() {
for {
w, err := l.conf.Watch(path...)
if err != nil {
logger.Error(err)
}
v, err := w.Next()
if err != nil {
logger.Error(err)
}
value := v.Bytes()
l.PrivateKey=value
logger.Info("New JWT privateKey:", string(l.PrivateKey))
}
}()
}
func (l *Token) String() string {
return l.Name
}
//調用這個方法 new 插件,也可以在啟動的時候這樣子寫
func NewPlugin() plugin.Plugin {
return &Token{
Name: "token-jwt",
}
}
//Decode 解碼
func (l *Token) Decode(tokenStr string) (*JwtPayloadInfo, error) {
token, err := jwt.ParseWithClaims(tokenStr, &JwtPayloadInfo{}, func(token *jwt.Token) (i interface{}, e error) {
return l.PrivateKey, 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
}
}
}
if token != nil {
if claims, ok := token.Claims.(*JwtPayloadInfo); ok && token.Valid {
return claims, nil
}
return nil, TokenInvalid
} else {
return nil, TokenInvalid
}
}
// Encode 將 User 用戶信息加密為 JWT 字符串
// expireTime := time.Now().Add(time.Hour * 24 * 3).Unix() 三天后過期
func (l *Token) CreateToken(claims JwtPayloadInfo) (string, error) {
logger.Infof("encode token info :%v",claims)
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return jwtToken.SignedString(l.PrivateKey)
}
//判斷數組包含
func Contains(array []string, val string) (index int) {
index = -1
for i,v:=range array {
if v==val{
index=i
return
}
}
return
}