Sa-TokenSa-Token
主题
首页文档博客
视频
乐之者java(登录认证/权限管理/apiKey等)抓蛙师(23集)朱老师的小课堂(7集)王清江唷 SSO篇(29集)fox说技术(7集)架构驿站(11集)王清江唷(99集)筑梦信仰-joy(20集)达达-Java(26集)晒太阳的盐(22集)[ + 课程提交 ]
案例
Gitee - Awesome-Sa-TokenGitHub - Awesome-Sa-TokenAtomGit - Awesome-Sa-Token
加群需求提交赞助🔥 SSO/OAuth2 商业版
安全推荐
开发者安全 ChecklistAPI 安全 Checklist腾讯代码安全指南Web 安全学习笔记OWASP Cheat Sheet SeriesPayloadsAllTheThings
相关资源
更新日志常见报错推荐公众号在线考试在线提问问卷调查
  • 开始

    • 框架介绍
    • 在 SpringBoot 环境集成
    • 在 WebFlux 环境集成
    • 在 Solon 环境集成
    • 其它环境集成示例
    • 源码运行指南
    • Sa-Token 集成示例大全下载
  • 基础

    • 登录认证
    • 权限认证
    • 踢人下线
    • 注解鉴权
    • 路由拦截鉴权
    • Session会话
    • 框架配置
  • 深入

    • 集成 Redis
    • 前后端分离
    • 自定义 Token 风格
    • Token 提交前缀
    • 同端互斥登录
    • 记住我模式
    • 登录参数 & 注销参数
    • 二级认证
    • 模拟他人 & 身份切换
    • 账号封禁
    • 密码加密
    • 会话查询
    • Http Basic/Digest 认证
    • 全局侦听器
    • 全局过滤器
    • 多账号认证
  • 单点登录

    • 单点登录简述
    • 搭建统一认证中心:SSO-Server
    • SSO-Server 认证中心开放 API 接口
    • SSO模式一 共享Cookie同步会话
    • SSO模式二 URL重定向传播会话
    • SSO模式三 Http请求获取会话
    • 配置域名校验
    • 定制化登录页面
    • 自定义API路由
    • 平台中心跳转模式
    • 匿名 client 接入
    • 单点注销
    • 前后端分离下的整合方案
    • 消息推送机制
    • 用户数据同步 / 迁移
    • NoSdk、ReSdk 模式与非 java 项目
    • SSO 代码 API 参考
    • 常见问题总结
    • Sa-Pro:单点登录商业版
  • OAuth2.0

    • OAuth2.0简述
    • OAuth2-Server搭建
    • OAuth2-Server端开放 API 接口
    • 自定义数据加载器
    • 配置 client 域名校验
    • 自定义 Scope 权限及处理器
    • 为 Scope 划分等级
    • 自定义 grant_type
    • 定制化登录页面与授权页面
    • 自定义 API 路由
    • OAuth2-Server端前后台分离
    • OpenId 与 UnionId
    • 开启 OIDC 协议
    • 使用注解校验 Access-Token
    • OAuth2-与登录会话实现数据互通
    • OAuth2 代码 API 参考
    • 常见问题总结
    • Sa-Max:统一认证商业版
  • 微服务

    • 分布式Session会话
    • 网关统一鉴权
    • 内部服务外网隔离
    • 依赖引入说明
  • 插件

    • AOP注解鉴权
    • 临时 Token 认证
    • Quick-Login快速登录插件
    • Alone独立Redis插件
    • Alone独立Redisson插件
    • 缓存层扩展
    • JSON 序列化扩展
    • 序列化插件扩展包
    • HTTP 请求扩展
    • 和 Thymeleaf 集成
    • 和 Freemarker 集成
    • 注解鉴权 SpEL 表达式
    • 和 jwt 集成
    • 和 Dubbo 集成
    • 和 gRPC 集成
    • API 接口参数签名
    • API Key 接口调用秘钥
    • Sa-Token 插件开发指南
    • 自定义 SaTokenContext 指南
  • API手册

    • StpUtil-鉴权工具类
    • SaSession-会话对象
    • SaTokenDao-数据持久接口
    • SaStrategy-全局策略
    • 全局类、方法
  • 框架设计

    • 仓库目录
    • 数据结构
  • 其它

    • 更新日志
    • 框架生态
    • 框架博客
    • 推荐公众号
    • 加入讨论群
    • Sa-Token 内容合作群
    • 赞助 Sa-Token
    • 需求提交
    • 问卷调查
  • 附录

    • 常见问题排查
    • 框架名词解释
    • Sa-Token功能结构图
    • 全局 Log 输出
    • 异步 & Mock 上下文
    • 未登录场景值详解
    • Token有效期详解
    • Session模型详解
    • 数据读写三大作用域
    • TokenInfo参数详解
    • 异常细分状态码
    • 自定义注解
    • 防火墙
    • 参考:把权限放在缓存里
    • 参考:把路由拦截鉴权动态化
    • 解决反向代理 uri 丢失的问题
    • 解决跨域问题
    • 技术选型:SSO 与 OAuth2 对比
    • 集成 MongoDB 参考一
    • 集成 MongoDB 参考二
    • 从 Shiro、SpringSecurity、JWT 迁移
    • issue 提问模板
    • 为Sa-Token贡献代码
    • Sa-Token开源大事记
    • 团队成员
    • Sa-Token框架掌握度--在线考试







----- 到底线了 -----

×

一个项目搞定:同域、跨域、共享Redis、跨Redis、前后端一体、前后端分离、纯 js、vue2、vue3、非 Sa-Token 项目、非 java 项目等架构下的 SSO 认证需求。

一次购买,永久授权。全源码交付,不含密 Jar。提供售后技术支持。

Sa-Token-SSO整合-常见问题总结 ​

SSO 集成常见问题整理


问:在模式一与模式二中,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-server 端的单点注销地址:http://{host}:{port}/sso/signout;
  • sso-client 端的注销地址:http://{host}:{port}/sso/logout;

都需要在配置文件配置:sa-token.sso-server.is-slo=true(client端为 sa-token.sso-client.is-slo=true )后,才会打开。

问:我参照文档搭建SSO-Client,一直提示:Ticket无效,请问怎么回事? ​

如果使用的是模式二,出现此异常概率最大的原因是因为 Client 与 Server 没有连接同一个Redis,SSO模式二中两者必须连接同一个 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);
	}	
}
1
2
3
4
5
6
7
8
9

如果使用的是模式三,则排查是否有重复校验 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 把实体类复制过来就好了)

在测试模式一时,出现一些难以理解的现象 ​

测试模式一时,三种异常现象:

1、在 sso-client 端点击登录,可以成功跳转到 sso-server 端,登录后可以跳回 sso-client 端,但显示 sso-client 端未登录

  • 原因:sso-server 后端没有配置 sa-token.cookie.domain 值。

2、在 sso-client 端点击登录,可以成功跳转到 sso-server 端,登录页面刷新一下,并没有跳转回 sso-client 端,依然提示让你登录

  • 可能1:sso-server 后端配置了错误的 sa-token.cookie.domain 值。
  • 可能2:sso-server 后端没有打开 sa-token.is-read-cookie 值。(测试模式二三时发生这种现象,有时候也是因为这个)

3、在 sso-client 端点击登录,页面只是闪了一下,肉眼没有观察到页面跳转,页面也没有显示登录上。

原因:sso-server 的页面存储了不带 . 的有效 Cookie,为什么会这样:常常是因为测试模式二三后,没有清除redis记录或者浏览器记录,直接再开始测试模式一, 模式二三登录成功后遗留的有效cookie,影响了模式一的行为逻辑。

解决方案:

  • 1、手动清空 redis里的所有数据,
  • 2、或者手动清空 sso-server 域名下的所有 Cookie
  • 3、换一个新的干净浏览器来测试。

问:模式三配置一堆 xxx-url ,有办法简化一下吗? ​

可以使用 sa-token.sso-client.server-url 来简化:

配置含义:配置 Server 端主机总地址,拼接在 authUrl、getDataUrl、signoutUrl、pushUrl 属性前面,用以简化各种 url 配置。

在开发 SSO 模块时,我们需要在 sso-client 配置认证中心的各种地址,特别是在模式三下,一般代码会变成这样:

yaml
sa-token: 
    sso-client: 
        # SSO-Server端 统一认证地址 
        auth-url: http://sa-sso-server.com:9000/sso/auth
        # 单点注销地址 
        slo-url: http://sa-sso-server.com:9000/sso/signout
        # SSO-Server端 查询数据地址 
        get-data-url: http://sa-sso-server.com:9000/sso/getData
1
2
3
4
5
6
7
8

一堆 xxx-url 配置比较繁琐,且含有大量重复字符,现在我们可以将其简化为:

yaml
sa-token: 
    sso-client: 
        server-url: http://sa-sso-server.com:9000
1
2
3

只要你配置了 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 配置项同理。

例3,auth-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 配置项同理。

问:我接手了一个项目,里面集成了 Sa-Token SSO ,请问怎么快速分辨它用的模式几? ​

方法一:看代码注释。

如果开发这个项目的人没有写清楚注释那就 gg 了。

方法二,根据配置项来分析,例如:

  • 先看配置项 sa-token.cookie.domain,如果此配置项有值,一般是在使用模式一开发,否则就是模式二或者模式三。
  • 再看配置项 sa-token.sso-client.is-http ,如果有值且为 true,一般是在使用模式三,否则就是模式二。

方法三,根据配置项 sa-token.sso-client.mode 的提示来判断

sa-token.sso-client.mode 是框架预留的约定型配置项,此配置项不对代码逻辑产生任何影响,只为系统做一个标记,标注此系统用到了SSO的哪个模式。

例如你可以将其配置为 sa-token.sso-client.mode=client-2,代表当前系统为 sso-client 端,使用 SSO 模式二来对接。

需要注意,这个配置项不是必须的,你不写也不会对代码造成任何影响,只有在你需要为系统做一个明确的标记时才需要去配置它,方便后人阅读代码时快速分析使用的模式。

例如我们可以使用以下约定:

  • sa-token.sso-client.mode=client-2:代表当前系统为 sso-client 端,使用 SSO 模式二来对接。
  • sa-token.sso-client.mode=client-2,h5:代表当前系统为 sso-client 端,使用 SSO 模式二来对接,并且是前后端分离模式。
  • sa-token.sso-server.mode=server-123:代表当前系统为 sso-server 端,同时开放了 SSO 模式一、模式二、模式三。
  • sa-token.sso-server.mode=server-2,client-2:代表当前系统既是 sso-server 端,又是 sso-clent 端,使用模式二来对接。
  • 等等等等...

此配置项可以是任意字符串,你也可以自己整理一套合适的表达规则。

问:SSO模式二或模式三,第一个 client 登录成功之后再访问其它两个 client 不会自动登录,需要点一下登录按钮才会登录上? ​

答:这是正常现象,系统 1 登录成功之后,系统 2 与系统 3 需要点击登录按钮,才会登录成功。

第一个系统,需要:点击 [登录] 按钮 -> 跳转到登录页 -> 输账号密码 -> 登录成功
第二个系统,需要:点击 [登录] 按钮 -> 登录成功
第三个系统,需要:点击 [登录] 按钮 -> 登录成功

(系统二、三 免去重复跳转登录页输入账号密码的步骤)

追问:那我是否可以设计成不需要点登录按钮的,只要访问页面,它就能登录成功? ​

可以的。

其实思路很简单,我们只需要给 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();
                    }
                })
                ;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

更多登录姿势可以参考 [何时引导用户去登录] 给出的建议进行设计。

问:Client 信息可以做成从数据库读取的吗? ​

可以,自定义 SaSsoServerTemplate 实现类,重写 getClient 与 getClients 方法即可:

java
/**
 * 重写 SaSsoServerTemplate 部分方法,增强功能
 */
@Component
public class CustomSaSsoServerTemplate extends SaSsoServerTemplate {

    // 获取指定 client 的配置信息
    @Override
    public SaSsoClientModel getClient(String client) {
        if("sso-client1".equals(client)) {
            SaSsoClientModel scm = new SaSsoClientModel();
            scm.setAllowUrl("sso-client1");
            scm.setSecretKey("kQwIOrYvnXmSDkwEiFngrKidMcdrgKor");
            return scm;
        }

        // ...

        return null;
    }

    // 返回所有 client 信息
    @Override
    public List<SaSsoClientModel> getClients() {
        // 模拟示例代码,真实项目可改为从数据查询

        SaSsoClientModel scm1 = new SaSsoClientModel();
        scm1.setAllowUrl("sso-client1");
        scm1.setSecretKey("kQwIOrYvnXmSDkwEiFngrKidMcdrgKor");

        SaSsoClientModel scm2 = new SaSsoClientModel();
        scm2.setAllowUrl("sso-client2");
        scm2.setSecretKey("kQwIOrYvnXmSDkwEiFngrKidMcdrgKor");

        // ...

        return Arrays.asList(scm1, scm2);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

问:如果 sso-client 端我没有集成 sa-token-sso,如何对接? ​

需要手动调用 http 请求来对接 sso-server 开放的接口,参考示例: sa-token-demo-sso3-client-nosdk

问:如果 sso-client 端不是 java语言,可以对接吗? ​

可以,只不过有点麻烦,基本思路和上个问题一致,需要手动调用 http 请求来对接 sso-server 开放的接口,参考: SSO-Server 认证中心开放接口

问:将旧有系统改造为单点登录时,应该注意哪些? ​

答:建议不要把其中一个系统改造为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/signout", "/sso/pushS"})
	public Object ssoServerRequest() {
		return SaSsoServerProcessor.instance.dister();
	}
	
	// 处理 SSO-Client 端所有请求 
	@RequestMapping({"/sso/login", "/sso/logout", "/sso/logoutCall", "/sso/pushC"})
	public Object ssoClientRequest() {
		return SaSsoClientProcessor.instance.dister();
	}
	
	// 配置SSO相关参数 
	@Autowired
	private void configSsoServer(SaSsoServerTemplate ssoServerTemplate) {
		// SSO Server 配置代码,参考文档前几章 ... 
	}
	@Autowired
	private void configSsoClient(SaSsoClientTemplate ssoClientTemplate) {
		// SSO Client 配置代码,参考文档前几章 ... 
	}
	
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

问:我一个项目里有两套账号体系,都需要单点登录,怎么在一个项目里同时搭建两个 sso-server 服务? ​

首先推荐你不要在一个项目里同时搭建两个 sso-server,建议创建两个项目,分别搭建各自的 sso-server 服务。

如果一定要在一个项目中搭建两套 sso-server 服务,参考方案如下:

第一套,还是用前面几章文档给出的示例代码。

第二套,修改一些参数属性,使之与第一套不产生冲突,参考代码如下:

java
/**
 * Sa-Token-SSO 第二套 SSO-Server端 Controller
 */
@RestController
public class SsoUserServerController {

    /**
     * 新建一个 SaSsoServerProcessor 请求处理器
     */
    public static SaSsoServerProcessor ssoUserServerProcessor = new SaSsoServerProcessor();
    static {

        // 自定义一个 getServerConfig
        SaSsoServerConfig serverConfig = new SaSsoServerConfig();
        serverConfig.setSecretKey("xxx");
        // 更多配置 ...

        // 自定义一个 SaSsoServerTemplate 对象
        SaSsoServerTemplate ssoUserTemplate = new SaSsoServerTemplate() {
            @Override
            public SaSsoServerConfig getServerConfig() {
                return serverConfig;
            }
        };

        // 使用自定义的 StpLogic 会话对象
        ssoUserTemplate.setStpLogic(StpUserUtil.stpLogic);

        // 让这个SSO请求处理器,使用的路由前缀是 /sso-user,而不是原先的 /sso
        ssoUserTemplate.apiName.replacePrefix("/sso-user");

        // 给这个 SSO 请求处理器使用自定义的 SaSsoTemplate 对象
        ssoUserServerProcessor.ssoServerTemplate = ssoUserTemplate;
    }

    /*
     * 第二套 sso-server 服务:处理所有SSO相关请求
     * 		http://{host}:{port}/sso-user/auth			-- 单点登录授权地址 
     * 		http://{host}:{port}/sso-user/doLogin		-- 账号密码登录接口 
     * 		http://{host}:{port}/sso-user/signout		-- 单点注销地址(isSlo=true时打开) 
     */
    @RequestMapping("/sso-user/*")
    public Object ssoUserRequest() {
        return ssoUserServerProcessor.dister();
    }

    // 自定义 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("登录失败!");
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60


还有其它问题?

可以加群反馈一下,比较典型的问题我们解决之后都会提交到此页面方便大家快速排查









发现错误? 您可以在 Gitee 或 GitHub 或 AtomGit 帮助我们完善此页文档! 或 加入讨论群 交流反馈。

我们坚信,即使再复杂的技术,也可以用清晰、干练、易懂的文字描述出它的具体细节,如果你在阅读文档时有难以理解的章节,那一定是我们还没有优化好它, 请向我们 反馈 你的困惑之处,我们将持续优化文档。


鲁ICP备18046274号-5|公安备案鲁公网安备37011202002956号
目录

本页无章节

推荐关闭
Sa-Token 商业版:轻松搭建 SSO 单点登录、OAuth2.0 统一认证、API Key 认证。全源码交付、可二开。
加入 Sa-Token 框架交流群

离线版文档历史所有版本文档Demo 示例大全下载

如果 Sa-Token 帮助到了你,希望你可以向同事、朋友推荐了解本框架,这对我们非常重要,感谢支持!

加油,工程师!