mirror of
https://gitee.com/dromara/sa-token.git
synced 2025-04-05 17:37:53 +08:00
重构SSO模块
This commit is contained in:
parent
ef1bdbd867
commit
35814bbb66
@ -45,7 +45,16 @@ public interface SaRequest {
|
||||
*/
|
||||
public default boolean isParam(String name, String value) {
|
||||
String paramValue = getParam(name);
|
||||
return paramValue != null && paramValue.equals(value);
|
||||
return SaFoxUtil.isNotEmpty(paramValue) && paramValue.equals(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测请求是否提供了指定参数
|
||||
* @param name 参数名称
|
||||
* @return 是否提供
|
||||
*/
|
||||
public default boolean hasParam(String name) {
|
||||
return SaFoxUtil.isNotEmpty(getParam(name));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,7 +115,7 @@ public interface SaRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前请求的url,例:http://xxx.com/
|
||||
* 返回当前请求的url,不带query参数,例:http://xxx.com/
|
||||
* @return see note
|
||||
*/
|
||||
public String getUrl();
|
||||
@ -124,5 +133,14 @@ public interface SaRequest {
|
||||
public default boolean isAjax() {
|
||||
return getHeader("X-Requested-With") != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转发请求
|
||||
* @param url 转发地址
|
||||
* @return 任意值
|
||||
*/
|
||||
public default Object forward(String path) {
|
||||
throw new SaTokenException("No implementation");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -68,7 +68,10 @@ public class SaSsoConsts {
|
||||
/** 表示OK的返回结果 */
|
||||
public static final String OK = "ok";
|
||||
|
||||
/** 表示自己 */
|
||||
public static final String SELF = "self";
|
||||
|
||||
/** 表示请求没有得到任何有效处理 {msg: "not handle"} */
|
||||
public static final String NOT_HANDLE = "{\"msg\": \"not handle\"}";
|
||||
|
||||
|
||||
}
|
||||
|
@ -247,11 +247,9 @@ public class SaSsoHandle {
|
||||
|
||||
// 开始处理
|
||||
stpLogic.logout();
|
||||
if(req.getParam(ParamName.back) == null) {
|
||||
return SaResult.ok("单点注销成功");
|
||||
} else {
|
||||
return res.redirect(req.getParam(ParamName.back, "/"));
|
||||
}
|
||||
|
||||
// 返回
|
||||
return ssoLogoutBack(req, res);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -273,14 +271,12 @@ public class SaSsoHandle {
|
||||
// 调用SSO-Server认证中心API,进行注销
|
||||
String url = SaSsoUtil.buildSloUrl(stpLogic.getLoginId());
|
||||
String body = String.valueOf(cfg.sendHttp.apply(url));
|
||||
if(SaSsoConsts.OK.equals(body)) {
|
||||
if(req.getParam(ParamName.back) == null) {
|
||||
return SaResult.ok("单点注销成功");
|
||||
} else {
|
||||
return res.redirect(req.getParam(ParamName.back, "/"));
|
||||
}
|
||||
if(SaSsoConsts.OK.equals(body) == false) {
|
||||
return SaResult.error("单点注销失败");
|
||||
}
|
||||
return SaResult.error("单点注销失败");
|
||||
|
||||
// 返回
|
||||
return ssoLogoutBack(req, res);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -301,4 +297,28 @@ public class SaSsoHandle {
|
||||
return SaSsoConsts.OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装:单点注销成功后返回结果
|
||||
* @param req SaRequest对象
|
||||
* @param res SaResponse对象
|
||||
* @return 返回结果
|
||||
*/
|
||||
public static Object ssoLogoutBack(SaRequest req, SaResponse res) {
|
||||
/*
|
||||
* 三种情况:
|
||||
* 1. 有back参数,值为SELF -> 回退一级并刷新
|
||||
* 2. 有back参数,值为url -> 跳转back地址
|
||||
* 3. 无back参数 -> 返回json数据
|
||||
*/
|
||||
String back = req.getParam(ParamName.back);
|
||||
if(SaFoxUtil.isNotEmpty(back)) {
|
||||
if(back.equals(SaSsoConsts.SELF)) {
|
||||
return "<script>if(document.referrer != location.href){ location.replace(document.referrer || '/'); }</script>";
|
||||
}
|
||||
return res.redirect(back);
|
||||
} else {
|
||||
return SaResult.ok("单点注销成功");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -212,8 +212,7 @@ public class SaSsoTemplate {
|
||||
}
|
||||
|
||||
// 3、是否在[允许地址列表]之中
|
||||
String authUrl = SaManager.getConfig().getSso().getAllowUrl().replaceAll(" ", "");
|
||||
List<String> authUrlList = Arrays.asList(authUrl.split(","));
|
||||
List<String> authUrlList = Arrays.asList(getAllowUrl().replaceAll(" ", "").split(","));
|
||||
if(SaManager.getSaTokenAction().hasElement(authUrlList, url) == false) {
|
||||
throw new SaTokenException("非法redirect:" + url);
|
||||
}
|
||||
@ -222,6 +221,15 @@ public class SaSsoTemplate {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取:所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
|
||||
* @return see note
|
||||
*/
|
||||
public String getAllowUrl() {
|
||||
// 默认从配置文件中返回
|
||||
return SaManager.getConfig().getSso().getAllowUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对url中的back参数进行URL编码, 解决超链接重定向后参数丢失的bug
|
||||
* @param url url
|
||||
|
@ -99,10 +99,18 @@ public class SaSsoUtil {
|
||||
* 校验重定向url合法性
|
||||
* @param url 下放ticket的url地址
|
||||
*/
|
||||
public static void checkAuthUrl(String url) {
|
||||
public static void checkRedirectUrl(String url) {
|
||||
saSsoTemplate.checkRedirectUrl(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取:所有允许的授权回调地址,多个用逗号隔开 (不在此列表中的URL将禁止下放ticket)
|
||||
* @return see note
|
||||
*/
|
||||
public static String getAllowUrl() {
|
||||
return saSsoTemplate.getAllowUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL:Server端 账号资料查询地址
|
||||
* @param loginId 账号id
|
||||
|
@ -64,6 +64,7 @@ public class StpLogic {
|
||||
*/
|
||||
public StpLogic setLoginType(String loginType){
|
||||
this.loginType = loginType;
|
||||
SaManager.putStpLogic(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,15 @@ public class SaFoxUtil {
|
||||
public static boolean isEmpty(Object str) {
|
||||
return str == null || "".equals(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定元素是否不为 (null或者空字符串)
|
||||
* @param str 指定元素
|
||||
* @return 是否为null或者空字符串
|
||||
*/
|
||||
public static boolean isNotEmpty(Object str) {
|
||||
return isEmpty(str) == false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 以当前时间戳和随机int数字拼接一个随机字符串
|
||||
|
@ -21,8 +21,7 @@ public class SsoClientController {
|
||||
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='/sso/logout?back=' + encodeURIComponent(location.href);\">注销</a></p>";
|
||||
// "<a href='/sso/logout' target='_blank'>注销</a></p>"; // 上面是[跳页面]方式,这个是[RestAPI]方式 区别在于是否加了back参数
|
||||
"<a href='/sso/logout?back=self'>注销</a></p>";
|
||||
return str;
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ public class SsoClientController {
|
||||
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' target='_blank'>注销</a></p>";
|
||||
" <a href='/sso/logout?back=self'>注销</a></p>";
|
||||
return str;
|
||||
}
|
||||
|
||||
|
@ -4,12 +4,6 @@ server:
|
||||
|
||||
# sa-token配置
|
||||
sa-token:
|
||||
# Token名称
|
||||
token-name: satoken
|
||||
# Token有效期
|
||||
timeout: 2592000
|
||||
# Token风格
|
||||
token-style: uuid
|
||||
# SSO-相关配置
|
||||
sso:
|
||||
# SSO-Server端 单点登录地址
|
||||
|
@ -14,7 +14,7 @@
|
||||
```
|
||||
|
||||
##### 1.2、后端拦截重定向
|
||||
在后端注册全局过滤器(或拦截器),拦截需要登录后才能访问的页面资源,将未登录的访问重定向至登录接口
|
||||
在后端注册全局过滤器(或拦截器、或全局异常处理),拦截需要登录后才能访问的页面资源,将未登录的访问重定向至登录接口
|
||||
``` java
|
||||
/**
|
||||
* Sa-Token 配置类
|
||||
|
@ -195,7 +195,7 @@ public class SsoClientController {
|
||||
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' target='_blank'>注销</a></p>";
|
||||
"<a href='/sso/logout?back=self'>注销</a></p>";
|
||||
return str;
|
||||
}
|
||||
|
||||
|
@ -182,7 +182,7 @@ sa-token:
|
||||
|
||||
点击 **`[注销]`** 按钮,即可单点注销成功
|
||||
|
||||

|
||||
<!--  -->
|
||||
|
||||

|
||||
|
||||
|
@ -1,8 +1,5 @@
|
||||
package cn.dev33.satoken.quick;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -11,11 +8,11 @@ import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.filter.SaServletFilter;
|
||||
import cn.dev33.satoken.quick.config.SaQuickConfig;
|
||||
import cn.dev33.satoken.quick.web.SaQuickController;
|
||||
import cn.dev33.satoken.spring.SpringMVCUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
|
||||
@ -65,26 +62,16 @@ public class SaQuickBean implements WebMvcConfigurer {
|
||||
.addExclude("/favicon.ico", "/saLogin", "/doLogin", "/sa-res/**").
|
||||
// 认证函数: 每次请求执行
|
||||
setAuth(r -> {
|
||||
// System.out.println("---------- 进入sa-token全局认证 -----------");
|
||||
|
||||
// 未登录时直接转发到login.html页面
|
||||
if (SaQuickManager.getConfig().getAuth() && StpUtil.isLogin() == false) {
|
||||
try {
|
||||
HttpServletRequest request = SpringMVCUtil.getRequest();
|
||||
HttpServletResponse response = SpringMVCUtil.getResponse();
|
||||
request.getRequestDispatcher("/saLogin").forward(request, response);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
SaHolder.getRequest().forward("/saLogin");
|
||||
// 抛出异常,不再继续执行
|
||||
throw NotLoginException.newInstance(StpUtil.getLoginType(), "");
|
||||
}
|
||||
|
||||
}).
|
||||
|
||||
// 异常处理函数:每次认证函数发生异常时执行此函数
|
||||
setError(e -> {
|
||||
// System.out.println("---------- 进入sa-token异常处理 -----------");
|
||||
return e.getMessage();
|
||||
});
|
||||
}
|
||||
|
@ -16,6 +16,11 @@ public class SaReactorHolder {
|
||||
*/
|
||||
public static final Class<ServerWebExchange> CONTEXT_KEY = ServerWebExchange.class;
|
||||
|
||||
/**
|
||||
* chain_key
|
||||
*/
|
||||
public static final String CHAIN_KEY = "WEB_FILTER_CHAIN_KEY";
|
||||
|
||||
/**
|
||||
* 获取上下文对象
|
||||
* @return see note
|
||||
|
@ -151,6 +151,10 @@ public class SaReactorFilter implements WebFilter {
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
|
||||
// 写入WebFilterChain对象
|
||||
exchange.getAttributes().put(SaReactorHolder.CHAIN_KEY, chain);
|
||||
|
||||
// ---------- 全局认证处理
|
||||
try {
|
||||
// 写入全局上下文 (同步)
|
||||
|
@ -3,8 +3,12 @@ package cn.dev33.satoken.reactor.model;
|
||||
|
||||
import org.springframework.http.HttpCookie;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.reactor.context.SaReactorHolder;
|
||||
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
|
||||
|
||||
/**
|
||||
* Request for Reactor
|
||||
@ -85,4 +89,19 @@ public class SaRequestForReactor implements SaRequest {
|
||||
public String getMethod() {
|
||||
return request.getMethodValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转发请求
|
||||
*/
|
||||
@Override
|
||||
public Object forward(String path) {
|
||||
ServerWebExchange exchange = SaReactorSyncHolder.getContent();
|
||||
WebFilterChain chain = exchange.getAttribute(SaReactorHolder.CHAIN_KEY);
|
||||
|
||||
ServerHttpRequest newRequest = request.mutate().path(path).build();
|
||||
ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
|
||||
|
||||
return chain.filter(newExchange);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,9 +1,15 @@
|
||||
package cn.dev33.satoken.servlet.model;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
|
||||
/**
|
||||
* Request for Servlet
|
||||
@ -89,4 +95,18 @@ public class SaRequestForServlet implements SaRequest {
|
||||
return request.getMethod();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转发请求
|
||||
*/
|
||||
@Override
|
||||
public Object forward(String path) {
|
||||
try {
|
||||
HttpServletResponse response = (HttpServletResponse)SaManager.getSaTokenContext().getResponse().getSource();
|
||||
request.getRequestDispatcher(path).forward(request, response);
|
||||
return null;
|
||||
} catch (ServletException | IOException e) {
|
||||
throw new SaTokenException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
package cn.dev33.satoken.solon.model;
|
||||
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import org.noear.solon.core.handle.Context;
|
||||
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
|
||||
/**
|
||||
* @author noear
|
||||
* @since 1.4
|
||||
@ -49,4 +50,5 @@ public class SaRequestForSolon implements SaRequest {
|
||||
public String getMethod() {
|
||||
return ctx.method();
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user