micro统一认证 Token

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
}


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351