mirror of
https://gitee.com/dromara/sa-token.git
synced 2026-02-27 16:50:24 +08:00
前后端分离模式下接入SSO的示例
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Sa-Token-SSO-Client端-测试页(前后端分离版)</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Sa-Token SSO-Client 应用端(前后端分离版)</h2>
|
||||
<p>当前是否登录:<b class="is-login"></b></p>
|
||||
<p>
|
||||
<a href="javascript:location.href='sso-login.html?back=' + encodeURIComponent(location.href);">登录</a>
|
||||
<a href="javascript:location.href=baseUrl + '/sso/logout?satoken=' + localStorage.satoken + '&back=' + encodeURIComponent(location.href);">注销</a>
|
||||
</p>
|
||||
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
// 后端接口地址
|
||||
var baseUrl = "http://sa-sso-client1.com:9001";
|
||||
|
||||
// 查询当前会话是否登录
|
||||
$.ajax({
|
||||
url: baseUrl + '/isLogin',
|
||||
type: "post",
|
||||
dataType: 'json',
|
||||
headers: {
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
"satoken": localStorage.getItem("satoken")
|
||||
},
|
||||
success: function(res){
|
||||
$('.is-login').html(res.data + '');
|
||||
},
|
||||
error: function(xhr, type, errorThrown){
|
||||
return alert("异常:" + JSON.stringify(xhr));
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,91 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Sa-Token-SSO-Client端-登录页</title>
|
||||
<style type="text/css">
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-box">
|
||||
|
||||
</div>
|
||||
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
|
||||
<script>window.jQuery || alert('当前页面CDN服务商已宕机,请将所有js包更换为本地依赖')</script>
|
||||
<script type="text/javascript">
|
||||
|
||||
// 后端接口地址
|
||||
var baseUrl = "http://sa-sso-client1.com:9001";
|
||||
|
||||
var back = getParam('back', '/');
|
||||
var ticket = getParam('ticket');
|
||||
$(function() {
|
||||
if(ticket) {
|
||||
doLoginByTicket(ticket);
|
||||
} else {
|
||||
goSsoAuthUrl();
|
||||
}
|
||||
})
|
||||
|
||||
// 重定向至认证中心
|
||||
function goSsoAuthUrl() {
|
||||
sa.ajax('/getSsoAuthUrl', {clientLoginUrl: location.href}, function(res) {
|
||||
console.log(res);
|
||||
location.href = res.data;
|
||||
})
|
||||
}
|
||||
|
||||
// 根据ticket值登录
|
||||
function doLoginByTicket(ticket) {
|
||||
sa.ajax('/doLoginByTicket', {ticket: ticket}, function(res) {
|
||||
console.log(res);
|
||||
if(res.code == 200) {
|
||||
localStorage.setItem('satoken', res.data);
|
||||
location.href = decodeURIComponent(back);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 从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];}
|
||||
}
|
||||
return(defaultValue == undefined ? null : defaultValue);
|
||||
}
|
||||
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
var sa = {};
|
||||
|
||||
// 封装一下Ajax
|
||||
sa.ajax = function(url, data, successFn) {
|
||||
// sa.loading("正在努力加载...");
|
||||
$.ajax({
|
||||
url: baseUrl + url,
|
||||
type: "post",
|
||||
data: data,
|
||||
dataType: 'json',
|
||||
headers: {
|
||||
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));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</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,50 @@
|
||||
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所需的代码
|
||||
* @author kong
|
||||
*
|
||||
*/
|
||||
@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 = SaSsoUtil.checkTicket(ticket);
|
||||
if(loginId != null) {
|
||||
StpUtil.login(loginId);
|
||||
return SaResult.data(StpUtil.getTokenValue());
|
||||
}
|
||||
return SaResult.error("无效ticket:" + ticket);
|
||||
}
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,7 +9,8 @@ sa-token:
|
||||
# Ticket有效期 (单位: 秒),默认五分钟
|
||||
ticket-timeout: 300
|
||||
# 所有允许的授权回调地址
|
||||
allow-url: http://sa-sso-client1.com:9001/sso/login, http://sa-sso-client2.com:9001/sso/login, http://sa-sso-client3.com:9001/sso/login
|
||||
# allow-url: http://sa-sso-client1.com:9001/sso/login, http://sa-sso-client2.com:9001/sso/login, http://sa-sso-client3.com:9001/sso/login
|
||||
allow-url: "*"
|
||||
|
||||
spring:
|
||||
# Redis配置
|
||||
|
||||
Reference in New Issue
Block a user