欢迎来到电脑知识学习网,专业的电脑知识大全学习平台!

手机版

单点登录sso实现方案(单点登录sso企业级解决方案)

网络知识 发布时间:2022-01-10 14:54:38

单点登录sso实现方案(单点登录sso企业级解决方案)(1)

阅读收获

✔️1. 了解单点登录实现原理

✔️2. 掌握快速使用xxl-sso接入单点登录功能

一、早期的多系统登录解决方案

  • 单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器与服务器之间维护会话状态。但cookie是有限制的,这个限制就是cookie的域(通常对应网站的域名),浏览器发送http请求时会自动携带与该域匹配的cookie,而不是所有cookie
  • 既然这样,为什么不将web应用群中所有子系统的域名统一在一个顶级域名下,例如“*.baidu.com”,然后将它们的cookie域设置为“baidu.com”,这种做法理论上是可以的,甚至早期很多多系统登录就采用这种同域名共享cookie的方式。
  • 共享cookie的方式存在众多局限。
    • 应用群域名得统一
    • 应用群各系统使用的技术(至少是web服务器)要相同,不然cookie的key值(tomcat为JSESSIONID)不同,无法维持会话,共享cookie的方式是无法实现跨语言技术平台登录的,比如java、php、.net系统之间
    • cookie本身不安全。
  • 因此,我们需要一种全新的登录方式来实现多系统应用群的登录,这就是单点登录

二、什么是单点登录

  • 单点登录英文全称Single Sign On,简称SSO。
  • 指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分

三、为什么需要单点登录

  • web系统早已从久远的单系统发展成为如今由多系统组成的应用群,面对如此众多的系统,用户难道要一个一个登录、然后一个一个注销吗?
  • web系统由单系统发展成多系统组成的应用群,复杂性应该由系统内部承担,而不是用户。无论web系统内部多么复杂,对用户而言,都是一个统一的整体,也就是说,用户访问web系统的整个应用群与访问单个系统一样,登录/注销只要一次就够了

四、单点登录原理

4.1 登录

sso需要一个独立的认证中心,只有认证中心能接受用户的用户名密码等安全信息,其他系统不提供登录入口,只接受认证中心的间接授权。

间接授权通过令牌实现,sso认证中心验证用户的用户名密码没问题,创建授权令牌,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌,即得到了授权,可以借此创建局部会话,局部会话登录方式与单系统的登录方式相同。

这个过程,也就是单点登录的原理,用下图说明

单点登录sso实现方案(单点登录sso企业级解决方案)(2)

  1. 用户访问系统1的受保护资源,系统1发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数
  2. sso认证中心发现用户未登录,将用户引导至登录页面(带系统1地址)
  3. 用户输入用户名密码提交登录申请
  4. sso认证中心校验用户信息,创建用户与sso认证中心之间的会话,称为全局会话,同时创建授权令牌
  5. sso认证中心带着令牌跳转到最初的请求地址(系统1)
  6. 系统1拿到令牌,去sso认证中心校验令牌是否有效
  7. sso认证中心校验令牌,返回有效,注册系统1
  8. 系统1使用该令牌创建与用户的会话,称为局部会话,返回受保护资源
  9. 用户访问系统2的受保护资源
  10. 系统2发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数
  11. sso认证中心发现用户已登录,跳转回系统2的地址,并附上令牌
  12. 系统2拿到令牌,去sso认证中心校验令牌是否有效
  13. sso认证中心校验令牌,返回有效,注册系统2
  14. 系统2使用该令牌创建与用户的局部会话,返回受保护资源

用户登录成功之后,会与sso认证中心及访问的子系统建立会话,用户与sso认证中心建立的会话称为全局会话,用户与各个子系统建立的会话称为局部会话,局部会话建立之后,用户访问子系统受保护资源将不再通过sso认证中心,全局会话与局部会话有如下约束关系

  1. 局部会话存在,全局会话一定存在
  2. 全局会话存在,局部会话不一定存在
  3. 全局会话销毁,局部会话必须销毁

4.2 注销

在一个子系统中注销,所有子系统的会话都将被销毁,用下面的图来说明

单点登录sso实现方案(单点登录sso企业级解决方案)(3)

sso认证中心一直监听全局会话的状态,一旦全局会话销毁,监听器将通知所有注册系统执行注销操作

下面对上图简要说明

  1. 用户向系统1发起注销请求
  2. 系统1根据用户与系统1建立的会话id拿到令牌,向sso认证中心发起注销请求
  3. sso认证中心校验令牌有效,销毁全局会话,同时取出所有用此令牌注册的系统地址
  4. sso认证中心向所有注册系统发起注销请求
  5. 各注册系统接收sso认证中心的注销请求,销毁局部会话
  6. sso认证中心引导用户至登录页面

五、快速接入SSO

5.1 xxl-sso特性

  • 简洁:API直观简洁,可快速上手
  • 轻量级:环境依赖小,部署与接入成本较低
  • 单点登录:只需要登录一次就可以访问所有相互信任的应用系统
  • 分布式:接入SSO认证中心的应用,支持分布式部署
  • HA:Server端与Client端,均支持集群部署,提高系统可用性
  • 跨域:支持跨域应用接入SSO认证中心
  • Cookie+Token均支持:支持基于Cookie和基于Token两种接入方式,并均提供Sample项目
  • Web+APP均支持:支持Web和APP接入
  • 实时性:系统登陆、注销状态,全部Server与Client端实时共享
  • CS结构:基于CS结构,包括Server"认证中心"与Client"受保护应用"
  • 记住密码:未记住密码时,关闭浏览器则登录态失效;记住密码时,支持登录态自动延期,在自定义延期时间的基础上,原则上可以无限延期
  • 路径排除:支持自定义多个排除路径,支持Ant表达式,用于排除SSO客户端不需要过滤的路径

5.2 环境

  • JDK:1.7+
  • Redis:4.0+

5.3 下载xxl-sso源码

源码仓库地址

Release Download

github.com/xuxueli/xxl…

Download

gitee.com/xuxueli0323…

Download

5.4 文档地址

  • 中文文档

5.5 项目结构说明

- xxl-sso-server:中央认证服务,支持集群
- xxl-sso-core:Client端依赖
- xxl-sso-samples:单点登陆Client端接入示例项目
    - xxl-sso-web-sample-springboot:基于Cookie接入方式,供用户浏览器访问,springboot版本
    - xxl-sso-token-sample-springboot:基于Token接入方式,常用于无法使用Cookie的场景使用,如APP、Cookie被禁用等,springboot版本
复制代码

5.6 基于Token方式部署

  • 由于前后端分离开发的模式较多,这里只介绍基于Token方式部署,在一些无法使用Cookie的场景下,可使用该方式,如需要Cookie查看基于cookie方式部署

5.6.1 认证中心(SSO Server)搭建

  • 项目名:xxl-sso-server
  • 配置文件位置:application.properties
### redis 地址: 如 "{ip}"、"{ip}:{port}"、"{redis/rediss}://xxl-sso:{password}@{ip}:{port:6379}/{db}";多地址逗号分隔
xxl.sso.redis.address=redis://127.0.0.1:6379
### 登录态有效期窗口,默认24H,当登录态有效期窗口过半时,自动顺延一个周期
xxl.sso.redis.expire.minute=1440
复制代码

5.6.2 单点登陆Client端搭建

  • 项目名:xxl-sso-token-sample-springboot
  • maven依赖
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-sso-core</artifactId>
    <version>${最新稳定版}</version>
</dependency>
复制代码
  • 配置文件:application.properties
##### SSO Server认证中心地址(推荐以域名方式配置认证中心)
xxl.sso.server=http://xxlssoserver.com:8080/xxl-sso-server

##### 注销登陆path,值为Client端应用的相对路径
xxl.sso.logout.path=/logout

##### 路径排除Path,允许设置多个,且支持Ant表达式。用于排除SSO客户端不需要过滤的路径
xxl-sso.excluded.paths=

### redis   // redis address, like "{ip}"、"{ip}:{port}"、"{redis/rediss}://xxl-sso:{password}@{ip}:{port:6379}/{db}";Multiple "," separated
xxl.sso.redis.address=redis://xxl-sso:password@127.0.0.1:6379/0
xxl.sso.redis.address=redis://127.0.0.1:6379
复制代码
  • 配置 XxlSsoTokenFilter
package com.xxl.sso.sample.config;

import com.xxl.sso.core.conf.Conf;
import com.xxl.sso.core.filter.XxlSsoTokenFilter;
import com.xxl.sso.core.util.JedisUtil;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author xuxueli 2018-11-15
 */
@Configuration
public class XxlSsoConfig implements DisposableBean {


    @Value("${xxl.sso.server}")
    private String xxlSsoServer;

    @Value("${xxl.sso.logout.path}")
    private String xxlSsoLogoutPath;

    @Value("${xxl.sso.redis.address}")
    private String xxlSsoRedisAddress;

    @Value("${xxl-sso.excluded.paths}")
    private String xxlSsoExcludedPaths;


    @Bean
    public FilterRegistrationBean xxlSsoFilterRegistration() {

        // xxl-sso, redis init
        JedisUtil.init(xxlSsoRedisAddress);

        // xxl-sso, filter init
        FilterRegistrationBean registration = new FilterRegistrationBean();

        registration.setName("XxlSsoWebFilter");
        registration.setOrder(1);
        registration.addUrlPatterns("/*");
        //token使用的是XxlSsoTokenFilter
        registration.setFilter(new XxlSsoTokenFilter());
        registration.addInitParameter(Conf.SSO_SERVER, xxlSsoServer);
        registration.addInitParameter(Conf.SSO_LOGOUT_PATH, xxlSsoLogoutPath);
        registration.addInitParameter(Conf.SSO_EXCLUDED_PATHS, xxlSsoExcludedPaths);

        return registration;
    }

    @Override
    public void destroy() throws Exception {

        // xxl-sso, redis close
        JedisUtil.close();
    }

}
复制代码

5.6.3 验证 (模拟请求 Token 方式接入SSO的接口)

  • 修改Host文件:域名方式访问认证中心,模拟跨域与线上真实环境
### 在host文件中添加以下内容0
127.0.0.1 xxlssoserver.com
127.0.0.1 xxlssoclient1.com
127.0.0.1 xxlssoclient2.com
复制代码
  • 分别运行 "xxl-sso-server" 与 "xxl-sso-token-sample-springboot"
  • 认证中心搭建成功后,默认为Token方式登陆提供API接口:
  • 1、登陆接口:/app/login参数:POST参数username:账号password:账号响应:JSON格式code:200 表示成功、其他失败msg:错误提示data: 登陆用户的 sso sessionid
  • 2、注销接口:/app/logout参数:POST参数sessionId:登陆用户的 sso sessionid响应:JSON格式code:200 表示成功、其他失败msg:错误提示
  • 3、登陆状态校验接口:/app/logincheck参数:POST参数sessionId:登陆用户的 sso sessionid响应:JSON格式code:200 表示成功、其他失败msg:错误提示data:登陆用户信息userid:用户IDusername:用户名
  • idea的http client测试如下:
##client1
POST http://xxlssoclient1.com:8082/xxl-sso-token-sample-springboot/
Content-Type: application/x-www-form-urlencoded
xxl_sso_sessionid: 1000_bac4555c627e4233b6da7d3f9b91aff5

###

##client2
http://xxlssoclient2.com:8082/xxl-sso-token-sample-springboot/
Content-Type: application/x-www-form-urlencoded
xxl_sso_sessionid: 1000_bac4555c627e4233b6da7d3f9b91aff5



###

##校验
POST http://xxlssoserver.com:8080/xxl-sso-server/app/logincheck
Content-Type: application/x-www-form-urlencoded

sessionId=1000_9df8ee645d7f435fb5f76c9223dfe6e8


###

##注销
POST http://xxlssoserver.com:8080/xxl-sso-server/app/logout
Content-Type: application/x-www-form-urlencoded

sessionId=1000_0ec8e28158da4241926314e0b3b9b834


###

#登录sso
POST http://xxlssoserver.com:8080/xxl-sso-server/app/login
Content-Type: application/x-www-form-urlencoded

username=user&password=123456

复制代码
  • SSO登录/注销流程验证:
    • 可参考测试用例 :com.xxl.app.sample.test.TokenClientTest
  • 正常情况下,登录流程如下:
    • 1、获取用户输入的账号密码后,请求SSO Server的登录接口,获取用户 sso sessionid ;(参考代码:TokenClientTest.loginTest)
    • 2、登陆成功后,获取到 sso sessionid ,需要主动存储,后续请求时需要设置在 Header参数 中
    • 3、此时,使用 sso sessionid 访问受保护的 "Client01应用" 和 "Client02应用" 提供的接口,接口均正常返回(参考代码:TokenClientTest.clientApiRequestTest)
  • 正常情况下,注销流程如下:
    • 1、请求SSO Server的注销接口,注销登陆凭证 sso sessionid ;(参考代码:TokenClientTest.logoutTest)
    • 2、注销成功后,sso sessionid 将会全局失效
    • 3、此时,使用 sso sessionid 访问受保护的 "Client01应用" 和 "Client02应用" 提供的接口,接口请求将会被拦截,提示未登录并返回状态码 501(参考代码:TokenClientTest.clientApiRequestTest)

六、总体设计

6.1 架构图

单点登录sso实现方案(单点登录sso企业级解决方案)(4)

6.2 功能定位

XXL-SSO 是一个分布式单点登录框架。只需要登录一次就可以访问所有相互信任的应用系统。

借助 XXL-SSO,可以快速实现分布式系统单点登录。

6.3 核心概念

概念

说明

SSO Server

中央认证服务,支持集群

SSO Client

接入SSO认证中心的Client应用

SSO SessionId

登录用户会话ID,SSO 登录成功为用户自动分配

SSO User

登录用户信息,与 SSO SessionId 相对应

6.4 登录流程剖析

  • 用户于Client端应用访问受限资源时,将会自动 redirect 到 SSO Server 进入统一登录界面
  • 用户登录成功之后将会为用户分配 SSO SessionId 并 redirect 返回来源Client端应用,同时附带分配的 SSO SessionId
  • 在Client端的SSO Filter里验证 SSO SessionId 无误,将 SSO SessionId 写入到用户浏览器Client端域名下 cookie 中
  • SSO Filter验证 SSO SessionId 通过,受限资源请求放行

6.5 注销流程剖析

  • 用户与Client端应用请求注销Path时,将会 redirect 到 SSO Server 自动销毁全局 SSO SessionId,实现全局销毁
  • 然后,访问接入SSO保护的任意Client端应用时,SSO Filter 均会拦截请求并 redirect 到 SSO Server 的统一登录界面

6.6 基于Cookie,相关概念

  • 登录凭证存储:登录成功后,用户登录凭证被自动存储在浏览器Cookie中
  • Client端校验登录状态:通过校验请求Cookie中的是否包含用户登录凭证判断
  • 系统角色模型:
    • SSO Server:认证中心,提供用户登录、注销以及登录状态校验等功能
    • Client应用:受SSO保护的Client端Web应用,为用户浏览器访问提供服务
    • 用户:发起请求的用户,使用浏览器访问

6.7 基于Token,相关概念

  • 登录凭证存储:登录成功后,获取到登录凭证(xxl_sso_sessionid=xxx),需要主动存储,如存储在 localStorage、Sqlite 中
  • Client端校验登录状态:通过校验请求 Header参数 中的是否包含用户登录凭证(xxl_sso_sessionid=xxx)判断;因此,发送请求时需要在 Header参数 中设置登陆凭证
  • 系统角色模型:
    • SSO Server:认证中心,提供用户登录、注销以及登录状态校验等功能
    • Client应用:受SSO保护的Client端Web应用,为用户请求提供接口服务
    • 用户:发起请求的用户,如使用Android、IOS、桌面客户端等请求访问

6.8 未登录状态请求处理

基于Cookie,未登录状态请求:

  • 页面请求:redirect 到SSO Server登录界面
  • JSON请求:返回未登录的JSON格式响应数据
    • 数据格式:
      • code:501 错误码
      • msg:sso not login.

基于Token,未登录状态请求:

  • 返回未登录的JSON格式响应数据
    • 数据格式:
      • code:501 错误码
      • msg:sso not login.

6.9 登录态自动延期

支持自定义登录态有效期窗口,默认24H,当登录态有效期窗口过半时,自动顺延一个周期。

6.10 记住密码

未记住密码时,关闭浏览器则登录态失效;记住密码时,登录态自动延期,在自定义延期时间的基础上,原则上可以无限延期。

6.11 路径排除

自定义路径排除Path,允许设置多个,且支持Ant表达式。用于排除SSO客户端不需要过滤的路径。

责任编辑:电脑知识学习网

网络知识