micro统一认证 Token

2020-07-09  本文已影响0人  路人甲Boger

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
}


上一篇下一篇

猜你喜欢

热点阅读