diff --git a/sa-token-demo/sa-token-demo-alone-redis/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-alone-redis/src/main/resources/application.yml index 65ebe15e..62957c4c 100644 --- a/sa-token-demo/sa-token-demo-alone-redis/src/main/resources/application.yml +++ b/sa-token-demo/sa-token-demo-alone-redis/src/main/resources/application.yml @@ -15,13 +15,13 @@ sa-token: # Redis数据库索引(默认为0) database: 2 # Redis服务器地址 - host: 127.0.0.1 + host: 49.235.117.153 # Redis服务器连接端口 port: 6379 # Redis服务器连接密码(默认为空) - password: + password: Kdfsjia.d.1212dsa # 连接超时时间(毫秒) - timeout: 10ms + timeout: 10s lettuce: pool: # 连接池最大连接数 @@ -45,7 +45,7 @@ spring: # Redis服务器连接密码(默认为空) password: # 连接超时时间(毫秒) - timeout: 10ms + timeout: 10s lettuce: pool: # 连接池最大连接数 diff --git a/sa-token-demo/sa-token-demo-springboot/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-springboot/src/main/resources/application.yml index 5ac6109e..bdc3c641 100644 --- a/sa-token-demo/sa-token-demo-springboot/src/main/resources/application.yml +++ b/sa-token-demo/sa-token-demo-springboot/src/main/resources/application.yml @@ -30,8 +30,8 @@ spring: port: 6379 # Redis服务器连接密码(默认为空) password: - # 连接超时时间(毫秒) - timeout: 10ms + # 连接超时时间 + timeout: 10s lettuce: pool: # 连接池最大连接数 diff --git a/sa-token-demo/sa-token-demo-sso2-client/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-sso2-client/src/main/resources/application.yml index 639dfcf5..5bc9f3ad 100644 --- a/sa-token-demo/sa-token-demo-sso2-client/src/main/resources/application.yml +++ b/sa-token-demo/sa-token-demo-sso2-client/src/main/resources/application.yml @@ -21,8 +21,8 @@ sa-token: port: 6379 # Redis服务器连接密码(默认为空) password: - # 连接超时时间(毫秒) - timeout: 10ms + # 连接超时时间 + timeout: 10s lettuce: pool: # 连接池最大连接数 diff --git a/sa-token-demo/sa-token-demo-sso2-server/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-sso2-server/src/main/resources/application.yml index 672a5d0b..a4f07600 100644 --- a/sa-token-demo/sa-token-demo-sso2-server/src/main/resources/application.yml +++ b/sa-token-demo/sa-token-demo-sso2-server/src/main/resources/application.yml @@ -23,8 +23,8 @@ spring: port: 6379 # Redis服务器连接密码(默认为空) password: - # 连接超时时间(毫秒) - timeout: 10ms + # 连接超时时间 + timeout: 10s lettuce: pool: # 连接池最大连接数 diff --git a/sa-token-demo/sa-token-demo-sso3-client/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-sso3-client/src/main/resources/application.yml index 3f6194dd..bdf0510a 100644 --- a/sa-token-demo/sa-token-demo-sso3-client/src/main/resources/application.yml +++ b/sa-token-demo/sa-token-demo-sso3-client/src/main/resources/application.yml @@ -32,8 +32,8 @@ spring: port: 6379 # Redis服务器连接密码(默认为空) password: - # 连接超时时间(毫秒) - timeout: 10ms + # 连接超时时间 + timeout: 10s lettuce: pool: # 连接池最大连接数 diff --git a/sa-token-demo/sa-token-demo-sso3-server/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-sso3-server/src/main/resources/application.yml index b9448c1c..2dbe65df 100644 --- a/sa-token-demo/sa-token-demo-sso3-server/src/main/resources/application.yml +++ b/sa-token-demo/sa-token-demo-sso3-server/src/main/resources/application.yml @@ -29,8 +29,8 @@ spring: port: 6379 # Redis服务器连接密码(默认为空) password: - # 连接超时时间(毫秒) - timeout: 10ms + # 连接超时时间 + timeout: 10s lettuce: pool: # 连接池最大连接数 diff --git a/sa-token-doc/doc/README.md b/sa-token-doc/doc/README.md index a9e42238..3971ee20 100644 --- a/sa-token-doc/doc/README.md +++ b/sa-token-doc/doc/README.md @@ -19,7 +19,7 @@ 为了保证新同学不迷路,请允许我唠叨一下:无论您从何处看到本篇文章,最新开发文档永远在:[http://sa-token.dev33.cn/](http://sa-token.dev33.cn/), 建议收藏在浏览器书签,如果您已经身处本网站下,则请忽略此条说明。 -用心阅读文档,你学习到的将不止是 `Sa-Token` 框架本身,更是绝大多数场景下权限设计的最佳实践。 +本文档将会尽力讲解每个功能的设计原因、应用场景,用心阅读文档,你学习到的将不止是 `Sa-Token` 框架本身,更是绝大多数场景下权限设计的最佳实践。 ## Sa-Token 介绍 **Sa-Token** 是一个轻量级 Java 权限认证框架,主要解决:**`登录认证`**、**`权限认证`**、**`Session会话`**、**`单点登录`**、**`OAuth2.0`**、**`微服务网关鉴权`** diff --git a/sa-token-doc/doc/_sidebar.md b/sa-token-doc/doc/_sidebar.md index 07ceb474..e7e200e8 100644 --- a/sa-token-doc/doc/_sidebar.md +++ b/sa-token-doc/doc/_sidebar.md @@ -36,7 +36,11 @@ - [SSO模式一 共享Cookie同步会话](/sso/sso-type1) - [SSO模式二 URL重定向传播会话](/sso/sso-type2) - [SSO模式三 Http请求获取会话](/sso/sso-type3) - - [SSO整合-常见问题总结](/sso/sso-cd) + - [SSO整合:配置域名校验](/sso/sso-check-domain) + - [SSO整合:定制化登录页面](/sso/sso-custom-login) + - [SSO整合:前后端分离下的整合方案](/sso/sso-h5) + - [SSO整合:常见问题总结](/sso/sso-questions) + - [Sa-Sso-Pro:单点登录商业版](/sso/sso-pro) - **OAuth2.0** - [OAuth2.0简述](/oauth2/readme) diff --git a/sa-token-doc/doc/index.html b/sa-token-doc/doc/index.html index 761001c0..a38a1eb3 100644 --- a/sa-token-doc/doc/index.html +++ b/sa-token-doc/doc/index.html @@ -71,7 +71,7 @@ // basePath: '/sa-token-doc/', // 设置文件加载的父路径, 这在一些带项目名部署的文件中非常有效 auto2top: true, // 是否在切换页面后回到顶部 // coverpage: true, // 开启封面 - subMaxLevel: 3, // 标题解析层级, 写几就在目录树中解析到几级标题 ,一般写2吧也就 + subMaxLevel: 4, // 标题解析层级, 写几就在目录树中解析到几级标题 ,一般写2吧也就 loadSidebar: true, // 加载自定义侧边栏 , 目录定制在: _sidebar.md 文件 (需要创建 .nojekyll 的空文件,阻止 GitHub Pages 忽略命名是下划线开头的文件) copyCode: { // 复制插件 buttonText: '复制到剪贴板', diff --git a/sa-token-doc/doc/lib/index.css b/sa-token-doc/doc/lib/index.css index 1d07e407..3c6c44aa 100644 --- a/sa-token-doc/doc/lib/index.css +++ b/sa-token-doc/doc/lib/index.css @@ -8,6 +8,7 @@ body{font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu #main h3 {font-size: 1.25rem;} .main-box .markdown-section{ padding: 30px 20px; max-width: 75%; margin-left: 10%;} +.main-box .markdown-section h4{font-size: 1rem;} @media screen and (max-width: 800px) { .logo-box {display: none;} .main-box .markdown-section{max-width: 1000px; margin-left: auto;} diff --git a/sa-token-doc/doc/plugin/alone-redis.md b/sa-token-doc/doc/plugin/alone-redis.md index 9dd817c3..647028af 100644 --- a/sa-token-doc/doc/plugin/alone-redis.md +++ b/sa-token-doc/doc/plugin/alone-redis.md @@ -33,7 +33,7 @@ sa-token: # Token风格 token-style: uuid - # 配置Sa-Token单独使用的Redis连接 + # 配置 Sa-Token 单独使用的 Redis 连接 alone-redis: # Redis数据库索引(默认为0) database: 2 @@ -43,11 +43,11 @@ sa-token: port: 6379 # Redis服务器连接密码(默认为空) password: - # 连接超时时间(毫秒) - timeout: 10ms + # 连接超时时间 + timeout: 10s spring: - # 配置业务使用的Redis连接 + # 配置业务使用的 Redis 连接 redis: # Redis数据库索引(默认为0) database: 0 @@ -57,8 +57,8 @@ spring: port: 6379 # Redis服务器连接密码(默认为空) password: - # 连接超时时间(毫秒) - timeout: 10ms + # 连接超时时间 + timeout: 10s ``` 具体可参考示例:[码云:application.yml](https://gitee.com/dromara/sa-token/blob/dev/sa-token-demo/sa-token-demo-alone-redis/src/main/resources/application.yml) diff --git a/sa-token-doc/doc/sso/_sidebar.md b/sa-token-doc/doc/sso/_sidebar.md deleted file mode 100644 index caca6efd..00000000 --- a/sa-token-doc/doc/sso/_sidebar.md +++ /dev/null @@ -1,12 +0,0 @@ - - -- **单点登录** - - [单点登录简述](/sso/readme) - - [SSO模式一 共享Cookie同步会话](/sso/sso-type1) - - [SSO模式二 URL重定向传播会话](/sso/sso-type2) - - [SSO模式三 Http请求获取会话](/sso/sso-type3) - - - - diff --git a/sa-token-doc/doc/sso/sso-cd.md b/sa-token-doc/doc/sso/sso-cd.md deleted file mode 100644 index 59fb8e17..00000000 --- a/sa-token-doc/doc/sso/sso-cd.md +++ /dev/null @@ -1,263 +0,0 @@ -# Sa-Token-SSO整合-常见问题总结 - ---- - - -### 一、何时引导用户去登录? - -以下方案三选一: - -##### 1.1、前端按钮跳转 -前端页面准备一个**`[登录]`**按钮,当用户点击按钮时,跳转到登录接口 -``` js -登录 -``` - -##### 1.2、后端拦截重定向 -在后端注册全局过滤器(或拦截器、或全局异常处理),拦截需要登录后才能访问的页面资源,将未登录的访问重定向至登录接口 -``` java -/** - * Sa-Token 配置类 - */ -@Configuration -public class SaTokenConfigure implements WebMvcConfigurer { - /** 注册 [Sa-Token全局过滤器] */ - @Bean - public SaServletFilter getSaServletFilter() { - return new SaServletFilter() - .addInclude("/**") - .addExclude("/sso/*", "/favicon.ico") - .setAuth(r -> { - 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.3、后端拦截 + 前端跳转 -首先,后端仍需要提供拦截,但是不直接引导用户重定向,而是返回未登录的提示信息 -``` java -/** - * Sa-Token 配置类 - */ -@Configuration -public class SaTokenConfigure implements WebMvcConfigurer { - /** 注册 [Sa-Token全局过滤器] */ - @Bean - public SaServletFilter getSaServletFilter() { - return new SaServletFilter() - .addInclude("/**") - .addExclude("/sso/*", "/favicon.ico") - .setAuth(r -> { - if(StpUtil.isLogin() == false) { - // 与前端约定好,code=401时代表会话未登录 - SaRouter.back(SaResult.ok().setCode(401)); - } - }) - ; - } -} -``` - -前端接受到返回结果 `code=401` 时,开始跳转至登录接口 -``` js -if(res.code == 401) { - location.href = '/sso/login?back=' + encodeURIComponent(location.href); -} -``` - -这种方案比较适合以 Ajax 访问的 RestAPI 接口重定向 - - - - -### 二、如何自定义登录视图? - -- 方式一:在demo示例中直接更改页面代码 -- 方式二:在配置中配置登录视图地址 - -``` java -cfg.sso -// 配置:未登录时返回的View -.setNotLoginView(() -> { - return new ModelAndView("xxx.html"); -}) -``` - - -### 三、如何自定义登录API的接口? -根据需求点选择解决方案: - -##### 3.1、如果只是想在 setDoLoginHandle 函数里获取除 name、pwd 以外的参数? -``` java -// 在任意代码处获取前端提交的参数 -String xxx = SaHolder.getRequest().getParam("xxx"); -``` - -##### 3.2、想完全自定义一个接口来接受前端登录请求? -``` java -// 直接定义一个拦截路由为 `/sso/doLogin` 的接口即可 -@RequestMapping("/sso/doLogin") -public SaResult ss(String name, String pwd) { - System.out.println("------ 请求进入了自定义的API接口 ---------- "); - if("sa".equals(name) && "123456".equals(pwd)) { - StpUtil.login(10001); - return SaResult.ok("登录成功!"); - } - return SaResult.error("登录失败!"); -} -``` - -##### 3.3、不想使用`/sso/doLogin`这个接口,想自定义一个API地址? - -答:直接在前端更改点击按钮时 Ajax 的请求地址即可 - - -### 四、前后端分离架构下的整合方案 - -如果我们已有的系统是前后端分离模式,我们显然不能为了接入SSO而改造系统的基础架构,官方仓库的示例采用的是前后端一体方案,要将其改造为前后台分离架构模式非常简单 - -以`sa-token-demo-sso2-client`为例: - -##### 4.1、新建`H5Controller`开放接口 -``` java -/** - * 前后台分离架构下集成SSO所需的代码 - */ -@RestController -public class H5Controller { - - // 当前是否登录 - @RequestMapping("/isLogin") - public Object isLogin() { - return SaResult.data(StpUtil.isLogin()); - } - - // 返回SSO认证中心登录地址 - @RequestMapping("/getSsoAuthUrl") - public SaResult getSsoAuthUrl(String clientLoginUrl) { - String serverAuthUrl = SaSsoUtil.buildServerAuthUrl(clientLoginUrl, ""); - return SaResult.data(serverAuthUrl); - } - - // 根据ticket进行登录 - @RequestMapping("/doLoginByTicket") - public SaResult doLoginByTicket(String ticket) { - Object loginId = checkTicket(ticket); - if(loginId != null) { - StpUtil.login(loginId); - return SaResult.data(StpUtil.getTokenValue()); - } - return SaResult.error("无效ticket:" + ticket); - } - - // 校验 Ticket码,获取账号Id - private Object checkTicket(String ticket) { - return SaSsoUtil.checkTicket(ticket); - } - - // 全局异常拦截 - @ExceptionHandler - public SaResult handlerException(Exception e) { - e.printStackTrace(); - return SaResult.error(e.getMessage()); - } - -} -``` - -##### 4.2、增加跨域过滤器`CorsFilter.java` -源码详见:[CorsFilter.java](https://gitee.com/dromara/sa-token/tree/master/sa-token-demo/sa-token-demo-sso2-client/src/main/java/com/pj/h5/CorsFilter.java), -将其复制到项目中即可 - -##### 4.3、新建前端项目 -任意文件夹新建前端项目:`sa-token-demo-sso2-client-h5`,在根目录添加测试文件:`index.html` -``` js - - - - - Sa-Token-SSO-Client端-测试页(前后端分离版) - - -

Sa-Token SSO-Client 应用端(前后端分离版)

-

当前是否登录:

-

- 登录 - 注销 -

- - - - -``` - -##### 4.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), -将其复制到项目中即可,与`index.html`一样放在根目录下 - - -##### 4.5、测试运行 -先启动Server服务端与Client服务端,再随便找个能预览html的工具打开前端项目(比如[HBuilderX](https://www.dcloud.io/hbuilderx.html)),测试流程与一体版一致 - -##### 4.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模式三中成功整合前后台分离模式 。 - - - - -### 五、常见疑问 - -##### 问:在模式一与模式二中,Client端 必须通过 Alone-Redis 插件来访问Redis吗? -答:不必须,只是推荐,权限缓存与业务缓存分离后会减少SSO-Redis的访问压力,且可以避免多个Client端的缓存读写冲突 - -##### 问:将旧有系统改造为单点登录时,应该注意哪些? -建议不要把其中一个系统改造为SSO服务端,而是新起一个项目作为Server端,所有旧有项目全部作为Client端与此对接 - - - diff --git a/sa-token-doc/doc/sso/sso-check-domain.md b/sa-token-doc/doc/sso/sso-check-domain.md new file mode 100644 index 00000000..58bfabce --- /dev/null +++ b/sa-token-doc/doc/sso/sso-check-domain.md @@ -0,0 +1,43 @@ +# SSO整合-配置域名校验 + +--- + +### 1、Ticket劫持攻击 +在前面章节的 SSO-Server 示例中,配置项 `sa-token.sso.allow-url=*` 意为配置所有允许的Client端授权地址,不在此配置项中的URL将无法单点登录成功 + +为了方便测试,上述代码将其配置为`*`,但是,在生产环境中,此配置项绝对不能配置为 * ,否则会有被 Ticket 劫持的风险 + +假设攻击者根据模仿我们的授权地址,巧妙的构造一个URL + +> [http://sa-sso-server.com:9000/sso/auth?redirect=https://www.baidu.com/](http://sa-sso-server.com:9000/sso/auth?redirect=https://www.baidu.com/) + +当不知情的小红被诱导访问了这个URL时,它将被重定向至百度首页 + +![sso-ticket-jc](https://oss.dev33.cn/sa-token/doc/sso/sso-ticket-jc.png 's-w-sh') + +可以看到,代表着用户身份的 Ticket 码也显现到了URL之中,借此漏洞,攻击者完全可以构建一个URL将小红的 Ticket 码自动提交到攻击者自己的服务器,伪造小红身份登录网站 + +### 2、防范方法 + +造成此漏洞的直接原因就是SSO-Server认证中心没有对 `redirect地址` 进行任何的限制,防范的方法也很简单,就是对`redirect参数`进行校验,如果其不在指定的URL列表中时,拒绝下放ticket + +我们将其配置为一个具体的URL:`allow-url=http://sa-sso-client1.com:9001/sso/login`,再次访问上述连接: + +![sso-feifa-rf](https://oss.dev33.cn/sa-token/doc/sso/sso-feifa-rf.png 's-w-sh') + +域名没有通过校验,拒绝授权! + +### 3、配置安全性参考表 + +| 配置方式 | 举例 | 安全性 | 建议 | +| :-------- | :-------- | :-------- | :-------- | +| 配置为* | `*` | | **禁止在生产环境下使用** | +| 配置到域名 | `http://sa-sso-client1.com/*` | | 不建议在生产环境下使用 | +| 配置到详细地址| `http://sa-sso-client1.com:9001/sso/login` | | 可以在生产环境下使用 | + + +### 4、疑问:为什么不直接回传 Token,而是先回传 Ticket,再用 Ticket 去查询对应的账号id? +Token 作为长时间有效的会话凭证,在任何时候都不应该直接暴露在 URL 之中(虽然 Token 直接的暴露本身不会造成安全漏洞,但会为很多漏洞提供可乘之机) + +因此 Sa-Token-SSO 选择先回传 Ticket,再由 Ticket 获取账号id,且 Ticket 一次性用完即废,提高安全性 + diff --git a/sa-token-doc/doc/sso/sso-custom-login.md b/sa-token-doc/doc/sso/sso-custom-login.md new file mode 100644 index 00000000..0965c4d3 --- /dev/null +++ b/sa-token-doc/doc/sso/sso-custom-login.md @@ -0,0 +1,117 @@ +# SSO整合-定制化登录页面 + +--- + +### 1、何时引导用户去登录? + +#### 方案一:前端按钮跳转 +前端页面准备一个**`[登录]`**按钮,当用户点击按钮时,跳转到登录接口 +``` js +登录 +``` + +#### 方案二:后端拦截重定向 +在后端注册全局过滤器(或拦截器、或全局异常处理),拦截需要登录后才能访问的页面资源,将未登录的访问重定向至登录接口 +``` java +/** + * Sa-Token 配置类 + */ +@Configuration +public class SaTokenConfigure implements WebMvcConfigurer { + /** 注册 [Sa-Token全局过滤器] */ + @Bean + public SaServletFilter getSaServletFilter() { + return new SaServletFilter() + .addInclude("/**") + .addExclude("/sso/*", "/favicon.ico") + .setAuth(r -> { + 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(); + } + }) + ; + } +} +``` + +#### 方案三:后端拦截 + 前端跳转 +首先,后端仍需要提供拦截,但是不直接引导用户重定向,而是返回未登录的提示信息 +``` java +/** + * Sa-Token 配置类 + */ +@Configuration +public class SaTokenConfigure implements WebMvcConfigurer { + /** 注册 [Sa-Token全局过滤器] */ + @Bean + public SaServletFilter getSaServletFilter() { + return new SaServletFilter() + .addInclude("/**") + .addExclude("/sso/*", "/favicon.ico") + .setAuth(r -> { + if(StpUtil.isLogin() == false) { + // 与前端约定好,code=401时代表会话未登录 + SaRouter.back(SaResult.ok().setCode(401)); + } + }) + ; + } +} +``` + +前端接受到返回结果 `code=401` 时,开始跳转至登录接口 +``` js +if(res.code == 401) { + location.href = '/sso/login?back=' + encodeURIComponent(location.href); +} +``` + +这种方案比较适合以 Ajax 访问的 RestAPI 接口重定向 + + + + +### 2、如何自定义登录视图? + +#### 方式一:在demo示例中直接更改 login.html 页面代码即可 + +#### 方式二:在配置中配置登录视图地址 + +``` java +cfg.sso +// 配置:未登录时返回的View +.setNotLoginView(() -> { + return new ModelAndView("xxx.html"); +}) +``` + + +### 3、如何自定义登录API的接口? +根据需求点选择解决方案: + +#### 3.1、如果只是想在 setDoLoginHandle 函数里获取除 name、pwd 以外的参数? +``` java +// 在任意代码处获取前端提交的参数 +String xxx = SaHolder.getRequest().getParam("xxx"); +``` + +#### 3.2、想完全自定义一个接口来接受前端登录请求? +``` java +// 直接定义一个拦截路由为 `/sso/doLogin` 的接口即可 +@RequestMapping("/sso/doLogin") +public SaResult ss(String name, String pwd) { + System.out.println("------ 请求进入了自定义的API接口 ---------- "); + if("sa".equals(name) && "123456".equals(pwd)) { + StpUtil.login(10001); + return SaResult.ok("登录成功!"); + } + return SaResult.error("登录失败!"); +} +``` + +#### 3.3、不想使用`/sso/doLogin`这个接口,想自定义一个API地址? + +答:直接在前端更改点击按钮时 Ajax 的请求地址即可 + diff --git a/sa-token-doc/doc/sso/sso-h5.md b/sa-token-doc/doc/sso/sso-h5.md new file mode 100644 index 00000000..139f3646 --- /dev/null +++ b/sa-token-doc/doc/sso/sso-h5.md @@ -0,0 +1,135 @@ +# SSO整合-前后端分离架构下的整合方案 + +--- + +如果我们已有的系统是前后端分离模式,我们显然不能为了接入SSO而改造系统的基础架构,官方仓库的示例采用的是前后端一体方案,要将其改造为前后台分离架构模式非常简单 + +以`sa-token-demo-sso2-client`为例: + +### 1、新建`H5Controller`开放接口 +``` java +/** + * 前后台分离架构下集成SSO所需的代码 + */ +@RestController +public class H5Controller { + + // 当前是否登录 + @RequestMapping("/isLogin") + public Object isLogin() { + return SaResult.data(StpUtil.isLogin()); + } + + // 返回SSO认证中心登录地址 + @RequestMapping("/getSsoAuthUrl") + public SaResult getSsoAuthUrl(String clientLoginUrl) { + String serverAuthUrl = SaSsoUtil.buildServerAuthUrl(clientLoginUrl, ""); + return SaResult.data(serverAuthUrl); + } + + // 根据ticket进行登录 + @RequestMapping("/doLoginByTicket") + public SaResult doLoginByTicket(String ticket) { + Object loginId = checkTicket(ticket); + if(loginId != null) { + StpUtil.login(loginId); + return SaResult.data(StpUtil.getTokenValue()); + } + return SaResult.error("无效ticket:" + ticket); + } + + // 校验 Ticket码,获取账号Id + private Object checkTicket(String ticket) { + return SaSsoUtil.checkTicket(ticket); + } + + // 全局异常拦截 + @ExceptionHandler + public SaResult handlerException(Exception e) { + e.printStackTrace(); + return SaResult.error(e.getMessage()); + } + +} +``` + +### 2、增加跨域过滤器`CorsFilter.java` +源码详见:[CorsFilter.java](https://gitee.com/dromara/sa-token/tree/master/sa-token-demo/sa-token-demo-sso2-client/src/main/java/com/pj/h5/CorsFilter.java), +将其复制到项目中即可 + +### 3、新建前端项目 +任意文件夹新建前端项目:`sa-token-demo-sso2-client-h5`,在根目录添加测试文件:`index.html` +``` js + + + + + Sa-Token-SSO-Client端-测试页(前后端分离版) + + +

Sa-Token SSO-Client 应用端(前后端分离版)

+

当前是否登录:

+

+ 登录 + 注销 +

+ + + + +``` + +### 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), +将其复制到项目中即可,与`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模式三中成功整合前后台分离模式 。 + + + + diff --git a/sa-token-doc/doc/sso/sso-pro.md b/sa-token-doc/doc/sso/sso-pro.md new file mode 100644 index 00000000..6ed925e9 --- /dev/null +++ b/sa-token-doc/doc/sso/sso-pro.md @@ -0,0 +1,52 @@ +# Sa-Sso-Pro 单点登录商业版 + +### 项目介绍 + +根据 Sa-Token SSO 模块文档,以及官网提供的源码示例,您可以很方便的搭建一个SSO模式的认证Demo。 + + + +然而,要真正开发一个商业级项目的认证中心系统,绝非一朝一夕可以搭建完毕,其必不可少的一些功能, +比如:用户账号增删改查维护、登录日志统计、新增用户数据报表、新增 Client 应用接入域名配置……等等, +仍需要我们大量的开发时间。 + +为此,我们特意准备了项目:[[Sa-Sso-Pro 单点登录商业版]](http://sa-pro.dev33.cn/index.html?hmsr=sa-token), +项目集成了单点登录常见技术点, 绝大多数功能无需二次开发,直接可用,可大大缩短您的项目接入单点登录的开发周期。 + + +### 释疑 + +##### 1、Sa-Sso-Pro 是收费项目吗?与 Sa-Token 有什么不同? + +`Sa-Sso-Pro` 是付费项目,暂不开放源码,如需使用需要购买项目授权,您可以在其主页了解更多详细信息。 + +`Sa-Sso-Pro` 与 `Sa-Token` 的区别,简单来讲: +- `Sa-Token` 是一个框架,需要在项目中通过 pom.xml 引入 +- `Sa-Sso-Pro` 是一个完整项目,下载源码后可直接启动 + + +##### 2、Sa-Token 会不会在某一天收费?导致我们项目无法正常运行? +首先我们需要了解一点:**已经发布到 Maven 中央仓库的代码,是不可以删除的**,所以这部分代码是无法做到收费的 + +其次,像中间件框架,业界没有收费的先例,也没有对应的商业模式,一般的付费项目都是一些成型的完整项目,以解决特定场景的业务需求为目的, +比如:聊天通信、刷脸认证、短信验证码、聚合支付……等等。 + +Sa-Sso-Pro 并非随意收费,只有当您的系统需要 **统一认证中心** 时您才会用到它,花一笔小钱节省大量开发工期,整体来看,这是非常划算的。 + +另外:即使您没有购买 `Sa-Sso-Pro`,也不会影响到您对 `Sa-Token` 的使用,举个例子:MySql具有社区版与企业版,即使您没有购买其付费版,也不会影响到您对免费 MySql 的使用。 + + + +##### 3、Sa-Token 团队日后的主要精力是不是放在 Sa-Sso-Pro 上,降低对 Sa-Token 的支持?毕竟 Sa-Token 是免费的! + +答案是不会。 + +再次强调一下:`Sa-Token` 与 `Sa-Sso-Pro` 是两个独立的项目,两者互不影响。 +付费项目的出现不会降低对 `Sa-Token` 的支持,`Sa-Token`将会按照原有的发展继续升级迭代。 + +实际结果可能会恰恰相反:有了盈利来源,`Sa-Token`将发展的更快。 + + + + + diff --git a/sa-token-doc/doc/sso/sso-questions.md b/sa-token-doc/doc/sso/sso-questions.md new file mode 100644 index 00000000..442f241a --- /dev/null +++ b/sa-token-doc/doc/sso/sso-questions.md @@ -0,0 +1,29 @@ +# Sa-Token-SSO整合-常见问题总结 + +--- + +### 问:在模式一与模式二中,Client端 必须通过 Alone-Redis 插件来访问Redis吗? +答:不必须,只是推荐,权限缓存与业务缓存分离后会减少 `SSO-Redis` 的访问压力,且可以避免多个 `Client端` 的缓存读写冲突 + +### 问:将旧有系统改造为单点登录时,应该注意哪些? +答:建议不要把其中一个系统改造为SSO服务端,而是新起一个项目作为Server端,所有旧有项目全部作为Client端与此对接 + +### 问:SSO模式二,第一个域名登录成功之后其他两个不会自动登录? +答:系统1登录成功之后,系统二与系统三需要点击登录按钮,才会登录成功 + +> 第一个系统,需要:点击 [登录] 按钮 -> 跳转到登录页 -> 输账号密码 -> 登录成功
+> 第二个系统,需要:点击 [登录] 按钮 -> 登录成功
+> 第三个系统,需要:点击 [登录] 按钮 -> 登录成功 (免去重复跳转登录页输入账号密码的步骤) + +### 追问:那我是否可以设计成不需要点登录按钮的,只要访问页面,它就能登录成功 +可以:加个过滤器检测到未登录 自动跳转就行了,详细可以参照章节:[[何时引导用户去登录]](/sso/sso-custom-login) 给出的建议进行设计 + +### 问:我参照文档的SSO模式二搭建,一直提示:Ticket无效,请问怎么回事? +根据群友的反馈,出现此异常概率最大的原因是因为 `Client` 与 `Server` 没有连接同一个Redis,SSO模式二中两者必须连接同一个 Redis 才可以登录成功, +如果您排查之后不是此原因,可以加入QQ群或者在issues反馈一下 + +### 还有其它问题? +可以加群反馈一下,比较典型的问题我们解决之后都会提交到此页面方便大家快速排查 + + + diff --git a/sa-token-doc/doc/sso/sso-type2.md b/sa-token-doc/doc/sso/sso-type2.md index 757b87e4..7d9f9bf0 100644 --- a/sa-token-doc/doc/sso/sso-type2.md +++ b/sa-token-doc/doc/sso/sso-type2.md @@ -138,7 +138,7 @@ spring: # Redis服务器连接密码(默认为空) password: ``` -注意点:`allow-url`为了方便测试配置为`*`,线上生产环境一定要配置为详细URL地址 (详见下方“配置域名校验”) +注意点:`allow-url`为了方便测试配置为`*`,线上生产环境一定要配置为详细URL地址 (之后的章节我们会详细阐述此配置项) ##### 2.4、创建SSO-Server端启动类 ``` java @@ -313,53 +313,12 @@ public class SaSsoClientApplication { 默认测试密码:`sa / 123456`,其余流程保持不变 -### 6、配置域名校验 - -##### 6.1、Ticket劫持攻击 -在以上的SSO-Server端示例中,配置项 `sa-token.sso.allow-url=*` 意为配置所有允许的Client端授权地址,不在此配置项中的URL将无法单点登录成功 - -以上示例为了方便测试被配置为*,但是,在生产环境中,此配置项绝对不能配置为 * ,否则会有被ticket劫持的风险 - -假设攻击者根据模仿我们的授权地址,巧妙的构造一个URL - -> [http://sa-sso-server.com:9000/sso/auth?redirect=https://www.baidu.com/](http://sa-sso-server.com:9000/sso/auth?redirect=https://www.baidu.com/) - -当不知情的小红被诱导访问了这个URL时,它将被重定向至百度首页 - -![sso-ticket-jc](https://oss.dev33.cn/sa-token/doc/sso/sso-ticket-jc.png 's-w-sh') - -可以看到,代表着用户身份的ticket码也显现到了URL之中,借此漏洞,攻击者完全可以构建一个URL将小红的ticket码自动提交到攻击者自己的服务器,伪造小红身份登录网站 - -##### 6.2、防范方法 - -造成此漏洞的直接原因就是SSO-Server认证中心没有对 `redirect地址` 进行任何的限制,防范的方法也很简单,就是对`redirect参数`进行校验,如果其不在指定的URL列表中时,拒绝下放ticket - -我们将其配置为一个具体的URL:`allow-url=http://sa-sso-client1.com:9001/sso/login`,再次访问上述连接: - -![sso-feifa-rf](https://oss.dev33.cn/sa-token/doc/sso/sso-feifa-rf.png 's-w-sh') - -域名没有通过校验,拒绝授权! - -##### 6.3、配置安全性参考表 - -| 配置方式 | 举例 | 安全性 | 建议 | -| :-------- | :-------- | :-------- | :-------- | -| 配置为* | `*` | | **禁止在生产环境下使用** | -| 配置到域名 | `http://sa-sso-client1.com/*` | | 不建议在生产环境下使用 | -| 配置到详细地址| `http://sa-sso-client1.com:9001/sso/login` | | 可以在生产环境下使用 | -##### 6.4、疑问:为什么不直接回传Token,而是先回传ticket,再用ticket去查询对应的账号id? -Token作为长时间有效的会话凭证,在任何时候都不应该直接在暴露URL之中(虽然Token直接的暴露本身不会造成安全漏洞,但会为很多漏洞提供可乘之机) - -因此Sa-Token-SSO选择先回传ticket,再由ticket获取账号id,且ticket一次性用完即废,提高安全性 - - - -### 7、跨Redis的单点登录 +### 6、跨Redis的单点登录 以上流程解决了跨域模式下的单点登录,但是后端仍然采用了共享Redis来同步会话,如果我们的架构设计中Client端与Server端无法共享Redis,又该怎么完成单点登录? -这就要采用模式三了,且往下看:[Http请求获取会话](/sso/sso-type3) +这就要采用模式三了,且往下看:[SSO模式三:Http请求获取会话](/sso/sso-type3)