micro 使用 jwt 做统一的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
}
网友评论