gtoken替换jwt实现sso登录

科技公元 工具 2022-05-20

今天和大家分享一下使用gtoken替换jwt实现sso登录的经验,为了让大家更好的理解会带大家读一下重点的源码。

jwt的问题

首先说明一个jwt存在的问题,也就是要替换jwt的原因:

  1. jwt无法在服务端主动退出的问题
  2. jwt无法作废已颁布的令牌,只能等到令牌过期问题
  3. jwt携带大量用户扩展信息导致降低传输效率问题

jwt的请求流程图

gtoken替换jwt实现sso登录

gtoken的优势

gtoken的请求流程和jwt的基本一致。

gtoken的优势就是能帮助我们解决jwt的问题,另外还提供好用的特性,比如:

  1. gtoken支撑单点应用测试使用内存存储,支持个人项目文件存储,也支持企业集群使用redis存储;完全适用于企业生产级使用;
  2. 有效的避免了jwt服务端无法退出问题;
  3. 解决jwt无法作废已颁布的令牌,只能等到令牌过期问题;
  4. 通过用户扩展信息存储在服务端,有效规避了jwt携带大量用户扩展信息导致降低传输效率问题;
  5. 有效避免jwt需要客户端实现续签功能,增加客户端复杂度;支持服务端自动续期,客户端不需要关心续签逻辑;

注意问题

  1. 支持服务端缓存自动续期功能,不需要通过refresh_token刷新token,简化了客户端的操作

  2. 版本问题千万注意:在gtoken v1.5.0全面适配GoFrame v2.0.0 ; GoFrame v1.X.X 请使用GfToken v1.4.X相关版本

TIPS:下面我的演示demo和源码阅读都是基于v1.4.x版本的。

演示demo

下面的演示demo可以复制到本地main.go文件中执行,更新依赖的时候千万注意版本。

重点说一下踩的坑:

Login方法会要求我们返回两个值:

第一个值对应userKey,后续我们可以根据userKey获得token第二个值对应data,是interface{}类型,我们可以在这里定义例如userid、username等数据。

先有这个概念即可,后面会带大家读源码。

package main

import (
   "github.com/goflyfox/gtoken/gtoken"
   "github.com/gogf/gf/frame/g"
   "github.com/gogf/gf/net/ghttp"
   "github.com/gogf/gf/os/glog"
)

var TestServerName string

//var TestServerName string = "gtoken"

func main() {
   glog.Info("########service start...")

   g.Cfg().SetPath("example/sample")
   s := g.Server(TestServerName)
   initRouter(s)

   glog.Info("########service finish.")
   s.Run()
}

var gfToken *gtoken.GfToken

/*
统一路由注册
*/
func initRouter(s *ghttp.Server) {
   // 不认证接口
   s.Group("/", func(group *ghttp.RouterGroup) {
      group.Middleware(CORS)

      // 调试路由
      group.ALL("/hello", func(r *ghttp.Request) {
         r.Response.WriteJson(gtoken.Succ("hello"))
      })
   })

   // 认证接口
   loginFunc := Login
   // 启动gtoken
   gfToken := &gtoken.GfToken{
      ServerName:       TestServerName,
      LoginPath:        "/login",
      LoginBeforeFunc:  loginFunc,
      LogoutPath:       "/user/logout",
      AuthExcludePaths: g.SliceStr{"/user/info", "/system/user/info"}, // 不拦截路径 /user/info,/system/user/info,/system/user,
      MultiLogin:       g.Config().GetBool("gToken.MultiLogin"),
   }
   s.Group("/", func(group *ghttp.RouterGroup) {
      group.Middleware(CORS)
      gfToken.Middleware(group)

      group.ALL("/system/user", func(r *ghttp.Request) {
         r.Response.WriteJson(gtoken.Succ("system user"))
      })
      group.ALL("/user/data", func(r *ghttp.Request) {
         r.Response.WriteJson(gfToken.GetTokenData(r))
      })
      group.ALL("/user/info", func(r *ghttp.Request) {
         r.Response.WriteJson(gtoken.Succ("user info"))
      })
      group.ALL("/system/user/info", func(r *ghttp.Request) {
         r.Response.WriteJson(gtoken.Succ("system user info"))
      })
   })

   // 启动gtoken
   gfAdminToken := &gtoken.GfToken{
      ServerName: TestServerName,
      //Timeout:         10 * 1000,
      LoginPath:        "/login",
      LoginBeforeFunc:  loginFunc,
      LogoutPath:       "/user/logout",
      AuthExcludePaths: g.SliceStr{"/admin/user/info", "/admin/system/user/info"}, // 不拦截路径 /user/info,/system/user/info,/system/user,
      MultiLogin:       g.Config().GetBool("gToken.MultiLogin"),
   }
   s.Group("/admin", func(group *ghttp.RouterGroup) {
      group.Middleware(CORS)
      gfAdminToken.Middleware(group)

      group.ALL("/system/user", func(r *ghttp.Request) {
         r.Response.WriteJson(gtoken.Succ("system user"))
      })
      group.ALL("/user/info", func(r *ghttp.Request) {
         r.Response.WriteJson(gtoken.Succ("user info"))
      })
      group.ALL("/system/user/info", func(r *ghttp.Request) {
         r.Response.WriteJson(gtoken.Succ("system user info"))
      })
   })
}

func Login(r *ghttp.Request) (string, interface{}) {
   username := r.GetString("username")
   passwd := r.GetString("passwd")

   if username == "" || passwd == "" {
      r.Response.WriteJson(gtoken.Fail("账号或密码错误."))
      r.ExitAll()
   }

   return username, "1"
   /**
   返回的第一个参数对应:userKey
   返回的第二个参数对应:data
   {
       "code": 0,
       "msg": "success",
       "data": {
           "createTime": 1652838582190,
           "data": "1",
           "refreshTime": 1653270582190,
           "userKey": "王中阳",
           "uuid": "ac75676efeb906f9959cf35f779a1d38"
       }
   }
   */
}

// 跨域
func CORS(r *ghttp.Request) {
   r.Response.CORSDefault()
   r.Middleware.Next()
}
复制代码

启动项目:

gtoken替换jwt实现sso登录

访问不认证接口:返回成功

gtoken替换jwt实现sso登录

未登录时访问认证接口:返回错误

gtoken替换jwt实现sso登录

请求登录接口:返回token

gtoken替换jwt实现sso登录

携带token再次访问认证接口:返回成功

gtoken替换jwt实现sso登录

以上就跑通了主体流程,就是这么简单。

Apipost 私有化火热进行中

评论