本文主要介绍一个在源代码分析中用gtoken代替jwt进行sso登录的例子。有需要的朋友可以借鉴一下,希望能有所帮助。祝大家进步很大,早日升职加薪。
目录
jwt的问题jwt的请求流程图gtoken的优势笔记问题演示Demo分析源代码刷新tokenGfToken结构思考问题总结
jwt的问题
首先说明一下jwt的问题,也就是更换jwt的原因:
Jwt不能在服务器上主动退出。jwt不能让颁发的令牌失效,只能等到令牌过期。jwt携带了大量的用户扩展信息,导致传输效率降低的问题。
jwt的请求流程图gtoken的优势
gtoken的请求流程和jwt基本相同。
gtoken的优势在于可以帮助我们解决jwt的问题。此外,它还提供易于使用的功能,例如:
Gtoken支持使用内存存储的单点应用测试、个人项目文件存储、使用redis存储的企业集群;完全适合企业生产使用;有效避免了jwt服务器无法退出的问题;解决jwt无法使颁发的令牌失效,只能等到令牌过期的问题;用户扩展信息存储在服务器中,有效避免了jwt携带大量用户扩展信息降低传输效率的问题;有效避免jwt要求客户端实现更新功能,增加客户端的复杂度;支持服务器自动更新,客户端不需要关心更新逻辑;
注意问题
支持服务器端缓存自动更新功能,不需要通过refresh_token刷新令牌,简化了客户端的操作版本。请注意:在gtoken v1.5.0中全面适配GoFrame v 2 . 0 . 0;Go v1.x.x请使用GfToken v1.4.X相关版本。
提示:我下面的演示和源码阅读都是基于v1.4.x的
演示demo
下面的演示可以复制到本地main.go文件中执行。更新依赖项时请注意版本。
重点踩坑:
登录方法将要求我们返回两个值:
第一个值对应userKey,然后我们可以根据userKey得到对应数据的token的第二个值,是接口{}类型。我们可以在这里定义userid和username等数据。
先有这个概念,后面我们带你看源代码。
主包装
导入(
github . com/goflyfox/GTO ken/GTO ken
github.com/gogf/gf/frame/g
github.com/gogf/gf/net/ghttp
github.com/gogf/gf/os/glog
)
var TestServerName字符串
//var test servername string= gt oken
func main() {
格洛格。信息( # # # # # # # #服务启动.)
g.Cfg()。SetPath(示例/样本)
s :=g.Server(测试服务器名称)
初始化路由器
格洛格。信息( # # # # # # # # #服务完成。)
南运行()
}
var gfToken *gtoken。GfToken
/*
统一路线注册
*/
func initRouter(s *ghttp。服务器){
//不验证接口
南Group(/),func(group *ghttp。RouterGroup) {
团体。中间件(CORS)
//调试路由
团体。ALL(/hello ,func(r *ghttp。请求){
r.Response.WriteJson(gtoken。Succ(hello ))
})
})
//认证接口
loginFunc :=登录
//启动gtoken
gf token:=amp;gtoken。GfToken{
服务器名:TestServerName,
登录路径:“/login”,
LoginBeforeFunc: loginFunc,
logout path:“/user/logout”,
AuthExcludePaths:g . slice str { /user/info ,/system/user/info},//不要阻塞路径/user/info,/system/user/info,/system/user,
MultiLogin: g.Config()。GetBool(gToken。多重登录’),
}
南Group(/),func(group *ghttp。RouterGroup) {
团体。中间件(CORS)
gfToken。中间件(集团)
团体。ALL(/system/user ,func(r *ghttp。请求){
r.Response.WriteJson(gtoken。Succ(“系统用户”))
})
团体。ALL(/user/data ,func(r *ghttp。请求){
r.Response.WriteJson(gfToken。GetTokenData(r))
})
团体。ALL(/user/info ,func(r *ghttp。请求){
r.Response.WriteJson(gtoken。Succ(“用户信息”))
})
团体。ALL(/system/user/info ,func(r *ghttp。请求){
r.Response.WriteJson(gtoken。Succ(“系统用户信息”))
})
})
//启动gtoken
gfAdminToken:=amp;gtoken .GfToken{
服务器名:测试服务器名称,
//超时:10 * 1000,
登录路径:"/登录",
LoginBeforeFunc: loginFunc,
注销路径:"/用户/注销",
AuthExcludePaths:g . slice str { /admin/user/info ,/admin/system/user/info},//不拦截路径/用户/信息,/系统/用户/信息,/系统/用户,
多重登录:g.Config().GetBool(gToken .多重登录),
}
南Group(/admin ,func(group *ghttp .RouterGroup) {
团体。中间件(CORS)
gfAdminToken .中间件(集团)
团体ALL(/system/user ,func(r *ghttp .请求){
r.Response.WriteJson(gtoken .Succ(“系统用户"))
})
团体ALL(/user/info ,func(r *ghttp .请求){
r.Response.WriteJson(gtoken .Succ(“用户信息"))
})
团体ALL(/system/user/info ,func(r *ghttp .请求){
r.Response.WriteJson(gtoken .Succ(“系统用户信息"))
})
})
}
功能登录(r *ghttp .请求)(字符串,接口{}) {
用户名:=r.GetString(用户名)
passwd :=r.GetString(passwd )
如果用户名== ||密码== {
r.Response.WriteJson(gtoken .失败(账号或密码错误.))
r.ExitAll()
}
返回用户名,"1"
/**
返回的第一个参数对应:用户键
返回的第二个参数对应:数据
{
代码:0,
消息:成功,
数据:{
创建时间:1652838582190,
数据: 1 ,
刷新时间:1653270582190,
用户密钥":"王中阳,
uuid : AC 75676 efeb 906 f 9959 cf 35 f 779 a1 d 38
}
}
*/
}
//跨域
func CORS(r *ghttp .请求){
r.Response.CORSDefault()
r.中间件。下一个()
}
启动项目:
访问不认证接口:返回成功
未登录时访问认证接口:返回错误
请求登录接口:返回代币
携带代币再次访问认证接口:返回成功
以上就跑通了主体流程,就是这么简单。
分析源码
小贴士:下面带大家看的是v1.4.1
下面带大家分析一下源码,学习一下作者是如何设计的。
刷新token首先我认为托肯很好的设计思想是不使用刷新_令牌来刷新令牌,而是服务端主动刷新。
在源码的格托肯方法中做了更新刷新时间和创建时间的处理。
更新创建时间为当前时间,刷新时间为当前时间自定义的刷新时间。
如下图所示,getToken方法在每次执行有效令牌校验代币的时候都会调用,即每次校验代币有效性时,如果符合刷新代币有效期的条件,就会进行刷新操作(刷新代币的过期时间,令牌值不变)
这样就实现了无感刷新令牌。
GfToken结构体我们再来看一下GfToken的结构体,更好的理解一下作者的设计思路:
因为缓存模式支持格雷迪斯,也就意味着支持集群模式。我们在启动托肯的时候,只需要设置登录和登出路径,另外登录和登出都提供了之前函数和AfterFunc,让我们能清晰的界定使用场景。
//GfToken gtoken结构体
类型GfToken结构{
//GoFrame服务器名称
服务器名字符串
//缓存模式1 gcache 2 gredis默认一
CacheMode int8
//缓存键
缓存键字符串
//超时时间默认10天(毫秒)
超时(同Internationalorganizations)国际组织
//缓存刷新时间默认为超时时间的一半(毫秒)
MaxRefresh int
//令牌分隔符
令牌分隔符字符串
//令牌加密键
加密密钥[]字节
//认证失败中文提示
AuthFailMsg字符串
//是否支持多端登录,默认错误的
多重登录布尔
//是否是全局认证,兼容历史版本,已废弃
全球中间件弯曲件
//中间件类型一组中间件2绑定中间件3全局中间件
中间件类型单元
//登录路径
登录路径字符串
//登录验证方法返回用户密钥用户标识如果用户密钥为空,结束执行
登录前功能函数(r *ghttp .请求)(字符串,接口{})
//登录返回方法
LoginAfterFunc func(r *ghttp .请求、响应、数据响应)
//登出地址
LogoutPath字符串
//注销验证方法返回true继续执行,否则结束执行。
logoutbeforefuncfunc func(r * ghttp。请求)布尔值
//注销返回方法
LogoutAfterFunc func(r *ghttp。请求、响应、数据响应)
//拦截地址
AuthPaths g.SliceStr
//拦截排除的地址
AuthExcludePaths g.SliceStr
//认证验证方法return true继续执行,否则执行结束。
authbeforefuncfunc func(r * ghttp。请求)布尔值
//身份验证返回方法
AuthAfterFunc func(r *ghttp。请求、响应、数据响应)
}
思考题
我有n个子系统。如何使用gtoken实现sso登录?即一个子系统登录,其他所有子系统自动登录,而不是两次登录?
考虑一下
我想到的解决方案是用cookie实现:各子系统二级域名不一致,但一级域名一致。
登录后,我将令牌写入主域名的cookie中进行共享,前端网站通过cookie获取令牌请求服务接口。
同时,刷新令牌后,cookie的有效期也要刷新,以免cookie无法获取令牌。
在仔细阅读了一遍源代码后,我们发现了一个合适的刷新cookie有效期的场景:AuthAfterFunc。我们可以重写这个方法来实现验证后的操作:
如果令牌验证有效,则刷新cookie有效期;如果验证无效,自定义返回的信息。(通常我们自己项目中的代码代码与gtoken预制的代码不一致,但是gtoken支持非常方便的返回值重写)
总结
在项目之前,我们使用jwt实现sso登录。当我们刚拿到重写的需求时,可以说是无所适从。
在仔细阅读gtoken源码之前,我已经设计好了refresh_token刷新策略。
仔细看了源代码,发现真的很香。
这段经历最大的收获是,带着需求阅读源代码是一种非常高效的学习方式。
问问你自己:
如果让我来设计,我会怎么设计?
作者为什么设计这么多?作者为什么用560行代码设计gtoken?
最后向作者Jflyfox致敬。这是官方文件。
以上是源代码分析的细节,用gtoken代替jwt实现sso登录。更多关于gtoken替代jwt登录sso的信息,请关注我们的其他相关文章!