当前位置:网站首页 > 经验分享 > 正文

单点登录概念

业务背景

在企业发展初期,企业使用的系统很少,通常一个或者两个,每个系统都有自己的登录模块,用户用不同的账号即可登录,很方便。 但随着企业的发展,用到的系统随之增多,用户在操作不同的系统时,需要多次登录,而且每个系统的账号都不一样,这对于用户来说,很不方便。于是,就想到是不是可以在一个系统登录,其他系统就不用登录了呢?这就是单点登录要解决的问题。

技术实现

在说单点登录之前,我们先来了解一下普通登录的流程:

image.png

如上图所示,我们在浏览器中填写完用户名和密码后,完成登陆验证。这时,我们在这个用户的session中标记为(yes)已登录,同时在浏览器中写入用户的唯一标识符Cookie。这样下次再访问时,请求中带入Cookie,服务器通过Cookie找到对应的Session状态判断是否登录。

同域名下的单点登录

一个企业一般情况下只有一个域名,通过二级域名区分不同的系统。比如我们有个域名叫做:a.com,同时有两个业务系统分别为:app1.a.com和app2.a.com。我们要做单点登录(SSO),需要一个登录系统,叫做:sso.a.com。

我们只要在sso.a.com登录,app1.a.com和app2.a.com就也登录了。通过上面的登陆认证机制,我们可以知道,在sso.a.com中登录了,其实是在sso.a.com的服务端的session中记录了登录状态,同时在浏览器端(Browser)的sso.a.com下写入了Cookie。那么我们怎么才能让app1.a.com和app2.a.com登录呢?这里有两个问题:

  • Cookie是不能跨域的,我们Cookie的domain属性是sso.a.com,在给app1.a.com和app2.a.com发送请求是带不上的。
  • sso、app1和app2是不同的应用,它们的session存在自己的应用内,是不共享的。
  • 针对第一个问题,我们可以把Cookie的域设置为顶域,这样所有子域的系统都可以访问到顶域的Cookie。
  • 针对第二个问题,我们可以把Session进行共享,共享的方法有很多,这里就不展开说了。

在这种情况下,同域名下的单点登录就实现了,但这并不是真正的单点登录。

不同域下的单点登录

当企业系统分布在不同的顶级域名下时,情况变得更加复杂。例如,假设企业除了拥有app1.a.com和app2.a.com,还有一个合作伙伴系统位于partner.b.com。此时,简单的Cookie顶域设置和Session共享不再适用,因为它们无法跨越不同的顶级域名。为了解决跨域的单点登录问题,我们通常采用以下几种策略:

  1. 中介Token机制 步骤:
  2. 用户在SSO系统(sso.a.com)登录,认证成功后,SSO系统生成一个全局唯一的Token。
  3. SSO系统将Token通过URL重定向的方式传给客户端,客户端再携带此Token访问目标系统(如app1.a.com)。
  4. app1.a.com接收到Token后,向SSO系统发起验证请求,确认Token的有效性(通常包含用户身份信息)。
  5. 验证通过后,app1.a.com在本地存储(如Cookie或LocalStorage)中保存该Token,并标记用户为已登录状态。 优势:解耦了各个系统的登录状态,提高了安全性,支持跨域。

image.png

  1. OAuth / OpenID Connect
    概念:OAuth 2.0 和 OpenID Connect 是广泛采用的身份验证和授权标准,非常适合处理跨域的单点登录。通俗点说,就是外卖小哥进入小区,不需要刷门禁卡,而是打电话给你,你跟门卫叔叔说让外卖小哥进入小区里面。 流程:
  2. 用户在客户端应用请求登录时,被重定向到认证服务器(可能是SSO系统)。
  3. 用户在认证服务器完成身份验证,认证服务器返回一个授权码或ID Token给客户端。
  4. 客户端携带授权码向认证服务器请求访问令牌(Access Token)。
  5. 认证服务器验证后,发放Access Token给客户端。
  6. 客户端使用Access Token向资源服务器请求受保护资源,实现登录。 优点:标准化流程,安全可靠,广泛兼容,适用于不同组织和第三方应用之间的单点登录。

通过需求分析与目标人群的考量,团队选择了中介Token的机制,并借鉴了authelia项目中一重身份验证相关的思想。

前端部分代码实现

只要理解了单点登录的流程机制,编写相关的代码就不难了:

const http = axios.create() //...http基础配置设置 //登录流程,返回的是授权码 function generateState(length = 16) { let result = '' const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0' const charactersLength = characters.length for (let i = 0; i < length; i++) { result += characters.charAt(Math.floor(Math.random() * charactersLength)) } return result } //客户端ID,代表身份凭证 const clientId = 'BDa5Vso_19vk5XI1fNzAxZ' //认证成功后将用户重定向回应用程序的URI地址 const redirectUri = 'https://xxxxx' const responseType = 'code' const scope = 'openid profile email' //授权服务器站点 const authorizationEndpoint = 'https://xxxxxxx' //生成State,generateState生成了一个随机的字符串,用于防止CSRF攻击 const state= generateState() const authUrl = `${authorizationEndpoint}?response_type=${encodeURIComponent( responseType )}&client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent( redirectUri )}&scope=${encodeURIComponent(scope)}&state=${encodeURIComponent(state)}` const query = getUrlparms() if (!token && !query.code) { window.location.replace(authUrl) } //...请求响应拦截器,当服务器校验无法通过时,重定向到authURL 

下面是对authURL各个参数的解析:

  • authorizationEndpoint: 这是授权服务器的授权端点,客户端将用户重定向到这个地址以开始授权流程。
  • response_type: 这个参数告诉授权服务器客户端期望的响应类型。在这里,responseType 被设置为 'code',表示使用授权码流程。
  • client_id: 这是注册应用程序时由授权服务器提供的唯一标识符,用于识别请求授权的客户端应用程序。
  • redirect_uri: 这是用户在授权服务器上成功认证后将被重定向回的客户端应用程序的地址。它必须与客户端注册时提供给授权服务器的 URI 完全匹配。
  • scope: 这定义了客户端请求访问的权限范围。在这个例子中,scope 包括 'openid profile email',这意味着客户端请求用户的身份标识以及基本的个人信息和电子邮件地址。
  • state: 这是一个客户端生成的随机字符串,用于维护请求和回调之间的状态,并防止 CSRF 攻击

  • 上一篇: 建设性批评的七大原则
  • 下一篇: frpc服务器
  • 版权声明


    相关文章:

  • 建设性批评的七大原则2025-04-27 18:30:03
  • 高速can总线和低速can总线的异同2025-04-27 18:30:03
  • 美的单点统一登录平台2025-04-27 18:30:03
  • 语法填空提示词是形容词2025-04-27 18:30:03
  • typescript long2025-04-27 18:30:03
  • linux和win10双系统安装教程2025-04-27 18:30:03
  • frpc服务器2025-04-27 18:30:03
  • Linux磁盘分区消失怎么恢复2025-04-27 18:30:03
  • 表单form的基本语法2025-04-27 18:30:03
  • js多线程如何简单实现2025-04-27 18:30:03