mirror of
https://gitee.com/dromara/sa-token.git
synced 2025-06-28 13:34:18 +08:00
SSO-Server端前后台分离示例
This commit is contained in:
parent
ad79e3595e
commit
1cea62ff22
@ -109,6 +109,9 @@ public class SaResult extends LinkedHashMap<String, Object> implements Serializa
|
|||||||
public static SaResult ok(String msg) {
|
public static SaResult ok(String msg) {
|
||||||
return new SaResult(CODE_SUCCESS, msg, null);
|
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) {
|
public static SaResult data(Object data) {
|
||||||
return new SaResult(CODE_SUCCESS, "ok", data);
|
return new SaResult(CODE_SUCCESS, "ok", data);
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,8 @@ import cn.dev33.satoken.stp.StpUtil;
|
|||||||
import cn.dev33.satoken.util.SaResult;
|
import cn.dev33.satoken.util.SaResult;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 前后台分离架构下集成SSO所需的代码
|
* 前后台分离架构下集成SSO所需的代码 (SSO-Client端)
|
||||||
|
* <p>(注:如果不需要前后端分离架构下集成SSO,可删除此包下所有代码)</p>
|
||||||
* @author kong
|
* @author kong
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -8,6 +8,7 @@ sa-token:
|
|||||||
sso:
|
sso:
|
||||||
# SSO-Server端 统一认证地址
|
# SSO-Server端 统一认证地址
|
||||||
auth-url: http://sa-sso-server.com:9000/sso/auth
|
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
|
is-slo: true
|
||||||
|
|
||||||
|
41
sa-token-demo/sa-token-demo-sso2-server-h5/login.css
Normal file
41
sa-token-demo/sa-token-demo-sso2-server-h5/login.css
Normal file
@ -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; }
|
102
sa-token-demo/sa-token-demo-sso2-server-h5/login.js
Normal file
102
sa-token-demo/sa-token-demo-sso2-server-h5/login.js
Normal file
@ -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<vars.length;i++) {
|
||||||
|
var pair = vars[i].split("=");
|
||||||
|
if(pair[0] == name){return pair[1] + (pair[2] ? '=' + pair[2] : '');}
|
||||||
|
}
|
||||||
|
return(defaultValue == undefined ? null : defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打印信息
|
||||||
|
var str = "This page is provided by Sa-Token, Please refer to: " + "http://sa-token.dev33.cn/";
|
||||||
|
console.log(str);
|
44
sa-token-demo/sa-token-demo-sso2-server-h5/sso-auth.html
Normal file
44
sa-token-demo/sa-token-demo-sso2-server-h5/sso-auth.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
|
<head>
|
||||||
|
<title>Sa-SSO-Server 认证中心-登录</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<base th:href="@{/}" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
|
<link rel="stylesheet" href="./login.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="view-box">
|
||||||
|
<div class="bg-1"></div>
|
||||||
|
<div class="content-box">
|
||||||
|
<div class="login-box">
|
||||||
|
<div class="from-box">
|
||||||
|
<h2 class="from-title">Sa-SSO-Server 认证中心(前后端分离版)</h2>
|
||||||
|
<div class="from-item">
|
||||||
|
<input class="s-input" name="name" placeholder="请输入账号" />
|
||||||
|
</div>
|
||||||
|
<div class="from-item">
|
||||||
|
<input class="s-input" name="pwd" type="password" placeholder="请输入密码" />
|
||||||
|
</div>
|
||||||
|
<div class="from-item">
|
||||||
|
<button class="s-input s-btn login-btn">登录</button>
|
||||||
|
</div>
|
||||||
|
<div class="from-item reset-box">
|
||||||
|
<a href="javascript: location.reload();" >刷新</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 底部 版权 -->
|
||||||
|
<div style="position: absolute; bottom: 40px; width: 100%; text-align: center; color: #666;">
|
||||||
|
This page is provided by Sa-Token-SSO
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- scripts -->
|
||||||
|
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
|
||||||
|
<script src="https://www.layuicdn.com/layer-v3.1.1/layer.js"></script>
|
||||||
|
<script src="./login.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -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() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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端)
|
||||||
|
* <p>(注:如果不需要前后端分离架构下集成SSO,可删除此包下所有代码)</p>
|
||||||
|
* @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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -44,7 +44,7 @@ public class SsoServerController {
|
|||||||
// 此处仅做模拟登录,真实环境应该查询数据进行登录
|
// 此处仅做模拟登录,真实环境应该查询数据进行登录
|
||||||
if("sa".equals(name) && "123456".equals(pwd)) {
|
if("sa".equals(name) && "123456".equals(pwd)) {
|
||||||
StpUtil.login(10001);
|
StpUtil.login(10001);
|
||||||
return SaResult.ok("登录成功!");
|
return SaResult.ok("登录成功!").setData(StpUtil.getTokenValue());
|
||||||
}
|
}
|
||||||
return SaResult.error("登录失败!");
|
return SaResult.error("登录失败!");
|
||||||
})
|
})
|
||||||
|
@ -63,7 +63,7 @@ public class SsoServerController {
|
|||||||
// 此处仅做模拟登录,真实环境应该查询数据进行登录
|
// 此处仅做模拟登录,真实环境应该查询数据进行登录
|
||||||
if("sa".equals(name) && "123456".equals(pwd)) {
|
if("sa".equals(name) && "123456".equals(pwd)) {
|
||||||
StpUtil.login(10001);
|
StpUtil.login(10001);
|
||||||
return SaResult.ok("登录成功!");
|
return SaResult.ok("登录成功!").setData(StpUtil.getTokenValue());
|
||||||
}
|
}
|
||||||
return SaResult.error("登录失败!");
|
return SaResult.error("登录失败!");
|
||||||
})
|
})
|
||||||
|
@ -109,7 +109,7 @@ public class H5Controller {
|
|||||||
### 5、测试运行
|
### 5、测试运行
|
||||||
先启动Server服务端与Client服务端,再随便找个能预览html的工具打开前端项目(比如[HBuilderX](https://www.dcloud.io/hbuilderx.html)),测试流程与一体版一致
|
先启动Server服务端与Client服务端,再随便找个能预览html的工具打开前端项目(比如[HBuilderX](https://www.dcloud.io/hbuilderx.html)),测试流程与一体版一致
|
||||||
|
|
||||||
### 6、疑问:我在SSO模式三的demo中加入上述代码,提示我ticket无效,是怎么回事?
|
### 6、疑问:我在SSO模式三的demo中加入上述代码,提示我 ticket无效,是怎么回事?
|
||||||
上述代码是以SSO模式二为基础的,提示“Ticket无效”的原因很简单,因为SSO模式三中 Server端 与 Client端 连接的不是同一个Redis,
|
上述代码是以SSO模式二为基础的,提示“Ticket无效”的原因很简单,因为SSO模式三中 Server端 与 Client端 连接的不是同一个Redis,
|
||||||
所以Client端校验Ticket时无法在Redis中查询到相应的值,才会产生异常:“Ticket无效”
|
所以Client端校验Ticket时无法在Redis中查询到相应的值,才会产生异常:“Ticket无效”
|
||||||
|
|
||||||
@ -131,5 +131,26 @@ private Object checkTicket(String ticket) {
|
|||||||
重新运行项目,即可在SSO模式三中成功整合前后台分离模式 。
|
重新运行项目,即可在SSO模式三中成功整合前后台分离模式 。
|
||||||
|
|
||||||
|
|
||||||
|
### 7、SSO-Server 端的前后台分离
|
||||||
|
疑问:上述代码都是针对 Client 端进行拆分,如果我想在 SSO-Server 端也进行前后台分离改造,应该怎么做?
|
||||||
|
|
||||||
|
> 答:解决思路都是大同小异的,与Client一样,我们需要把原本在 “后端处理的授权重定向逻辑” 拿到前端来实现。
|
||||||
|
|
||||||
|
由于集成代码与 Client 端类似,这里暂不贴详细代码,我们可以下载官方仓库,里面有搭建好的demo
|
||||||
|
|
||||||
|
使用前端ide导入项目 `/sa-token-demo/sa-token-demo-sso2-h5`,浏览器访问 `sso-auth.html` 页面:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
复制上述地址,将其配置到 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 端的授权登录。
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
然而,要真正开发一个商业级项目的认证中心系统,绝非一朝一夕可以搭建完毕,其必不可少的一些功能,
|
然而,要真正开发一个商业级项目的认证中心系统,绝非一朝一夕可以搭建完毕,其必不可少的一些功能,
|
||||||
比如:用户账号增删改查维护、登录日志统计、新增用户数据报表、新增 Client 应用接入域名配置……等等,
|
比如:用户账号增删改查维护、登录日志统计、新增用户数据报表、新增 Client 应用接入域名配置……等等,
|
||||||
仍需要我们大量的开发时间。
|
仍需要大量的开发时间。
|
||||||
|
|
||||||
为此,我们特意准备了项目:[[Sa-Sso-Pro 单点登录商业版]](http://sa-pro.dev33.cn/index.html?hmsr=sa-token),
|
为此,我们特意准备了项目:[[Sa-Sso-Pro 单点登录商业版]](http://sa-pro.dev33.cn/index.html?hmsr=sa-token),
|
||||||
项目集成了单点登录常见技术点, 绝大多数功能无需二次开发,直接可用,<b style="color: #FF5722;">可大大缩短您的项目接入单点登录的开发周期</b>。
|
项目集成了单点登录常见技术点, 绝大多数功能无需二次开发,直接可用,<b style="color: #FF5722;">可大大缩短您的项目接入单点登录的开发周期</b>。
|
||||||
|
Loading…
Reference in New Issue
Block a user