天天微速讯:用go设计开发一个自己的轻量级登录库/框架吧(业务篇)
本篇会讲讲框架的登录业务的实现。实现三种登录模式:
同一用户只能登录一次同一用户多次登录多token同一用户多次登录共享一个token源码:weloe/token-go: a light login library (github.com)
存储结构首先从我们要考虑是底层该怎么存储登录信息来去达成这三种登录模式
(资料图片)
同一用户只能登录一次同一用户多次登录多token同一用户多次登录共享一个token我们不能使用无状态token模式,要有状态,在后端存储会话信息才能达成想要实现的一些逻辑,因此,存储会话信息是必要的。
对于每个请求,我们会存储一个token-loginId的k-v结构。
对于整个会话,我们会存储一个loginId-session的k-v结构。
基于这个存储结构我们就可以方便的实现这三种模式。
Session结构体
session包括了多个tokenValue,这就是我们用来实现同一用户多次登录多token,或者同一用户多次登录共享一个token的关键点
type TokenSign struct { Value string Device string}type Session struct { Id string TokenSignList *list.List}
总之,我们实现的业务将基于这两种k-v结构
功能实现源码:https://github.com/weloe/token-go/blob/f58ba4d93f0f012972bf6a35b9127229b5c328fe/enforcer.go#L167
我们再来梳理一些功能和配置的对应关系
同一用户只能登录一次:IsConcurrent == false
同一用户多次登录多token: IsConcurrent == true && IsShare == false
这时候配置MaxLoginCount
才生效
同一用户多次登录共享一个token: IsConcurrent == true && IsShare == true
接着我们再讲讲登录的具体流程:
我们大致将它分为几个阶段:
生成token
生成session
存储token-id , id-session
返回信息
调用watcher和logger
检测登录人数
生成tokenhttps://github.com/weloe/token-go/blob/f58ba4d93f0f012972bf6a35b9127229b5c328fe/enforcer_internal_api.go#L12
生成token的时候,我们要判断他是否是可多次登录,也就是isConcurrent
是否为false
。
如果可多次登录并且共享token即IsConcurrent == true && IsShare == true
,就判断能否复用之前的token
这里我们还允许用户自定义token。
loginModel *model.Login
是为了支持自定义这几个参数
type model.Login struct {Device stringIsLastingCookie boolTimeout int64Token stringIsWriteHeader bool}
// createLoginToken create by config.TokenConfig and model.Loginfunc (e *Enforcer) createLoginToken(id string, loginModel *model.Login) (string, error) {tokenConfig := e.configvar tokenValue stringvar err error// if isConcurrent is false,if !tokenConfig.IsConcurrent {err = e.Replaced(id, loginModel.Device)if err != nil {return "", err}}// if loginModel set token, return directlyif loginModel.Token != "" {return loginModel.Token, nil}// if share tokenif tokenConfig.IsConcurrent && tokenConfig.IsShare {// reuse the previous token.if v := e.GetSession(id); v != nil {tokenValue = v.GetLastTokenByDevice(loginModel.Device)if tokenValue != "" {return tokenValue, nil}}}// create new tokentokenValue, err = e.generateFunc.Exec(tokenConfig.TokenStyle)if err != nil {return "", err}return tokenValue, nil}
生成sessionhttps://github.com/weloe/token-go/blob/f58ba4d93f0f012972bf6a35b9127229b5c328fe/enforcer.go#L183
先判断是否已经存在session,如果不存在需要先创建,避免空指针
// add tokenSignif session = e.GetSession(id); session == nil {session = model.NewSession(e.spliceSessionKey(id), "account-session", id)}session.AddTokenSign(&model.TokenSign{Value: tokenValue,Device: loginModel.Device,})
存储https://github.com/weloe/token-go/blob/f58ba4d93f0f012972bf6a35b9127229b5c328fe/enforcer.go#L192
在存储的时候,需要拼接key防止与其他的key重复
// reset sessionerr = e.SetSession(id, session, loginModel.Timeout)if err != nil {return "", err}// set token-iderr = e.adapter.SetStr(e.spliceTokenKey(tokenValue), id, loginModel.Timeout)if err != nil {return "", err}
返回tokenhttps://github.com/weloe/token-go/blob/f58ba4d93f0f012972bf6a35b9127229b5c328fe/enforcer_internal_api.go#L51
这个操作对应我们配置的TokenConfig的IsReadCookie
、IsWriteHeader
和CookieConfig
// responseToken set token to cookie or headerfunc (e *Enforcer) responseToken(tokenValue string, loginModel *model.Login, ctx ctx.Context) error { if ctx == nil { return nil } tokenConfig := e.config // set token to cookie if tokenConfig.IsReadCookie { cookieTimeout := tokenConfig.Timeout if loginModel.IsLastingCookie { cookieTimeout = -1 } // add cookie use tokenConfig.CookieConfig ctx.Response().AddCookie(tokenConfig.TokenName, tokenValue, tokenConfig.CookieConfig.Path, tokenConfig.CookieConfig.Domain, cookieTimeout) } // set token to header if loginModel.IsWriteHeader { ctx.Response().SetHeader(tokenConfig.TokenName, tokenValue) } return nil}
调用watcher和loggerhttps://github.com/weloe/token-go/blob/f58ba4d93f0f012972bf6a35b9127229b5c328fe/enforcer.go#L210
在事件发生后回调,提供扩展点
// called watcherm := &model.Login{Device: loginModel.Device,IsLastingCookie: loginModel.IsLastingCookie,Timeout: loginModel.Timeout,JwtData: loginModel.JwtData,Token: tokenValue,IsWriteHeader: loginModel.IsWriteHeader,}// called loggere.logger.Login(e.loginType, id, tokenValue, m)if e.watcher != nil {e.watcher.Login(e.loginType, id, tokenValue, m)}
检测登录人数https://github.com/weloe/token-go/blob/f58ba4d93f0f012972bf6a35b9127229b5c328fe/enforcer.go#L227
要注意的是检测登录人数需要配置IsConcurrent == true && IsShare == false
,也就是:同一用户多次登录多token模式,提供一个特殊值-1,如果为-1就认为不对登录数量进行限制,不然就开始强制退出,就是删除一部分的token
// if login success check itif tokenConfig.IsConcurrent && !tokenConfig.IsShare {// check if the number of sessions for this account exceeds the maximum limit.if tokenConfig.MaxLoginCount != -1 {if session = e.GetSession(id); session != nil {// logout account until loginCount == maxLoginCount if loginCount > maxLoginCountfor element, i := session.TokenSignList.Front(), 0; element != nil && i < session.TokenSignList.Len()-int(tokenConfig.MaxLoginCount); element, i = element.Next(), i+1 {tokenSign := element.Value.(*model.TokenSign)// delete tokenSignsession.RemoveTokenSign(tokenSign.Value)// delete token-iderr = e.adapter.Delete(e.spliceTokenKey(tokenSign.Value))if err != nil {return "", err}}// check TokenSignList length, if length == 0, delete this sessionif session != nil && session.TokenSignList.Len() == 0 {err = e.deleteSession(id)if err != nil {return "", err}}}}
测试同一用户只能登录一次https://github.com/weloe/token-go/blob/f58ba4d93f0f012972bf6a35b9127229b5c328fe/enforcer_test.go#L295
IsConcurrent = falseIsShare = false
func TestEnforcerNotConcurrentNotShareLogin(t *testing.T) {err, enforcer, ctx := NewTestNotConcurrentEnforcer(t)if err != nil {t.Errorf("InitWithConfig() failed: %v", err)}loginModel := model.DefaultLoginModel()for i := 0; i < 4; i++ {_, err = enforcer.LoginByModel("id", loginModel, ctx)if err != nil {t.Errorf("Login() failed: %v", err)}}session := enforcer.GetSession("id")if session.TokenSignList.Len() != 1 {t.Errorf("Login() failed: unexpected session.TokenSignList length = %v", session.TokenSignList.Len())}}
同一用户多次登录共享一个tokenhttps://github.com/weloe/token-go/blob/f58ba4d93f0f012972bf6a35b9127229b5c328fe/enforcer_test.go#L335
IsConcurrent = trueIsShare = false
func TestEnforcer_ConcurrentNotShareMultiLogin(t *testing.T) {err, enforcer, ctx := NewTestConcurrentEnforcer(t)if err != nil {t.Errorf("InitWithConfig() failed: %v", err)}loginModel := model.DefaultLoginModel()for i := 0; i < 14; i++ {_, err = enforcer.LoginByModel("id", loginModel, ctx)if err != nil {t.Errorf("Login() failed: %v", err)}}session := enforcer.GetSession("id")if session.TokenSignList.Len() != 12 {t.Errorf("Login() failed: unexpected session.TokenSignList length = %v", session.TokenSignList.Len())}}
同一用户多次登录多tokenhttps://github.com/weloe/token-go/blob/f58ba4d93f0f012972bf6a35b9127229b5c328fe/enforcer_test.go#LL316C17-L316C17
IsConcurrent = trueIsShare = true
func TestEnforcer_ConcurrentShare(t *testing.T) {err, enforcer, ctx := NewTestEnforcer(t)if err != nil {t.Errorf("InitWithConfig() failed: %v", err)}loginModel := model.DefaultLoginModel()for i := 0; i < 5; i++ {_, err = enforcer.LoginByModel("id", loginModel, ctx)if err != nil {t.Errorf("Login() failed: %v", err)}}session := enforcer.GetSession("id")if session.TokenSignList.Len() != 1 {t.Errorf("Login() failed: unexpected session.TokenSignList length = %v", session.TokenSignList.Len())}}
至此,我们就实现了三种登录模式,
标签:
-
13
2023-05天天微速讯:用go设计开发一个自己的轻量级登录库/框架吧(业务篇)
本篇将讲讲登录库中的三种登录模式的实现:同一用户只能登录一次,同一用户多次登录多token,同一用户多次登录 -
13
2023-05每日报道:浙江龙泉:千年古城“正青春”
找碴、对缝、定点、打孔、锔钉、打磨、补缝……见到24岁的高泽琳时,她正在学习如何做一名“瓷器医生”。半 -
13
2023-05湘潭县十中举行第四届教职工气排球比赛活动 环球热点
湖南日报·新湖南客户端5月12日讯(通讯员 崔灿)为丰富教职工文体生活,着力打造快乐校园、健康校园、奋 -
13
2023-05天天看热讯:今年立秋是早秋还是晚秋2021_年的立秋是早秋还是晚秋
早立秋和晚立秋有两种方式定义,一种是一天之中的早晚,一种是农历历法上的早晚。按上午下午区分:2022年为 -
13
2023-05经常吃海鲜的好处和坏处_海鲜的营养价值及功效
1、海鲜的营养价值很丰富。2、如以下几类:1 螃蟹:螃蟹含有丰富的蛋白质和营养物质。3、螃蟹的蟹肉中的铜 -
13
2023-05吉林龙井市法学会:深入开展法治营商环境普法宣传活动|资讯
民主与法制网讯(记者刘永强□雷洪涛通讯员金银实)5月8日,吉林省龙井市法学会联合市总工会、市信访局深入 -
13
2023-05世界今头条!和联集团苏州科技园招聘是真的吗 和联集团
今天来聊聊关于和联集团苏州科技园招聘是真的吗,和联集团的文章,现在就为大家来简单介绍下和联集团苏州科 -
12
2023-05全球实时:天份薛之谦mp3下载百度云_天份薛之谦歌词
1、当天份是一次迟到的告白,那一次感情里的隐忍都无法拒绝了。2、因为那都是自愿的开始,直到分开为止有一 -
12
2023-05【报资讯】罗马诺:我认为贝林厄姆会加盟皇马 今夏签姆巴佩会非常困难
意大利记者罗马诺接受CBS的采访,谈到了贝林厄姆和姆巴佩的未来,他认为贝林厄姆会成为皇马球员,并表示皇 -
12
2023-05搅动MPV市场 广汽丰田赛那突破10万台销量大关!
近几年,随着经济步入高质量发展新时代,中等收入群体规模不断扩大,高端MPV市场蓬勃发展,各自主、新势力 -
12
2023-05广东白云学院艺术设计学院视觉传达设计系完成毕业设计答辩_全球热推荐
5月7日上午,广东白云学院艺术设计学院视觉传达设计专业开展了2023届毕业设计答辩。此次答辩人数创历史新高 -
12
2023-05杭州市第四届青少年手球锦标赛中学组火热开赛 速讯
5月12日,浙江省严州中学梅城校区举办了“奔跑吧·少年”杭州市第四届青少年手球锦标赛中学组暨首届浙江省
苹果考虑将iPhone系列进一步延伸 2024年上市?
卷出一块好曲屏 真我10系列新品发布会举行
英国猴痘病例数预计将大幅上升
上海:视情适当延长毕业生在校生身份时间
国家电网确定新型电力系统科技攻关十大重点项目
比亚迪发布CTB电池车身一体化技术
商务部:坚定致力于实现全面、高水平的亚太自贸区
中办国办印发《意见》 推进实施国家文化数字化战略
初夏看市场:“菜篮子”产品生产供应充足 蔬菜在田面积达9877.2万亩
上海浦东重点生产企业复工复产超1100家
-
1
Intel最新处理器Arrow-S曝光 最高可达24核
-
2
配置拉满的电竞神机 雷神ZERO2023大黄蜂发布
-
3
真我10Pro系列发布 首发量产2160Hz超高频调光技术
-
4
阿富汗塔利班组建正规军
-
5
萨赫勒地区反恐形势面临新变数
-
6
北约北扩加剧欧洲安全风险
-
7
贵州毕节七星关区百所学校创办百个“红军班”
-
8
湖北省孝感军分区组织军地联合应急救援研究性演练
-
9
青藏高原等区域将新设一批国家公园
-
10
河北省承德军分区退役军人担纲教练主力