,,源码解析gtoken替换jwt实现sso登录

,,源码解析gtoken替换jwt实现sso登录

本文主要介绍一个在源代码分析中用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的信息,请关注我们的其他相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: