go微服务框架Kratos笔记(七)使用jwt认证中间件
引言
Json web token (JWT) 是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
jwt构成
JWT是由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串,就像这样
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IjEiLCJkaWZmZXJlbnRpYXRlIjoiMSIsImV4cCI6MTYzNjUyMTYzNywiaXNzIjoiTWljcm9zZXJ2aWNlLkJGRiJ9.5LQ_mBJLkHUxjSqoE8evh7_UahrK8WqLgvhpZ2HIIiI
第一部分我们称它为头部(header),第二部分我们称其为载荷(payload),第三部分是签证(signature)
header
jwt的头部承载两部分信息
typ:声明类型
alg:声明加密的算法
然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分.
playload
载荷就是存放有效信息的地方,这些有效信息包含三个部分
- 标准中注册的声明
- 公共声明
- 私有声明
标准声明(建议但不强制使用):
- iss: jwt签发者
- sub: jwt所面向的用户
- aud: 接收jwt的一方
- exp: jwt的过期时间,这个过期时间必须要大于签发时间
- nbf: 定义在什么时间之前,该jwt都是不可用的.
- iat: jwt的签发时间
- jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
公共声明:
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
私有声明:
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
然后将其进行base64加密,得到Jwt的第二部分。
signature
jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
- header(base64后的)
- payload(base64后的)
- secret
此部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。
将这三部分用.连接成一个完整的字符串,构成了最终的jwt
在kratos框架中的使用
实现jwt自签逻辑
新建auth授权方法,可以在用户登录成功后调用Auth方法传入参数后返回jwt
package biz
import (
"kanxun_bff/internal/conf"
"time"
"github.com/golang-jwt/jwt"
jwtv4 "github.com/golang-jwt/jwt/v4"
)
type AuthUseCase struct {
key string
}
func NewAuthUseCase() *AuthUseCase {
return &AuthUseCase{
key: "testKey",
}
}
type MyClaims struct {
UserName string `json:"username"`
jwtv4.StandardClaims
}
func (receiver AuthUseCase) Auth(username string) (string, error) {
myClaims := MyClaims{
username,
jwtv4.StandardClaims{
ExpiresAt: time.Now().Add(time.Hour * 2).Unix(), //设置JWT过期时间,此处设置为2小时
Issuer: conf.GetConfig().GetString("project.name"), //设置签发人
},
}
claims := jwt.NewWithClaims(jwt.SigningMethodHS256, myClaims)
//加盐
return claims.SignedString([]byte(receiver.key))
}
自定义jwt中间件
本文使用自定义jwt中间件,未使用kratos官方提供的jwt
kratos jwt middleware
// NewAuthServer jwt Server中间件
func NewAuthServer(authUc *biz.AuthUseCase) func(handler middleware.Handler) middleware.Handler {
return func(handler middleware.Handler) middleware.Handler {
return func(ctx context.Context, req interface{}) (reply interface{}, err error) {
var jwtToken string
if md, ok := metadata.FromIncomingContext(ctx); ok {
jwtToken = md.Get("x-md-global-jwt")[0]
} else if header, ok := transport.FromServerContext(ctx); ok {
jwtToken = strings.SplitN(header.RequestHeader().Get("Authorization"), " ", 2)[1]
} else {
// 缺少可认证的token,返回错误
return nil, auth.ErrAuthFail
}
token, err := authUc.CheckJWT(jwtToken)
if err != nil {
// 缺少合法的token,返回错误
return nil, auth.ErrAuthFail
}
ctx = context.WithValue(ctx, "username", token["username"])
ctx = context.WithValue(ctx, "differentiate", token["differentiate"])
reply, err = handler(ctx, req)
return
}
}
}
// NewAuthClient jwt Client中间件
func NewAuthClient(authUc *biz.AuthUseCase) middleware.Middleware {
return func(handler middleware.Handler) middleware.Handler {
return func(ctx context.Context, req interface{}) (interface{}, error) {
if header, ok := transport.FromServerContext(ctx); ok {
//如果Token为空则生成token,如果
if header.RequestHeader().Get(authorizationKey) != "" {
jwtToken := strings.SplitN(header.RequestHeader().Get(authorizationKey), " ", 2)[1]
//元数据传递
ctx = metadata.AppendToOutgoingContext(ctx, "x-md-global-jwt", jwtToken)
} else {
token, err := authUc.Auth(
req.(*user.GetUserNameRequest).Username,
req.(*user.GetUserNameRequest).Differentiate,
)
if err != nil {
return nil, jwt.ErrGetKey
}
ctx = metadata.AppendToOutgoingContext(ctx, "x-md-global-jwt", token)
}
}
return handler(ctx, req)
}
}
}
路由白名单与jwt中间件的引入
//client
conn, err := grpc.DialInsecure(
context.Background(),
grpc.WithEndpoint("discovery:///xxx.xxx.grpc"),
grpc.WithDiscovery(r),
grpc.WithMiddleware(
recovery.Recovery(),
logging.Client(logger), //日志中间件,
tracing.Client(), //链路追踪中间件
metadata.Client(), //元数据传递中间件
jwt.NewAuthClient(authUc), //jwt中间件
),
)
//server
var opts = []grpc.ServerOption{
grpc.Middleware(
recovery.Recovery(), //异常恢复中间件
tracing.Server(), //链路追踪中间件
logging.Server(logger), //日志中间件
metrics.Server(), //监控中间件
validate.Validator(), //参数校验
metadata.Server(), //元数据中间件
selector.Server( //jwt中间件
jwt.NewAuthServer(authUc),
).Match(func(operation string) bool {
// 白名单
r, err := regexp.Compile("/api.user.v1.User/GetUserName")
if err != nil {
// 自定义错误处理
return true
}
return r.FindString(operation) != operation
}).Build(),
),
}
下游服务获取用户信息
value := ctx.Value("username")
fmt.Println(value)
如有错误请留言反馈
References
https://www.jianshu.com/p/576dbf44b2ae
https://zhuanlan.zhihu.com/p/86937325
https://go-kratos.dev/docs/component/middleware/auth
https://go-kratos.dev/docs/component/metadata
go微服务框架Kratos笔记(七)使用jwt认证中间件的更多相关文章
- go微服务框架Kratos笔记(一)入门教程
kratos简介 Kratos 一套轻量级 Go 微服务框架,包含大量微服务相关功能及工具 本文基于kratos v2.0.3,windows平台,其他系统平台均可借鉴参考 环境搭建 Golang开发 ...
- go微服务框架Kratos笔记(六)链路追踪实战
什么是链路追踪 借用阿里云链路追踪文档来解释 分布式链路追踪(Distributed Tracing),也叫 分布式链路跟踪,分布式跟踪,分布式追踪 等等,它为分布式应用的开发者提供了完整的调用链路还 ...
- go微服务框架Kratos笔记(三)引入GORM框架
介绍 GORM是一个使用Go语言编写的ORM框架.中文文档齐全,对开发者友好,支持主流数据库. GORM官方文档 安装 go get -u github.com/jinzhu/gorm 在kratos ...
- go微服务框架kratos学习笔记七(kratos warden 负载均衡 balancer)
目录 go微服务框架kratos学习笔记七(kratos warden 负载均衡 balancer) demo demo server demo client 池 dao service p2c ro ...
- kratos微服务框架学习笔记一(kratos-demo)
目录 kratos微服务框架学习笔记一(kratos-demo) kratos本体 demo kratos微服务框架学习笔记一(kratos-demo) 今年大部分时间飘过去了,没怎么更博和githu ...
- go微服务框架kratos学习笔记五(kratos 配置中心 paladin config sdk [断剑重铸之日,骑士归来之时])
目录 go微服务框架kratos学习笔记五(kratos 配置中心 paladin config sdk [断剑重铸之日,骑士归来之时]) 静态配置 flag注入 在线热加载配置 远程配置中心 go微 ...
- # go微服务框架kratos学习笔记六(kratos 服务发现 discovery)
目录 go微服务框架kratos学习笔记六(kratos 服务发现 discovery) http api register 服务注册 fetch 获取实例 fetchs 批量获取实例 polls 批 ...
- go微服务框架kratos学习笔记四(kratos warden-quickstart warden-direct方式client调用)
目录 go微服务框架kratos学习笔记四(kratos warden-quickstart warden-direct方式client调用) warden direct demo-server gr ...
- go微服务框架kratos学习笔记八 (kratos的依赖注入)
目录 go微服务框架kratos学习笔记八(kratos的依赖注入) 什么是依赖注入 google wire kratos中的wire Providers injector(注入器) Binding ...
随机推荐
- 你说要你想玩爬虫,但你说你不懂Python正则表达式,我信你个鬼,那你还不来看看?
前言 正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配. re 模块也提供了与这些方法功能完全一致的函数,这些函数使用一个模式字符串做为它们的第一个参数. re.mat ...
- 学习Tomcat(五)之Context和Wrapper容器
前面的文章中,我们介绍了Tomcat的Engine和Host容器,我们知道一个Tomcat最多只有一个Engine容器,一个Engine容器可以包含多个Host容器,请求中的不同的Host对应不用的H ...
- 微信小程序Canvas添加水印字体,通过setGlobalAlpha设置字体透明度。
微信小程序自带的设置透明度只有setGlobalAlpha,但是CanvasContext.setGlobalAlpha设置透明度,是全局透明,整张图片都透明了.所以直接使用是不行的. 换种思路实现: ...
- Vue自定义标签页,并且在其中渲染Echarts图表
目录 一.需求说明 二.标签页功能实现 一.需求说明 1.点击标签按钮切换不同的echarts图表,考虑用Ant Design Vue,但是其样式无法自定义 2.div的整体布局样式使用tailwin ...
- MacOS下Java与JDK关系与相关路径
MacOS下Java与JDK关系与相关路径 macOS下的Java与JDK的路径曾经困扰过我一段时间,今天稍有些忘记,故记下笔记,整理一下.Java与JDK的关系不在本文笔记之内,Javaer常识. ...
- 华为在HDC2021发布全新HMS Core 6 宣布跨OS能力开放
[2021年10月22日·东莞]华为开发者大会 2021(Together)于今天正式开幕,华为在主题演讲中正式发布全新的HMS Core 6,向全球开发者开放7大领域的69个Kit和21,738个A ...
- Java中的函数式编程(五)Java集合框架中的高阶函数
写在前面 随着Java 8引入了函数式接口和lambda表达式,Java 8中的集合框架(Java Collections Framework, JCF)也增加相应的接口以适应函数式编程. 本文的 ...
- 【UE4 C++ 基础知识】<10>资源的引用
2种引用方式 硬引用(Hard Reference) 即对象 A 引用对象 B,并导致对象 B 在对象 A 加载时加载 硬引用过多会导致运行时很多暂时用不到的资源也被加载到内存中 大量资源会导致进程阻 ...
- leetcode 5/300 最长回文子串 py
目录 题目说明 方法一:动态规划--状态转移方程 方法二:优化中心扩展算法 题目说明 要看明白求得是什么,最长回文字串是指例如cababa中ababa是最长的,不是求回文的部分aba 方法一:动态规划 ...
- Java版流媒体编解码和图像处理(JavaCPP+FFmpeg)
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...