sa-token/sa-token-doc/sso/sso-questions.md
2023-05-03 06:48:28 +08:00

286 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Sa-Token-SSO整合-常见问题总结
SSO 集成常见问题整理
[[toc]]
---
### 问在模式一与模式二中Client端 必须通过 Alone-Redis 插件来访问 Redis 吗?
答:不必须,只是推荐,权限缓存与业务缓存分离后会减少 `SSO-Redis` 的访问压力,且可以避免多个 `Client端` 的缓存读写冲突。
### 问:搭建好 sso-server 或 sso-client 服务后,访问返回:`{"msg": "not handle"}`。
返回这个信息,代表你访问的路由有错误,比如说:
- 统一认证登录地址是:`http://{host}:{port}/sso/auth`。
- 而你访问的却是:`http://{host}:{port}/sso/auth2`。
地址写错了,框架就不会处理这个请求,会直接返回 `{"msg": "not handle"}`,所有开放地址可参考:[SSO 开放接口](/sso/sso-apidoc)
如果仔细检查地址后没有写错,却依然返回了这个信息,那有可能是对应的接口没有打开,比如说:
- sso-server 端的单点注销地址:`http://{host}:{port}/sso/signout`
- sso-client 端的注销地址:`http://{host}:{port}/sso/logout`
都需要在配置文件配置:`sa-token.sso.is-slo=true`后,才会打开。
### 问我参照文档搭建SSO-Client一直提示Ticket无效请问怎么回事
如果使用的是模式二,出现此异常概率最大的原因是因为 `Client``Server` 没有连接同一个RedisSSO模式二中两者必须连接同一个 Redis 才可以登录成功。
你可能会问:我看配置文件明明是同一个啊?
我的建议是:排查时不要仅凭肉眼判断,分别在你的 `Client``Server` 启动后调用 `SaManager.getSaTokenDao().set("name", "value", 100000);`
随便写入一个值看看能不能根据你的预期写进同一个Redis里如果能的话才能证明 `Client``Server` 连接的Reids 是同一个,再进行下一步排查。
``` java
@SpringBootApplication
public class SaSsoServerApplication {
public static void main(String[] args) {
SpringApplication.run(SaSsoServerApplication.class, args);
System.out.println("\n------ Sa-Token-SSO 统一认证中心启动成功 ");
// 分别在 Client 与 Server 启动后调用 set 数据代码,看看能否根据预期写入同一个 reids
SaManager.getSaTokenDao().set("name", "value", 100000);
}
}
```
如果使用的是模式三,则排查是否有重复校验 ticket 的代码,一个 ticket 码只能使用一次,多次重复使用就会提示这个。
### 问模式一或者模式二报错Could not write JSON: No serializer found for class com.pj.sso.SysUser and no properties discovered to create BeanSerializer
一般是因为在 sso-server 端往 session 上写入了某个实体类(比如 User而在 sso-client 端没有这个实体类,导致反序列化失败。
解决方案:在 sso-client 也新建上这个类,而且包名需要与 sso-server 端的一致(直接从 sso-server 把实体类复制过来就好了)
### 问:模式三配置一堆 xxx-url ,有办法简化一下吗?
可以使用 `sa-token.sso.server-url` 来简化:
配置含义:配置 Server 端主机总地址,拼接在 authUrl、checkTicketUrl、getDataUrl、sloUrl 属性前面,用以简化各种 url 配置。
在开发 SSO 模块时,我们需要在 sso-client 配置认证中心的各种地址,特别是在模式三下,一般代码会变成这样:
``` yaml
sa-token:
sso:
# SSO-Server端 统一认证地址
auth-url: http://sa-sso-server.com:9000/sso/auth
# SSO-Server端 ticket校验地址
check-ticket-url: http://sa-sso-server.com:9000/sso/checkTicket
# 单点注销地址
slo-url: http://sa-sso-server.com:9000/sso/signout
# SSO-Server端 查询数据地址
get-data-url: http://sa-sso-server.com:9000/sso/getData
```
一堆 xxx-url 配置比较繁琐,且含有大量重复字符,现在我们可以将其简化为:
``` yaml
sa-token:
sso:
server-url: http://sa-sso-server.com:9000
```
只要你配置了 `server-url` 地址Sa-Token 就可以自动拼接出其它四个地址:
**例1使用 server-url 简化:**
- 你配置的 server-url 值是:`http://sa-sso-server.com:9000`。
- 框架拼接出的 auth-url 值就是:`http://sa-sso-server.com:9000/sso/auth`,其它三个 url 配置项同理。
**例2使用 server-url + auth-url 简化:**
- 你配置的 server-url 值是:`http://sa-sso-server.com:9000`auth-url 是:`/sso/auth2`。
- 框架拼接出的 auth-url 值就是:`http://sa-sso-server.com:9000/sso/auth2`,其它三个 url 配置项同理。
**例3auth-url 地址以 http 字符开头:**
- 你配置的 server-url 值是:`http://sa-sso-server.com:9000`auth-url 是:`http://my-site.com/sso/auth2`。
- 此时框架只以 auth-url 值为准,得到的 auth-url 值是:`http://my-site.com/sso/auth2`,其它三个 url 配置项同理。
### 问SSO模式二或模式三第一个 client 登录成功之后再访问其它两个 client 不会自动登录,需要点一下登录按钮才会登录上?
答:这是正常现象,系统 1 登录成功之后,系统 2 与系统 3 需要点击登录按钮,才会登录成功。
> 第一个系统,需要:点击 [登录] 按钮 -> 跳转到登录页 -> 输账号密码 -> 登录成功 <br>
> 第二个系统,需要:点击 [登录] 按钮 -> 登录成功 <br>
> 第三个系统,需要:点击 [登录] 按钮 -> 登录成功
>
> (系统二、三 免去重复跳转登录页输入账号密码的步骤)
### 追问:那我是否可以设计成不需要点登录按钮的,只要访问页面,它就能登录成功?
可以的。
其实思路很简单,我们只需要给 client 项目加个过滤器,拦截所有请求,只要检测到未登录就将其重定向至登录页面:
``` java
/**
* Sa-Token 配置类
*/
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
/** 注册 [Sa-Token全局过滤器] */
@Bean
public SaServletFilter getSaServletFilter() {
return new SaServletFilter()
.addInclude("/**")
.addExclude("/sso/*", "/favicon.ico") // 这里需要注意排除掉 /sso/* 相关请求不拦截,否则就会触发无限重定向
.setAuth(obj -> {
/*
* 这里会被分为两种情况:
* 情况1这个请求在当前 client 已经登录,此时会顺利进入网站
* 情况2这个请求在当前 client 尚未登录,此时会被拦截,重定向至当前系统的 /sso/login?back=当前地址
*
* 情况2会带领着用户继续重定向至 sso-server 认证中心,此时又分为两种情况:
* 情况2.1:此用户在 sso-server 尚未登录,此时会停留在登录页面,开始输入账号密码进行登录
* 情况2.2:此用户在 sso-server 已经登录(这证明此用户已经在其它至少一个 sso-client 处完成了登录)
* 此时用户会继续重定向回当前 client并携带 ticket 参数,完成登录。
*/
if(StpUtil.isLogin() == false) {
String back = SaFoxUtil.joinParam(SaHolder.getRequest().getUrl(), SpringMVCUtil.getRequest().getQueryString());
SaHolder.getResponse().redirect("/sso/login?back=" + SaFoxUtil.encodeUrl(back));
SaRouter.back();
}
})
;
}
}
```
更多登录姿势可以参考 [[何时引导用户去登录]](/sso/sso-custom-login) 给出的建议进行设计。
### 问:如果 sso-client 端我没有集成 sa-token-sso如何对接
需要手动调用 http 请求来对接 sso-server 开放的接口,参考示例:[sa-token-demo-sso3-client-nosdk](https://gitee.com/dromara/sa-token/tree/master/sa-token-demo/sa-token-demo-sso3-client-nosdk)
### 问:如果 sso-client 端不是 java语言可以对接吗
可以,只不过有点麻烦,基本思路和上个问题一致,需要手动调用 http 请求来对接 sso-server 开放的接口,参考:
[SSO-Server 认证中心开放接口](/sso/sso-apidoc)
### 问:将旧有系统改造为单点登录时,应该注意哪些?
建议不要把其中一个系统改造为SSO服务端而是新起一个项目作为 SSO-Server 端,所有旧有项目全部作为 Client 端与此对接。
### 问:怎么在一个项目里同时搭建 sso-server 和 sso-client
难点在于解决两边的路由冲突,示例代码:
``` java
// Sa-Token SSO Controller
@RestController
public class SsoController {
// 处理 SSO-Server 端所有请求
@RequestMapping({"/sso/auth", "/sso/doLogin", "/sso/checkTicket", "/sso/signout"})
public Object ssoServerRequest() {
return SaSsoProcessor.instance.serverDister();
}
// 处理 SSO-Client 端所有请求
@RequestMapping({"/sso/login", "/sso/logout", "/sso/logoutCall"})
public Object ssoClientRequest() {
return SaSsoProcessor.instance.clientDister();
}
// 配置SSO相关参数
@Autowired
private void configSso(SaSsoConfig sso) {
// SSO配置代码参考文档前几章 ...
}
}
```
### 问:我一个项目里有两套账号体系,都需要单点登录,怎么在一个项目里同时搭建两个 sso-server 服务?
首先推荐你不要在一个项目里同时搭建两个 sso-server建议创建两个项目分别搭建各自的 sso-server 服务。
如果一定要在一个项目中搭建两套 sso-server 服务,参考方案如下:
第一套,还是用前面几章文档给出的示例代码。
第二套,修改一些参数属性,使之与第一套不产生冲突,参考代码如下:
``` java
/**
* Sa-Token-SSO 第二套 SSO-Server端 Controller
*/
@RestController
public class SsoUserServerController {
/**
* 新建一个 SaSsoProcessor 请求处理器
*/
public static SaSsoProcessor ssoUserProcessor = new SaSsoProcessor();
static {
// 自定义一个 SaSsoTemplate 对象
SaSsoTemplate ssoUserTemplate = new SaSsoTemplate() {
// 使用的会话对象 是自定义的 StpUserUtil
@Override
public StpLogic getStpLogic() {
return StpUserUtil.stpLogic;
}
// 使用自定义的签名秘钥
SaSignConfig signConfig = new SaSignConfig().setSecretKey("xxxx-新的秘钥-xxxx");
SaSignTemplate userSignTemplate = new SaSignTemplate().setSignConfig(signConfig);
@Override
public SaSignTemplate getSignTemplate() {
return userSignTemplate;
}
};
// 让这个SSO请求处理器使用的路由前缀是 /sso-user而不是原先的 /sso
ssoUserTemplate.apiName.replacePrefix("/sso-user");
// 给这个 SSO 请求处理器使用自定义的 SaSsoTemplate 对象
ssoUserProcessor.ssoTemplate = ssoUserTemplate;
}
/*
* 第二套 sso-server 服务处理所有SSO相关请求
* http://{host}:{port}/sso-user/auth -- 单点登录授权地址接受参数redirect=授权重定向地址
* http://{host}:{port}/sso-user/doLogin -- 账号密码登录接口接受参数name、pwd
* http://{host}:{port}/sso-user/checkTicket -- Ticket校验接口isHttp=true时打开接受参数ticket=ticket码、ssoLogoutCall=单点注销回调地址 [可选]
* http://{host}:{port}/sso-user/signout -- 单点注销地址isSlo=true时打开接受参数loginId=账号id、secretkey=接口调用秘钥
*/
@RequestMapping("/sso-user/*")
public Object ssoUserRequest() {
return ssoUserProcessor.serverDister();
}
// 自定义 doLogin 方法 */
// 注意点:
// 1、第2套 sso-server 对应的 RestApi 登录接口也应该更换为 /sso-user/doLogin而不是原先的 /sso/doLogin
// 2、在这里登录函数要使用自定义的 StpUserUtil.login(),而不是原先的 StpUtil.login()
@RequestMapping("/sso-user/doLogin")
public Object ssoUserRequest(String name, String pwd) {
if("sa".equals(name) && "123456".equals(pwd)) {
StpUserUtil.login(10001);
return SaResult.ok("登录成功!").setData(StpUserUtil.getTokenValue());
}
return SaResult.error("登录失败!");
}
}
```
<br/>
---
<details>
<summary>还有其它问题?</summary>
可以加群反馈一下,比较典型的问题我们解决之后都会提交到此页面方便大家快速排查
</details>