> 技术文档 > SpringCloud 前端不同域和后端同Redis 集成Sa-Token-SSO单点登录模块 账号统一认证中心 多端同步登录 登录状态共享 前后端分离_sa-token-redis-template

SpringCloud 前端不同域和后端同Redis 集成Sa-Token-SSO单点登录模块 账号统一认证中心 多端同步登录 登录状态共享 前后端分离_sa-token-redis-template


介绍

举个场景,假设我们的系统被切割为N个部分:商城、论坛、直播、社交…… 如果用户每访问一个模块都要登录一次,那么用户将会疯掉, 为了优化用户体验,我们急需一套机制将这N个系统的认证授权互通共享,让用户在一个系统登录之后,便可以畅通无阻的访问其它所有系统。
SpringCloud 前端不同域和后端同Redis 集成Sa-Token-SSO单点登录模块 账号统一认证中心 多端同步登录 登录状态共享 前后端分离_sa-token-redis-template

单点登录——就是为了解决这个问题而生!

简而言之,单点登录可以做到: 在多个互相信任的系统中,用户只需登录一次,就可以访问所有系统。

—Sa-Token

官方文档:https://sa-token.cc/doc.html#/sso/readme

SpringCloud 前端不同域和后端同Redis 集成Sa-Token-SSO单点登录模块 账号统一认证中心 多端同步登录 登录状态共享 前后端分离_sa-token-redis-template
模式一:采用共享 Cookie 来做到前端 Token 的共享,从而达到后端的 Session 会话共享。

模式二:采用 URL 重定向,以 ticket 码为授权中介,做到多个系统间的会话传播。

模式三:采用 Http 请求主动查询会话,做到 Client 端与 Server 端的会话同步。

搭建统一认证中心 SSO-Server 服务端

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.14</version><relativePath/></parent><properties><sa-token.version>1.44.0</sa-token.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-spring-boot-starter</artifactId> <version>${sa-token.version}</version> </dependency> <dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-sso</artifactId> <version>${sa-token.version}</version> </dependency> <dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-redis-template</artifactId> <version>${sa-token.version}</version> </dependency><dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>cn.dev33</groupId><artifactId>sa-token-forest</artifactId><version>${sa-token.version}</version></dependency></dependencies>

认证中心配置文件

# 端口# 端口server: port: 9000# Sa-Token 配置sa-token: # 打印操作日志 is-log: true # SSO-Server 配置 sso-server: # Ticket有效期 (单位: 秒),默认五分钟  ticket-timeout: 300 # 主页路由:在 /sso/auth 登录页不指定 redirect 参数时,默认跳转的地址 # home-route: /home # 是否启用匿名 client (开启匿名 client 后,允许客户端接入时不提交 client 参数) allow-anon-client: true # 所有允许的授权回调地址 (匿名 client 使用) allow-url: \"*\" # API 接口调用秘钥 (全局默认 + 匿名 client 使用) secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor # 应用列表:配置接入的应用信息 clients: # 应用 sso-client1:采用模式一对接 (同域、同Redis) sso-client1: client: sso-client1 allow-url: \"*\" # 应用 sso-client2:采用模式二对接 (跨域、同Redis) sso-client2: client: sso-client2  # allow-url: \"*\" #生产模式禁止使用 allow-url: http://127.0.0.1:82/sso-login.html , http://127.0.0.1:81/sso-login.html secret-key: SSO-C2-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor spring: # Redis配置 (SSO模式一和模式二使用 Redis 来同步会话) redis: # Redis数据库索引(默认为0) database: 1 # Redis服务器地址 host: 127.0.0.1 # Redis服务器连接端口 port: 6379 # Redis服务器连接密码(默认为空) password: # 连接超时时间 timeout: 10s lettuce: pool: # 连接池最大连接数 max-active: 200 # 连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: -1ms # 连接池中的最大空闲连接 max-idle: 10 # 连接池中的最小空闲连接 min-idle: 0 forest: # 关闭 forest 请求日志打印 log-enabled: false  

跨域配置

处理统一认证中心的前端跨域问题

/** * [Sa-Token 权限认证] 配置类 (解决跨域问题) */@Configurationpublic class SaTokenConfigure { /** * CORS 跨域处理策略 */ @Bean public SaCorsHandleFunction corsHandle() { return (req, res, sto) -> { res.  // 允许指定域访问跨域资源 setHeader(\"Access-Control-Allow-Origin\", \"*\")  // 允许所有请求方式  .setHeader(\"Access-Control-Allow-Methods\", \"POST, GET, OPTIONS, DELETE\")  // 有效时间  .setHeader(\"Access-Control-Max-Age\", \"3600\")  // 允许的header参数  .setHeader(\"Access-Control-Allow-Headers\", \"*\"); // 如果是预检请求,则立即返回到前端 SaRouter.match(SaHttpMethod.OPTIONS)  .free(r -> {})  .back(); }; }}

SpringCloud 前端不同域和后端同Redis 集成Sa-Token-SSO单点登录模块 账号统一认证中心 多端同步登录 登录状态共享 前后端分离_sa-token-redis-template

异常处理

/** * 全局异常处理 */@RestControllerAdvicepublic class GlobalExceptionHandler {// 全局异常拦截 @ExceptionHandlerpublic SaResult handlerException(Exception e) {e.printStackTrace(); return SaResult.error(e.getMessage());}}

登录接口

在统一身份认证点击登录后会进入该方法

@RestControllerpublic class SsoServerController {/** * SSO-Server端:处理所有SSO相关请求 * http://{host}:{port}/sso/auth-- 单点登录授权地址 * http://{host}:{port}/sso/doLogin-- 账号密码登录接口,接受参数:name、pwd * http://{host}:{port}/sso/signout-- 单点注销地址(isSlo=true时打开) */@RequestMapping(\"/sso/*\")public Object ssoRequest() {return SaSsoServerProcessor.instance.dister();}// 配置SSO相关参数@Autowiredprivate void configSso(SaSsoServerTemplate ssoServerTemplate) {ssoServerTemplate.strategy.doLoginHandle = (name, pwd) -> {//可以在方法里通过 SaHolder.getRequest().getParam(\"xxx\") 来获取前端提交的其它参数。// 此处仅做模拟登录,真实环境应该查询数据库进行登录if(\"sa\".equals(name) && \"123456\".equals(pwd)) {String deviceId = SaHolder.getRequest().getParam(\"deviceId\", SaFoxUtil.getRandomString(32));StpUtil.login(10001, new SaLoginParameter().setDeviceId(deviceId));return SaResult.ok(\"登录成功!\").setData(StpUtil.getTokenValue());}return SaResult.error(\"登录失败!\");};// 添加消息处理器:userinfo (获取用户资料) (用于为 client 端开放拉取数据的接口)ssoServerTemplate.messageHolder.addHandle(\"userinfo\", (ssoTemplate, message) -> {System.out.println(\"收到消息:\" + message);// 自定义返回结果(模拟)return SaResult.ok().set(\"id\", message.get(\"loginId\")).set(\"name\", \"LinXiaoYu\").set(\"sex\", \"女\").set(\"age\", 18);});}}

以上就是统一认证中心的后端搭建


子系统登录认证

搭建好了统一认证中心的后端后,其他的系统就可以与他进行对接。

依赖

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.14</version><relativePath/></parent><properties><sa-token.version>1.44.0</sa-token.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-spring-boot-starter</artifactId> <version>${sa-token.version}</version> </dependency> <dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-sso</artifactId> <version>${sa-token.version}</version> </dependency> <dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-redis-template</artifactId> <version>${sa-token.version}</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-alone-redis</artifactId> <version>${sa-token.version}</version></dependency><dependency><groupId>cn.dev33</groupId><artifactId>sa-token-forest</artifactId><version>${sa-token.version}</version></dependency></dependencies>

配置文件

# 端口server: port: 9002# sa-token配置 sa-token: # 打印操作日志 is-log: true # SSO-相关配置 sso-client: # 应用标识 client: sso-client2 # SSO-Server 端主机地址 server-url: http://sa-sso-server.com:9000 # 在 sso-server 端前后端分离时需要单独配置 auth-url 参数(上面的不要注释,auth-url 配置项和 server-url 要同时存在) auth-url: http://127.0.0.1/sso-auth.html #统一认证中心的前端地址 # API 接口调用秘钥 (单点注销时会用到) secret-key: SSO-C2-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor # 配置 Sa-Token 单独使用的Redis连接(此处需要和 SSO-Server 端连接同一个 Redis) # 注:使用 alone-redis 需要在 pom.xml 引入 sa-token-alone-redis 依赖 alone-redis: # Redis数据库索引 database: 1 # Redis服务器地址 host: 127.0.0.1 # Redis服务器连接端口 port: 6379 # Redis服务器连接密码(默认为空) password: # 连接超时时间 timeout: 10s lettuce: pool: # 连接池最大连接数 max-active: 200 # 连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: -1ms # 连接池中的最大空闲连接 max-idle: 10 # 连接池中的最小空闲连接 min-idle: 0forest: # 关闭 forest 请求日志打印 log-enabled: false

子系统信息接口

如果不需要前后端分离架构下集成SSO,可删除此包下所有代码

/** * 前后台分离架构下集成SSO所需的代码 (SSO-Client端) * 

(注:如果不需要前后端分离架构下集成SSO,可删除此包下所有代码)

* */
@RestControllerpublic class H5Controller {// 判断当前是否登录@RequestMapping(\"/sso/isLogin\")public Object isLogin() {return SaResult.data(StpUtil.isLogin()).set(\"loginId\", StpUtil.getLoginIdDefaultNull());}// 返回SSO认证中心登录地址 @RequestMapping(\"/sso/getSsoAuthUrl\")public SaResult getSsoAuthUrl(String clientLoginUrl) {String serverAuthUrl = SaSsoClientUtil.buildServerAuthUrl(clientLoginUrl, \"\");return SaResult.data(serverAuthUrl);}// 根据 ticket 进行登录@RequestMapping(\"/sso/doLoginByTicket\")public SaResult doLoginByTicket(String ticket) {SaCheckTicketResult ctr = SaSsoClientProcessor.instance.checkTicket(ticket);StpUtil.login(ctr.loginId, new SaLoginParameter().setTimeout(ctr.remainTokenTimeout).setDeviceId(ctr.deviceId));return SaResult.data(StpUtil.getTokenValue());}}

此外 跨域-异常处理 均与服务端的一致 !!!

子系统登录接口

/** * Sa-Token-SSO Client端 Controller * @author click33 */@RestControllerpublic class SsoClientController {/* * SSO-Client端:处理所有SSO相关请求 * http://{host}:{port}/sso/login-- Client 端登录地址 * http://{host}:{port}/sso/logout-- Client 端注销地址(isSlo=true时打开) * http://{host}:{port}/sso/pushC-- Client 端接收消息推送地址 */@RequestMapping(\"/sso/*\")public Object ssoRequest() {return SaSsoClientProcessor.instance.dister();}// 配置SSO相关参数@Autowiredprivate void configSso(SaSsoClientTemplate ssoClientTemplate) {}// 当前应用独自注销 (不退出其它应用)@RequestMapping(\"/sso/logoutByAlone\")public Object logoutByAlone() {StpUtil.logout();return SaSsoClientProcessor.instance._ssoLogoutBack(SaHolder.getRequest(), SaHolder.getResponse());}// 查询我的账号信息:sso-client 前端 -> sso-center 后端 -> sso-server 后端@RequestMapping(\"/sso/myInfo\")public Object myInfo() {// 如果尚未登录if( ! StpUtil.isLogin()) {return \"尚未登录,无法获取\";}// 获取本地 loginIdObject loginId = StpUtil.getLoginId();// 推送消息SaSsoMessage message = new SaSsoMessage();message.setType(\"userinfo\");message.set(\"loginId\", loginId);SaResult result = SaSsoClientUtil.pushMessageAsSaResult(message);// 返回给前端return result;}}

前端请求顺序

官方流程图SpringCloud 前端不同域和后端同Redis 集成Sa-Token-SSO单点登录模块 账号统一认证中心 多端同步登录 登录状态共享 前后端分离_sa-token-redis-template
SpringCloud 前端不同域和后端同Redis 集成Sa-Token-SSO单点登录模块 账号统一认证中心 多端同步登录 登录状态共享 前后端分离_sa-token-redis-template