mirror of
https://gitee.com/dromara/sa-token.git
synced 2025-08-20 00:44:30 +08:00
feat(sso): 调整 SSO 示例适配最新版 & 新增 sso-resdk 示例 & 新增 sso-anon 示例
This commit is contained in:
parent
213d98d848
commit
2ecd52b3be
@ -149,8 +149,10 @@ public class SaResult extends LinkedHashMap<String, Object> implements Serializa
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaResult setMap(Map<String, ?> map) {
|
||||
for (String key : map.keySet()) {
|
||||
this.put(key, map.get(key));
|
||||
if(map != null) {
|
||||
for (String key : map.keySet()) {
|
||||
this.put(key, map.get(key));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
@ -44,8 +44,9 @@
|
||||
<module>sa-token-demo-sso/sa-token-demo-sso1-client</module>
|
||||
<module>sa-token-demo-sso/sa-token-demo-sso2-client</module>
|
||||
<module>sa-token-demo-sso/sa-token-demo-sso3-client</module>
|
||||
<module>sa-token-demo-sso/sa-token-demo-sso3-client-test2</module>
|
||||
<module>sa-token-demo-sso/sa-token-demo-sso3-client-nosdk</module>
|
||||
<module>sa-token-demo-sso/sa-token-demo-sso3-client-resdk</module>
|
||||
<module>sa-token-demo-sso/sa-token-demo-sso3-client-anon</module>
|
||||
<module>sa-token-demo-sso-for-solon/sa-token-demo-sso-server-solon</module>
|
||||
<module>sa-token-demo-sso-for-solon/sa-token-demo-sso1-client-solon</module>
|
||||
<module>sa-token-demo-sso-for-solon/sa-token-demo-sso2-client-solon</module>
|
||||
|
@ -8,22 +8,59 @@
|
||||
<h2>Sa-Token SSO-Client 应用端(前后端分离版-原生h5)</h2>
|
||||
<p>当前是否登录:<b class="is-login"></b></p>
|
||||
<p>
|
||||
<a class="login-btn">登录</a>
|
||||
<a class="logout-btn">注销</a>
|
||||
<a href="javascript: login();">登录</a> -
|
||||
<a href="javascript: doLogoutByAlone();">单应用注销</a> -
|
||||
<a href="javascript: doLogoutBySingleDeviceId();">单浏览器注销</a> -
|
||||
<a href="javascript: doLogout();">全端注销</a> -
|
||||
<a href="javascript: doMyInfo();">账号资料</a>
|
||||
</p>
|
||||
<script src="common.js"></script>
|
||||
<script src="sso-common.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
document.querySelector('.login-btn').href = 'sso-login.html?back=' + encodeURIComponent(location.href);
|
||||
document.querySelector('.logout-btn').href = baseUrl + '/sso/logout?satoken=' + localStorage.satoken + '&back=' + encodeURIComponent(location.href);
|
||||
// 登录
|
||||
function login() {
|
||||
location.href = 'sso-login.html?back=' + encodeURIComponent(location.href);
|
||||
}
|
||||
|
||||
ajax('/sso/isLogin', {}, function(res){
|
||||
if(res.data) {
|
||||
setHtml('.is-login', res.data + ' (' + res.loginId + ')');
|
||||
} else {
|
||||
setHtml('.is-login', res.data);
|
||||
}
|
||||
})
|
||||
// 单应用注销
|
||||
function doLogoutByAlone() {
|
||||
ajax('/sso/logoutByAlone', {}, function(res){
|
||||
doIsLogin();
|
||||
})
|
||||
}
|
||||
|
||||
// 单浏览器注销
|
||||
function doLogoutBySingleDeviceId() {
|
||||
ajax('/sso/logout', { singleDeviceIdLogout: true }, function(res){
|
||||
doIsLogin();
|
||||
})
|
||||
}
|
||||
|
||||
// 全端注销
|
||||
function doLogout() {
|
||||
ajax('/sso/logout', { }, function(res){
|
||||
doIsLogin();
|
||||
})
|
||||
}
|
||||
|
||||
// 账号资料
|
||||
function doMyInfo() {
|
||||
ajax('/sso/myInfo', { }, function(res){
|
||||
alert(JSON.stringify(res));
|
||||
})
|
||||
}
|
||||
|
||||
// 判断是否登录
|
||||
function doIsLogin() {
|
||||
ajax('/sso/isLogin', {}, function(res){
|
||||
if(res.data) {
|
||||
setHtml('.is-login', res.data + ' (' + res.loginId + ')');
|
||||
} else {
|
||||
setHtml('.is-login', res.data);
|
||||
}
|
||||
})
|
||||
}
|
||||
doIsLogin();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
@ -1,5 +1,6 @@
|
||||
// 服务器接口主机地址
|
||||
var baseUrl = "http://sa-sso-client1.com:9003";
|
||||
// var baseUrl = "http://sa-sso-client1.com:9002"; // 模式二后端
|
||||
var baseUrl = "http://sa-sso-client1.com:9003"; // 模式三后端
|
||||
|
||||
// 封装一下Ajax
|
||||
function ajax(path, data, successFn, errorFn) {
|
@ -2,16 +2,16 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Sa-Token-SSO-Client端-登录页</title>
|
||||
<title>Sa-Token-SSO-Client端-登录中转页页</title>
|
||||
<style type="text/css">
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-box">
|
||||
|
||||
加载中 ...
|
||||
</div>
|
||||
<script src="common.js"></script>
|
||||
<script src="sso-common.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var back = getParam('back', '/');
|
||||
|
@ -1,10 +1,12 @@
|
||||
import axios from 'axios'
|
||||
|
||||
// sso-client 的后端服务地址
|
||||
export const baseUrl = "http://sa-sso-client1.com:9001";
|
||||
// export const baseUrl = "http://sa-sso-client1.com:9002"; // 模式二后端
|
||||
export const baseUrl = "http://sa-sso-client1.com:9003"; // 模式三后端
|
||||
|
||||
// 封装一下 Ajax 方法
|
||||
export const ajax = function(path, data, successFn) {
|
||||
console.log('发起请求:', baseUrl + path, JSON.stringify(data));
|
||||
axios({
|
||||
url: baseUrl + path,
|
||||
method: 'post',
|
||||
@ -16,9 +18,14 @@ export const ajax = function(path, data, successFn) {
|
||||
}).
|
||||
then(function (response) { // 成功时执行
|
||||
const res = response.data;
|
||||
console.log('返回数据:', res);
|
||||
if(res.code === 500) {
|
||||
return alert(res.msg);
|
||||
}
|
||||
successFn(res);
|
||||
}).
|
||||
catch(function (error) {
|
||||
console.error('请求失败:', error);
|
||||
return alert("异常:" + JSON.stringify(error));
|
||||
})
|
||||
}
|
@ -2,35 +2,76 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2> Sa-Token SSO-Client 应用端(前后端分离版-Vue2) </h2>
|
||||
<p>当前是否登录:<b>{{isLogin}}</b></p>
|
||||
<p>当前是否登录:<b>{{isLogin}} ({{ loginId }})</b></p>
|
||||
<p>
|
||||
<router-link :to="loginUrl">登录</router-link>
|
||||
<a :href="logoutUrl">注销</a>
|
||||
<a href="javascript: null;" @click="login()" >登录</a> -
|
||||
<a href="javascript: null;" @click="doLogoutByAlone()" >单应用注销</a> -
|
||||
<a href="javascript: null;" @click="doLogoutBySingleDeviceId();">单浏览器注销</a> -
|
||||
<a href="javascript: null;" @click="doLogout();">全端注销</a> -
|
||||
<a href="javascript: null;" @click="doMyInfo();">账号资料</a>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {baseUrl, ajax} from './method-util.js'
|
||||
import {ajax} from './sso-common.js'
|
||||
import router from "@/router";
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
data() {
|
||||
return {
|
||||
// 单点登录地址
|
||||
loginUrl: '/sso-login?back=' + encodeURIComponent(location.href),
|
||||
// 单点注销地址
|
||||
logoutUrl: baseUrl + '/sso/logout?satoken=' + localStorage.satoken + '&back=' + encodeURIComponent(location.href),
|
||||
// 是否登录
|
||||
isLogin: false
|
||||
isLogin: false,
|
||||
// 登录账号
|
||||
loginId: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
// 登录
|
||||
login: function() {
|
||||
router.push('/sso-login?back=' + encodeURIComponent(location.href));
|
||||
},
|
||||
|
||||
// 单应用注销
|
||||
doLogoutByAlone: function() {
|
||||
ajax('/sso/logoutByAlone', {}, function(){
|
||||
this.doIsLogin();
|
||||
}.bind(this))
|
||||
},
|
||||
|
||||
// 单浏览器注销
|
||||
doLogoutBySingleDeviceId: function() {
|
||||
ajax('/sso/logout', { singleDeviceIdLogout: true }, function(){
|
||||
this.doIsLogin();
|
||||
}.bind(this))
|
||||
},
|
||||
|
||||
// 全端注销
|
||||
doLogout: function () {
|
||||
ajax('/sso/logout', { }, function(){
|
||||
this.doIsLogin();
|
||||
}.bind(this))
|
||||
},
|
||||
|
||||
// 账号资料
|
||||
doMyInfo: function() {
|
||||
ajax('/sso/myInfo', { }, function(res){
|
||||
alert(JSON.stringify(res));
|
||||
})
|
||||
},
|
||||
|
||||
// 判断是否登录
|
||||
doIsLogin: function() {
|
||||
ajax('/sso/isLogin', {}, function(res){
|
||||
this.isLogin = res.data;
|
||||
this.loginId = res.loginId;
|
||||
}.bind(this))
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 查询当前会话是否登录
|
||||
ajax('/sso/isLogin', {}, function (res) {
|
||||
console.log('/isLogin 返回数据:', res);
|
||||
this.isLogin = res.data;
|
||||
}.bind(this))
|
||||
this.doIsLogin();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,10 +1,10 @@
|
||||
<!-- Sa-Token-SSO-Client端-登录页 -->
|
||||
<!-- Sa-Token-SSO-Client端-登录中转页 -->
|
||||
<template>
|
||||
<div></div>
|
||||
<div>加载中...</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {ajax, getParam} from './method-util.js';
|
||||
import {ajax, getParam} from './sso-common.js';
|
||||
import router from '../router';
|
||||
|
||||
|
||||
@ -31,14 +31,12 @@ export default {
|
||||
// 重定向至认证中心
|
||||
goSsoAuthUrl: function() {
|
||||
ajax('/sso/getSsoAuthUrl', {clientLoginUrl: location.href}, function(res) {
|
||||
console.log('/sso/getSsoAuthUrl 返回数据', res);
|
||||
location.href = res.data;
|
||||
})
|
||||
},
|
||||
// 根据ticket值登录
|
||||
doLoginByTicket: function(ticket) {
|
||||
ajax('/sso/doLoginByTicket', {ticket: ticket}, function(res) {
|
||||
console.log('/sso/doLoginByTicket 返回数据', res);
|
||||
if(res.code === 200) {
|
||||
localStorage.setItem('satoken', res.data);
|
||||
location.href = decodeURIComponent(this.back);
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { createRouter, createWebHashHistory } from 'vue-router';
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
|
||||
/**
|
||||
* 创建 vue-router 实例
|
||||
*/
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
history: createWebHistory(),
|
||||
routes: [
|
||||
// 首页
|
||||
{
|
||||
|
@ -1,10 +1,12 @@
|
||||
import axios from 'axios'
|
||||
|
||||
// sso-client 的后端服务地址
|
||||
export const baseUrl = "http://sa-sso-client1.com:9001";
|
||||
// export const baseUrl = "http://sa-sso-client1.com:9002"; // 模式二后端
|
||||
export const baseUrl = "http://sa-sso-client1.com:9003"; // 模式三后端
|
||||
|
||||
// 封装一下 Ajax 方法
|
||||
export const ajax = function(path, data, successFn) {
|
||||
console.log('发起请求:', baseUrl + path, JSON.stringify(data));
|
||||
axios({
|
||||
url: baseUrl + path,
|
||||
method: 'post',
|
||||
@ -16,9 +18,14 @@ export const ajax = function(path, data, successFn) {
|
||||
}).
|
||||
then(function (response) { // 成功时执行
|
||||
const res = response.data;
|
||||
console.log('返回数据:', res);
|
||||
if(res.code === 500) {
|
||||
return alert(res.msg);
|
||||
}
|
||||
successFn(res);
|
||||
}).
|
||||
catch(function (error) {
|
||||
console.error('请求失败:', error);
|
||||
return alert("异常:" + JSON.stringify(error));
|
||||
})
|
||||
}
|
@ -1,29 +1,70 @@
|
||||
<!-- 项目首页 -->
|
||||
<template>
|
||||
<h2> Sa-Token SSO-Client 应用端(前后端分离版-Vue3) </h2>
|
||||
<p>当前是否登录:<b>{{isLogin}}</b></p>
|
||||
<p>
|
||||
<router-link :to="loginUrl">登录</router-link>
|
||||
<a :href="logoutUrl">注销</a>
|
||||
</p>
|
||||
<div>
|
||||
<h2> Sa-Token SSO-Client 应用端(前后端分离版-Vue3) </h2>
|
||||
<p>当前是否登录:<b>{{ state.isLogin }} ({{ state.loginId }})</b></p>
|
||||
<p>
|
||||
<a href="javascript: null;" @click="login()" >登录</a> -
|
||||
<a href="javascript: null;" @click="doLogoutByAlone()" >单应用注销</a> -
|
||||
<a href="javascript: null;" @click="doLogoutBySingleDeviceId();">单浏览器注销</a> -
|
||||
<a href="javascript: null;" @click="doLogout();">全端注销</a> -
|
||||
<a href="javascript: null;" @click="doMyInfo();">账号资料</a>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import {baseUrl, ajax} from './method-util.js'
|
||||
import { reactive } from 'vue'
|
||||
import { ajax } from './sso-common.js'
|
||||
import router from "../router/index.js";
|
||||
|
||||
// 单点登录地址
|
||||
const loginUrl = '/sso-login?back=' + encodeURIComponent(location.href);
|
||||
// 单点注销地址
|
||||
const logoutUrl = baseUrl + '/sso/logout?satoken=' + localStorage.satoken + '&back=' + encodeURIComponent(location.href);
|
||||
|
||||
// 是否登录
|
||||
const isLogin = ref(false);
|
||||
|
||||
// 查询当前会话是否登录
|
||||
ajax('/sso/isLogin', {}, function (res) {
|
||||
console.log('/isLogin 返回数据:', res);
|
||||
isLogin.value = res.data;
|
||||
// 数据
|
||||
const state = reactive({
|
||||
isLogin: false,
|
||||
loginId: '',
|
||||
})
|
||||
|
||||
// 登录
|
||||
function login() {
|
||||
router.push('/sso-login?back=' + encodeURIComponent(location.href));
|
||||
}
|
||||
|
||||
// 单应用注销
|
||||
function doLogoutByAlone() {
|
||||
ajax('/sso/logoutByAlone', {}, function(res){
|
||||
doIsLogin();
|
||||
})
|
||||
}
|
||||
|
||||
// 单浏览器注销
|
||||
function doLogoutBySingleDeviceId() {
|
||||
ajax('/sso/logout', { singleDeviceIdLogout: true }, function(res){
|
||||
doIsLogin();
|
||||
})
|
||||
}
|
||||
|
||||
// 全端注销
|
||||
function doLogout() {
|
||||
ajax('/sso/logout', { }, function(res){
|
||||
doIsLogin();
|
||||
})
|
||||
}
|
||||
|
||||
// 账号资料
|
||||
function doMyInfo() {
|
||||
ajax('/sso/myInfo', { }, function(res){
|
||||
alert(JSON.stringify(res));
|
||||
})
|
||||
}
|
||||
|
||||
// 判断是否登录
|
||||
function doIsLogin() {
|
||||
ajax('/sso/isLogin', {}, function(res){
|
||||
state.isLogin = res.data;
|
||||
state.loginId = res.loginId;
|
||||
})
|
||||
}
|
||||
doIsLogin();
|
||||
|
||||
|
||||
</script>
|
||||
|
@ -1,11 +1,11 @@
|
||||
<!-- Sa-Token-SSO-Client端-登录页 -->
|
||||
<template>
|
||||
|
||||
<div>加载中...</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onMounted} from "vue";
|
||||
import {ajax, getParam} from './method-util.js';
|
||||
import {ajax, getParam} from './sso-common.js';
|
||||
import router from '../router';
|
||||
|
||||
// 获取参数
|
||||
@ -27,7 +27,6 @@ onMounted(() => {
|
||||
// 重定向至认证中心
|
||||
function goSsoAuthUrl() {
|
||||
ajax('/sso/getSsoAuthUrl', {clientLoginUrl: location.href}, function(res) {
|
||||
console.log('/sso/getSsoAuthUrl 返回数据', res);
|
||||
location.href = res.data;
|
||||
})
|
||||
}
|
||||
@ -35,7 +34,6 @@ function goSsoAuthUrl() {
|
||||
// 根据ticket值登录
|
||||
function doLoginByTicket(ticket) {
|
||||
ajax('/sso/doLoginByTicket', {ticket: ticket}, function(res) {
|
||||
console.log('/sso/doLoginByTicket 返回数据', res);
|
||||
if(res.code === 200) {
|
||||
localStorage.setItem('satoken', res.data);
|
||||
location.href = decodeURIComponent(back);
|
||||
|
@ -14,7 +14,7 @@ public class SaSsoServerApplication {
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.pj.home;
|
||||
package com.pj.sso;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
@ -1,7 +1,6 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.sign.SaSignUtil;
|
||||
import cn.dev33.satoken.sso.processor.SaSsoServerProcessor;
|
||||
import cn.dev33.satoken.sso.template.SaSsoServerTemplate;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
@ -23,10 +22,9 @@ 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、sign=参数签名
|
||||
* http://{host}:{port}/sso/auth -- 单点登录授权地址
|
||||
* http://{host}:{port}/sso/doLogin -- 账号密码登录接口,接受参数:name、pwd
|
||||
* http://{host}:{port}/sso/signout -- 单点注销地址(isSlo=true时打开)
|
||||
*/
|
||||
@RequestMapping("/sso/*")
|
||||
public Object ssoRequest() {
|
||||
@ -53,24 +51,19 @@ public class SsoServerController {
|
||||
return SaResult.error("登录失败!");
|
||||
};
|
||||
|
||||
}
|
||||
// 添加消息处理器:userinfo (获取用户资料) (用于在模式三下,为 client 端开放拉取数据的接口)
|
||||
ssoServerTemplate.messageHolder.addHandle("userinfo", (ssoTemplate, message) -> {
|
||||
System.out.println("收到消息:" + message);
|
||||
System.out.println("loginId=" + message.get("loginId"));
|
||||
|
||||
// 示例:获取数据接口(用于在模式三下,为 client 端开放拉取数据的接口)
|
||||
@RequestMapping("/sso/getData")
|
||||
public SaResult getData(String apiType, String loginId) {
|
||||
System.out.println("---------------- 获取数据 ----------------");
|
||||
System.out.println("apiType=" + apiType);
|
||||
System.out.println("loginId=" + loginId);
|
||||
// 自定义返回结果(模拟)
|
||||
return SaResult.ok()
|
||||
.set("id", message.get("loginId"))
|
||||
.set("name", "LinXiaoYu")
|
||||
.set("sex", "女")
|
||||
.set("age", 18);
|
||||
});
|
||||
|
||||
// 校验签名:只有拥有正确秘钥发起的请求才能通过校验
|
||||
SaSignUtil.checkRequest(SaHolder.getRequest());
|
||||
|
||||
// 自定义返回结果(模拟)
|
||||
return SaResult.ok()
|
||||
.set("id", loginId)
|
||||
.set("name", "LinXiaoYu")
|
||||
.set("sex", "女")
|
||||
.set("age", 18);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,43 +7,57 @@ sa-token:
|
||||
# 打印操作日志
|
||||
is-log: true
|
||||
|
||||
# ------- SSO-模式一相关配置 (非模式一不需要配置)
|
||||
# cookie:
|
||||
# 配置 Cookie 作用域
|
||||
# domain: stp.com
|
||||
# ------- SSO 模式一配置 (非模式一不需要配置)
|
||||
# cookie:
|
||||
# # 配置 Cookie 作用域
|
||||
# domain: stp.com
|
||||
|
||||
# ------- SSO-模式二相关配置
|
||||
# SSO-Server 配置
|
||||
sso-server:
|
||||
# Ticket有效期 (单位: 秒),默认五分钟
|
||||
ticket-timeout: 300
|
||||
# 所有允许的授权回调地址
|
||||
# 主页路由,在不指定 redirect 参数时,默认跳转的地址
|
||||
home-route: /home
|
||||
# 是否启用匿名 client (开启匿名 client 后,允许客户端接入时不提交 client 参数)
|
||||
allow-anon-client: true
|
||||
# 所有允许的授权回调地址 (匿名 client 使用)
|
||||
allow-url: "*"
|
||||
# API 接口调用秘钥 (默认)
|
||||
# API 接口调用秘钥 (全局默认 + 匿名 client 使用)
|
||||
secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
# 是否启用匿名 client
|
||||
allow-anon-client: false
|
||||
|
||||
# ------- SSO-模式三相关配置 (下面的配置在使用SSO模式三时打开)
|
||||
# 是否打开模式三
|
||||
is-http: true
|
||||
|
||||
# 应用列表:配置接入的应用信息
|
||||
clients:
|
||||
# 应用 sso-client2,采用模式二对接 (跨域、同Redis)
|
||||
# 应用 sso-client2:采用模式二对接 (跨域、同Redis)
|
||||
sso-client2:
|
||||
client: sso-client2
|
||||
allow-url: "*"
|
||||
# 应用 sso-client3,采用模式三对接 (跨域、跨Redis)
|
||||
secret-key: SSO-C2-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
# 应用 sso-client3:采用模式三对接 (跨域、跨Redis)
|
||||
sso-client3:
|
||||
# 应用名称
|
||||
client: sso-client3
|
||||
# 允许授权地址
|
||||
allow-url: "*"
|
||||
secret-key: SSO-C3-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
# 是否接收消息推送
|
||||
is-push: true
|
||||
# 消息推送地址
|
||||
push-url: http://sa-sso-client1.com:9003/sso/pushC
|
||||
|
||||
|
||||
# ---- 除了以上配置项,你还需要为 Sa-Token 配置http请求处理器(文档有步骤说明)
|
||||
# 接口调用秘钥 (如果不配置则使用全局默认秘钥)
|
||||
secret-key: SSO-C3-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
# 应用 sso-client3-resdk:采用 ReSdk 模式对接
|
||||
sso-client3-resdk:
|
||||
# 应用名称
|
||||
client: sso-client3-resdk
|
||||
# 允许授权地址
|
||||
allow-url: "*"
|
||||
# 是否接收消息推送
|
||||
is-push: true
|
||||
# 消息推送地址
|
||||
push-url: http://sa-sso-client1.com:9005/sso/pushC
|
||||
# 接口调用秘钥 (如果不配置则使用全局默认秘钥)
|
||||
secret-key: SSO-C3-ReSdk-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
|
||||
spring:
|
||||
# Redis配置 (SSO模式一和模式二使用Redis来同步会话)
|
||||
# Redis配置 (SSO模式一和模式二使用 Redis 来同步会话)
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
database: 1
|
||||
|
@ -21,7 +21,7 @@ public class SaSso1ClientApplication {
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -18,11 +18,11 @@ public class SsoClientController {
|
||||
@RequestMapping("/")
|
||||
public String index() {
|
||||
String authUrl = SaSsoManager.getClientConfig().splicingAuthUrl();
|
||||
String solUrl = SaSsoManager.getClientConfig().splicingSignoutUrl();
|
||||
String str = "<h2>Sa-Token SSO-Client 应用端</h2>" +
|
||||
"<p>当前会话是否登录:" + StpUtil.isLogin() + "</p>" +
|
||||
String signoutUrl = SaSsoManager.getClientConfig().splicingSignoutUrl();
|
||||
String str = "<h2>Sa-Token SSO-Client 应用端 (模式一)</h2>" +
|
||||
"<p>当前会话是否登录:" + StpUtil.isLogin() + " (" + StpUtil.getLoginId("") + ")</p>" +
|
||||
"<p><a href=\"javascript:location.href='" + authUrl + "?mode=simple&redirect=' + encodeURIComponent(location.href);\">登录</a> " +
|
||||
"<a href=\"javascript:location.href='" + solUrl + "?back=' + encodeURIComponent(location.href);\">注销</a> </p>";
|
||||
"<a href=\"javascript:location.href='" + signoutUrl + "?back=' + encodeURIComponent(location.href);\">注销</a> </p>";
|
||||
return str;
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ sa-token:
|
||||
# SSO-Server端主机地址
|
||||
server-url: http://sso.stp.com:9000
|
||||
|
||||
# 配置 Sa-Token 单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis)
|
||||
# 配置 Sa-Token 单独使用的Redis连接 (需要引入 sa-token-alone-redis 依赖) (此处需要和 SSO-Server 端连接同一个Redis)
|
||||
alone-redis:
|
||||
# Redis数据库索引
|
||||
database: 1
|
||||
|
@ -61,11 +61,11 @@
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Http请求工具 -->
|
||||
<!-- Sa-Token 插件:整合 Forest 请求工具 -->
|
||||
<dependency>
|
||||
<groupId>com.dtflys.forest</groupId>
|
||||
<artifactId>forest-spring-boot-starter</artifactId>
|
||||
<version>1.5.26</version>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-forest</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
@ -16,7 +16,7 @@ public class SaSso2ClientApplication {
|
||||
System.out.println("测试访问应用端一: http://sa-sso-client1.com:9002");
|
||||
System.out.println("测试访问应用端二: http://sa-sso-client2.com:9002");
|
||||
System.out.println("测试访问应用端三: http://sa-sso-client3.com:9002");
|
||||
System.out.println("测试前需要根据官网文档修改hosts文件,测试账号密码:sa / 123456");
|
||||
System.out.println("测试前需要根据官网文档修改 hosts 文件,测试账号密码:sa / 123456");
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
|
@ -2,11 +2,10 @@ 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.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@ -28,14 +27,14 @@ public class H5Controller {
|
||||
// 返回SSO认证中心登录地址
|
||||
@RequestMapping("/sso/getSsoAuthUrl")
|
||||
public SaResult getSsoAuthUrl(String clientLoginUrl) {
|
||||
String serverAuthUrl = SaSsoUtil.buildServerAuthUrl(clientLoginUrl, "");
|
||||
String serverAuthUrl = SaSsoClientUtil.buildServerAuthUrl(clientLoginUrl, "");
|
||||
return SaResult.data(serverAuthUrl);
|
||||
}
|
||||
|
||||
// 根据ticket进行登录
|
||||
@RequestMapping("/sso/doLoginByTicket")
|
||||
public SaResult doLoginByTicket(String ticket) {
|
||||
SaCheckTicketResult ctr = SaSsoClientProcessor.instance.checkTicket(ticket, "/sso/doLoginByTicket");
|
||||
SaCheckTicketResult ctr = SaSsoClientProcessor.instance.checkTicket(ticket);
|
||||
StpUtil.login(ctr.loginId, new SaLoginParameter()
|
||||
.setTimeout(ctr.remainTokenTimeout)
|
||||
.setDeviceId(ctr.deviceId)
|
||||
@ -43,11 +42,4 @@ public class H5Controller {
|
||||
return SaResult.data(StpUtil.getTokenValue());
|
||||
}
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* [Sa-Token 权限认证] 配置类 (解决跨域问题)
|
||||
* [Sa-Token 权限认证] 配置类
|
||||
*
|
||||
* @author click33
|
||||
*/
|
||||
|
@ -0,0 +1,22 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
* @author click33
|
||||
*
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
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.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@ -20,19 +21,23 @@ public class SsoClientController {
|
||||
// 首页
|
||||
@RequestMapping("/")
|
||||
public String index() {
|
||||
String solUrl = SaSsoManager.getClientConfig().splicingSignoutUrl();
|
||||
String str = "<h2>Sa-Token SSO-Client 应用端</h2>" +
|
||||
"<p>当前会话是否登录:" + StpUtil.isLogin() + "</p>" +
|
||||
"<p><a href=\"javascript:location.href='/sso/login?back=' + encodeURIComponent(location.href);\">登录</a> " +
|
||||
"<a href=\"javascript:location.href='" + solUrl + "?back=' + encodeURIComponent(location.href);\">注销</a> </p>";
|
||||
String str = "<h2>Sa-Token SSO-Client 应用端 (模式二)</h2>" +
|
||||
"<p>当前会话是否登录:" + StpUtil.isLogin() + " (" + StpUtil.getLoginId("") + ")</p>" +
|
||||
"<p> " +
|
||||
"<a href='/sso/login?back=/'>登录</a> - " +
|
||||
"<a href='/sso/logoutByAlone?back=/'>单应用注销</a> - " +
|
||||
"<a href='/sso/logout?back=self&singleDeviceIdLogout=true'>单浏览器注销</a> - " +
|
||||
"<a href='/sso/logout?back=self'>全端注销</a> - " +
|
||||
"<a href='/sso/myInfo' target='_blank'>账号资料</a>" +
|
||||
"</p>";
|
||||
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时打开),此接口为框架回调,开发者无需关心
|
||||
* http://{host}:{port}/sso/login -- Client 端登录地址
|
||||
* http://{host}:{port}/sso/logout -- Client 端注销地址(isSlo=true时打开)
|
||||
* http://{host}:{port}/sso/pushC -- Client 端接收消息推送地址
|
||||
*/
|
||||
@RequestMapping("/sso/*")
|
||||
public Object ssoRequest() {
|
||||
@ -45,11 +50,32 @@ public class SsoClientController {
|
||||
|
||||
}
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
// 当前应用独自注销 (不退出其它应用)
|
||||
@RequestMapping("/sso/logoutByAlone")
|
||||
public Object logoutByAlone() {
|
||||
StpUtil.logout();
|
||||
return SaSsoClientProcessor.instance._ssoLogoutBack(SaHolder.getRequest(), SaHolder.getResponse());
|
||||
}
|
||||
|
||||
|
||||
// 查询我的账号信息:sso-client 前端 -> sso-center 后端 -> sso-server 后端
|
||||
@RequestMapping("/sso/myInfo")
|
||||
public Object myInfo() {
|
||||
// 如果尚未登录
|
||||
if( ! StpUtil.isLogin()) {
|
||||
return "尚未登录,无法获取";
|
||||
}
|
||||
|
||||
// 获取本地 loginId
|
||||
Object loginId = StpUtil.getLoginId();
|
||||
|
||||
// 推送消息
|
||||
SaSsoMessage message = new SaSsoMessage();
|
||||
message.setType("userinfo");
|
||||
message.set("loginId", loginId);
|
||||
SaResult result = SaSsoClientUtil.pushMessageAsSaResult(message);
|
||||
|
||||
// 返回给前端
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,18 +6,17 @@ server:
|
||||
sa-token:
|
||||
# SSO-相关配置
|
||||
sso-client:
|
||||
# 应用标识
|
||||
client: sso-client2
|
||||
# SSO-Server 端主机地址
|
||||
server-url: http://sa-sso-server.com:9000
|
||||
# 在 sso-server 端前后端分离时打开这个(上面的不要注释,auth-url 配置项和 server-url 要同时存在)
|
||||
# 在 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
|
||||
|
||||
client: sso-client2
|
||||
|
||||
sign:
|
||||
# API 接口调用秘钥
|
||||
# API 接口调用秘钥 (单点注销时会用到)
|
||||
secret-key: SSO-C2-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
|
||||
# 配置Sa-Token单独使用的Redis连接 (此处需要和SSO-Server端连接同一个Redis)
|
||||
# 配置 Sa-Token 单独使用的Redis连接(此处需要和 SSO-Server 端连接同一个 Redis)
|
||||
# 注:使用 alone-redis 需要在 pom.xml 引入 sa-token-alone-redis 依赖
|
||||
alone-redis:
|
||||
# Redis数据库索引
|
||||
database: 1
|
||||
|
@ -3,7 +3,7 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-demo-sso3-client-test2</artifactId>
|
||||
<artifactId>sa-token-demo-sso3-client-anon</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
@ -53,15 +53,15 @@
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Http请求工具 -->
|
||||
|
||||
<!-- Sa-Token 插件:整合 Forest 请求工具 -->
|
||||
<dependency>
|
||||
<groupId>com.dtflys.forest</groupId>
|
||||
<artifactId>forest-spring-boot-starter</artifactId>
|
||||
<version>1.5.26</version>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-forest</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
@ -0,0 +1,23 @@
|
||||
package com.pj;
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SaSso3ClientAnonApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaSso3ClientAnonApplication.class, args);
|
||||
|
||||
System.out.println();
|
||||
System.out.println("---------------------- Sa-Token SSO 模式三 (匿名应用) Client 端启动成功 ----------------------");
|
||||
System.out.println("配置信息:" + SaSsoManager.getClientConfig());
|
||||
System.out.println("测试访问应用端一: http://sa-sso-client1.com:9006");
|
||||
System.out.println("测试访问应用端二: http://sa-sso-client2.com:9006");
|
||||
System.out.println("测试访问应用端三: http://sa-sso-client3.com:9006");
|
||||
System.out.println("测试前需要根据官网文档修改 hosts 文件,测试账号密码:sa / 123456");
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
* @author click33
|
||||
*
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
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.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Sa-Token-SSO Client端 Controller
|
||||
* @author click33
|
||||
*/
|
||||
@RestController
|
||||
public class SsoClientController {
|
||||
|
||||
// 首页
|
||||
@RequestMapping("/")
|
||||
public String index() {
|
||||
String str = "<h2>Sa-Token SSO-Client 应用端 (模式三-匿名应用)</h2>" +
|
||||
"<p>当前会话是否登录:" + StpUtil.isLogin() + " (" + StpUtil.getLoginId("") + ")</p>" +
|
||||
"<p> " +
|
||||
"<a href='/sso/login?back=/'>登录</a> - " +
|
||||
"<a href='/sso/logoutByAlone?back=/'>单应用注销</a> - " +
|
||||
"<a href='/sso/logout?back=self&singleDeviceIdLogout=true'>单浏览器注销</a> - " +
|
||||
"<a href='/sso/logout?back=self'>全端注销</a> - " +
|
||||
"<a href='/sso/myInfo' target='_blank'>账号资料</a>" +
|
||||
"</p>";
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* SSO-Client端:处理所有SSO相关请求
|
||||
* http://{host}:{port}/sso/login -- Client 端登录地址
|
||||
* http://{host}:{port}/sso/logout -- Client 端注销地址(isSlo=true时打开)
|
||||
* http://{host}:{port}/sso/pushC -- Client 端接收消息推送地址
|
||||
*/
|
||||
@RequestMapping("/sso/*")
|
||||
public Object ssoRequest() {
|
||||
return SaSsoClientProcessor.instance.dister();
|
||||
}
|
||||
|
||||
// 配置SSO相关参数
|
||||
@Autowired
|
||||
private void configSso(SaSsoClientTemplate ssoClientTemplate) {
|
||||
// // 将 centerId 转换为 loginId 的函数
|
||||
// ssoClientTemplate.strategy.convertCenterIdToLoginId = (centerId) -> {
|
||||
// return "Stu" + centerId;
|
||||
// };
|
||||
// // 将 loginId 转换为 centerId 的函数
|
||||
// ssoClientTemplate.strategy.convertLoginIdToCenterId = (loginId) -> {
|
||||
// return loginId.toString().substring(3);
|
||||
// };
|
||||
}
|
||||
|
||||
// 当前应用独自注销 (不退出其它应用)
|
||||
@RequestMapping("/sso/logoutByAlone")
|
||||
public Object logoutByAlone() {
|
||||
StpUtil.logout();
|
||||
return SaSsoClientProcessor.instance._ssoLogoutBack(SaHolder.getRequest(), SaHolder.getResponse());
|
||||
}
|
||||
|
||||
// 查询我的账号信息:sso-client 前端 -> sso-center 后端 -> sso-server 后端
|
||||
@RequestMapping("/sso/myInfo")
|
||||
public Object myInfo() {
|
||||
// 如果尚未登录
|
||||
if( ! StpUtil.isLogin()) {
|
||||
return "尚未登录,无法获取";
|
||||
}
|
||||
|
||||
// 获取本地 loginId 对应的认证中心 centerId
|
||||
Object centerId = SaSsoClientUtil.getSsoTemplate().strategy.convertLoginIdToCenterId.run(StpUtil.getLoginId());
|
||||
|
||||
// 推送消息
|
||||
SaSsoMessage message = new SaSsoMessage();
|
||||
message.setType("userinfo");
|
||||
message.set("loginId", centerId);
|
||||
SaResult result = SaSsoClientUtil.pushMessageAsSaResult(message);
|
||||
|
||||
// 返回给前端
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -1,19 +1,21 @@
|
||||
# 端口
|
||||
server:
|
||||
port: 9032
|
||||
port: 9006
|
||||
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# 配置一个不同的 token-name,以避免在和模式三 demo 一起测试时发生数据覆盖
|
||||
token-name: satoken-client-anon
|
||||
# sso-client 相关配置
|
||||
sso-client:
|
||||
# client 标识
|
||||
client: sso-client3-test2
|
||||
# client 标识 匿名应用就是指不配置 client 标识的应用
|
||||
# client: sso-client3
|
||||
# sso-server 端主机地址
|
||||
server-url: http://sa-sso-server.com:9000
|
||||
# 使用 Http 请求校验ticket (模式三)
|
||||
is-http: true
|
||||
|
||||
sign:
|
||||
# 是否在登录时注册单点登录回调接口 (匿名应用想要参与单点注销必须打开这个)
|
||||
reg-logout-call: true
|
||||
# API 接口调用秘钥
|
||||
secret-key: kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
||||
|
||||
@ -21,7 +23,7 @@ spring:
|
||||
# 配置 Redis 连接 (此处与SSO-Server端连接不同的Redis)
|
||||
redis:
|
||||
# Redis数据库索引
|
||||
database: 4
|
||||
database: 6
|
||||
# Redis服务器地址
|
||||
host: 127.0.0.1
|
||||
# Redis服务器连接端口
|
@ -8,15 +8,17 @@ public class SaSsoClientNoSdkApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaSsoClientNoSdkApplication.class, args);
|
||||
System.out.println("\nSa-Token SSO模式三 Client端 (无SDK版本) 启动成功");
|
||||
|
||||
System.out.println();
|
||||
System.out.println("---------------------- Sa-Token SSO 模式三 NoSdk 模式 demo 启动成功 ----------------------");
|
||||
System.out.println("---------------------- Sa-Token SSO 模式三 (NoSdk版) demo 启动成功 ----------------------");
|
||||
System.out.println("测试访问应用端一: http://sa-sso-client1.com:9004");
|
||||
System.out.println("测试访问应用端二: http://sa-sso-client2.com:9004");
|
||||
System.out.println("测试访问应用端三: http://sa-sso-client3.com:9004");
|
||||
System.out.println("测试前需要根据官网文档修改hosts文件,测试账号密码:sa / 123456");
|
||||
System.out.println();
|
||||
|
||||
System.err.println("自 v1.43.0 版本起,Sa-Token SSO 不再维护 NoSdk 示例,此项目仅做留档");
|
||||
System.err.println("如您需要非 Sa-Token 技术栈项目接入 SSO-Server 认证中心,请参考 ReSdk 版本示例");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-demo-sso3-client-resdk</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
|
||||
<!-- SpringBoot -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.5.14</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.42.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringBoot依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 权限认证, 在线文档:https://sa-token.cc/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 插件:整合SSO -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-sso</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 插件:整合 Forest 请求工具 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-forest</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
@ -1,23 +1,21 @@
|
||||
package com.pj;
|
||||
|
||||
import cn.dev33.satoken.sso.SaSsoManager;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SaSso3ClientTest2Application {
|
||||
public class SaSsoClientReSdkApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SaSso3ClientTest2Application.class, args);
|
||||
SpringApplication.run(SaSsoClientReSdkApplication.class, args);
|
||||
|
||||
System.out.println();
|
||||
System.out.println("---------------------- Sa-Token SSO 模式三 Client 端启动成功 ----------------------");
|
||||
System.out.println("配置信息:" + SaSsoManager.getClientConfig());
|
||||
System.out.println("测试访问应用端一: http://sa-sso-client1.com:9032");
|
||||
System.out.println("测试访问应用端二: http://sa-sso-client2.com:9032");
|
||||
System.out.println("测试访问应用端三: http://sa-sso-client3.com:9032");
|
||||
System.out.println("---------------------- Sa-Token SSO 模式三 (ReSdk版) demo 启动成功 ----------------------");
|
||||
System.out.println("测试访问应用端一: http://sa-sso-client1.com:9005");
|
||||
System.out.println("测试访问应用端二: http://sa-sso-client2.com:9005");
|
||||
System.out.println("测试访问应用端三: http://sa-sso-client3.com:9005");
|
||||
System.out.println("测试前需要根据官网文档修改hosts文件,测试账号密码:sa / 123456");
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package com.pj.resdk;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.servlet.http.HttpSessionEvent;
|
||||
import javax.servlet.http.HttpSessionListener;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 记录所有已创建的 HttpSession 对象
|
||||
*
|
||||
* <b> 此种方式有性能问题,仅做demo示例,真实项目中请更换为其它方案记录用户会话数据 </b>
|
||||
*
|
||||
* @author click33
|
||||
* @since 2022-4-30
|
||||
*/
|
||||
@Component
|
||||
public class MyHttpSessionHolder implements HttpSessionListener {
|
||||
|
||||
public static List<HttpSession> sessionList = new ArrayList<>();
|
||||
|
||||
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
|
||||
sessionList.add(httpSessionEvent.getSession());
|
||||
}
|
||||
|
||||
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
|
||||
HttpSession session = httpSessionEvent.getSession();
|
||||
sessionList.remove(session);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package com.pj.resdk;
|
||||
|
||||
import cn.dev33.satoken.spring.SpringMVCUtil;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.parameter.SaLogoutParameter;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 会话对象 - httpSession 版
|
||||
*
|
||||
* @author click33
|
||||
* @since 2025/5/6
|
||||
*/
|
||||
public class StpLogicForHttpSession extends StpLogic {
|
||||
|
||||
/**
|
||||
* 初始化 StpLogic, 并指定账号类型
|
||||
*
|
||||
* @param type /
|
||||
*
|
||||
*/
|
||||
public StpLogicForHttpSession(String type) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
// 判断当前会话是否已登录
|
||||
@Override
|
||||
public boolean isLogin() {
|
||||
|
||||
return SpringMVCUtil.getRequest().getSession().getAttribute("userId") != null;
|
||||
}
|
||||
|
||||
// 获取当前会话的登录ID
|
||||
@Override
|
||||
public Object getLoginId() {
|
||||
Object userId = SpringMVCUtil.getRequest().getSession().getAttribute("userId");
|
||||
if(userId == null) {
|
||||
throw new RuntimeException("当前会话未登录");
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
// 获取当前登录设备 id
|
||||
@Override
|
||||
public String getLoginDeviceId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 当前会话注销
|
||||
@Override
|
||||
public void logout(SaLogoutParameter logoutParameter) {
|
||||
SpringMVCUtil.getRequest().getSession().removeAttribute("userId");
|
||||
}
|
||||
|
||||
// 当前账号id注销
|
||||
@Override
|
||||
public void _logout(Object loginId, SaLogoutParameter logoutParameter) {
|
||||
System.out.println("--- 注销账号id:" + loginId);
|
||||
for (HttpSession session: MyHttpSessionHolder.sessionList) {
|
||||
Object userId = session.getAttribute("userId");
|
||||
if(Objects.equals(String.valueOf(userId), String.valueOf(loginId))) {
|
||||
session.removeAttribute("userId");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
* @author click33
|
||||
*
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.spring.SpringMVCUtil;
|
||||
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 com.pj.resdk.StpLogicForHttpSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
/**
|
||||
* SSO Client端 Controller
|
||||
* @author click33
|
||||
*/
|
||||
@RestController
|
||||
public class SsoClientController {
|
||||
|
||||
// SSO-Client端:首页
|
||||
@RequestMapping("/")
|
||||
public String index(HttpSession session) {
|
||||
boolean isLogin = session.getAttribute("userId") != null;
|
||||
Object loginId = session.getAttribute("userId");
|
||||
String str = "<h2>Sa-Token SSO-Client 应用端 (模式三-ReSdk)</h2>" +
|
||||
"<p>当前会话是否登录:" + isLogin + " (" + loginId + ")</p>" +
|
||||
"<p> " +
|
||||
"<a href='/sso/login?back=/'>登录</a> - " +
|
||||
"<a href='/sso/logoutByAlone?back=/'>单应用注销</a> - " +
|
||||
"<a href='/sso/logout?back=self'>全端注销</a> - " +
|
||||
"<a href='/sso/myInfo' target='_blank'>账号资料</a>" +
|
||||
"</p>";
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* SSO-Client端:处理所有 SSO 相关请求
|
||||
* http://{host}:{port}/sso/login -- Client 端登录地址
|
||||
* http://{host}:{port}/sso/logout -- Client 端注销地址(isSlo=true时打开)
|
||||
* http://{host}:{port}/sso/pushC -- Client 端接收消息推送地址
|
||||
*/
|
||||
@RequestMapping("/sso/*")
|
||||
public Object ssoLogin() {
|
||||
return SaSsoClientProcessor.instance.dister();
|
||||
}
|
||||
|
||||
// 当前应用独自注销 (不退出其它应用)
|
||||
@RequestMapping("/sso/logoutByAlone")
|
||||
public Object logoutByAlone(HttpSession session) {
|
||||
session.removeAttribute("userId");
|
||||
return SaSsoClientProcessor.instance._ssoLogoutBack(SaHolder.getRequest(), SaHolder.getResponse());
|
||||
}
|
||||
|
||||
// 配置SSO相关参数
|
||||
@Autowired
|
||||
private void configSso(SaSsoClientTemplate ssoClientTemplate) {
|
||||
|
||||
// 自定义底层使用的会话操作对象
|
||||
ssoClientTemplate.setStpLogic(new StpLogicForHttpSession(StpUtil.TYPE));
|
||||
|
||||
// 自定义校验 ticket 返回值的处理逻辑 (每次从认证中心获取校验 ticket 的结果后调用)
|
||||
ssoClientTemplate.strategy.ticketResultHandle = (ctr, back) -> {
|
||||
HttpSession session = SpringMVCUtil.getRequest().getSession();
|
||||
session.setAttribute("userId", ctr.loginId);
|
||||
return SaHolder.getResponse().redirect(back);
|
||||
};
|
||||
}
|
||||
|
||||
// 查询我的账号信息:sso-client 前端 -> sso-center 后端 -> sso-server 后端
|
||||
@RequestMapping("/sso/myInfo")
|
||||
public Object myInfo(HttpSession session) {
|
||||
// 如果尚未登录
|
||||
if(session.getAttribute("userId") == null) {
|
||||
return "尚未登录,无法获取";
|
||||
}
|
||||
|
||||
// 推送消息
|
||||
SaSsoMessage message = new SaSsoMessage();
|
||||
message.setType("userinfo");
|
||||
message.set("loginId", session.getAttribute("userId"));
|
||||
SaResult result = SaSsoClientUtil.pushMessageAsSaResult(message);
|
||||
|
||||
// 返回给前端
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
# 端口
|
||||
server:
|
||||
port: 9005
|
||||
|
||||
# sa-token 配置
|
||||
sa-token:
|
||||
# 是否打印操作日志
|
||||
is-log: true
|
||||
# sso-client 相关配置
|
||||
sso-client:
|
||||
# client 标识
|
||||
client: sso-client3-resdk
|
||||
# sso-server 端主机地址
|
||||
server-url: http://sa-sso-server.com:9000
|
||||
# 使用 Http 请求校验ticket (模式三)
|
||||
is-http: true
|
||||
# API 接口调用秘钥
|
||||
secret-key: SSO-C3-ReSdk-kQwIOrYvnXmSDkwEiFngrKidMcdrgKor
|
@ -1,78 +0,0 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import cn.dev33.satoken.sso.config.SaSsoClientConfig;
|
||||
import cn.dev33.satoken.sso.processor.SaSsoClientProcessor;
|
||||
import cn.dev33.satoken.sso.template.SaSsoUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import com.dtflys.forest.Forest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Sa-Token-SSO Client端 Controller
|
||||
* @author click33
|
||||
*/
|
||||
@RestController
|
||||
public class SsoClientController {
|
||||
|
||||
// SSO-Client端:首页
|
||||
@RequestMapping("/")
|
||||
public String index() {
|
||||
String str = "<h2>Sa-Token SSO-Client 应用端</h2>" +
|
||||
"<p>当前会话是否登录:" + StpUtil.isLogin() + "</p>" +
|
||||
"<p><a href=\"javascript:location.href='/sso/login?back=' + encodeURIComponent(location.href);\">登录</a>" +
|
||||
" <a href='/sso/logout?back=self'>注销</a></p>";
|
||||
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时打开),此接口为框架回调,开发者无需关心
|
||||
*/
|
||||
@RequestMapping("/sso/*")
|
||||
public Object ssoRequest() {
|
||||
return SaSsoClientProcessor.instance.dister();
|
||||
}
|
||||
|
||||
// 配置SSO相关参数
|
||||
@Autowired
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
// 查询我的账号信息
|
||||
@RequestMapping("/sso/myInfo")
|
||||
public Object myInfo() {
|
||||
// 组织请求参数
|
||||
Map<String, Object> 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;
|
||||
}
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
@ -16,7 +16,7 @@ public class SaSso3ClientApplication {
|
||||
System.out.println("测试访问应用端一: http://sa-sso-client1.com:9003");
|
||||
System.out.println("测试访问应用端二: http://sa-sso-client2.com:9003");
|
||||
System.out.println("测试访问应用端三: http://sa-sso-client3.com:9003");
|
||||
System.out.println("测试前需要根据官网文档修改hosts文件,测试账号密码:sa / 123456");
|
||||
System.out.println("测试前需要根据官网文档修改 hosts 文件,测试账号密码:sa / 123456");
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
|
@ -2,19 +2,18 @@ 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.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 前后台分离架构下集成SSO所需的代码 (SSO-Client端)
|
||||
* <p>(注:如果不需要前后端分离架构下集成SSO,可删除此包下所有代码)</p>
|
||||
* @author click33
|
||||
*
|
||||
* @author click33
|
||||
*/
|
||||
@RestController
|
||||
public class H5Controller {
|
||||
@ -28,7 +27,7 @@ public class H5Controller {
|
||||
// 返回SSO认证中心登录地址
|
||||
@RequestMapping("/sso/getSsoAuthUrl")
|
||||
public SaResult getSsoAuthUrl(String clientLoginUrl) {
|
||||
String serverAuthUrl = SaSsoUtil.buildServerAuthUrl(clientLoginUrl, "");
|
||||
String serverAuthUrl = SaSsoClientUtil.buildServerAuthUrl(clientLoginUrl, "");
|
||||
return SaResult.data(serverAuthUrl);
|
||||
}
|
||||
|
||||
@ -43,11 +42,4 @@ public class H5Controller {
|
||||
return SaResult.data(StpUtil.getTokenValue());
|
||||
}
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* [Sa-Token 权限认证] 配置类 (解决跨域问题)
|
||||
* [Sa-Token 权限认证] 配置类
|
||||
*
|
||||
* @author click33
|
||||
*/
|
||||
|
@ -0,0 +1,22 @@
|
||||
package com.pj.sso;
|
||||
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
* @author click33
|
||||
*
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
@ -1,18 +1,16 @@
|
||||
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.SaSsoUtil;
|
||||
import cn.dev33.satoken.sso.template.SaSsoClientUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Sa-Token-SSO Client端 Controller
|
||||
* @author click33
|
||||
@ -23,18 +21,23 @@ public class SsoClientController {
|
||||
// SSO-Client端:首页
|
||||
@RequestMapping("/")
|
||||
public String index() {
|
||||
String str = "<h2>Sa-Token SSO-Client 应用端</h2>" +
|
||||
"<p>当前会话是否登录:" + StpUtil.isLogin() + " (" + StpUtil.getLoginId("") + ")</p>" +
|
||||
"<p><a href=\"javascript:location.href='/sso/login?back=' + encodeURIComponent(location.href);\">登录</a>" +
|
||||
" <a href='/sso/logout?back=self'>注销</a></p>";
|
||||
String str = "<h2>Sa-Token SSO-Client 应用端 (模式三)</h2>" +
|
||||
"<p>当前会话是否登录:" + StpUtil.isLogin() + " (" + StpUtil.getLoginId("") + ")</p>" +
|
||||
"<p> " +
|
||||
"<a href='/sso/login?back=/'>登录</a> - " +
|
||||
"<a href='/sso/logoutByAlone?back=/'>单应用注销</a> - " +
|
||||
"<a href='/sso/logout?back=self&singleDeviceIdLogout=true'>单浏览器注销</a> - " +
|
||||
"<a href='/sso/logout?back=self'>全端注销</a> - " +
|
||||
"<a href='/sso/myInfo' target='_blank'>账号资料</a>" +
|
||||
"</p>";
|
||||
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 端接收消息推送地址
|
||||
*/
|
||||
@RequestMapping("/sso/*")
|
||||
public Object ssoRequest() {
|
||||
@ -44,36 +47,35 @@ public class SsoClientController {
|
||||
// 配置SSO相关参数
|
||||
@Autowired
|
||||
private void configSso(SaSsoClientTemplate ssoClientTemplate) {
|
||||
// 将 centerId 转换为 loginId 的函数
|
||||
ssoClientTemplate.strategy.convertCenterIdToLoginId = (centerId) -> {
|
||||
return "Stu_" + centerId;
|
||||
};
|
||||
// 将 loginId 转换为 centerId 的函数
|
||||
ssoClientTemplate.strategy.convertLoginIdToCenterId = (loginId) -> {
|
||||
return loginId.toString().substring(4);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// 查询我的账号信息
|
||||
|
||||
// 当前应用独自注销 (不退出其它应用)
|
||||
@RequestMapping("/sso/logoutByAlone")
|
||||
public Object logoutByAlone() {
|
||||
StpUtil.logout();
|
||||
return SaSsoClientProcessor.instance._ssoLogoutBack(SaHolder.getRequest(), SaHolder.getResponse());
|
||||
}
|
||||
|
||||
// 查询我的账号信息:sso-client 前端 -> sso-center 后端 -> sso-server 后端
|
||||
@RequestMapping("/sso/myInfo")
|
||||
public Object myInfo() {
|
||||
// 组织请求参数
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("apiType", "userinfo");
|
||||
map.put("loginId", StpUtil.getLoginId());
|
||||
// 如果尚未登录
|
||||
if( ! StpUtil.isLogin()) {
|
||||
return "尚未登录,无法获取";
|
||||
}
|
||||
|
||||
// 发起请求
|
||||
Object resData = SaSsoUtil.getData(map);
|
||||
System.out.println("sso-server 返回的信息:" + resData);
|
||||
return resData;
|
||||
// 获取本地 loginId
|
||||
Object loginId = StpUtil.getLoginId();
|
||||
|
||||
// 推送消息
|
||||
SaSsoMessage message = new SaSsoMessage();
|
||||
message.setType("userinfo");
|
||||
message.set("loginId", loginId);
|
||||
SaResult result = SaSsoClientUtil.pushMessageAsSaResult(message);
|
||||
|
||||
// 返回给前端
|
||||
return result;
|
||||
}
|
||||
|
||||
// 全局异常拦截
|
||||
@ExceptionHandler
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,13 +13,15 @@ sa-token:
|
||||
client: sso-client3
|
||||
# sso-server 端主机地址
|
||||
server-url: http://sa-sso-server.com:9000
|
||||
# 使用 Http 请求校验ticket (模式三)
|
||||
# 在 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
|
||||
|
||||
spring:
|
||||
# 配置 Redis 连接 (此处与SSO-Server端连接不同的Redis)
|
||||
# 配置 Redis 连接 (此处与 SSO-Server 端连接不同的 Redis)
|
||||
redis:
|
||||
# Redis数据库索引
|
||||
database: 3
|
||||
|
@ -81,7 +81,7 @@ public class SaSsoClientConfig implements Serializable {
|
||||
public Boolean isHttp = false;
|
||||
|
||||
/**
|
||||
* 是否打开单点注销功能 (为 true 时,接收单点注销回调消息推送)
|
||||
* 是否打开单点注销功能 (为 true 时,开放 /sso/logout 接口,以及接收单点注销回调消息推送)
|
||||
*/
|
||||
public Boolean isSlo = true;
|
||||
|
||||
@ -153,14 +153,14 @@ public class SaSsoClientConfig implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否打开单点注销功能 (为 true 时,接收单点注销回调消息推送)
|
||||
* @return 是否打开单点注销功能 (为 true 时,开放 /sso/logout 接口,以及接收单点注销回调消息推送)
|
||||
*/
|
||||
public Boolean getIsSlo() {
|
||||
return isSlo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isSlo 是否打开单点注销功能 (为 true 时,接收单点注销回调消息推送)
|
||||
* @param isSlo 是否打开单点注销功能 (为 true 时,开放 /sso/logout 接口,以及接收单点注销回调消息推送)
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoClientConfig setIsSlo(Boolean isSlo) {
|
||||
|
@ -82,7 +82,7 @@ public class SaSsoServerConfig implements Serializable {
|
||||
/**
|
||||
* 是否允许匿名 Client 接入
|
||||
*/
|
||||
public Boolean allowAnonClient = true;
|
||||
public Boolean allowAnonClient = false;
|
||||
|
||||
/**
|
||||
* 所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket) (匿名 client 使用)
|
||||
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2020-2099 sa-token.cc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.sso.function;
|
||||
|
||||
import cn.dev33.satoken.sso.message.SaSsoMessage;
|
||||
import cn.dev33.satoken.sso.template.SaSsoTemplate;
|
||||
|
||||
/**
|
||||
* 函数式接口:处理 SSO 消息的函数式接口
|
||||
*
|
||||
* <p> 参数:ssoTemplate 模板对象, 要处理的 message 消息 </p>
|
||||
* <p> 返回:任意值 </p>
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.38.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SaSsoMessageHandleFunction {
|
||||
|
||||
Object execute(SaSsoTemplate ssoTemplate, SaSsoMessage message);
|
||||
|
||||
}
|
@ -27,6 +27,6 @@ import java.util.function.Function;
|
||||
* @since 1.38.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SendHttpFunction extends Function<String, String> {
|
||||
public interface SendRequestFunction extends Function<String, String> {
|
||||
|
||||
}
|
@ -18,7 +18,9 @@ package cn.dev33.satoken.sso.message;
|
||||
|
||||
import cn.dev33.satoken.sso.error.SaSsoErrorCode;
|
||||
import cn.dev33.satoken.sso.exception.SaSsoException;
|
||||
import cn.dev33.satoken.sso.function.SaSsoMessageHandleFunction;
|
||||
import cn.dev33.satoken.sso.message.handle.SaSsoMessageHandle;
|
||||
import cn.dev33.satoken.sso.message.handle.SaSsoMessageSimpleHandle;
|
||||
import cn.dev33.satoken.sso.template.SaSsoTemplate;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
@ -68,6 +70,18 @@ public class SaSsoMessageHolder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加指定类型的简单消息处理器
|
||||
*
|
||||
* @param type 要处理的消息类型
|
||||
* @param handle 要执行的方法
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaSsoMessageHolder addHandle(String type, SaSsoMessageHandleFunction handle) {
|
||||
messageHandleMap.put(type, new SaSsoMessageSimpleHandle(type, handle));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定类型的消息处理器
|
||||
*
|
||||
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2020-2099 sa-token.cc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.sso.message.handle;
|
||||
|
||||
|
||||
import cn.dev33.satoken.sso.function.SaSsoMessageHandleFunction;
|
||||
import cn.dev33.satoken.sso.message.SaSsoMessage;
|
||||
import cn.dev33.satoken.sso.template.SaSsoTemplate;
|
||||
|
||||
/**
|
||||
* SSO 消息处理器 - 简单实现,方便 lambda 表达式编程
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.43.0
|
||||
*/
|
||||
public class SaSsoMessageSimpleHandle implements SaSsoMessageHandle{
|
||||
|
||||
public String type;
|
||||
|
||||
public SaSsoMessageHandleFunction handle;
|
||||
|
||||
/**
|
||||
* SSO 消息处理器 - 简单实现,方便 lambda 表达式编程
|
||||
* @param type 要处理的消息类型
|
||||
* @param handle 要执行的方法
|
||||
*/
|
||||
public SaSsoMessageSimpleHandle(String type, SaSsoMessageHandleFunction handle) {
|
||||
this.type = type;
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所要处理的消息类型
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
@Override
|
||||
public String getHandlerType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 具体要执行的处理方法
|
||||
*
|
||||
* @param ssoTemplate /
|
||||
* @param message /
|
||||
* @return /
|
||||
*/
|
||||
@Override
|
||||
public Object handle(SaSsoTemplate ssoTemplate, SaSsoMessage message){
|
||||
return handle.execute(ssoTemplate, message);
|
||||
}
|
||||
|
||||
}
|
@ -16,8 +16,6 @@
|
||||
package cn.dev33.satoken.sso.message.handle.client;
|
||||
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.sso.message.SaSsoMessage;
|
||||
import cn.dev33.satoken.sso.message.handle.SaSsoMessageHandle;
|
||||
import cn.dev33.satoken.sso.name.ParamName;
|
||||
@ -56,7 +54,7 @@ public class SaSsoMessageLogoutCallHandle implements SaSsoMessageHandle {
|
||||
|
||||
// 1、获取对象
|
||||
SaSsoClientTemplate ssoClientTemplate = (SaSsoClientTemplate) ssoTemplate;
|
||||
StpLogic stpLogic = ssoClientTemplate.getStpLogic();
|
||||
StpLogic stpLogic = ssoClientTemplate.getStpLogicOrGlobal();
|
||||
ParamName paramName = ssoClientTemplate.paramName;
|
||||
|
||||
// 2、判断当前应用是否开启单点注销功能
|
||||
|
@ -55,7 +55,7 @@ public class SaSsoMessageCheckTicketHandle implements SaSsoMessageHandle {
|
||||
// 1、获取对象
|
||||
SaSsoServerTemplate ssoServerTemplate = (SaSsoServerTemplate) ssoTemplate;
|
||||
ParamName paramName = ssoServerTemplate.paramName;
|
||||
StpLogic stpLogic = ssoServerTemplate.getStpLogic();
|
||||
StpLogic stpLogic = ssoServerTemplate.getStpLogicOrGlobal();
|
||||
String client = message.getString(paramName.client);
|
||||
String ticket = message.getValueNotNull(paramName.ticket).toString();
|
||||
String sloCallback = message.getString(paramName.ssoLogoutCall);
|
||||
|
@ -65,7 +65,7 @@ public class SaSsoMessageSignoutHandle implements SaSsoMessageHandle {
|
||||
String deviceId = message.getString(paramName.deviceId);
|
||||
|
||||
// 4、单点注销
|
||||
SaLogoutParameter logoutParameter = ssoServerTemplate.getStpLogic().createSaLogoutParameter().setDeviceId(deviceId);
|
||||
SaLogoutParameter logoutParameter = ssoServerTemplate.getStpLogicOrGlobal().createSaLogoutParameter().setDeviceId(deviceId);
|
||||
ssoServerTemplate.ssoLogout(loginId, logoutParameter);
|
||||
|
||||
// 5、响应
|
||||
|
@ -164,7 +164,7 @@ public class SaSsoClientProcessor {
|
||||
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
StpLogic stpLogic = ssoClientTemplate.getStpLogic();
|
||||
StpLogic stpLogic = ssoClientTemplate.getStpLogicOrGlobal();
|
||||
ParamName paramName = ssoClientTemplate.paramName;
|
||||
SaSsoClientConfig ssoConfig = ssoClientTemplate.getClientConfig();
|
||||
|
||||
@ -181,7 +181,7 @@ public class SaSsoClientProcessor {
|
||||
}
|
||||
|
||||
// 注销当前应用端会话
|
||||
SaLogoutParameter logoutParameter = ssoClientTemplate.getStpLogic().createSaLogoutParameter();
|
||||
SaLogoutParameter logoutParameter = ssoClientTemplate.getStpLogicOrGlobal().createSaLogoutParameter();
|
||||
stpLogic.logout(loginId, logoutParameter.setDeviceId(deviceId));
|
||||
|
||||
// 响应
|
||||
@ -199,7 +199,7 @@ public class SaSsoClientProcessor {
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
SaSsoClientConfig cfg = ssoClientTemplate.getClientConfig();
|
||||
StpLogic stpLogic = ssoClientTemplate.getStpLogic();
|
||||
StpLogic stpLogic = ssoClientTemplate.getStpLogicOrGlobal();
|
||||
ParamName paramName = ssoClientTemplate.paramName;
|
||||
|
||||
// 获取参数
|
||||
@ -229,7 +229,7 @@ public class SaSsoClientProcessor {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
StpLogic stpLogic = ssoClientTemplate.getStpLogic();
|
||||
StpLogic stpLogic = ssoClientTemplate.getStpLogicOrGlobal();
|
||||
ParamName paramName = ssoClientTemplate.paramName;
|
||||
ApiName apiName = ssoClientTemplate.apiName;
|
||||
|
||||
@ -239,8 +239,6 @@ public class SaSsoClientProcessor {
|
||||
|
||||
// 1、校验 ticket,获取 loginId 等数据
|
||||
SaCheckTicketResult ctr = checkTicket(ticket, apiName.ssoLogin);
|
||||
ctr.centerId = ctr.loginId;
|
||||
ctr.loginId = ssoClientTemplate.strategy.convertCenterIdToLoginId.run(ctr.centerId);
|
||||
|
||||
// 2、如果开发者自定义了 ticket 结果值处理函数,则使用自定义的函数
|
||||
if(ssoClientTemplate.strategy.ticketResultHandle != null) {
|
||||
@ -263,7 +261,7 @@ public class SaSsoClientProcessor {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
StpLogic stpLogic = ssoClientTemplate.getStpLogic();
|
||||
StpLogic stpLogic = ssoClientTemplate.getStpLogicOrGlobal();
|
||||
boolean singleDeviceIdLogout = req.isParam(ssoClientTemplate.paramName.singleDeviceIdLogout, "true");
|
||||
|
||||
// 如果未登录,则无需注销
|
||||
@ -292,6 +290,16 @@ public class SaSsoClientProcessor {
|
||||
return _ssoLogoutBack(req, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装:校验ticket,取出loginId,如果 ticket 无效则抛出异常 (适用于模式二或模式三)
|
||||
*
|
||||
* @param ticket ticket码
|
||||
* @return SaCheckTicketResult
|
||||
*/
|
||||
public SaCheckTicketResult checkTicket(String ticket) {
|
||||
return checkTicket(ticket, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装:校验ticket,取出loginId,如果 ticket 无效则抛出异常 (适用于模式二或模式三)
|
||||
*
|
||||
@ -357,6 +365,10 @@ public class SaSsoClientProcessor {
|
||||
ctr.remainSessionTimeout = result.get(paramName.remainSessionTimeout, Long.class);
|
||||
ctr.result = result;
|
||||
|
||||
// 转换 loginId 和 centerId
|
||||
ctr.centerId = ctr.loginId;
|
||||
ctr.loginId = ssoClientTemplate.strategy.convertCenterIdToLoginId.run(ctr.centerId);
|
||||
|
||||
return ctr;
|
||||
}
|
||||
|
||||
@ -373,7 +385,7 @@ public class SaSsoClientProcessor {
|
||||
// 可能会导致调用失败(注意是可能,而非一定,主要取决于你是否改变了数据读写格式),
|
||||
// 解决方案为:在当前 sso-client 端也按照 sso-server 端的格式重写 SaSsoClientProcessor 里的方法
|
||||
|
||||
StpLogic stpLogic = ssoClientTemplate.getStpLogic();
|
||||
StpLogic stpLogic = ssoClientTemplate.getStpLogicOrGlobal();
|
||||
TicketModel ticketModel = SaSsoServerProcessor.instance.ssoServerTemplate.checkTicketParamAndDelete(ticket, ssoClientTemplate.getClient());
|
||||
|
||||
SaCheckTicketResult ctr = new SaCheckTicketResult();
|
||||
@ -384,6 +396,10 @@ public class SaSsoClientProcessor {
|
||||
ctr.remainSessionTimeout = stpLogic.getSessionTimeoutByLoginId(ticketModel.getLoginId());
|
||||
ctr.result = null;
|
||||
|
||||
// 转换 loginId 和 centerId
|
||||
ctr.centerId = ctr.loginId;
|
||||
ctr.loginId = ssoClientTemplate.strategy.convertCenterIdToLoginId.run(ctr.centerId);
|
||||
|
||||
return ctr;
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ public class SaSsoServerProcessor {
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
SaSsoServerConfig cfg = ssoServerTemplate.getServerConfig();
|
||||
StpLogic stpLogic = ssoServerTemplate.getStpLogic();
|
||||
StpLogic stpLogic = ssoServerTemplate.getStpLogicOrGlobal();
|
||||
ParamName paramName = ssoServerTemplate.paramName;
|
||||
|
||||
// 两种情况:
|
||||
@ -177,7 +177,7 @@ public class SaSsoServerProcessor {
|
||||
// 获取对象
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
StpLogic stpLogic = ssoServerTemplate.getStpLogic();
|
||||
StpLogic stpLogic = ssoServerTemplate.getStpLogicOrGlobal();
|
||||
Object loginId = stpLogic.getLoginIdDefaultNull();
|
||||
boolean singleDeviceIdLogout = req.isParam(ssoServerTemplate.paramName.singleDeviceIdLogout, "true");
|
||||
|
||||
@ -191,7 +191,7 @@ public class SaSsoServerProcessor {
|
||||
}
|
||||
|
||||
// 完成
|
||||
return SaSsoProcessorHelper.ssoLogoutBack(req, res, ssoServerTemplate.paramName);
|
||||
return _ssoLogoutBack(req, res);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -228,4 +228,14 @@ public class SaSsoServerProcessor {
|
||||
return ssoServerTemplate.handleMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装:单点注销成功后返回结果
|
||||
* @param req SaRequest对象
|
||||
* @param res SaResponse对象
|
||||
* @return 返回结果
|
||||
*/
|
||||
public Object _ssoLogoutBack(SaRequest req, SaResponse res) {
|
||||
return SaSsoProcessorHelper.ssoLogoutBack(req, res, ssoServerTemplate.paramName);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ package cn.dev33.satoken.sso.strategy;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.fun.SaParamRetFunction;
|
||||
import cn.dev33.satoken.sso.function.SendHttpFunction;
|
||||
import cn.dev33.satoken.sso.function.SendRequestFunction;
|
||||
import cn.dev33.satoken.sso.function.TicketResultHandleFunction;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
@ -34,7 +34,7 @@ public class SaSsoClientStrategy {
|
||||
/**
|
||||
* 发送 Http 请求的处理函数
|
||||
*/
|
||||
public SendHttpFunction sendHttp = url -> {
|
||||
public SendRequestFunction sendHttp = url -> {
|
||||
return SaManager.getSaHttpTemplate().get(url);
|
||||
};
|
||||
|
||||
|
@ -16,11 +16,12 @@
|
||||
package cn.dev33.satoken.sso.strategy;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.fun.SaFunction;
|
||||
import cn.dev33.satoken.fun.SaParamFunction;
|
||||
import cn.dev33.satoken.sso.function.CheckTicketAppendDataFunction;
|
||||
import cn.dev33.satoken.sso.function.DoLoginHandleFunction;
|
||||
import cn.dev33.satoken.sso.function.NotLoginViewFunction;
|
||||
import cn.dev33.satoken.sso.function.SendHttpFunction;
|
||||
import cn.dev33.satoken.sso.function.SendRequestFunction;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
import java.util.Map;
|
||||
@ -36,10 +37,19 @@ public class SaSsoServerStrategy {
|
||||
/**
|
||||
* 发送 Http 请求的处理函数
|
||||
*/
|
||||
public SendHttpFunction sendHttp = url -> {
|
||||
public SendRequestFunction sendRequest = url -> {
|
||||
return SaManager.getSaHttpTemplate().get(url);
|
||||
};
|
||||
|
||||
/**
|
||||
* 使用异步模式执行一个任务
|
||||
*/
|
||||
public SaParamFunction<SaFunction> asyncRun = fun -> {
|
||||
new Thread(() -> {
|
||||
fun.run();
|
||||
}).start();
|
||||
};
|
||||
|
||||
/**
|
||||
* 未登录时返回的 View
|
||||
*/
|
||||
@ -75,7 +85,7 @@ public class SaSsoServerStrategy {
|
||||
* @return 返回的结果
|
||||
*/
|
||||
public SaResult requestAsSaResult(String url) {
|
||||
String body = sendHttp.apply(url);
|
||||
String body = sendRequest.apply(url);
|
||||
Map<String, Object> map = SaManager.getSaJsonTemplate().jsonToMap(body);
|
||||
return new SaResult(map);
|
||||
}
|
||||
|
@ -213,7 +213,6 @@ public class SaSsoClientTemplate extends SaSsoTemplate {
|
||||
* @return 单点注销URL
|
||||
*/
|
||||
public SaSsoMessage buildSloMessage(Object loginId, SaLogoutParameter logoutParameter) {
|
||||
SaSsoClientConfig ssoConfig = getClientConfig();
|
||||
SaSsoMessage message = new SaSsoMessage();
|
||||
message.setType(SaSsoConsts.MESSAGE_SIGNOUT);
|
||||
message.set(paramName.client, getClient());
|
||||
|
@ -507,7 +507,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
|
||||
return;
|
||||
}
|
||||
|
||||
SaSession session = getStpLogic().getSessionByLoginId(loginId);
|
||||
SaSession session = getStpLogicOrGlobal().getSessionByLoginId(loginId);
|
||||
|
||||
// 取出原来的
|
||||
List<SaSsoClientInfo> scmList = session.get(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, ArrayList::new);
|
||||
@ -522,7 +522,9 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
|
||||
for (;;) {
|
||||
if(scmList.size() > maxRegClient) {
|
||||
SaSsoClientInfo removeScm = scmList.remove(0);
|
||||
notifyClientLogout(loginId, null, removeScm, true, true);
|
||||
strategy.asyncRun.run(() -> {
|
||||
notifyClientLogout(loginId, null, removeScm, true, true);
|
||||
});
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@ -562,7 +564,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
|
||||
* @param loginId 指定账号
|
||||
*/
|
||||
public void ssoLogout(Object loginId) {
|
||||
ssoLogout(loginId, getStpLogic().createSaLogoutParameter());
|
||||
ssoLogout(loginId, getStpLogicOrGlobal().createSaLogoutParameter());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -577,32 +579,37 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
|
||||
pushToAllClientByLogoutCall(loginId, logoutParameter);
|
||||
|
||||
// 2、SaSession 挂载的 Client 端注销会话
|
||||
SaSession session = getStpLogic().getSessionByLoginId(loginId, false);
|
||||
SaSession session = getStpLogicOrGlobal().getSessionByLoginId(loginId, false);
|
||||
if(session == null) {
|
||||
return;
|
||||
}
|
||||
List<SaSsoClientInfo> scmList = session.get(SaSsoConsts.SSO_CLIENT_MODEL_LIST_KEY_, ArrayList::new);
|
||||
scmList.forEach(scm -> {
|
||||
notifyClientLogout(loginId, logoutParameter.getDeviceId(), scm, false, false);
|
||||
strategy.asyncRun.run(() -> {
|
||||
notifyClientLogout(loginId, logoutParameter.getDeviceId(), scm, false, false);
|
||||
});
|
||||
});
|
||||
|
||||
// 3、Server 端本身注销
|
||||
getStpLogic().logout(loginId, logoutParameter);
|
||||
getStpLogicOrGlobal().logout(loginId, logoutParameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知指定账号的指定客户端注销
|
||||
*
|
||||
* @param loginId 指定账号
|
||||
* @param deviceId 指定设备 id
|
||||
* @param scm 客户端信息对象
|
||||
* @param autoLogout 是否为超过 maxRegClient 的自动注销
|
||||
* @param isPushWork 如果该 client 没有注册注销回调地址,是否使用 push 消息的方式进行注销回调通知
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
public void notifyClientLogout(Object loginId, String deviceId, SaSsoClientInfo scm, boolean autoLogout, boolean isPushWork) {
|
||||
public String notifyClientLogout(Object loginId, String deviceId, SaSsoClientInfo scm, boolean autoLogout, boolean isPushWork) {
|
||||
|
||||
// 如果给个null值,不进行任何操作
|
||||
if(scm == null || scm.mode != SaSsoConsts.SSO_MODE_3) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
// 如果此 Client 并没有注册 单点注销 回调地址
|
||||
@ -610,9 +617,10 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
|
||||
if(SaFoxUtil.isEmpty(sloCallUrl)) {
|
||||
// TODO 代码有效性待验证
|
||||
if(isPushWork && SaFoxUtil.isNotEmpty(scm.getClient())) {
|
||||
pushToClientByLogoutCall(getClient(scm.getClient()), loginId, getStpLogic().createSaLogoutParameter());
|
||||
SaSsoClientModel client = getClient(scm.getClient());
|
||||
return pushToClientByLogoutCall(client, loginId, getStpLogicOrGlobal().createSaLogoutParameter());
|
||||
}
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
// 参数
|
||||
@ -627,7 +635,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
|
||||
String finalUrl = SaFoxUtil.joinParam(sloCallUrl, signParamsStr);
|
||||
|
||||
// 发起请求
|
||||
strategy.sendHttp.apply(finalUrl);
|
||||
return strategy.sendRequest.apply(finalUrl);
|
||||
}
|
||||
|
||||
|
||||
@ -644,7 +652,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
|
||||
String noticeUrl = clientModel.splicingPushUrl();
|
||||
String paramsStr = getSignTemplate(clientModel.getClient()).addSignParamsAndJoin(message);
|
||||
String finalUrl = SaFoxUtil.joinParam(noticeUrl, paramsStr);
|
||||
return strategy.sendHttp.apply(finalUrl);
|
||||
return strategy.sendRequest.apply(finalUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -691,7 +699,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
|
||||
public void pushToAllClient(SaSsoMessage message) {
|
||||
List<SaSsoClientModel> needPushClients = getNeedPushClients();
|
||||
for (SaSsoClientModel client : needPushClients) {
|
||||
pushMessage(client, message);
|
||||
strategy.asyncRun.run(() -> pushMessage(client, message));
|
||||
}
|
||||
}
|
||||
|
||||
@ -705,7 +713,9 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
|
||||
List<SaSsoClientModel> npClients = getNeedPushClients();
|
||||
for (SaSsoClientModel client : npClients) {
|
||||
if(client.getIsSlo()) {
|
||||
pushToClientByLogoutCall(client, loginId, logoutParameter);
|
||||
strategy.asyncRun.run(() -> {
|
||||
pushToClientByLogoutCall(client, loginId, logoutParameter);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -715,13 +725,14 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
|
||||
*
|
||||
* @param loginId /
|
||||
* @param logoutParameter 注销参数
|
||||
* @return /
|
||||
*/
|
||||
public void pushToClientByLogoutCall(SaSsoClientModel client, Object loginId, SaLogoutParameter logoutParameter) {
|
||||
public String pushToClientByLogoutCall(SaSsoClientModel client, Object loginId, SaLogoutParameter logoutParameter) {
|
||||
SaSsoMessage message = new SaSsoMessage();
|
||||
message.setType(SaSsoConsts.MESSAGE_LOGOUT_CALL);
|
||||
message.set(paramName.loginId, loginId);
|
||||
message.set(paramName.deviceId, logoutParameter.getDeviceId());
|
||||
pushMessage(client, message);
|
||||
return pushMessage(client, message);
|
||||
}
|
||||
|
||||
|
||||
@ -766,7 +777,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
|
||||
* @return key
|
||||
*/
|
||||
public String splicingTicketModelSaveKey(String ticket) {
|
||||
return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket:" + ticket;
|
||||
return getStpLogicOrGlobal().getConfigOrGlobal().getTokenName() + ":ticket:" + ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -780,7 +791,7 @@ public class SaSsoServerTemplate extends SaSsoTemplate {
|
||||
if(SaFoxUtil.isEmpty(client) || SaSsoConsts.CLIENT_WILDCARD.equals(client)) {
|
||||
client = SaSsoConsts.CLIENT_ANON;
|
||||
}
|
||||
return getStpLogic().getConfigOrGlobal().getTokenName() + ":ticket-index:" + client + ":" + id;
|
||||
return getStpLogicOrGlobal().getConfigOrGlobal().getTokenName() + ":ticket-index:" + client + ":" + id;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -61,11 +61,39 @@ public class SaSsoTemplate {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取底层使用的会话对象
|
||||
* 底层使用的 StpLogic 对象
|
||||
*/
|
||||
StpLogic stpLogic;
|
||||
|
||||
/**
|
||||
* 写入底层使用的会话对象
|
||||
*
|
||||
* @param stpLogic /
|
||||
* @return /
|
||||
*/
|
||||
public SaSsoTemplate setStpLogic(StpLogic stpLogic) {
|
||||
this.stpLogic = stpLogic;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取底层使用的会话对象
|
||||
* @return /
|
||||
*/
|
||||
public StpLogic getStpLogic() {
|
||||
return StpUtil.stpLogic;
|
||||
return this.stpLogic;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取底层使用的会话对象,如果没有配置则返回全局默认 StpLogic
|
||||
* @return /
|
||||
*/
|
||||
public StpLogic getStpLogicOrGlobal() {
|
||||
StpLogic stpLogic = getStpLogic();
|
||||
if (stpLogic == null) {
|
||||
return StpUtil.stpLogic;
|
||||
}
|
||||
return stpLogic;
|
||||
}
|
||||
|
||||
// ----------- 消息处理
|
||||
|
Loading…
Reference in New Issue
Block a user