From 4e8b44352ff056829796f05018787e2cfb14d62b Mon Sep 17 00:00:00 2001 From: Github_lizhw <171332584@qq.com> Date: Thu, 9 Jan 2025 23:55:27 +0800 Subject: [PATCH] =?UTF-8?q?gateway=E6=94=AF=E6=8C=81sso=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-site/ruoyi-gateway/pom.xml | 26 ++++ .../dromara/gateway/filter/AuthFilter.java | 3 +- .../dromara/gateway/sso/SsoController.java | 122 ++++++++++++++++++ .../dromara/gateway/sso/UserController.java | 18 +++ .../src/main/resources/application.yml | 24 ++++ 5 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 ruoyi-site/ruoyi-gateway/src/main/java/org/dromara/gateway/sso/SsoController.java create mode 100644 ruoyi-site/ruoyi-gateway/src/main/java/org/dromara/gateway/sso/UserController.java diff --git a/ruoyi-site/ruoyi-gateway/pom.xml b/ruoyi-site/ruoyi-gateway/pom.xml index 7b5bc93ed..8e6f4b197 100644 --- a/ruoyi-site/ruoyi-gateway/pom.xml +++ b/ruoyi-site/ruoyi-gateway/pom.xml @@ -66,6 +66,32 @@ ruoyi-common-satoken + + + + cn.dev33 + sa-token-sso + ${satoken.version} + + + + cn.dev33 + sa-token-jwt + ${satoken.version} + + + + + cn.dev33 + sa-token-redis-jackson + ${satoken.version} + + + org.apache.commons + commons-pool2 + + + org.dromara diff --git a/ruoyi-site/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/AuthFilter.java b/ruoyi-site/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/AuthFilter.java index b84d8117d..06591704f 100644 --- a/ruoyi-site/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/AuthFilter.java +++ b/ruoyi-site/ruoyi-gateway/src/main/java/org/dromara/gateway/filter/AuthFilter.java @@ -32,7 +32,8 @@ public class AuthFilter { return new SaReactorFilter() // 拦截地址 .addInclude("/**") - .addExclude("/favicon.ico", "/actuator", "/actuator/**") + .addExclude("/actuator", "/actuator/**") + .addExclude("/favicon.ico", "/sso/getSsoAuthUrl", "/sso/isLogin", "/sso/doLoginByTicket") // 鉴权方法:每次访问进入 .setAuth(obj -> { // 登录校验 -- 拦截所有路由 diff --git a/ruoyi-site/ruoyi-gateway/src/main/java/org/dromara/gateway/sso/SsoController.java b/ruoyi-site/ruoyi-gateway/src/main/java/org/dromara/gateway/sso/SsoController.java new file mode 100644 index 000000000..b67e2376e --- /dev/null +++ b/ruoyi-site/ruoyi-gateway/src/main/java/org/dromara/gateway/sso/SsoController.java @@ -0,0 +1,122 @@ +package org.dromara.gateway.sso; + +import cn.dev33.satoken.sso.SaSsoManager; +import cn.dev33.satoken.sso.config.SaSsoClientConfig; +import cn.dev33.satoken.sso.model.SaCheckTicketResult; +import cn.dev33.satoken.sso.name.ParamName; +import cn.dev33.satoken.sso.processor.SaSsoClientProcessor; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaFoxUtil; +import cn.dev33.satoken.util.SaResult; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * 前后台分离架构下集成SSO所需的代码 (SSO-Client端),TODO 后续用户相关接口通过rpc调用获取 + * + * @author click33 + */ +@RestController +public class SsoController { + + // 当前是否登录 + @RequestMapping("/sso/isLogin") + public Object isLogin() { + return SaResult.data(StpUtil.isLogin()); + } + + // 返回SSO认证中心登录地址 + @RequestMapping("/sso/getSsoAuthUrl") + public SaResult getSsoAuthUrl(@RequestParam() String clientLoginUrl) { + String serverAuthUrl = buildServerAuthUrl(clientLoginUrl, ""); + return SaResult.data(serverAuthUrl); + } + + + // ---------------------- 构建URL ---------------------- + + /** + * 构建URL:Server端 单点登录地址,支持vue页面的url构造 + * + * @param clientLoginUrl Client端登录地址 + * @param back 回调路径 + * @return [SSO-Server端-认证地址 ] + */ + public String buildServerAuthUrl(String clientLoginUrl, String back) { + SaSsoClientConfig ssoConfig = SaSsoManager.getClientConfig(); + ParamName paramName = SaSsoClientProcessor.instance.ssoClientTemplate.paramName; + + // 服务端认证地址,认证地址支持路由地址 + String serverUrl = ssoConfig.splicingAuthUrl(); + + // 拼接客户端标识 + String client = ssoConfig.getClient(); + if (SaFoxUtil.isNotEmpty(client)) { + serverUrl = joinParam(serverUrl, paramName.client, client); + } + + + // 对back地址编码 + back = (back == null ? "" : back); + back = SaFoxUtil.encodeUrl(back); + + // 开始拼接 sso 统一认证地址,形如:serverAuthUrl = http://xxx.com?redirectUrl=xxx.com?back=xxx.com + + /* + * 部分 Servlet 版本 request.getRequestURL() 返回的 url 带有 query 参数,形如:http://domain.com?id=1, + * 如果不加判断会造成最终生成的 serverAuthUrl 带有双 back 参数 ,这个 if 判断正是为了解决此问题 + */ + if (!clientLoginUrl.contains(paramName.back + "=" + back)) { + clientLoginUrl = joinParam(clientLoginUrl, paramName.back, back); + } + + // 返回 + return joinParam(serverUrl, paramName.redirect, clientLoginUrl); + } + + /** + * 在url上拼接上kv参数并返回,支持路由url 拼接 + * + * @param url url + * @param key 参数名称 + * @param value 参数值 + * @return 拼接后的url字符串 + */ + public static String joinParam(String url, String key, Object value) { + // 如果url或者key为空, 直接返回 + if (!StringUtils.hasText(url) || !StringUtils.hasText(key)) { + return url; + } + String routerUrlSuffix = ""; + int routerUrlIdx = url.indexOf("#/"); + if (routerUrlIdx != -1) { + routerUrlSuffix = url.substring(routerUrlIdx); + url = url.substring(0, routerUrlIdx); + } + return SaFoxUtil.joinParam(url, key + "=" + value) + routerUrlSuffix; + } + + + // 根据ticket进行登录 + @RequestMapping("/sso/doLoginByTicket") + public SaResult doLoginByTicket(@RequestParam() String ticket) { + SaCheckTicketResult ctr = SaSsoClientProcessor.instance.checkTicket(ticket, "/sso/doLoginByTicket"); + StpUtil.login(ctr.loginId, ctr.remainSessionTimeout); + return SaResult.data(StpUtil.getTokenValue()); + } + + + /* + * 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时打开),此接口为框架回调,开发者无需关心 + */ + @RequestMapping("/sso/*") + public Object ssoRequest() { + return SaSsoClientProcessor.instance.dister(); + } + +} diff --git a/ruoyi-site/ruoyi-gateway/src/main/java/org/dromara/gateway/sso/UserController.java b/ruoyi-site/ruoyi-gateway/src/main/java/org/dromara/gateway/sso/UserController.java new file mode 100644 index 000000000..013b7b5d9 --- /dev/null +++ b/ruoyi-site/ruoyi-gateway/src/main/java/org/dromara/gateway/sso/UserController.java @@ -0,0 +1,18 @@ +package org.dromara.gateway.sso; + +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class UserController { + + @GetMapping("/user/getUserInfo") + public SaResult getUserInfo() { + //TODO 后续改成统一从指定用户服务取,用户应该通一接口 + return SaResult.data(StpUtil.getTokenSession().get("loginUser")); + } + + +} diff --git a/ruoyi-site/ruoyi-gateway/src/main/resources/application.yml b/ruoyi-site/ruoyi-gateway/src/main/resources/application.yml index c64e50ec3..9bdcc40d1 100644 --- a/ruoyi-site/ruoyi-gateway/src/main/resources/application.yml +++ b/ruoyi-site/ruoyi-gateway/src/main/resources/application.yml @@ -13,6 +13,30 @@ spring: # 环境配置 active: @profiles.active@ + +# sa-token配置 +sa-token: + sign: + # API client和server之间调用 接口签名秘钥 (随便乱摁几个字母即可) + secret-key: kQwIOrYbtXmSDkwEiFngrKidMcdrgKor + # token名称 (同时也是cookie名称) + token-name: Authorization + # 允许动态设置 token 有效期 + dynamic-active-timeout: true + # 允许从 header 读取 token + is-read-header: true + # 关闭 cookie 鉴权 从根源杜绝 csrf 漏洞风险 + is-read-cookie: false + # SSO-相关配置 + sso-client: + # SSO-Server 端主机地址 + server-url: http://sa-sso-server.com:19000 + auth-url: http://localhost:9000/#/sso-login + client: "pc" + #jwt密钥 + jwt-secret-key: abcdcosctlklmnopqrstuvwxyz + token-prefix: Bearer + --- # nacos 配置 spring: cloud: