diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/pom.xml b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/pom.xml index 64143179..eb7f9370 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/pom.xml +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/pom.xml @@ -55,14 +55,13 @@ solon.view.thymeleaf ${solon.version} - - - - org.noear - forest-solon-plugin - ${solon.version} - + + + cn.dev33 + sa-token-forest + ${sa-token.version} + diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/SaConfig.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/SaConfig.java new file mode 100644 index 00000000..04017fc9 --- /dev/null +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/SaConfig.java @@ -0,0 +1,23 @@ +package com.pj; + +import cn.dev33.satoken.dao.SaTokenDao; +import cn.dev33.satoken.dao.SaTokenDaoForRedisx; +import org.noear.solon.annotation.Bean; +import org.noear.solon.annotation.Configuration; +import org.noear.solon.annotation.Inject; + +/** + * @author noear 2023/3/13 created + */ +@Configuration +public class SaConfig { + + /** + * 构建建 SaToken redis dao(如果不需要 redis;可以注释掉) + * */ + @Bean + public SaTokenDao saTokenDaoInit(@Inject("${sa-token.dao.redis}") SaTokenDaoForRedisx saTokenDao) { + return saTokenDao; + } + +} diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/SaSsoServerApp.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/SaSsoServerApp.java index f10580bc..ecd7d807 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/SaSsoServerApp.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/SaSsoServerApp.java @@ -2,9 +2,14 @@ package com.pj; import cn.dev33.satoken.sso.SaSsoManager; +import cn.dev33.satoken.sso.model.SaSsoClientInfo; +import cn.dev33.satoken.stp.StpUtil; import org.noear.solon.Solon; import org.noear.solon.annotation.SolonMain; +import java.util.ArrayList; +import java.util.List; + @SolonMain public class SaSsoServerApp { @@ -12,11 +17,25 @@ public class SaSsoServerApp { Solon.start(SaSsoServerApp.class, args); System.out.println(); - System.out.println("---------------------- Solon Sa-Token SSO 统一认证中心启动成功 ----------------------"); + System.out.println("---------------------- Sa-Token SSO 统一认证中心启动成功 ----------------------"); System.out.println("配置信息:" + SaSsoManager.getServerConfig()); System.out.println("统一认证登录地址:http://sa-sso-server.com:9000/sso/auth"); - System.out.println("测试前需要根据官网文档修改hosts文件,测试账号密码:sa / 123456"); + System.out.println("测试前需要根据官网文档修改 hosts 文件,测试账号密码:sa / 123456"); System.out.println(); + +// SaSsoClientInfo sci = new SaSsoClientInfo(); +// sci.setClient("client1"); +// +// List list = new ArrayList<>(); +// list.add(sci); +// +// StpUtil.getSessionByLoginId(10001).set("list", list); +// +// List list2 = (List)StpUtil.getSessionByLoginId(10001).get("list"); +// for (SaSsoClientInfo info : list2) { +// System.out.println(info); +// } + } } \ No newline at end of file diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/h5/CorsFilter.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/h5/CorsFilter.java deleted file mode 100644 index db7651e3..00000000 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/h5/CorsFilter.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.pj.h5; - -import org.noear.solon.annotation.Component; -import org.noear.solon.core.handle.Context; -import org.noear.solon.core.handle.Filter; -import org.noear.solon.core.handle.FilterChain; - - -/** - * 跨域过滤器 - * @author click33 - */ -@Component(index = -200) -public class CorsFilter implements Filter { - - static final String OPTIONS = "OPTIONS"; - - @Override - public void doFilter(Context ctx, FilterChain chain) throws Throwable { - // 允许指定域访问跨域资源 - ctx.headerSet("Access-Control-Allow-Origin", "*"); - // 允许所有请求方式 - ctx.headerSet("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); - // 有效时间 - ctx.headerSet("Access-Control-Max-Age", "3600"); - // 允许的header参数 - ctx.headerSet("Access-Control-Allow-Headers", "x-requested-with,satoken"); - - // 如果是预检请求,直接返回 - if (OPTIONS.equals(ctx.method())) { - System.out.println("=======================浏览器发来了OPTIONS预检请求=========="); - ctx.output(""); - return; - } - - // System.out.println("*********************************过滤器被使用**************************"); - chain.doFilter(ctx); - } -} diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/h5/H5Controller.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/h5/H5Controller.java index d6b2649d..d5a0ce1c 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/h5/H5Controller.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/h5/H5Controller.java @@ -1,15 +1,13 @@ package com.pj.h5; -import cn.dev33.satoken.sso.util.SaSsoConsts; import cn.dev33.satoken.sso.template.SaSsoUtil; +import cn.dev33.satoken.sso.util.SaSsoConsts; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaFoxUtil; import cn.dev33.satoken.util.SaResult; import org.noear.solon.annotation.Controller; import org.noear.solon.annotation.Mapping; -import org.noear.solon.core.handle.Context; -import org.noear.solon.core.handle.Render; /** * 前后台分离架构下集成SSO所需的代码 (SSO-Server端) @@ -18,40 +16,42 @@ import org.noear.solon.core.handle.Render; * */ @Controller -public class H5Controller implements Render { +public class H5Controller { /** * 获取 redirectUrl */ @Mapping("/sso/getRedirectUrl") - private Object getRedirectUrl(String redirect, String mode, String client) { - // 未登录情况下,返回 code=401 - if (StpUtil.isLogin() == false) { + public SaResult getRedirectUrl(String client, String redirect, String mode) { + // 未登录情况下,返回 code=401 + if(StpUtil.isLogin() == false) { return SaResult.code(401); } - // 已登录情况下,构建 redirectUrl - if (SaSsoConsts.MODE_SIMPLE.equals(mode)) { - // 模式一 - SaSsoUtil.checkRedirectUrl(SaFoxUtil.decoderUrl(redirect)); + // 已登录情况下,构建 redirectUrl + redirect = SaFoxUtil.decoderUrl(redirect); + if(SaSsoConsts.MODE_SIMPLE.equals(mode)) { + // 模式一 + SaSsoUtil.checkRedirectUrl(client, redirect); return SaResult.data(redirect); } else { - // 模式二或模式三 - String redirectUrl = SaSsoUtil.buildRedirectUrl(StpUtil.getLoginId(), client, redirect); + // 模式二或模式三 + String redirectUrl = SaSsoUtil.buildRedirectUrl(client, redirect, StpUtil.getLoginId(), StpUtil.getLoginDeviceId()); return SaResult.data(redirectUrl); } } - /** - * 控制当前类的异常 - */ - @Override - public void render(Object data, Context ctx) throws Throwable { - if (data instanceof Throwable) { - Throwable e = (Throwable) data; - e.printStackTrace(); - ctx.render(SaResult.error(e.getMessage())); - } else { - ctx.render(data); - } - } +// /** +// * 控制当前类的异常 +// */ +// @Override +// public void render(Object data, Context ctx) throws Throwable { +// if (data instanceof Throwable) { +// Throwable e = (Throwable) data; +// e.printStackTrace(); +// ctx.render(SaResult.error(e.getMessage())); +// } else { +// ctx.render(data); +// } +// } + } diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/h5/SaTokenConfigure.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/h5/SaTokenConfigure.java new file mode 100644 index 00000000..5bd7e5ed --- /dev/null +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/h5/SaTokenConfigure.java @@ -0,0 +1,40 @@ +package com.pj.h5; + +import cn.dev33.satoken.fun.strategy.SaCorsHandleFunction; +import cn.dev33.satoken.router.SaHttpMethod; +import cn.dev33.satoken.router.SaRouter; +import org.noear.solon.annotation.Bean; +import org.noear.solon.annotation.Configuration; + +/** + * [Sa-Token 权限认证] 配置类 (解决跨域问题) + * + * @author click33 + */ +@Configuration +public 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 -> System.out.println("--------OPTIONS预检请求,不做处理")) + .back(); + }; + } + +} diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/GlobalExceptionFilter.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/GlobalExceptionFilter.java index fc311ae3..a10ec817 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/GlobalExceptionFilter.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/GlobalExceptionFilter.java @@ -21,8 +21,8 @@ public class GlobalExceptionFilter implements Filter { chain.doFilter(ctx); } catch (Exception e) { e.printStackTrace(); - ctx.render(SaResult.error(e.getMessage())); } } + } diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/HomeController.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/HomeController.java new file mode 100644 index 00000000..a3bc98d8 --- /dev/null +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/HomeController.java @@ -0,0 +1,36 @@ +package com.pj.sso; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.stp.StpUtil; +import org.noear.solon.annotation.Controller; +import org.noear.solon.annotation.Mapping; + +/** + * SSO 平台中心模式示例,跳连接进入子系统 + */ +@Controller +public class HomeController { + + // 平台化首页 + @Mapping("/home") + public Object index() { + // 如果未登录,则先去登录 + if(!StpUtil.isLogin()) { + return SaHolder.getResponse().redirect("/sso/auth"); + } + + // 拼接各个子系统的地址,格式形如:/sso/auth?client=xxx&redirect=${子系统首页}/sso/login?back=${子系统首页} + String link1 = "/sso/auth?client=sso-client3&redirect=http://sa-sso-client1.com:9003/sso/login?back=http://sa-sso-client1.com:9003/"; + String link2 = "/sso/auth?client=sso-client3&redirect=http://sa-sso-client2.com:9003/sso/login?back=http://sa-sso-client2.com:9003/"; + String link3 = "/sso/auth?client=sso-client3&redirect=http://sa-sso-client3.com:9003/sso/login?back=http://sa-sso-client3.com:9003/"; + + // 组织网页结构返回到前端 + String title = "

SSO 平台首页 (平台中心模式)

"; + String client1 = "

进入Client1系统

"; + String client2 = "

进入Client2系统

"; + String client3 = "

进入Client3系统

"; + + return title + client1 + client2 + client3; + } + +} diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/SsoConfig.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/SsoConfig.java deleted file mode 100644 index e23de5f6..00000000 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/SsoConfig.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.pj.sso; - -import cn.dev33.satoken.dao.SaTokenDao; -import cn.dev33.satoken.dao.SaTokenDaoForRedisx; -import cn.dev33.satoken.sso.config.SaSsoServerConfig; -import cn.dev33.satoken.stp.StpUtil; -import cn.dev33.satoken.util.SaResult; -import com.dtflys.forest.Forest; -import org.noear.solon.annotation.Bean; -import org.noear.solon.annotation.Configuration; -import org.noear.solon.annotation.Inject; -import org.noear.solon.core.handle.ModelAndView; - -/** - * @author noear 2023/1/3 created - */ -@Configuration -public class SsoConfig { - - /** - * 构建建 SaToken redis dao(如果不需要 redis;可以注释掉) - * */ - @Bean - public SaTokenDao saTokenDaoInit(@Inject("${sa-token.dao.redis}") SaTokenDaoForRedisx saTokenDao) { - return saTokenDao; - } - - // 配置SSO相关参数 - @Bean - public void configSso(SaSsoServerConfig ssoServer) { //SaSsoConfig 已自动构建 - - // 配置:未登录时返回的View - ssoServer.notLoginView = () -> { - return new ModelAndView("sa-login.html"); - }; - - // 配置:登录处理函数 - ssoServer.doLoginHandle = (name, pwd) -> { - // 此处仅做模拟登录,真实环境应该查询数据进行登录 - if("sa".equals(name) && "123456".equals(pwd)) { - StpUtil.login(10001); - return SaResult.ok("登录成功!").setData(StpUtil.getTokenValue()); - } - return SaResult.error("登录失败!"); - }; - - // 配置 Http 请求处理器 - ssoServer.sendHttp = url -> { - try { - System.out.println("------ 发起请求:" + url); - String resStr = Forest.get(url).executeAsString(); - System.out.println("------ 请求结果:" + resStr); - return resStr; - } catch (Exception e) { - e.printStackTrace(); - return null; - } - }; - } -} diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/SsoServerController.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/SsoServerController.java index babbdd87..24428337 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/SsoServerController.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/java/com/pj/sso/SsoServerController.java @@ -1,9 +1,18 @@ package com.pj.sso; +import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.sso.processor.SaSsoServerProcessor; +import cn.dev33.satoken.sso.template.SaSsoServerTemplate; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; +import cn.dev33.satoken.util.SaFoxUtil; +import cn.dev33.satoken.util.SaResult; +import org.noear.solon.annotation.Bean; +import org.noear.solon.annotation.Configuration; import org.noear.solon.annotation.Controller; import org.noear.solon.annotation.Mapping; +import org.noear.solon.core.handle.ModelAndView; /** * Sa-Token-SSO Server端 Controller @@ -11,17 +20,52 @@ import org.noear.solon.annotation.Mapping; * */ @Controller +@Configuration 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/signout -- 单点注销地址(isSlo=true时打开),接受参数:loginId=账号id、secretkey=接口调用秘钥 + /** + * SSO-Server端:处理所有SSO相关请求 + * http://{host}:{port}/sso/auth -- 单点登录授权地址 + * http://{host}:{port}/sso/doLogin -- 账号密码登录接口,接受参数:name、pwd + * http://{host}:{port}/sso/signout -- 单点注销地址(isSlo=true时打开) */ @Mapping("/sso/*") public Object ssoRequest() { return SaSsoServerProcessor.instance.dister(); } + + // 配置SSO相关参数 + @Bean + private void configSso(SaSsoServerTemplate ssoServerTemplate) { + + // 配置:未登录时返回的View + ssoServerTemplate.strategy.notLoginView = () -> { + return new ModelAndView("sa-login.html"); + }; + + // 配置:登录处理函数 + ssoServerTemplate.strategy.doLoginHandle = (name, pwd) -> { + // 此处仅做模拟登录,真实环境应该查询数据库进行登录 + 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); + }); + + } + } diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/resources/app.yml b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/resources/app.yml index e895bbae..80282340 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/resources/app.yml +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon/src/main/resources/app.yml @@ -3,28 +3,50 @@ server: port: 9000 # Sa-Token 配置 -sa-token: - # ------- SSO-模式一相关配置 (非模式一不需要配置) - # cookie: - # 配置 Cookie 作用域 - # domain: stp.com - - # ------- SSO-模式二相关配置 +sa-token: + # 打印操作日志 + is-log: true + + # SSO 模式一配置 (非模式一不需要配置) +# cookie: +# # 配置 Cookie 作用域 +# domain: stp.com + + # SSO-Server 配置 sso-server: - # Ticket有效期 (单位: 秒),默认五分钟 + # Ticket有效期 (单位: 秒),默认五分钟 ticket-timeout: 300 - # 所有允许的授权回调地址 + # 主页路由:在 /sso/auth 登录页不指定 redirect 参数时,默认跳转的地址 + home-route: /home + # 是否启用匿名 client (开启匿名 client 后,允许客户端接入时不提交 client 参数) + allow-anon-client: true + # 所有允许的授权回调地址 (匿名 client 使用) allow-url: "*" - # 是否打开单点注销功能 - isSlo: true - - # ------- SSO-模式三相关配置 (下面的配置在SSO模式三并且 is-slo=true 时打开) - # 是否打开模式三 - isHttp: true - # 接口调用秘钥(用于SSO模式三的单点注销功能) - sign: + # API 接口调用秘钥 (全局默认 + 匿名 client 使用) secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor - # ---- 除了以上配置项,你还需要为 Sa-Token 配置http请求处理器(文档有步骤说明) + # 应用列表:配置接入的应用信息 + clients: + # 应用 sso-client1:采用模式一对接 (同域、同Redis) + sso-client1: + client: sso-client1 + allowUrl: "*" + # 应用 sso-client2:采用模式二对接 (跨域、同Redis) + sso-client2: + client: sso-client2 + allowUrl: "*" + secretKey: SSO-C2-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor + # 应用 sso-client3:采用模式三对接 (跨域、跨Redis) + sso-client3: + # 应用名称 + client: sso-client3 + # 允许授权地址 + allowUrl: "*" + # 是否接收消息推送 + isPush: true + # 消息推送地址 + pushUrl: http://sa-sso-client1.com:9003/sso/pushC + # 接口调用秘钥 (如果不配置则使用全局默认秘钥) + secretKey: SSO-C3-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor sa-token.dao: #名字可以随意取 redis: @@ -32,11 +54,7 @@ sa-token.dao: #名字可以随意取 # password: 123456 db: 1 maxTotal: 200 - -forest: - # 关闭 forest 请求日志打印 - log-enabled: false - - + + \ No newline at end of file diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/pom.xml b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/pom.xml index 08e71b81..96f0adf1 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/pom.xml +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/pom.xml @@ -21,11 +21,6 @@ - - org.noear solon-web diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/java/com/pj/SaSso1ClientApp.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/java/com/pj/SaSso1ClientApp.java index 8678b265..cbab1afa 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/java/com/pj/SaSso1ClientApp.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/java/com/pj/SaSso1ClientApp.java @@ -18,12 +18,12 @@ public class SaSso1ClientApp { System.out.println("\nSa-Token SSO模式一 Client端启动成功"); System.out.println(); - System.out.println("---------------------- Solon Sa-Token SSO 模式一 Client 端启动成功 ----------------------"); + System.out.println("---------------------- Sa-Token SSO 模式一 Client 端启动成功 ----------------------"); System.out.println("配置信息:" + SaSsoManager.getClientConfig()); System.out.println("测试访问应用端一: http://s1.stp.com:9001"); System.out.println("测试访问应用端二: http://s2.stp.com:9001"); System.out.println("测试访问应用端三: http://s3.stp.com:9001"); - System.out.println("测试前需要根据官网文档修改hosts文件,测试账号密码:sa / 123456"); + System.out.println("测试前需要根据官网文档修改 hosts 文件,测试账号密码:sa / 123456"); System.out.println(); } diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/java/com/pj/sso/SsoClientController.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/java/com/pj/sso/SsoClientController.java index 31d4d0a4..d0d9bba5 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/java/com/pj/sso/SsoClientController.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/java/com/pj/sso/SsoClientController.java @@ -1,7 +1,10 @@ package com.pj.sso; +import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.sso.SaSsoManager; +import cn.dev33.satoken.sso.config.SaSsoClientConfig; import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaFoxUtil; import cn.dev33.satoken.util.SaResult; import org.noear.solon.annotation.Controller; import org.noear.solon.annotation.Mapping; @@ -21,12 +24,16 @@ public class SsoClientController implements Render { @Produces(MimeType.TEXT_HTML_VALUE) @Mapping("/") public String index() { - String authUrl = SaSsoManager.getClientConfig().splicingAuthUrl(); - String solUrl = SaSsoManager.getClientConfig().splicingSignoutUrl(); - String str = "

Sa-Token SSO-Client 应用端

" + - "

当前会话是否登录:" + StpUtil.isLogin() + "

" + - "

登录 " + - "登录 - " + + "单浏览器注销 - " + + "全端注销 " + + "

"; return str; } @@ -36,7 +43,6 @@ public class SsoClientController implements Render { if(data instanceof Exception){ data = SaResult.error(((Exception)data).getMessage()); } - ctx.render(data); } diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/resources/app.yml b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/resources/app.yml index d2bb8247..0ce72151 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/resources/app.yml +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon/src/main/resources/app.yml @@ -3,9 +3,14 @@ server: port: 9001 # Sa-Token 配置 -sa-token: +sa-token: + # 打印操作日志 + is-log: true + # SSO-相关配置 sso-client: + # client 标识 + client: sso-client1 # SSO-Server端 - 主机地址 server-url: http://sso.stp.com:9000 diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/pom.xml b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/pom.xml index 90273482..306b0f57 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/pom.xml +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/pom.xml @@ -53,12 +53,14 @@ ${sa-token.version}
- + - org.noear - forest-solon-plugin + cn.dev33 + sa-token-forest + ${sa-token.version} +
diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/SaConfig.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/SaConfig.java index a77fad4d..07b1ef50 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/SaConfig.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/SaConfig.java @@ -2,8 +2,6 @@ package com.pj; import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.dao.SaTokenDaoForRedisx; -import cn.dev33.satoken.sso.config.SaSsoClientConfig; -import com.dtflys.forest.Forest; import org.noear.solon.annotation.Bean; import org.noear.solon.annotation.Configuration; import org.noear.solon.annotation.Inject; @@ -22,15 +20,4 @@ public class SaConfig { return saTokenDao; } - @Bean - public void configSso(SaSsoClientConfig ssoClient) { - // 配置Http请求处理器 - ssoClient.sendHttp = url -> { - System.out.println("------ 发起请求:" + url); - String resStr = Forest.get(url).executeAsString(); - System.out.println("------ 请求结果:" + resStr); - return resStr; - }; - } - } diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/SaSso2ClientApp.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/SaSso2ClientApp.java index a0ac5dfd..501fd8a5 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/SaSso2ClientApp.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/SaSso2ClientApp.java @@ -1,6 +1,5 @@ package com.pj; - import cn.dev33.satoken.sso.SaSsoManager; import org.noear.solon.Solon; import org.noear.solon.annotation.SolonMain; diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/h5/CorsFilter.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/h5/CorsFilter.java deleted file mode 100644 index 547aebed..00000000 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/h5/CorsFilter.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.pj.h5; - -import org.noear.solon.annotation.Component; -import org.noear.solon.core.handle.Context; -import org.noear.solon.core.handle.Filter; -import org.noear.solon.core.handle.FilterChain; - - -/** - * 跨域过滤器 - * @author click33 - */ -@Component(index = -200) -public class CorsFilter implements Filter { - static final String OPTIONS = "OPTIONS"; - - @Override - public void doFilter(Context ctx, FilterChain chain) throws Throwable { - // 允许指定域访问跨域资源 - ctx.headerSet("Access-Control-Allow-Origin", "*"); - // 允许所有请求方式 - ctx.headerSet("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); - // 有效时间 - ctx.headerSet("Access-Control-Max-Age", "3600"); - // 允许的header参数 - ctx.headerSet("Access-Control-Allow-Headers", "x-requested-with,satoken"); - - // 如果是预检请求,直接返回 - if (OPTIONS.equals(ctx.method())) { - System.out.println("=======================浏览器发来了OPTIONS预检请求=========="); - ctx.output(""); - return; - } - - // System.out.println("*********************************过滤器被使用**************************"); - chain.doFilter(ctx); - } -} diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/h5/H5Controller.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/h5/H5Controller.java index 8ef49dd1..9ca8f1db 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/h5/H5Controller.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/h5/H5Controller.java @@ -1,13 +1,13 @@ package com.pj.h5; +import cn.dev33.satoken.sso.model.SaCheckTicketResult; import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; -import cn.dev33.satoken.sso.template.SaSsoUtil; +import cn.dev33.satoken.sso.template.SaSsoClientUtil; import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; import cn.dev33.satoken.util.SaResult; import org.noear.solon.annotation.Controller; import org.noear.solon.annotation.Mapping; -import org.noear.solon.core.handle.Context; -import org.noear.solon.core.handle.Render; /** * 前后台分离架构下集成SSO所需的代码 (SSO-Client端) @@ -16,40 +16,30 @@ import org.noear.solon.core.handle.Render; * */ @Controller -public class H5Controller implements Render { +public class H5Controller { - // 当前是否登录 + // 判断当前是否登录 @Mapping("/sso/isLogin") public Object isLogin() { - return SaResult.data(StpUtil.isLogin()); + return SaResult.data(StpUtil.isLogin()).set("loginId", StpUtil.getLoginIdDefaultNull()); } - - // 返回SSO认证中心登录地址 + + // 返回SSO认证中心登录地址 @Mapping("/sso/getSsoAuthUrl") public SaResult getSsoAuthUrl(String clientLoginUrl) { - String serverAuthUrl = SaSsoUtil.buildServerAuthUrl(clientLoginUrl, ""); + String serverAuthUrl = SaSsoClientUtil.buildServerAuthUrl(clientLoginUrl, ""); return SaResult.data(serverAuthUrl); } - - // 根据ticket进行登录 + + // 根据 ticket 进行登录 @Mapping("/sso/doLoginByTicket") public SaResult doLoginByTicket(String ticket) { - Object loginId = SaSsoClientProcessor.instance.checkTicket(ticket, "/sso/doLoginByTicket"); - if(loginId != null) { - StpUtil.login(loginId); - return SaResult.data(StpUtil.getTokenValue()); - } - return SaResult.error("无效ticket:" + ticket); + SaCheckTicketResult ctr = SaSsoClientProcessor.instance.checkTicket(ticket); + StpUtil.login(ctr.loginId, new SaLoginParameter() + .setTimeout(ctr.remainTokenTimeout) + .setDeviceId(ctr.deviceId) + ); + return SaResult.data(StpUtil.getTokenValue()); } - // 全局异常拦截并转换 - @Override - public void render(Object data, Context ctx) throws Throwable { - if(data instanceof Exception){ - data = SaResult.error(((Exception)data).getMessage()); - } - - ctx.render(data); - } - } diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/h5/SaTokenConfigure.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/h5/SaTokenConfigure.java new file mode 100644 index 00000000..5bd7e5ed --- /dev/null +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/h5/SaTokenConfigure.java @@ -0,0 +1,40 @@ +package com.pj.h5; + +import cn.dev33.satoken.fun.strategy.SaCorsHandleFunction; +import cn.dev33.satoken.router.SaHttpMethod; +import cn.dev33.satoken.router.SaRouter; +import org.noear.solon.annotation.Bean; +import org.noear.solon.annotation.Configuration; + +/** + * [Sa-Token 权限认证] 配置类 (解决跨域问题) + * + * @author click33 + */ +@Configuration +public 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 -> System.out.println("--------OPTIONS预检请求,不做处理")) + .back(); + }; + } + +} diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/sso/GlobalExceptionFilter.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/sso/GlobalExceptionFilter.java new file mode 100644 index 00000000..a10ec817 --- /dev/null +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/sso/GlobalExceptionFilter.java @@ -0,0 +1,28 @@ +package com.pj.sso; + + +import cn.dev33.satoken.util.SaResult; +import org.noear.solon.annotation.Component; +import org.noear.solon.core.handle.Context; +import org.noear.solon.core.handle.Filter; +import org.noear.solon.core.handle.FilterChain; + +/** + * 全局异常处理 + * @author click33 + * + */ +@Component +public class GlobalExceptionFilter implements Filter { + + @Override + public void doFilter(Context ctx, FilterChain chain) throws Throwable { + try { + chain.doFilter(ctx); + } catch (Exception e) { + e.printStackTrace(); + ctx.render(SaResult.error(e.getMessage())); + } + } + +} diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/sso/SsoClientController.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/sso/SsoClientController.java index 76cbec98..bec46241 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/sso/SsoClientController.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/java/com/pj/sso/SsoClientController.java @@ -1,52 +1,83 @@ package com.pj.sso; +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.sso.message.SaSsoMessage; import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; +import cn.dev33.satoken.sso.template.SaSsoClientTemplate; +import cn.dev33.satoken.sso.template.SaSsoClientUtil; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaResult; -import org.noear.solon.annotation.Controller; -import org.noear.solon.annotation.Mapping; -import org.noear.solon.annotation.Produces; +import org.noear.solon.annotation.*; import org.noear.solon.boot.web.MimeType; -import org.noear.solon.core.handle.Context; -import org.noear.solon.core.handle.Render; /** * Sa-Token-SSO Client端 Controller * @author click33 */ @Controller -public class SsoClientController implements Render { +@Configuration +public class SsoClientController { // 首页 @Produces(MimeType.TEXT_HTML_VALUE) @Mapping("/") public String index() { - String str = "

Sa-Token SSO-Client 应用端

" + - "

当前会话是否登录:" + StpUtil.isLogin() + "

" + - "

登录 " + - "注销

"; + String str = "

Sa-Token SSO-Client 应用端 (模式二)

" + + "

当前会话是否登录:" + StpUtil.isLogin() + " (" + StpUtil.getLoginId("") + ")

" + + "

" + + "登录 - " + + "单应用注销 - " + + "单浏览器注销 - " + + "全端注销 - " + + "账号资料" + + "

"; return str; } /* - * SSO-Client端:处理所有SSO相关请求 - * http://{host}:{port}/sso/login -- Client端登录地址,接受参数:back=登录后的跳转地址 - * http://{host}:{port}/sso/logout -- Client端单点注销地址(isSlo=true时打开),接受参数:back=注销后的跳转地址 - * http://{host}:{port}/sso/logoutCall -- Client端单点注销回调地址(isSlo=true时打开),此接口为框架回调,开发者无需关心 + * SSO-Client端:处理所有SSO相关请求 + * http://{host}:{port}/sso/login -- Client 端登录地址 + * http://{host}:{port}/sso/logout -- Client 端注销地址(isSlo=true时打开) + * http://{host}:{port}/sso/pushC -- Client 端接收消息推送地址 */ @Mapping("/sso/*") public Object ssoRequest() { return SaSsoClientProcessor.instance.dister(); } - // 全局异常拦截并转换 - @Override - public void render(Object data, Context ctx) throws Throwable { - if(data instanceof Exception){ - data = SaResult.error(((Exception)data).getMessage()); + // 配置SSO相关参数 + @Bean + private void configSso(SaSsoClientTemplate ssoClientTemplate) { + + } + + // 当前应用独自注销 (不退出其它应用) + @Mapping("/sso/logoutByAlone") + public Object logoutByAlone() { + StpUtil.logout(); + return SaSsoClientProcessor.instance._ssoLogoutBack(SaHolder.getRequest(), SaHolder.getResponse()); + } + + // 查询我的账号信息:sso-client 前端 -> sso-center 后端 -> sso-server 后端 + @Mapping("/sso/myInfo") + public Object myInfo() { + // 如果尚未登录 + if( ! StpUtil.isLogin()) { + return "尚未登录,无法获取"; } - ctx.render(data); + // 获取本地 loginId + Object loginId = StpUtil.getLoginId(); + + // 推送消息 + SaSsoMessage message = new SaSsoMessage(); + message.setType("userinfo"); + message.set("loginId", loginId); + SaResult result = SaSsoClientUtil.pushMessageAsSaResult(message); + + // 返回给前端 + return result; } + } diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/resources/app.yml b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/resources/app.yml index 33875b88..fbe940b0 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/resources/app.yml +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon/src/main/resources/app.yml @@ -2,18 +2,21 @@ server: port: 9002 -# sa-token配置 -sa-token: +# sa-token配置 +sa-token: + # 打印操作日志 + is-log: true + # SSO-相关配置 sso-client: - # SSO-Server端 主机地址 + # 应用标识 + 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:8848/sa-token-demo-sso-server-h5/sso-auth.html - # 是否打开单点注销接口 - is-slo: true - sign: - # API 接口调用秘钥 - secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor + # API 接口调用秘钥 (单点注销时会用到) + secret-key: SSO-C2-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor # 配置 Sa-Token 单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis) sa-token.dao: #名字可以随意取 diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/pom.xml b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/pom.xml index 810696e8..65dbccd6 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/pom.xml +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/pom.xml @@ -21,23 +21,12 @@ - - - - - org.noear solon-web ${solon.version} - - - org.noear - forest-solon-plugin - - cn.dev33 @@ -59,6 +48,13 @@ ${sa-token.version} + + + cn.dev33 + sa-token-forest + ${sa-token.version} + + diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/h5/H5Controller.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/h5/H5Controller.java new file mode 100644 index 00000000..9ca8f1db --- /dev/null +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/h5/H5Controller.java @@ -0,0 +1,45 @@ +package com.pj.h5; + +import cn.dev33.satoken.sso.model.SaCheckTicketResult; +import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; +import cn.dev33.satoken.sso.template.SaSsoClientUtil; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; +import cn.dev33.satoken.util.SaResult; +import org.noear.solon.annotation.Controller; +import org.noear.solon.annotation.Mapping; + +/** + * 前后台分离架构下集成SSO所需的代码 (SSO-Client端) + *

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

+ * @author click33 + * + */ +@Controller +public class H5Controller { + + // 判断当前是否登录 + @Mapping("/sso/isLogin") + public Object isLogin() { + return SaResult.data(StpUtil.isLogin()).set("loginId", StpUtil.getLoginIdDefaultNull()); + } + + // 返回SSO认证中心登录地址 + @Mapping("/sso/getSsoAuthUrl") + public SaResult getSsoAuthUrl(String clientLoginUrl) { + String serverAuthUrl = SaSsoClientUtil.buildServerAuthUrl(clientLoginUrl, ""); + return SaResult.data(serverAuthUrl); + } + + // 根据 ticket 进行登录 + @Mapping("/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()); + } + +} diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/h5/SaTokenConfigure.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/h5/SaTokenConfigure.java new file mode 100644 index 00000000..5bd7e5ed --- /dev/null +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/h5/SaTokenConfigure.java @@ -0,0 +1,40 @@ +package com.pj.h5; + +import cn.dev33.satoken.fun.strategy.SaCorsHandleFunction; +import cn.dev33.satoken.router.SaHttpMethod; +import cn.dev33.satoken.router.SaRouter; +import org.noear.solon.annotation.Bean; +import org.noear.solon.annotation.Configuration; + +/** + * [Sa-Token 权限认证] 配置类 (解决跨域问题) + * + * @author click33 + */ +@Configuration +public 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 -> System.out.println("--------OPTIONS预检请求,不做处理")) + .back(); + }; + } + +} diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/GlobalExceptionFilter.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/GlobalExceptionFilter.java new file mode 100644 index 00000000..a10ec817 --- /dev/null +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/GlobalExceptionFilter.java @@ -0,0 +1,28 @@ +package com.pj.sso; + + +import cn.dev33.satoken.util.SaResult; +import org.noear.solon.annotation.Component; +import org.noear.solon.core.handle.Context; +import org.noear.solon.core.handle.Filter; +import org.noear.solon.core.handle.FilterChain; + +/** + * 全局异常处理 + * @author click33 + * + */ +@Component +public class GlobalExceptionFilter implements Filter { + + @Override + public void doFilter(Context ctx, FilterChain chain) throws Throwable { + try { + chain.doFilter(ctx); + } catch (Exception e) { + e.printStackTrace(); + ctx.render(SaResult.error(e.getMessage())); + } + } + +} diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SsoClientController.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SsoClientController.java index 8f3afeef..32756028 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SsoClientController.java +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SsoClientController.java @@ -1,69 +1,82 @@ package com.pj.sso; +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.sso.message.SaSsoMessage; import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; -import cn.dev33.satoken.sso.template.SaSsoUtil; +import cn.dev33.satoken.sso.template.SaSsoClientTemplate; +import cn.dev33.satoken.sso.template.SaSsoClientUtil; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaResult; -import org.noear.solon.annotation.Controller; -import org.noear.solon.annotation.Mapping; -import org.noear.solon.annotation.Produces; +import org.noear.solon.annotation.*; import org.noear.solon.boot.web.MimeType; -import org.noear.solon.core.handle.Context; -import org.noear.solon.core.handle.Render; - -import java.util.HashMap; -import java.util.Map; /** * Sa-Token-SSO Client端 Controller * @author click33 */ @Controller -public class SsoClientController implements Render { +@Configuration +public class SsoClientController { // SSO-Client端:首页 @Produces(MimeType.TEXT_HTML_VALUE) @Mapping("/") public String index() { - String str = "

Sa-Token SSO-Client 应用端

" + - "

当前会话是否登录:" + StpUtil.isLogin() + "

" + - "

登录" + - " 注销

"; + String str = "

Sa-Token SSO-Client 应用端 (模式三)

" + + "

当前会话是否登录:" + StpUtil.isLogin() + " (" + StpUtil.getLoginId("") + ")

" + + "

" + + "登录 - " + + "单应用注销 - " + + "单浏览器注销 - " + + "全端注销 - " + + "账号资料" + + "

"; return str; } - + /* - * SSO-Client端:处理所有SSO相关请求 - * http://{host}:{port}/sso/login -- Client端登录地址,接受参数:back=登录后的跳转地址 - * http://{host}:{port}/sso/logout -- Client端单点注销地址(isSlo=true时打开),接受参数:back=注销后的跳转地址 - * http://{host}:{port}/sso/logoutCall -- Client端单点注销回调地址(isSlo=true时打开),此接口为框架回调,开发者无需关心 + * SSO-Client端:处理所有SSO相关请求 + * http://{host}:{port}/sso/login -- Client 端登录地址 + * http://{host}:{port}/sso/logout -- Client 端注销地址(isSlo=true时打开) + * http://{host}:{port}/sso/pushC -- Client 端接收消息推送地址 */ @Mapping("/sso/*") public Object ssoRequest() { return SaSsoClientProcessor.instance.dister(); } - - // 查询我的账号信息 + + // 配置SSO相关参数 + @Bean + private void configSso(SaSsoClientTemplate ssoClientTemplate) { + + } + + // 当前应用独自注销 (不退出其它应用) + @Mapping("/sso/logoutByAlone") + public Object logoutByAlone() { + StpUtil.logout(); + return SaSsoClientProcessor.instance._ssoLogoutBack(SaHolder.getRequest(), SaHolder.getResponse()); + } + + // 查询我的账号信息:sso-client 前端 -> sso-center 后端 -> sso-server 后端 @Mapping("/sso/myInfo") public Object myInfo() { - // 组织请求参数 - Map map = new HashMap<>(); - map.put("apiType", "userinfo"); - map.put("loginId", StpUtil.getLoginId()); - - // 发起请求 - Object resData = SaSsoUtil.getData(map); - System.out.println("sso-server 返回的信息:" + resData); - return resData; - } - - // 全局异常拦截并转换 - @Override - public void render(Object data, Context ctx) throws Throwable { - if(data instanceof Exception){ - data = SaResult.error(((Exception)data).getMessage()); + // 如果尚未登录 + if( ! StpUtil.isLogin()) { + return "尚未登录,无法获取"; } - ctx.render(data); + // 获取本地 loginId + Object loginId = StpUtil.getLoginId(); + + // 推送消息 + SaSsoMessage message = new SaSsoMessage(); + message.setType("userinfo"); + message.set("loginId", loginId); + SaResult result = SaSsoClientUtil.pushMessageAsSaResult(message); + + // 返回给前端 + return result; } + } diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SsoConfig.java b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SsoConfig.java deleted file mode 100644 index 1f656d62..00000000 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/java/com/pj/sso/SsoConfig.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.pj.sso; - -import cn.dev33.satoken.sso.config.SaSsoClientConfig; -import com.dtflys.forest.Forest; -import org.noear.solon.annotation.Bean; -import org.noear.solon.annotation.Configuration; - -/** - * @author noear 2023/3/13 created - */ -@Configuration -public class SsoConfig { - // 配置SSO相关参数 - @Bean - private void configSso(SaSsoClientConfig ssoClient) { - // 配置Http请求处理器 - ssoClient.sendHttp = url -> { - System.out.println("------ 发起请求:" + url); - String resStr = Forest.get(url).executeAsString(); - System.out.println("------ 请求结果:" + resStr); - return resStr; - }; - } -} diff --git a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/resources/app.yml b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/resources/app.yml index 7bfeb9d3..67023924 100644 --- a/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/resources/app.yml +++ b/sa-token-demo/sa-token-demo-sso-for-solon/sa-token-demo-sso3-client-solon/src/main/resources/app.yml @@ -3,31 +3,29 @@ server: port: 9003 # sa-token配置 -sa-token: - # SSO-相关配置 - sso-client: - # SSO-Server端 主机地址 - server-url: http://sa-sso-server.com:9000 - # 使用Http请求校验ticket - is-http: true - # 打开单点注销功能 - is-slo: true - sign: - # 接口调用秘钥 - secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor +sa-token: + # 打印操作日志 + is-log: true + # sso-client 相关配置 + sso-client: + # 应用标识 + client: sso-client3 + # 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:8848/sa-token-demo-sso-server-h5/sso-auth.html + # 使用 Http 请求校验 ticket (模式三) + is-http: true + # API 接口调用秘钥 + secret-key: SSO-C3-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor # 配置 Sa-Token Dao(此处与SSO-Server端连接不同的Redis) sa-token.dao: #名字可以随意取 redis: server: "localhost:6379" # password: 123456 - db: 2 + db: 4 maxTotal: 200 - -forest: - # 关闭 forest 请求日志打印 - log-enabled: false - - + \ No newline at end of file diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/h5/SaTokenConfigure.java b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/h5/SaTokenConfigure.java index 663e232d..7c0dbc91 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/h5/SaTokenConfigure.java +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso-server/src/main/java/com/pj/h5/SaTokenConfigure.java @@ -1,13 +1,10 @@ package com.pj.h5; -import cn.dev33.satoken.context.SaHolder; -import cn.dev33.satoken.filter.SaServletFilter; +import cn.dev33.satoken.fun.strategy.SaCorsHandleFunction; import cn.dev33.satoken.router.SaHttpMethod; import cn.dev33.satoken.router.SaRouter; -import cn.dev33.satoken.util.SaResult; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * [Sa-Token 权限认证] 配置类 (解决跨域问题) @@ -15,50 +12,29 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; * @author click33 */ @Configuration -public class SaTokenConfigure implements WebMvcConfigurer { +public class SaTokenConfigure { /** - * 注册 [Sa-Token 全局过滤器] + * CORS 跨域处理策略 */ @Bean - public SaServletFilter getSaServletFilter() { - return new SaServletFilter() + 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", "*"); - // 指定 [拦截路由] 与 [放行路由] - .addInclude("/**").addExclude("/favicon.ico") - - // 认证函数: 每次请求执行 - .setAuth(obj -> { - // SaManager.getLog().debug("----- 请求path={} 提交token={}", SaHolder.getRequest().getRequestPath(), StpUtil.getTokenValue()); - // ... - }) - - // 异常处理函数:每次认证函数发生异常时执行此函数 - .setError(e -> { - return SaResult.error(e.getMessage()); - }) - - // 前置函数:在每次认证函数之前执行 - .setBeforeAuth(obj -> { - SaHolder.getResponse() - - // ---------- 设置跨域响应头 ---------- - // 允许指定域访问跨域资源 - .setHeader("Access-Control-Allow-Origin", "*") - // 允许所有请求方式 - .setHeader("Access-Control-Allow-Methods", "*") - // 允许的header参数 - .setHeader("Access-Control-Allow-Headers", "*") - // 有效时间 - .setHeader("Access-Control-Max-Age", "3600") - ; - - // 如果是预检请求,则立即返回到前端 - SaRouter.match(SaHttpMethod.OPTIONS) - .free(r -> System.out.println("--------OPTIONS预检请求,不做处理")) - .back(); - }) - ; + // 如果是预检请求,则立即返回到前端 + SaRouter.match(SaHttpMethod.OPTIONS) + .free(r -> System.out.println("--------OPTIONS预检请求,不做处理")) + .back(); + }; } } diff --git a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso1-client/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso1-client/src/main/resources/application.yml index dca31e4f..ff40f804 100644 --- a/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso1-client/src/main/resources/application.yml +++ b/sa-token-demo/sa-token-demo-sso/sa-token-demo-sso1-client/src/main/resources/application.yml @@ -3,7 +3,10 @@ server: port: 9001 # Sa-Token 配置 -sa-token: +sa-token: + # 打印操作日志 + is-log: true + # SSO-相关配置 sso-client: # client 标识 diff --git a/sa-token-doc/sso/anon-client.md b/sa-token-doc/sso/anon-client.md index 13814e34..1212717d 100644 --- a/sa-token-doc/sso/anon-client.md +++ b/sa-token-doc/sso/anon-client.md @@ -70,7 +70,7 @@ public class CustomSaSsoServerTemplate extends SaSsoServerTemplate { ``` -### 2、在 sso-server 端开启匿名 client 接入 +### 2、在 sso-client 端不要配置 client 字段 然后在对应的应用端不要配置 client 字段,例如: diff --git a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/config/SaSsoClientModel.java b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/config/SaSsoClientModel.java index fefe9896..5cedd273 100644 --- a/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/config/SaSsoClientModel.java +++ b/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/config/SaSsoClientModel.java @@ -42,7 +42,7 @@ public class SaSsoClientModel implements Serializable { /** * 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的 URL 将禁止下放 ticket ) */ - public String allowUrl = "*"; + public String allowUrl = ""; /** * 是否接收推送消息