From 1cea62ff22bbd11cb280653bd44a79ed076cd85d Mon Sep 17 00:00:00 2001 From: click33 <2393584716@qq.com> Date: Wed, 1 Sep 2021 03:43:58 +0800 Subject: [PATCH] =?UTF-8?q?SSO-Server=E7=AB=AF=E5=89=8D=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E5=88=86=E7=A6=BB=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/dev33/satoken/util/SaResult.java | 3 + .../src/main/java/com/pj/h5/H5Controller.java | 3 +- .../src/main/resources/application.yml | 1 + .../sa-token-demo-sso2-server-h5/login.css | 41 +++++++ .../sa-token-demo-sso2-server-h5/login.js | 102 ++++++++++++++++++ .../sso-auth.html | 44 ++++++++ .../src/main/java/com/pj/h5/CorsFilter.java | 61 +++++++++++ .../src/main/java/com/pj/h5/H5Controller.java | 41 +++++++ .../java/com/pj/sso/SsoServerController.java | 2 +- .../java/com/pj/sso/SsoServerController.java | 2 +- sa-token-doc/doc/sso/sso-h5.md | 23 +++- sa-token-doc/doc/sso/sso-pro.md | 2 +- 12 files changed, 320 insertions(+), 5 deletions(-) create mode 100644 sa-token-demo/sa-token-demo-sso2-server-h5/login.css create mode 100644 sa-token-demo/sa-token-demo-sso2-server-h5/login.js create mode 100644 sa-token-demo/sa-token-demo-sso2-server-h5/sso-auth.html create mode 100644 sa-token-demo/sa-token-demo-sso2-server/src/main/java/com/pj/h5/CorsFilter.java create mode 100644 sa-token-demo/sa-token-demo-sso2-server/src/main/java/com/pj/h5/H5Controller.java diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaResult.java b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaResult.java index 51222077..388ab60e 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaResult.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaResult.java @@ -109,6 +109,9 @@ public class SaResult extends LinkedHashMap implements Serializa public static SaResult ok(String msg) { return new SaResult(CODE_SUCCESS, msg, null); } + public static SaResult code(int code) { + return new SaResult(code, null, null); + } public static SaResult data(Object data) { return new SaResult(CODE_SUCCESS, "ok", data); } diff --git a/sa-token-demo/sa-token-demo-sso2-client/src/main/java/com/pj/h5/H5Controller.java b/sa-token-demo/sa-token-demo-sso2-client/src/main/java/com/pj/h5/H5Controller.java index 903e17ad..f2362f68 100644 --- a/sa-token-demo/sa-token-demo-sso2-client/src/main/java/com/pj/h5/H5Controller.java +++ b/sa-token-demo/sa-token-demo-sso2-client/src/main/java/com/pj/h5/H5Controller.java @@ -9,7 +9,8 @@ import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaResult; /** - * 前后台分离架构下集成SSO所需的代码 + * 前后台分离架构下集成SSO所需的代码 (SSO-Client端) + *

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

* @author kong * */ 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 1e629520..a8f622ff 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 @@ -8,6 +8,7 @@ sa-token: sso: # SSO-Server端 统一认证地址 auth-url: http://sa-sso-server.com:9000/sso/auth + # auth-url: http://127.0.0.1:8848/sa-token-demo-sso2-server-h5/sso-auth.html # 是否打开单点注销接口 is-slo: true diff --git a/sa-token-demo/sa-token-demo-sso2-server-h5/login.css b/sa-token-demo/sa-token-demo-sso2-server-h5/login.css new file mode 100644 index 00000000..d23be768 --- /dev/null +++ b/sa-token-demo/sa-token-demo-sso2-server-h5/login.css @@ -0,0 +1,41 @@ +*{margin: 0; padding: 0;} +body{font-family: Helvetica Neue,Helvetica,PingFang SC,Tahoma,Arial,sans-serif;} +::-webkit-input-placeholder{color: #ccc;} + +/* 视图盒子 */ +.view-box{position: relative; width: 100vw; height: 100vh; overflow: hidden;} +/* 背景 EAEFF3 */ +.bg-1{height: 100%; background: #c0cCf4;} + +/* 内容盒子 */ +.content-box{position: absolute; width: 100vw; height: 100vh; top: 0px;} + +/* 登录盒子 */ +/* .login-box{width: 400px; height: 400px; position: absolute; left: calc(50% - 200px); top: calc(50% - 200px); max-width: 90%; } */ +.login-box{width: 400px; margin: auto; max-width: 90%; height: 100%;} +.login-box{display: flex; align-items: center; text-align: center;} + +/* 表单 */ +.from-box{flex: 1; padding: 20px 50px; background-color: #FFF;} +.from-box{border-radius: 1px; box-shadow: 1px 1px 20px #666;} +.from-title{margin-top: 20px; margin-bottom: 30px; text-align: center;} + +/* 输入框 */ +.from-item{border: 0px #000 solid; margin-bottom: 15px;} +.s-input{width: 100%; line-height: 32px; height: 32px; text-indent: 1em; outline: 0; border: 1px #ccc solid; border-radius: 3px; transition: all 0.2s;} +.s-input{font-size: 12px;} +.s-input:focus{border-color: #409eff} + +/* 登录按钮 */ +.s-btn{ text-indent: 0; cursor: pointer; background-color: #409EFF; border-color: #409EFF; color: #FFF;} +.s-btn:hover{background-color: #50aEFF;} + +/* 重置按钮 */ +.reset-box{text-align: left; font-size: 12px;} +.reset-box a{text-decoration: none;} +.reset-box a:hover{text-decoration: underline;} + +/* loading框样式 */ +.ajax-layer-load.layui-layer-dialog{min-width: 0px !important; background-color: rgba(0,0,0,0.85);} +.ajax-layer-load.layui-layer-dialog .layui-layer-content{padding: 10px 20px 10px 40px; color: #FFF;} +.ajax-layer-load.layui-layer-dialog .layui-layer-content .layui-layer-ico{width: 20px; height: 20px; background-size: 20px 20px; top: 12px; } \ No newline at end of file diff --git a/sa-token-demo/sa-token-demo-sso2-server-h5/login.js b/sa-token-demo/sa-token-demo-sso2-server-h5/login.js new file mode 100644 index 00000000..86bff802 --- /dev/null +++ b/sa-token-demo/sa-token-demo-sso2-server-h5/login.js @@ -0,0 +1,102 @@ +// 服务端地址 +var baseUrl = "http://sa-sso-server.com:9000"; + +// sa +var sa = {}; + +// 打开loading +sa.loading = function(msg) { + layer.closeAll(); // 开始前先把所有弹窗关了 + return layer.msg(msg, {icon: 16, shade: 0.3, time: 1000 * 20, skin: 'ajax-layer-load'}); +}; + +// 隐藏loading +sa.hideLoading = function() { + layer.closeAll(); +}; + +// 封装一下Ajax +sa.ajax = function(url, data, successFn) { + $.ajax({ + url: baseUrl + url, + type: "post", + data: data, + dataType: 'json', + headers: { + 'X-Requested-With': 'XMLHttpRequest', + 'satoken': localStorage.getItem('satoken') + }, + success: function(res){ + console.log('返回数据:', res); + successFn(res); + }, + error: function(xhr, type, errorThrown){ + if(xhr.status == 0){ + return alert('无法连接到服务器,请检查网络'); + } + return alert("异常:" + JSON.stringify(xhr)); + } + }); +} + +// ----------------------------------- 相关事件 ----------------------------------- + +// 检查当前是否已经登录,如果已登录则直接开始跳转,如果未登录则等待用户输入账号密码 +sa.ajax("/getRedirectUrl", {redirect: getParam('redirect', '')}, function(res) { + if(res.code == 200) { + // redirect地址有效,开始跳转 + location.href = decodeURIComponent(res.data); + } else if(res.code == 401) { + console.log('未登录'); + } else { + layer.alert(res.msg); + } +}) + +// 登录 +$('.login-btn').click(function(){ + sa.loading("正在登录..."); + // 开始登录 + var data = { + name: $('[name=name]').val(), + pwd: $('[name=pwd]').val() + }; + sa.ajax("/sso/doLogin", data, function(res) { + sa.hideLoading(); + if(res.code == 200) { + localStorage.setItem('satoken', res.data); + layer.msg('登录成功', {anim: 0, icon: 6 }); + setTimeout(function() { + location.reload(); + }, 800); + } else { + layer.msg(res.msg, {anim: 6, icon: 2 }); + } + }) +}); + + +// 绑定回车事件 +$('[name=name],[name=pwd]').bind('keypress', function(event){ + if(event.keyCode == "13") { + $('.login-btn').click(); + } +}); + +// 输入框获取焦点 +$("[name=name]").focus(); + +// 从url中查询到指定名称的参数值 +function getParam(name, defaultValue){ + var query = window.location.search.substring(1); + var vars = query.split("&"); + for (var i=0;i + + + Sa-SSO-Server 认证中心-登录 + + + + + + +
+
+
+ +
+ +
+ This page is provided by Sa-Token-SSO +
+
+ + + + + + + + diff --git a/sa-token-demo/sa-token-demo-sso2-server/src/main/java/com/pj/h5/CorsFilter.java b/sa-token-demo/sa-token-demo-sso2-server/src/main/java/com/pj/h5/CorsFilter.java new file mode 100644 index 00000000..dd942224 --- /dev/null +++ b/sa-token-demo/sa-token-demo-sso2-server/src/main/java/com/pj/h5/CorsFilter.java @@ -0,0 +1,61 @@ +package com.pj.h5; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +/** + * 跨域过滤器 + * @author kong + */ +@Component +@Order(-200) +public class CorsFilter implements Filter { + + static final String OPTIONS = "OPTIONS"; + + @Override + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) req; + HttpServletResponse response = (HttpServletResponse) res; + + // 允许指定域访问跨域资源 + response.setHeader("Access-Control-Allow-Origin", "*"); + // 允许所有请求方式 + response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); + // 有效时间 + response.setHeader("Access-Control-Max-Age", "3600"); + // 允许的header参数 + response.setHeader("Access-Control-Allow-Headers", "x-requested-with,satoken"); + + // 如果是预检请求,直接返回 + if (OPTIONS.equals(request.getMethod())) { + System.out.println("=======================浏览器发来了OPTIONS预检请求=========="); + response.getWriter().print(""); + return; + } + + // System.out.println("*********************************过滤器被使用**************************"); + chain.doFilter(req, res); + } + + @Override + public void init(FilterConfig filterConfig) { + } + + @Override + public void destroy() { + } + +} diff --git a/sa-token-demo/sa-token-demo-sso2-server/src/main/java/com/pj/h5/H5Controller.java b/sa-token-demo/sa-token-demo-sso2-server/src/main/java/com/pj/h5/H5Controller.java new file mode 100644 index 00000000..5e6b9dc5 --- /dev/null +++ b/sa-token-demo/sa-token-demo-sso2-server/src/main/java/com/pj/h5/H5Controller.java @@ -0,0 +1,41 @@ +package com.pj.h5; + +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import cn.dev33.satoken.sso.SaSsoUtil; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; + +/** + * 前后台分离架构下集成SSO所需的代码 (SSO-Server端) + *

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

+ * @author kong + * + */ +@RestController +public class H5Controller { + + /** + * 获取 redirectUrl + */ + @RequestMapping("/getRedirectUrl") + private Object getRedirectUrl(String redirect) { + // 未登录情况下,返回 code=401 + if(StpUtil.isLogin() == false) { + return SaResult.code(401); + } + // 已登录情况下,构建 redirectUrl + String redirectUrl = SaSsoUtil.buildRedirectUrl(StpUtil.getLoginId(), redirect); + return SaResult.data(redirectUrl); + } + + // 全局异常拦截 + @ExceptionHandler + public SaResult handlerException(Exception e) { + e.printStackTrace(); + return SaResult.error(e.getMessage()); + } + +} diff --git a/sa-token-demo/sa-token-demo-sso2-server/src/main/java/com/pj/sso/SsoServerController.java b/sa-token-demo/sa-token-demo-sso2-server/src/main/java/com/pj/sso/SsoServerController.java index 4d23369a..e8c3c751 100644 --- a/sa-token-demo/sa-token-demo-sso2-server/src/main/java/com/pj/sso/SsoServerController.java +++ b/sa-token-demo/sa-token-demo-sso2-server/src/main/java/com/pj/sso/SsoServerController.java @@ -44,7 +44,7 @@ public class SsoServerController { // 此处仅做模拟登录,真实环境应该查询数据进行登录 if("sa".equals(name) && "123456".equals(pwd)) { StpUtil.login(10001); - return SaResult.ok("登录成功!"); + return SaResult.ok("登录成功!").setData(StpUtil.getTokenValue()); } return SaResult.error("登录失败!"); }) diff --git a/sa-token-demo/sa-token-demo-sso3-server/src/main/java/com/pj/sso/SsoServerController.java b/sa-token-demo/sa-token-demo-sso3-server/src/main/java/com/pj/sso/SsoServerController.java index cd1e4700..531d8ed4 100644 --- a/sa-token-demo/sa-token-demo-sso3-server/src/main/java/com/pj/sso/SsoServerController.java +++ b/sa-token-demo/sa-token-demo-sso3-server/src/main/java/com/pj/sso/SsoServerController.java @@ -63,7 +63,7 @@ public class SsoServerController { // 此处仅做模拟登录,真实环境应该查询数据进行登录 if("sa".equals(name) && "123456".equals(pwd)) { StpUtil.login(10001); - return SaResult.ok("登录成功!"); + return SaResult.ok("登录成功!").setData(StpUtil.getTokenValue()); } return SaResult.error("登录失败!"); }) diff --git a/sa-token-doc/doc/sso/sso-h5.md b/sa-token-doc/doc/sso/sso-h5.md index 55849814..81e56591 100644 --- a/sa-token-doc/doc/sso/sso-h5.md +++ b/sa-token-doc/doc/sso/sso-h5.md @@ -109,7 +109,7 @@ public class H5Controller { ### 5、测试运行 先启动Server服务端与Client服务端,再随便找个能预览html的工具打开前端项目(比如[HBuilderX](https://www.dcloud.io/hbuilderx.html)),测试流程与一体版一致 -### 6、疑问:我在SSO模式三的demo中加入上述代码,提示我ticket无效,是怎么回事? +### 6、疑问:我在SSO模式三的demo中加入上述代码,提示我 ticket无效,是怎么回事? 上述代码是以SSO模式二为基础的,提示“Ticket无效”的原因很简单,因为SSO模式三中 Server端 与 Client端 连接的不是同一个Redis, 所以Client端校验Ticket时无法在Redis中查询到相应的值,才会产生异常:“Ticket无效” @@ -131,5 +131,26 @@ private Object checkTicket(String ticket) { 重新运行项目,即可在SSO模式三中成功整合前后台分离模式 。 +### 7、SSO-Server 端的前后台分离 +疑问:上述代码都是针对 Client 端进行拆分,如果我想在 SSO-Server 端也进行前后台分离改造,应该怎么做? +> 答:解决思路都是大同小异的,与Client一样,我们需要把原本在 “后端处理的授权重定向逻辑” 拿到前端来实现。 + +由于集成代码与 Client 端类似,这里暂不贴详细代码,我们可以下载官方仓库,里面有搭建好的demo + +使用前端ide导入项目 `/sa-token-demo/sa-token-demo-sso2-h5`,浏览器访问 `sso-auth.html` 页面: + +![sso-type2-server-h5-auth.png](https://oss.dev33.cn/sa-token/doc/sso/sso-type2-server-h5-auth.png 's-w-sh') + +复制上述地址,将其配置到 Client 端的 yml 配置文件中,例如: + +``` yml +sa-token: + sso: + # SSO-Server端 统一认证地址 + auth-url: http://127.0.0.1:8848/sa-token-demo-sso2-server-h5/sso-auth.html +``` + +然后我们启动项目 `sa-token-demo-sso2-server` 与 `sa-token-demo-sso2-client`,按照之前的测试步骤访问: +[http://sa-sso-client1.com:9001/](http://sa-sso-client1.com:9001/),即可以前后端分离模式完成 SSO-Server 端的授权登录。 diff --git a/sa-token-doc/doc/sso/sso-pro.md b/sa-token-doc/doc/sso/sso-pro.md index 6ed925e9..15ab62a1 100644 --- a/sa-token-doc/doc/sso/sso-pro.md +++ b/sa-token-doc/doc/sso/sso-pro.md @@ -8,7 +8,7 @@ 然而,要真正开发一个商业级项目的认证中心系统,绝非一朝一夕可以搭建完毕,其必不可少的一些功能, 比如:用户账号增删改查维护、登录日志统计、新增用户数据报表、新增 Client 应用接入域名配置……等等, -仍需要我们大量的开发时间。 +仍需要大量的开发时间。 为此,我们特意准备了项目:[[Sa-Sso-Pro 单点登录商业版]](http://sa-pro.dev33.cn/index.html?hmsr=sa-token), 项目集成了单点登录常见技术点, 绝大多数功能无需二次开发,直接可用,可大大缩短您的项目接入单点登录的开发周期