重构SSO模块,抽离三种模式的统一认证中心

This commit is contained in:
click33
2021-10-09 05:43:31 +08:00
parent 643118177a
commit d491f4083f
85 changed files with 727 additions and 1364 deletions

View File

@@ -34,6 +34,7 @@
- **单点登录**
- [单点登录简述](/sso/readme)
- [搭建统一认证中心SSO-Server](/sso/sso-server)
- [SSO模式一 共享Cookie同步会话](/sso/sso-type1)
- [SSO模式二 URL重定向传播会话](/sso/sso-type2)
- [SSO模式三 Http请求获取会话](/sso/sso-type3)

View File

@@ -18,7 +18,7 @@ public class SsoServerController {
return SaSsoHandle.serverRequest();
}
// ... 其它方法
// ... 其它代码
}
```

View File

@@ -80,15 +80,14 @@ if(res.code == 401) {
#### 方式二:在配置中配置登录视图地址
``` java
cfg.sso
// 配置未登录时返回的View
.setNotLoginView(() -> {
cfg.sso.setNotLoginView(() -> {
return new ModelAndView("xxx.html");
})
```
### 3、如何自定义登录API的接口
### 3、如何自定义登录API的接口地址
根据需求点选择解决方案:
#### 3.1、如果只是想在 setDoLoginHandle 函数里获取除 name、pwd 以外的参数?

View File

@@ -30,7 +30,7 @@ public class H5Controller {
// 根据ticket进行登录
@RequestMapping("/doLoginByTicket")
public SaResult doLoginByTicket(String ticket) {
Object loginId = checkTicket(ticket);
Object loginId = SaSsoHandle.checkTicket(ticket);
if(loginId != null) {
StpUtil.login(loginId);
return SaResult.data(StpUtil.getTokenValue());
@@ -38,11 +38,6 @@ public class H5Controller {
return SaResult.error("无效ticket" + ticket);
}
// 校验 Ticket码获取账号Id
private Object checkTicket(String ticket) {
return SaSsoUtil.checkTicket(ticket);
}
// 全局异常拦截
@ExceptionHandler
public SaResult handlerException(Exception e) {
@@ -58,7 +53,7 @@ public class H5Controller {
将其复制到项目中即可
### 3、新建前端项目
任意文件夹新建前端项目:`sa-token-demo-sso2-client-h5`,在根目录添加测试文件:`index.html`
任意文件夹新建前端项目:`sa-token-demo-sso-client-h5`,在根目录添加测试文件:`index.html`
``` js
<!DOCTYPE html>
<html>
@@ -102,43 +97,22 @@ public class H5Controller {
```
### 4、添加登录处理文件`sso-login.html`
源码详见:[sso-login.html](https://gitee.com/dromara/sa-token/tree/master/sa-token-demo/sa-token-demo-sso2-client-h5/sso-login.html)
源码详见:[sso-login.html](https://gitee.com/dromara/sa-token/tree/master/sa-token-demo/sa-token-demo-sso-client-h5/sso-login.html)
将其复制到项目中即可,与`index.html`一样放在根目录下
### 5、测试运行
先启动Server服务端与Client服务端再随便找个能预览html的工具打开前端项目比如[HBuilderX](https://www.dcloud.io/hbuilderx.html)),测试流程与一体版一致
### 6、疑问我在SSO模式三的demo中加入上述代码提示我 ticket无效是怎么回事
上述代码是以SSO模式二为基础的提示“Ticket无效”的原因很简单因为SSO模式三中 Server端 与 Client端 连接的不是同一个Redis
所以Client端校验Ticket时无法在Redis中查询到相应的值才会产生异常“Ticket无效”
要使上述代码生效很简单我们只需更改一下校验Ticket的逻辑即可将 `H5Controller` 中的 `checkTicket` 方法代码改为:
``` java
// 校验 Ticket码获取账号Id
private Object checkTicket(String ticket) {
SaSsoConfig cfg = SaManager.getConfig().getSso();
String ssoLogoutCall = null;
if(cfg.isSlo) {
ssoLogoutCall = SaHolder.getRequest().getUrl().replace("/doLoginByTicket", Api.ssoLogoutCall);
}
String checkUrl = SaSsoUtil.buildCheckTicketUrl(ticket, ssoLogoutCall);
Object body = cfg.sendHttp.apply(checkUrl);
return (SaFoxUtil.isEmpty(body) ? null : body);
}
```
重新运行项目即可在SSO模式三中成功整合前后台分离模式 。
### 7、SSO-Server 端的前后台分离
### 6、SSO-Server 端的前后台分离
疑问:上述代码都是针对 Client 端进行拆分,如果我想在 SSO-Server 端也进行前后台分离改造,应该怎么做?
> 答解决思路都是大同小异的与Client一样我们需要把原本在 “后端处理的授权重定向逻辑” 拿到前端来实现。
由于集成代码与 Client 端类似这里暂不贴详细代码我们可以下载官方仓库里面有搭建好的demo
使用前端ide导入项目 `/sa-token-demo/sa-token-demo-sso2-h5`,浏览器访问 `sso-auth.html` 页面:
使用前端ide导入项目 `/sa-token-demo/sa-token-demo-sso-server-h5`,浏览器访问 `sso-auth.html` 页面:
![sso-type2-server-h5-auth.png](https://oss.dev33.cn/sa-token/doc/sso/sso-type2-server-h5-auth.png 's-w-sh')
@@ -148,7 +122,7 @@ private Object checkTicket(String ticket) {
sa-token:
sso:
# SSO-Server端 统一认证地址
auth-url: http://127.0.0.1:8848/sa-token-demo-sso2-server-h5/sso-auth.html
auth-url: http://127.0.0.1:8848/sa-token-demo-sso-server-h5/sso-auth.html
```
然后我们启动项目 `sa-token-demo-sso2-server` 与 `sa-token-demo-sso2-client`,按照之前的测试步骤访问:

View File

@@ -0,0 +1,297 @@
# 搭建统一认证中心 SSO-Server
在开始SSO三种模式的对接之前我们必须先搭建一个 SSO-Server 认证中心
> 搭建示例在官方仓库的 `/sa-token-demo/sa-token-demo-sso-server/`如遇到难点可结合源码进行测试学习demo里有制作好的登录页面
---
### 1、添加依赖
创建 SpringBoot 项目 `sa-token-demo-sso-server`,引入依赖:
``` xml
<!-- Sa-Token 权限认证, 在线文档http://sa-token.dev33.cn/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>${sa.top.version}</version>
</dependency>
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>${sa.top.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>
<!-- Http请求工具在模式三的单点注销功能下用到如不需要可以注释掉 -->
<dependency>
<groupId>com.ejlchina</groupId>
<artifactId>okhttps</artifactId>
<version>3.1.1</version>
</dependency>
```
除了 **sa-token-spring-boot-starter** 以外,其它包都是可选的:
- 在SSO模式三时 Redis 相关包是可选的
- 在前后端分离模式下可以删除 thymeleaf 相关包
- 在不需要SSO模式三单点注销的情况下可以删除 http 工具包
建议先完整测试三种模式之后再对pom依赖进行酌情删减。
### 2、开放认证接口
新建 `SsoServerController`,用于对外开放接口:
``` java
/**
* Sa-Token-SSO Server端 Controller
*/
@RestController
public class SsoServerController {
/*
* SSO-Server端处理所有SSO相关请求 (下面的章节我们会详细列出开放的接口)
*/
@RequestMapping("/sso/*")
public Object ssoRequest() {
return SaSsoHandle.serverRequest();
}
/**
* 配置SSO相关参数
*/
@Autowired
private void configSso(SaTokenConfig cfg) {
// 配置未登录时返回的View
cfg.sso.setNotLoginView(() -> {
String msg = "当前会话在SSO-Server端尚未登录请先访问"
+ "<a href='/sso/doLogin?name=sa&pwd=123456' target='_blank'> doLogin登录 </a>"
+ "进行登录之后,刷新页面开始授权";
return msg;
});
// 配置:登录处理函数
cfg.sso.setDoLoginHandle((name, pwd) -> {
// 此处仅做模拟登录,真实环境应该查询数据进行登录
if("sa".equals(name) && "123456".equals(pwd)) {
StpUtil.login(10001);
return SaResult.ok("登录成功!").setData(StpUtil.getTokenValue());
}
return SaResult.error("登录失败!");
});
// 配置 Http 请求处理器 (在模式三的单点注销功能下用到,如不需要可以注释掉)
cfg.sso.setSendHttp(url -> {
return OkHttps.sync(url).get().getBody().toString();
});
}
}
```
注:在`setDoLoginHandle`函数里如果要获取name, pwd以外的参数可通过`SaHolder.getRequest().getParam("xxx")`来获取
全局异常处理:
``` java
@RestControllerAdvice
public class GlobalExceptionHandler {
// 全局异常拦截
@ExceptionHandler
public SaResult handlerException(Exception e) {
e.printStackTrace();
return SaResult.error(e.getMessage());
}
}
```
### 3、application.yml配置
``` yml
# 端口
server:
port: 9000
# Sa-Token 配置
sa-token:
# -------------- SSO-模式一相关配置 (非模式一不需要配置)
# cookie:
# 配置Cookie作用域
# domain: stp.com
# ------- SSO-模式二相关配置
sso:
# Ticket有效期 (单位: 秒),默认五分钟
ticket-timeout: 300
# 所有允许的授权回调地址
allow-url: "*"
# 是否打开单点注销功能
is-slo: true
# ------- SSO-模式三相关配置 下面的配置在SSO模式三并且 is-slo=true 时打开) -------
# 是否打开模式三
isHttp: true
# 接口调用秘钥用于SSO模式三的单点注销功能
secretkey: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
# ---- 除了以上配置项,你还需要为 Sa-Token 配置http请求处理器文档有步骤说明
spring:
# Redis配置 SSO模式一和模式二使用Redis来同步会话
redis:
# Redis数据库索引默认为0
database: 1
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码默认为空
password:
```
注意点:`allow-url`为了方便测试配置为`*`线上生产环境一定要配置为详细URL地址 (之后的章节我们会详细阐述此配置项)
### 4、创建启动类
``` java
@SpringBootApplication
public class SaSsoServerApplication {
public static void main(String[] args) {
SpringApplication.run(SaSsoServerApplication.class, args);
System.out.println("\n------ Sa-Token-SSO 认证中心启动成功");
}
}
```
启动项目,不出意外的情况下我们将看到如下输出:
![sso-server-start](https://oss.dev33.cn/sa-token/doc/sso/sso-server-start.png 's-w-sh')
访问统一授权地址:
- [http://localhost:9000/sso/auth](http://localhost:9000/sso/auth)
![sso-server-init-login.png](https://oss.dev33.cn/sa-token/doc/sso/sso-server-init-login.png 's-w-sh')
可以看到这个页面非常简陋这是因为我们以上的代码示例主要目标是为了带大家从零搭建一个可用的SSO认证服务端所以就对一些不太必要的步骤做了简化
大家可以下载运行一下官方仓库里的示例`/sa-token-demo/sa-token-demo-sso-server/`,里面有制作好的登录页面:
![sso-server-init-login2.png](https://oss.dev33.cn/sa-token/doc/sso/sso-server-init-login2.png 's-w-sh')
默认账号密码为:`sa / 123456`,大家先别着急点击登录,因为我们还没有搭建对应的 Client 端项目,
真实项目中我们是不会直接从浏览器访问 `/sso/auth` 授权地址的,我们需要在 Client 端点击登录按钮重定向而来。
现在我们先来看看除了 `/sso/auth` 统一授权地址,这个 SSO-Server 认证中心还开放了哪些API呢且往下看
### 5、API 列表
如果你仅仅使用 Sa-Token 搭建 SSO-Server 端,而 Client 端使用其它框架的话,那么下面的 API 列表将给你的对接步骤做一份参考。
如果你在 Client 端也用到了 Sa-Token 框架那么你可以选择跳过本小节Sa-Token 对 Client 端也提供了相应的封装,你可以直接开始学习:[SSO模式一 共享Cookie同步会话](/sso/sso-type1)
#### 5.1、单点登录授权地址
``` url
http://{host}:{port}/sso/auth
```
接收参数:
| 参数 | 是否必填 | 说明 |
| :-------- | :-------- | :-------- |
| redirect | 是 | 登录成功后的重定向地址,一般填写 location.href从哪来回哪去 |
| mode | 否 | 授权模式,取值 [simple, ticket]simple=登录后直接重定向ticket=带着ticket参数重定向默认值为ticket |
访问接口后有两种情况:
- 情况一:当前会话在 SSO 认证中心未登录,会进入登录页开始登录。
- 情况二:当前会话在 SSO 认证中心已登录,会被重定向至 `redirect` 地址,并携带 `ticket` 参数。
#### 5.2、RestAPI 登录接口
``` url
http://{host}:{port}/sso/doLogin
```
接收参数:
| 参数 | 是否必填 | 说明 |
| :-------- | :-------- | :-------- |
| name | 是 | 用户名 |
| pwd | 是 | 密码 |
此接口属于 RestAPI (使用ajax访问),会进入后端配置的 `setDoLoginHandle` 函数中,另外需要注意:
此接口并非只能携带 name、pwd 参数,因为你可以在 setDoLoginHandle 函数里通过 `SaHolder.getRequest().getParam("xxx")` 来获取其它参数。
#### 5.3、Ticket 校验接口
此接口仅配置模式三 `(isHttp=true)` 时打开
``` url
http://{host}:{port}/sso/checkTicket
```
接收参数:
| 参数 | 是否必填 | 说明 |
| :-------- | :-------- | :-------- |
| ticket | 是 | 在步骤 5.1 中授权重定向时的 ticket 参数 |
| ssoLogoutCall | 否 | 单点注销时的回调通知地址只在SSO模式三时需要携带此参数|
返回值场景:
- 返回空,代表校验失败。
- 返回具体的 loginId例如10001代表校验成功值为此 ticket 码代表的用户id。
#### 5.4、单点注销接口
``` url
http://{host}:{port}/sso/logout
```
接受参数:
| 参数 | 是否必填 | 说明 |
| :-------- | :-------- | :-------- |
| loginId | 否 | 要注销的账号id |
| secretkey | 否 | 接口通信秘钥 |
| back | 否 | 注销成功后的重定向地址 |
此接口有两种调用方式
##### 方式一:在前端页面引导用户直接跳转,并带有 back 参数
例如:`http://{host}:{port}/sso/logout?back=xxx`代表用户注销成功后返回back地址
##### 方式二:在 Client 的后端通过 http 工具来调用
例如:`http://{host}:{port}/sso/logout?loginId={value}&secretkey={value}`,代表注销 账号=loginId 的账号返回json数据结果形如
``` js
{
"code": 200, // 200表示请求成功非200标识请求失败
"msg": "单点注销成功",
"data": null
}
```
<br>
SSO 认证中心只有这四个接口,接下来让我一起来看一下 Client 端的对接流程:[SSO模式一 共享Cookie同步会话](/sso/sso-type1)

View File

@@ -4,15 +4,15 @@
---
### 0、解决思路?
### 1、解决思路?
首先我们分析一下多个系统之间,为什么无法同步登录状态?
1. 前端的`Token`无法在多个系统下共享。
2. 后端的`Session`无法在多个系统间共享。
1. 前端的 `Token` 无法在多个系统下共享。
2. 后端的 `Session` 无法在多个系统间共享。
所以单点登录第一招,就是对症下药:
1. 使用`共享Cookie`来解决Token共享问题。
2. 使用`Redis`来解决Session共享问题。
1. 使用 `共享Cookie` 来解决 Token 共享问题。
2. 使用 `Redis` 来解决 Session 共享问题。
所谓共享Cookie就是主域名Cookie在二级域名下的共享举个例子写在父域名`stp.com`下的Cookie`s1.stp.com``s2.stp.com`等子域名都是可以共享访问的。
@@ -20,12 +20,8 @@
OK所有理论就绪下面开始实战
> Sa-Token整合同域单点登录非常简单相比于正常的登录你只需增加配置 `sa-token.cookie-domain=xxx.com` 指定一下Cookie写入时的父级域名即可。 <br>
> 整合示例在官方仓库的 `/sa-token-demo/sa-token-demo-sso1/`,如遇到难点可结合源码进行测试学习。
### 1、准备工作
### 2、准备工作
首先修改hosts文件`(C:\windows\system32\drivers\etc\hosts)`添加以下IP映射方便我们进行测试
``` url
@@ -35,109 +31,153 @@ OK所有理论就绪下面开始实战
127.0.0.1 s3.stp.com
```
<!-- 其中:`sso.stp.com`为统一认证地址,当用户在其它 Client 端发起登录请求时,均将其重定向至认证中心,待到登录成功之后再原路返回到 Client 端。 -->
其中:`sso.stp.com`为统一认证地址,其它均为 Client 端。
其中:`sso.stp.com`为统一认证中心地址,当用户在其它 Client 端发起登录请求时,均将其重定向至认证中心,待到登录成功之后再原路返回到 Client 端。
### 2、指定Cookie的作用域
在`s1.stp.com`访问服务器其Cookie也只能写入到`s1.stp.com`下为了将Cookie写入到其父级域名`stp.com`下,我们需要新增配置:
### 3、指定Cookie的作用域
在`sso.stp.com`访问服务器其Cookie也只能写入到`sso.stp.com`下为了将Cookie写入到其父级域名`stp.com`下,我们需要更改 SSO-Server 端的 yml 配置:
``` yml
sa-token:
# 写入Cookie时显式指定的作用域, 用于单点登录二级域名共享Cookie
cookie-domain: stp.com
cookie:
# 配置Cookie作用域 (这个配置原本是被注释掉的,现在我们将其打开)
domain: stp.com
```
### 3、新增测试Controller
新建`SsoController.java`控制器,写入代码:
在SSO模式一测试完毕之后一定要将这个配置再次注释掉因为模式一与模式二三使用不同的授权流程这行配置会影响到我们模式二和模式三的正常运行。
### 4、搭建 Client 端项目
> 整合示例在官方仓库的 `/sa-token-demo/sa-token-demo-sso1-client/`,如遇到难点可结合源码进行测试学习。
#### 4.1、引入依赖
新建项目 sa-token-demo-sso1-client并添加以下依赖
``` xml
<!-- Sa-Token 权限认证, 在线文档http://sa-token.dev33.cn/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>${sa-token-version}</version>
</dependency>
<!-- Sa-Token 整合redis (使用jackson序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>${sa-token-version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- Sa-Token插件权限缓存与业务缓存分离 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-alone-redis</artifactId>
<version>${sa-token-version}</version>
</dependency>
```
#### 4.2、新建 Controller 控制器
``` java
/**
* 测试: 同域单点登录
* Sa-Token-SSO Client端 Controller
* @author kong
*/
@RestController
@RequestMapping("/sso/")
public class SsoController {
public class SsoClientController {
// 测试:进行登录
@RequestMapping("doLogin")
public SaResult doLogin(@RequestParam(defaultValue = "10001") String id) {
System.out.println("---------------- 进行登录 ");
StpUtil.login(id);
return SaResult.ok("登录成功: " + id);
// SSO-Client端首页
@RequestMapping("/")
public String index() {
String authUrl = SaManager.getConfig().getSso().getAuthUrl();
String solUrl = SaManager.getConfig().getSso().getSloUrl();
String str = "<h2>Sa-Token SSO-Client 应用端</h2>" +
"<p>当前会话是否登录:" + StpUtil.isLogin() + "</p>" +
"<p><a href=\"javascript:location.href='" + authUrl + "?mode=simple&redirect=' + encodeURIComponent(location.href);\">登录</a> " +
"<a href=\"javascript:location.href='" + solUrl + "?back=' + encodeURIComponent(location.href);\">注销</a> </p>";
return str;
}
// 测试:是否登录
@RequestMapping("isLogin")
public SaResult isLogin() {
System.out.println("---------------- 是否登录 ");
boolean isLogin = StpUtil.isLogin();
return SaResult.ok("是否登录: " + isLogin);
// 全局异常拦截
@ExceptionHandler
public SaResult handlerException(Exception e) {
e.printStackTrace();
return SaResult.error(e.getMessage());
}
}
```
#### 4.3、application.yml 配置
``` yml
# 端口
server:
port: 9001
# sa-token配置
sa-token:
# SSO-相关配置
sso:
# SSO-Server端-单点登录授权地址
auth-url: http://sso.stp.com:9000/sso/auth
# SSO-Server端-单点注销地址
slo-url: http://sso.stp.com:9000/sso/logout
# 配置Sa-Token单独使用的Redis连接 此处需要和SSO-Server端连接同一个Redis
alone-redis:
# Redis数据库索引
database: 1
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码默认为空
password:
# 连接超时时间
timeout: 10s
```
#### 4.4、启动类
``` java
// 启动类
/**
* SSO模式一Client端 Demo
*/
@SpringBootApplication
public class SaSsoApplication {
public class SaSsoClientApplication {
public static void main(String[] args) {
SpringApplication.run(SaSsoApplication.class, args);
System.out.println("\n启动成功:Sa-Token配置如下:" + SaManager.getConfig());
SpringApplication.run(SaSsoClientApplication.class, args);
System.out.println("\nSa-Token SSO模式一 Client端启动成功");
}
}
```
### 4、访问测试
启动项目,依次访问:
- [http://s1.stp.com:8081/sso/isLogin](http://s1.stp.com:8081/sso/isLogin)
- [http://s2.stp.com:8081/sso/isLogin](http://s2.stp.com:8081/sso/isLogin)
- [http://s3.stp.com:8081/sso/isLogin](http://s3.stp.com:8081/sso/isLogin)
均返回以下结果:
![sso-type1-wd.png](https://oss.dev33.cn/sa-token/doc/sso/sso-type1-wd.png 's-w-sh')
现在访问SSO认证中心的登录接口[http://sso.stp.com:8081/sso/doLogin](http://sso.stp.com:8081/sso/doLogin)
![sso-type1-login.png](https://oss.dev33.cn/sa-token/doc/sso/sso-type1-login.png 's-w-sh')
然后再次刷新上面三个测试接口,均可以得到以下结果:
![sso-type1-yd.png](https://oss.dev33.cn/sa-token/doc/sso/sso-type1-yd.png 's-w-sh')
测试完毕!
### 5、完善统一认证中心
上面的示例我们简单的演示了SSO模式一的认证原理。
当然,在实际的正式项目中,我们肯定不会每个 Client 端都内置一个登录接口一般的做法是只在SSO认证中心保留登录接口我们所有 Client 端的登录请求都会被重定向至认证中心,
待到登录成功之后再原路返回到 Client 端。
我们可以运行一下官方仓库的示例,里面有制作好的登录页面
> 下载官方示例,依次运行:
> - `/sa-token-demo/sa-token-demo-sso1-server/`
> - `/sa-token-demo/sa-token-demo-sso1-client/`
访问三个应用端:
### 5、访问测试
启动项目,依次访问三个应用端
- [http://s1.stp.com:9001/](http://s1.stp.com:9001/)
- [http://s2.stp.com:9001/](http://s2.stp.com:9001/)
- [http://s3.stp.com:9001/](http://s3.stp.com:9001/)
均返回:
![sso1--index.png](https://oss.dev33.cn/sa-token/doc/sso/sso1--index.png 's-w-sh')
然后点击登录被重定向至SSO认证中心
![sso1--login-page.png](https://oss.dev33.cn/sa-token/doc/sso/sso1--login-page.png 's-w-sh')
![sso1--login-page2.png](https://oss.dev33.cn/sa-token/doc/sso/sso1--login-page2.png 's-w-sh')
输入默认测试账号:`sa / 123456`,点击登录
我们点击登录,然后刷新页面:
![sso1-login-ok.png](https://oss.dev33.cn/sa-token/doc/sso/sso1-login-ok.png 's-w-sh')
@@ -148,9 +188,10 @@ public class SaSsoApplication {
测试完成
### 6、跨域模式下的解决方案
如上,我们使用极其简单的步骤实现了同域下的单点登录,聪明如你😏,马上想到了这种模式有着一个不小的限制:
如上,我们使用简单的步骤实现了同域下的单点登录,聪明如你😏,马上想到了这种模式有着一个不小的限制:
> 所有子系统的域名,必须同属一个父级域名
@@ -161,3 +202,4 @@ public class SaSsoApplication {

View File

@@ -3,7 +3,7 @@
如果我们的多个系统部署在不同的域名之下但是后端可以连接同一个Redis那么便可以使用 **`[URL重定向传播会话]`** 的方式做到单点登录。
### 0、解题思路
### 1、解题思路
首先我们再次复习一下,多个系统之间为什么无法同步登录状态?
@@ -30,7 +30,7 @@
下面我们按照步骤依次完成上述过程:
### 1、准备工作
### 2、准备工作
首先修改hosts文件`(C:\windows\system32\drivers\etc\hosts)`添加以下IP映射方便我们进行测试
``` url
127.0.0.1 sa-sso-server.com
@@ -39,134 +39,24 @@
127.0.0.1 sa-sso-client3.com
```
### 2、搭建 SSO-Server 认证中心
> 搭建示例在官方仓库的 `/sa-token-demo/sa-token-demo-sso2-server/`,如遇到难点可结合源码进行测试学习
#### 2.1、创建 SSO-Server 端项目
创建 SpringBoot 项目 `sa-token-demo-sso-server`,引入依赖:
``` xml
<!-- Sa-Token 权限认证, 在线文档http://sa-token.dev33.cn/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>${sa.top.version}</version>
</dependency>
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>${sa.top.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
```
#### 2.2、开放认证接口
一个完整的SSO认证中心应该至少包含以下接口
- `/sso/auth`:单点登录统一认证地址。
- `/sso/doLogin`RestAPI 登录接口,根据账号密码进行登录。
- `/sso/logout`:统一单点注销地址,一次注销,全端下线。
别急,这里不需要你亲自完成这些接口 —— Sa-Token 已经为你封装了实现。你要做的,就是提供一个访问入口,接入 Sa-Token 的方法。
``` java
/**
* Sa-Token-SSO Server端 Controller
*/
@RestController
public class SsoServerController {
/*
* SSO-Server端处理所有SSO相关请求
* http://{host}:{port}/sso/auth -- 单点登录授权地址接受参数redirect=授权重定向地址
* http://{host}:{port}/sso/doLogin -- 账号密码登录接口接受参数name、pwd
* http://{host}:{port}/sso/checkTicket -- Ticket校验接口isHttp=true时打开接受参数ticket=ticket码、ssoLogoutCall=单点注销回调地址 [可选]
* http://{host}:{port}/sso/logout -- 单点注销地址isSlo=true时打开接受参数loginId=账号id、secretkey=接口调用秘钥
*/
@RequestMapping("/sso/*")
public Object ssoRequest() {
return SaSsoHandle.serverRequest();
}
// 配置SSO相关参数
@Autowired
private void configSso(SaTokenConfig cfg) {
cfg.sso
// 配置未登录时返回的View
.setNotLoginView(() -> {
String msg = "当前会话在SSO-Server端尚未登录请先访问"
+ "<a href='/sso/doLogin?name=sa&pwd=123456' target='_blank'> doLogin登录 </a>"
+ "进行登录之后,刷新页面开始授权";
return msg;
})
// 配置:登录处理函数
.setDoLoginHandle((name, pwd) -> {
// 此处仅做模拟登录,真实环境应该查询数据进行登录
if("sa".equals(name) && "123456".equals(pwd)) {
StpUtil.login(10001);
return SaResult.ok("登录成功!");
}
return SaResult.error("登录失败!");
})
;
}
}
```
注意:在`setDoLoginHandle`函数里如果要获取name, pwd以外的参数可通过`SaHolder.getRequest().getParam("xxx")`来获取
#### 2.3、application.yml配置
``` yml
# 端口
server:
port: 9000
# Sa-Token配置
sa-token:
# SSO-相关配置
sso:
# Ticket有效期 (单位: 秒),默认五分钟
ticket-timeout: 300
# 所有允许的授权回调地址 (此处为了方便测试配置为*,线上生产环境一定要配置为详细地地址)
allow-url: "*"
spring:
# Redis配置
redis:
# Redis数据库索引默认为0
database: 1
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码默认为空
password:
```
注意点:`allow-url`为了方便测试配置为`*`线上生产环境一定要配置为详细URL地址 (之后的章节我们会详细阐述此配置项)
#### 2.4、创建SSO-Server端启动类
``` java
@SpringBootApplication
public class SaSsoServerApplication {
public static void main(String[] args) {
SpringApplication.run(SaSsoServerApplication.class, args);
System.out.println("\nSa-Token-SSO 认证中心启动成功");
}
}
```
### 3、搭建 SSO-Client 应用端
### 3、搭建 Client 端项目
> 搭建示例在官方仓库的 `/sa-token-demo/sa-token-demo-sso2-client/`,如遇到难点可结合源码进行测试学习
#### 3.1、创建SSO-Client端项目
#### 3.1、去除 SSO-Server 的 Cookie 作用域配置
在SSO模式一章节中我们打开了配置
``` yml
sa-token:
cookie:
# 配置Cookie作用域
domain: stp.com
```
此为模式一专属配置,现在我们将其注释掉,并按照注释提示打开其他相应的注释
#### 3.2、创建 SSO-Client 端项目
创建一个 SpringBoot 项目 `sa-token-demo-sso-client`,引入依赖:
``` xml
<!-- Sa-Token 权限认证, 在线文档http://sa-token.dev33.cn/ -->
@@ -196,7 +86,7 @@ public class SaSsoServerApplication {
```
#### 3.2、创建 SSO-Client 端认证接口
#### 3.3、创建 SSO-Client 端认证接口
同 SSO-Server 一样Sa-Token 为 SSO-Client 端所需代码也提供了完整的封装,你只需提供一个访问入口,接入 Sa-Token 的方法即可。
@@ -232,7 +122,7 @@ public class SsoClientController {
}
```
##### 3.3、配置SSO认证中心地址
##### 3.4、配置SSO认证中心地址
你需要在 `application.yml` 配置如下信息:
``` yml
# 端口
@@ -261,7 +151,7 @@ sa-token:
```
注意点:`sa-token.alone-redis` 的配置需要和SSO-Server端连接同一个Redisdatabase也要一样
#### 3.4、写启动类
#### 3.5、写启动类
``` java
@SpringBootApplication
public class SaSsoClientApplication {
@@ -314,7 +204,7 @@ public class SaSsoClientApplication {
![sso-genzong](https://oss.dev33.cn/sa-token/doc/sso/sso-genzong.png 's-w-sh')
<!--
### 5、运行官方仓库
以上示例,虽然完整的复现了单点登录的过程,但是页面还是有些简陋,我们可以运行一下官方仓库的示例,里面有制作好的登录页面
@@ -329,30 +219,16 @@ public class SaSsoClientApplication {
![sso-server-login-hua](https://oss.dev33.cn/sa-token/doc/sso/sso-server-login-hua.png 's-w-sh')
默认测试密码:`sa / 123456`,其余流程保持不变
-->
### 6、跨Redis的单点登录
### 5、跨 Redis 的单点登录
以上流程解决了跨域模式下的单点登录但是后端仍然采用了共享Redis来同步会话如果我们的架构设计中Client端与Server端无法共享Redis又该怎么完成单点登录
这就要采用模式三了,且往下看:[SSO模式三Http请求获取会话](/sso/sso-type3)
<!--
### 6、如何单点注销
由于Server端与所有Client端都是在共用同一套会话因此只要一端注销即可全端下线达到单点注销的效果
在`SsoClientController`中添加以下代码:
``` java
// SSO-Client端单点注销 (所有端一起下线)
@RequestMapping("logout")
public SaResult logout() {
StpUtil.logout();
return SaResult.ok();
}
``` -->

View File

@@ -5,7 +5,7 @@
> 阅读本篇之前请务必先熟读SSO模式二因为模式三仅仅属于模式二的一个特殊场景熟读模式二有助于您快速理解本章内容
### 0、问题分析
### 1、问题分析
我们先来分析一下,当后端不使用共享 Redis 时,会对架构产生哪些影响:
1. Client 端无法直连 Redis 校验 ticket取出账号id。
@@ -14,72 +14,59 @@
所以模式三的主要目标:也就是在 模式二的基础上 解决上述 三个难题
> 模式三的 Demo 示例地址:
>
> - SSO-Server 端:`/sa-token-demo/sa-token-demo-sso3-server/` [源码链接](https://gitee.com/dromara/sa-token/tree/dev/sa-token-demo/sa-token-demo-sso3-server) <br/>
> - SSO-Client 端:`/sa-token-demo/sa-token-demo-sso3-client/` [源码链接](https://gitee.com/dromara/sa-token/tree/dev/sa-token-demo/sa-token-demo-sso3-client) <br/>
>
> 如遇难点可参考示例
> 模式三的 Demo 示例地址:`/sa-token-demo/sa-token-demo-sso3-client/`
> [源码链接](https://gitee.com/dromara/sa-token/tree/dev/sa-token-demo/sa-token-demo-sso3-client),如遇难点可参考示例
### 1、SSO-Server 认证中心开放 Ticket 校验接口
既然 Client 端无法直连 Redis 校验 Ticket那我们就在 Server 端开放 Ticket 校验接口,然后 Client 端通过 http 请求获取数据。
### 2、在Client 端更改 Ticket 校验方式
#### 1.1、添加依赖
首先在 Server 端和 Client 端均添加以下依赖(如果不需要单点注销功能则 Server 端可不引入)
#### 2.1、增加 pom.xml 配置
``` xml
<!-- Http请求工具 -->
<dependency>
<groupId>com.ejlchina</groupId>
<artifactId>okhttps</artifactId>
<version>3.1.1</version>
<groupId>com.ejlchina</groupId>
<artifactId>okhttps</artifactId>
<version>3.1.1</version>
</dependency>
```
> OkHttps是一个轻量级http请求工具详情参考[OkHttps](https://gitee.com/ejlchina-zhxu/okhttps)
#### 1.2、认证中心开放接口
在 SSO-Server 端的 `application.yml` 中,新增以下配置:
``` yml
sa-token:
sso:
# 使用Http请求校验ticket
is-http: true
```
此配置项的作用是开放ticket校验接口让Client端通过http请求获取会话
#### 1.3、Client端新增配置
#### 2.2、配置 http 请求处理器
在SSO-Client端的 `SsoClientController` 中,新增以下配置
``` java
// 配置SSO相关参数
@Autowired
private void configSso(SaTokenConfig cfg) {
cfg.sso
// 配置 Http 请求处理器
.setSendHttp(url -> {
return OkHttps.sync(url).get().getBody().toString();
})
;
// ... 其他代码
// 配置 Http 请求处理器
cfg.sso.setSendHttp(url -> {
return OkHttps.sync(url).get().getBody().toString();
});
}
```
#### 2.3、application.yml 新增配置
``` yml
sa-token:
sso:
# 使用Http请求校验ticket
# 打开模式三(使用Http请求校验ticket
is-http: true
# SSO-Server端 ticket校验地址
check-ticket-url: http://sa-sso-server.com:9000/sso/checkTicket
```
#### 1.4、启动项目测试
启动SSO-Server、SSO-Client,访问测试:[http://sa-sso-client1.com:9001/](http://sa-sso-client1.com:9001/)
#### 2.4、启动项目测试
重启项目,访问测试:[http://sa-sso-client1.com:9001/](http://sa-sso-client1.com:9001/)
> 注如果已测试运行模式二可先将Redis中的数据清空以防旧数据对测试造成干扰
### 2、获取 Userinfo
除了账号id我们可能还需要将用户的昵称、头像等信息从 Server端 带到 Client端用户资料的同步。要解决这个需求,我们只需:
### 3、获取 Userinfo
除了账号id我们可能还需要将用户的昵称、头像等信息从 Server端 带到 Client端用户资料的同步。
#### 2.1、在 Server 端自定义接口,查询用户资料
在模式二中我们只需要将需要同步的资料放到 SaSession 即可但是在模式三中两端不再连接同一个Redis这时候我们需要通过http接口来同步信息
#### 3.1、在 Server 端自定义接口,查询用户资料
``` java
// 自定义接口获取userinfo
@RequestMapping("/sso/userinfo")
@@ -98,7 +85,7 @@ public Object userinfo(String loginId, String secretkey) {
}
```
#### 2.2、在 Client 端调用此接口查询 userinfo
#### 3.2、在 Client 端调用此接口查询 userinfo
首先在yml中配置接口地址
``` yml
sa-token:
@@ -122,7 +109,7 @@ public Object myinfo() {
### 3、无刷单点注销
### 4、无刷单点注销
有了单点登录就必然要有单点注销网上给出的大多数解决方案是将注销请求重定向至SSO-Server中心逐个通知Client端下线
@@ -137,34 +124,7 @@ public Object myinfo() {
这些逻辑 Sa-Token 内部已经封装完毕,你只需按照文章增加以下配置即可:
#### 2.1、SSO-Server认证中心增加配置
在 `SsoServerController` 中新增配置
``` java
// 配置SSO相关参数
@Autowired
private void configSso(SaTokenConfig cfg) {
cfg.sso
// ... (其它配置保持不变)
// 配置Http请求处理器
.setSendHttp(url -> {
// 此处为了提高响应速度这里可将sync换为async
return OkHttps.sync(url).get();
})
;
}
```
并在 `application.yml` 下新增配置:
``` yml
sa-token:
sso:
# 打开单点注销功能
is-slo: true
# API调用秘钥用于SSO模式三的单点注销功能
secretkey: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
```
#### 2.2、SSO-Client 端新增配置
#### 4.1、SSO-Client 端新增配置
在 `application.yml` 增加配置:`API调用秘钥` 和 `单点注销接口URL`。
``` yml
@@ -177,9 +137,11 @@ sa-token:
# 接口调用秘钥
secretkey: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
```
注意 secretkey 秘钥需要与SSO认证中心的一致
#### 2.3 启动测试
启动SSO-Server、SSO-Client访问测试[http://sa-sso-client1.com:9001/](http://sa-sso-client1.com:9001/)
#### 4.2 启动测试
重启项目,访问测试:[http://sa-sso-client1.com:9001/](http://sa-sso-client1.com:9001/)
我们主要的测试点在于 `单点注销`,正常登录即可。
![sso-type3-client-index.png](https://oss.dev33.cn/sa-token/doc/sso/sso-type3-client-index.png 's-w-sh')

View File

@@ -108,15 +108,12 @@ implementation 'cn.dev33:sa-token-core:${sa.top.version}'
├── sa-token-demo-solon // [示例] Sa-Token 集成 Solon
├── sa-token-demo-quick-login // [示例] Sa-Token 集成 quick-login 模块
├── sa-token-demo-alone-redis // [示例] Sa-Token 集成 alone-redis 模块
├── sa-token-demo-sso1 // [示例] Sa-Token 集成 SSO单点登录-模式一简单测试
├── sa-token-demo-sso1-server // [示例] Sa-Token 集成 SSO单点登录-模式一 认证中心
├── sa-token-demo-sso-server // [示例] Sa-Token 集成 SSO单点登录-Server认证中心
├── sa-token-demo-sso1-client // [示例] Sa-Token 集成 SSO单点登录-模式一 应用端
├── sa-token-demo-sso2-server // [示例] Sa-Token 集成 SSO单点登录-模式二 认证中心
├── sa-token-demo-sso2-client-h5 // [示例] Sa-Token 集成 SSO单点登录-模式二 应用端
├── sa-token-demo-sso2-server // [示例] Sa-Token 集成 SSO单点登录-模式二 认证中心 (前后端分离)
├── sa-token-demo-sso2-client-h5 // [示例] Sa-Token 集成 SSO单点登录-模式二 应用端 (前后端分离)
├── sa-token-demo-sso3-server // [示例] Sa-Token 集成 SSO单点登录-模式三 认证中心
├── sa-token-demo-sso2-client // [示例] Sa-Token 集成 SSO单点登录-模式二 应用端
├── sa-token-demo-sso3-client // [示例] Sa-Token 集成 SSO单点登录-模式三 应用端
├── sa-token-demo-sso-server-h5 // [示例] Sa-Token 集成 SSO单点登录-Server认证中心 (前后端分离)
├── sa-token-demo-sso-client-h5 // [示例] Sa-Token 集成 SSO单点登录-client应用端 (前后端分离)
├── sa-token-demo-oauth2-server // [示例] Sa-Token 集成 OAuth2.0 (服务端)
├── sa-token-demo-oauth2-client // [示例] Sa-Token 集成 OAuth2.0 (客户端)
├── sa-token-doc // [文档] Sa-Token 开发文档