Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ee16e919fe | |||
| 670cd4ae98 | |||
| 4f84818100 | |||
| 9398f07cb5 | |||
| 8826565f6a | |||
| e076c71648 | |||
| f5307a2eb0 | |||
| da22832ccf | |||
| 6977d3d136 | |||
| 0f7e1e977c | |||
| e918f613b8 | |||
| 106a710dd8 | |||
| 4607f51d1a | |||
| 954efeb732 | |||
| 8db78e0a5d | |||
| e5fb308628 | |||
| f2416a6175 | |||
| 591ad6ac53 |
@@ -1,7 +1,7 @@
|
||||
<p align="center">
|
||||
<img alt="logo" src="https://sa-token.cc/logo.png" width="150" height="150">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.36.0</h1>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.37.0</h1>
|
||||
<h4 align="center">一个轻量级 Java 权限认证框架,让鉴权变得简单、优雅!</h4>
|
||||
<p align="center">
|
||||
<a href="https://gitee.com/dromara/sa-token/stargazers"><img src="https://gitee.com/dromara/sa-token/badge/star.svg?theme=gvp"></a>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
<!-- 一些属性 -->
|
||||
<properties>
|
||||
<revision>1.36.0</revision>
|
||||
<revision>1.37.0</revision>
|
||||
<jdk.version>1.8</jdk.version>
|
||||
<project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>utf-8</project.reporting.outputEncoding>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<url>https://github.com/dromara/sa-token</url>
|
||||
|
||||
<properties>
|
||||
<revision>1.36.0</revision>
|
||||
<revision>1.37.0</revision>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
||||
@@ -102,10 +102,13 @@ public class SaManager {
|
||||
SaTokenEventCenter.doRegisterComponent("SaTokenDao", saTokenDao);
|
||||
}
|
||||
private static void setSaTokenDaoMethod(SaTokenDao saTokenDao) {
|
||||
if((SaManager.saTokenDao instanceof SaTokenDaoDefaultImpl)) {
|
||||
((SaTokenDaoDefaultImpl)SaManager.saTokenDao).endRefreshThread();
|
||||
if (SaManager.saTokenDao != null) {
|
||||
SaManager.saTokenDao.destroy();
|
||||
}
|
||||
SaManager.saTokenDao = saTokenDao;
|
||||
if (SaManager.saTokenDao != null) {
|
||||
SaManager.saTokenDao.init();
|
||||
}
|
||||
}
|
||||
public static SaTokenDao getSaTokenDao() {
|
||||
if (saTokenDao == null) {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.application;
|
||||
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
/**
|
||||
* 应用全局信息
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.31.0
|
||||
*/
|
||||
public class ApplicationInfo {
|
||||
|
||||
/**
|
||||
* 应用前缀
|
||||
*/
|
||||
public static String routePrefix;
|
||||
|
||||
/**
|
||||
* 为指定 path 裁剪掉 routePrefix 前缀
|
||||
* @param path 指定 path
|
||||
* @return /
|
||||
*/
|
||||
public static String cutPathPrefix(String path) {
|
||||
if(! SaFoxUtil.isEmpty(routePrefix) && ! routePrefix.equals("/") && path.startsWith(routePrefix)){
|
||||
path = path.substring(routePrefix.length());
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -199,6 +199,20 @@ public interface SaTokenDao {
|
||||
* @return 查询到的数据集合
|
||||
*/
|
||||
List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType);
|
||||
|
||||
|
||||
|
||||
|
||||
// --------------------- 生命周期 ---------------------
|
||||
|
||||
/**
|
||||
* 当此 SaTokenDao 实例被装载时触发
|
||||
*/
|
||||
default void init() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 当此 SaTokenDao 实例被卸载时触发
|
||||
*/
|
||||
default void destroy() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,14 +40,6 @@ public class SaTokenDaoDefaultImpl implements SaTokenDao {
|
||||
* 存储数据过期时间的集合(单位: 毫秒), 记录所有 key 的到期时间 (注意存储的是到期时间,不是剩余存活时间)
|
||||
*/
|
||||
public Map<String, Long> expireMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public SaTokenDaoDefaultImpl() {
|
||||
initRefreshThread();
|
||||
}
|
||||
|
||||
|
||||
// ------------------------ String 读写操作
|
||||
|
||||
@@ -260,12 +252,21 @@ public class SaTokenDaoDefaultImpl implements SaTokenDao {
|
||||
});
|
||||
this.refreshThread.start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 结束定时任务,不再定时清理过期数据
|
||||
* 组件被安装时,开始刷新数据线程
|
||||
*/
|
||||
public void endRefreshThread() {
|
||||
this.refreshFlag = false;
|
||||
@Override
|
||||
public void init() {
|
||||
initRefreshThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件被卸载时,结束定时任务,不再定时清理过期数据
|
||||
*/
|
||||
@Override
|
||||
public void destroy() {
|
||||
this.refreshFlag = false;
|
||||
}
|
||||
}
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.exception;
|
||||
|
||||
/**
|
||||
* 一个异常:代表请求 path 无效或非法
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.37.0
|
||||
*/
|
||||
public class RequestPathInvalidException extends SaTokenException {
|
||||
|
||||
/**
|
||||
* 序列化版本号
|
||||
*/
|
||||
private static final long serialVersionUID = 8243974276159004739L;
|
||||
|
||||
/** 具体无效的 path */
|
||||
private final String path;
|
||||
|
||||
/**
|
||||
* @return 具体无效的 path
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public RequestPathInvalidException(String message, String path) {
|
||||
super(message);
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.fun.strategy;
|
||||
|
||||
import cn.dev33.satoken.exception.RequestPathInvalidException;
|
||||
|
||||
/**
|
||||
* 函数式接口:校验请求 path 的算法
|
||||
*
|
||||
* <p> 如果属于无效请求 path,则抛出异常 RequestPathInvalidException </p>
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.37.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SaCheckRequestPathFunction {
|
||||
|
||||
/**
|
||||
* 执行函数
|
||||
* @param path 请求 path
|
||||
* @param extArg1 扩展参数1
|
||||
* @param extArg2 扩展参数2
|
||||
*/
|
||||
void run(String path, Object extArg1, Object extArg2);
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.fun.strategy;
|
||||
|
||||
import cn.dev33.satoken.exception.RequestPathInvalidException;
|
||||
|
||||
/**
|
||||
* 函数式接口:当请求 path 校验不通过时处理方案的算法
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.37.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SaRequestPathInvalidHandleFunction {
|
||||
|
||||
/**
|
||||
* 执行函数
|
||||
* @param e 请求 path 无效的异常对象
|
||||
* @param extArg1 扩展参数1
|
||||
* @param extArg2 扩展参数2
|
||||
*/
|
||||
void run(RequestPathInvalidException e, Object extArg1, Object extArg2);
|
||||
|
||||
}
|
||||
@@ -1175,6 +1175,11 @@ public class StpLogic {
|
||||
*/
|
||||
public SaSession getSessionBySessionId(String sessionId, boolean isCreate, Consumer<SaSession> appendOperation) {
|
||||
|
||||
// 如果提供的 sessionId 为 null,则直接返回 null
|
||||
if(SaFoxUtil.isEmpty(sessionId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 先检查这个 SaSession 是否已经存在,如果不存在且 isCreate=true,则新建并返回
|
||||
SaSession session = getSaTokenDao().getSession(sessionId);
|
||||
|
||||
@@ -1725,11 +1730,7 @@ public class StpLogic {
|
||||
* @return /
|
||||
*/
|
||||
public List<String> getRoleList() {
|
||||
try {
|
||||
return getRoleList(getLoginId());
|
||||
} catch (NotLoginException e) {
|
||||
return SaFoxUtil.emptyList();
|
||||
}
|
||||
return getRoleList(getLoginId());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1773,7 +1774,7 @@ public class StpLogic {
|
||||
try {
|
||||
checkRoleAnd(roleArray);
|
||||
return true;
|
||||
} catch (NotLoginException | NotRoleException e) {
|
||||
} catch (NotRoleException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1788,7 +1789,7 @@ public class StpLogic {
|
||||
try {
|
||||
checkRoleOr(roleArray);
|
||||
return true;
|
||||
} catch (NotLoginException | NotRoleException e) {
|
||||
} catch (NotRoleException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1863,11 +1864,7 @@ public class StpLogic {
|
||||
* @return /
|
||||
*/
|
||||
public List<String> getPermissionList() {
|
||||
try {
|
||||
return getPermissionList(getLoginId());
|
||||
} catch (NotLoginException e) {
|
||||
return SaFoxUtil.emptyList();
|
||||
}
|
||||
return getPermissionList(getLoginId());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1911,7 +1908,7 @@ public class StpLogic {
|
||||
try {
|
||||
checkPermissionAnd(permissionArray);
|
||||
return true;
|
||||
} catch (NotLoginException | NotPermissionException e) {
|
||||
} catch (NotPermissionException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1926,7 +1923,7 @@ public class StpLogic {
|
||||
try {
|
||||
checkPermissionOr(permissionArray);
|
||||
return true;
|
||||
} catch (NotLoginException | NotPermissionException e) {
|
||||
} catch (NotPermissionException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package cn.dev33.satoken.strategy;
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.annotation.*;
|
||||
import cn.dev33.satoken.basic.SaBasicUtil;
|
||||
import cn.dev33.satoken.exception.RequestPathInvalidException;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.fun.strategy.*;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
@@ -329,6 +330,49 @@ public final class SaStrategy {
|
||||
return new StpLogic(loginType);
|
||||
};
|
||||
|
||||
/**
|
||||
* 请求 path 不允许出现的字符
|
||||
*/
|
||||
public static String[] INVALID_CHARACTER = {
|
||||
"//", "\\",
|
||||
"%2e", "%2E", // .
|
||||
"%2f", "%2F", // /
|
||||
"%5c", "%5C", // \
|
||||
"%25" // 空格
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验请求 path 的算法
|
||||
*/
|
||||
public SaCheckRequestPathFunction checkRequestPath = (requestPath, extArg1, extArg2) -> {
|
||||
|
||||
// 不允许为null
|
||||
if(requestPath == null) {
|
||||
throw new RequestPathInvalidException("非法请求:null", null);
|
||||
}
|
||||
// 不允许包含非法字符
|
||||
for (String item : INVALID_CHARACTER) {
|
||||
if (requestPath.contains(item)) {
|
||||
throw new RequestPathInvalidException("非法请求:" + requestPath, requestPath);
|
||||
}
|
||||
}
|
||||
// 不允许出现跨目录
|
||||
if(requestPath.contains("/.") || requestPath.contains("\\.")) {
|
||||
throw new RequestPathInvalidException("非法请求:" + requestPath, requestPath);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 当请求 path 校验不通过时处理方案的算法,自定义示例:
|
||||
* <pre>
|
||||
* SaStrategy.instance.requestPathInvalidHandle = (e, extArg1, extArg2) -> {
|
||||
* // 自定义处理逻辑 ...
|
||||
* };
|
||||
* </pre>
|
||||
*/
|
||||
public SaRequestPathInvalidHandleFunction requestPathInvalidHandle = null;
|
||||
|
||||
|
||||
// ----------------------- 重写策略 set连缀风格
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ public class SaTokenConsts {
|
||||
/**
|
||||
* Sa-Token 当前版本号
|
||||
*/
|
||||
public static final String VERSION_NO = "v1.36.0";
|
||||
public static final String VERSION_NO = "v1.37.0";
|
||||
|
||||
/**
|
||||
* Sa-Token 开源地址 Gitee
|
||||
@@ -186,6 +186,28 @@ public class SaTokenConsts {
|
||||
*/
|
||||
public static final int ASSEMBLY_ORDER = -100;
|
||||
|
||||
/**
|
||||
* 请求 path 校验过滤器的注册顺序
|
||||
*/
|
||||
public static final int PATH_CHECK_FILTER_ORDER = -1000;
|
||||
|
||||
/**
|
||||
* Content-Type key
|
||||
*/
|
||||
public static final String CONTENT_TYPE_KEY = "Content-Type";
|
||||
|
||||
/**
|
||||
* Content-Type text/plain; charset=utf-8
|
||||
*/
|
||||
public static final String CONTENT_TYPE_TEXT_PLAIN = "text/plain; charset=utf-8";
|
||||
|
||||
/**
|
||||
* Content-Type application/json;charset=UTF-8
|
||||
*/
|
||||
public static final String CONTENT_TYPE_APPLICATION_JSON = "application/json;charset=UTF-8";
|
||||
|
||||
|
||||
|
||||
|
||||
// =================== 废弃 ===================
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -73,7 +73,7 @@
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-bom</artifactId>
|
||||
<version>1.36.0</version>
|
||||
<version>1.37.0</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
<java.run.main.class>com.pj.SaTokenCrossCookieApplication</java.run.main.class>
|
||||
</properties>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
<java.run.main.class>com.pj.SaTokenCrossHeaderApplication</java.run.main.class>
|
||||
</properties>
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
<dubbo.version>2.7.21</dubbo.version>
|
||||
<nacos.version>1.4.2</nacos.version>
|
||||
</properties>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
<dubbo.version>2.7.21</dubbo.version>
|
||||
<nacos.version>1.4.2</nacos.version>
|
||||
</properties>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
<dubbo.version>3.2.2</dubbo.version>
|
||||
<nacos.version>2.2.2</nacos.version>
|
||||
</properties>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
<dubbo.version>3.2.2</dubbo.version>
|
||||
<nacos.version>2.2.2</nacos.version>
|
||||
</properties>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<lombok.version>1.18.10</lombok.version>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
</properties>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
</properties>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
+12
-2
@@ -1,10 +1,11 @@
|
||||
package com.pj.test;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.spring.SpringMVCUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
/**
|
||||
* 测试专用Controller
|
||||
* @author click33
|
||||
@@ -27,4 +28,13 @@ public class TestController {
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/getRequestPath
|
||||
@RequestMapping("getRequestPath")
|
||||
public SaResult getRequestPath() {
|
||||
System.out.println("-------------- 测试请求 path 获取");
|
||||
System.out.println("request.getRequestURI() " + SpringMVCUtil.getRequest().getRequestURI());
|
||||
System.out.println("saRequest.getRequestPath() " + SaHolder.getRequest().getRequestPath());
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ sa-token:
|
||||
# 是否输出操作日志
|
||||
is-log: true
|
||||
|
||||
spring:
|
||||
spring:
|
||||
data:
|
||||
# redis配置
|
||||
redis:
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
<solon.version>2.2.3</solon.version>
|
||||
</properties>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -11,13 +11,14 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.5.14</version>
|
||||
<!--<version>2.3.0.RELEASE</version>-->
|
||||
<!-- <version>1.5.9.RELEASE</version> -->
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
<java.run.main.class>com.pj.SaTokenApplication</java.run.main.class>
|
||||
</properties>
|
||||
|
||||
|
||||
@@ -23,5 +23,5 @@ public class NotFoundHandle implements ErrorController {
|
||||
response.setStatus(200);
|
||||
return SaResult.get(404, "not found", null);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.pj.test;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.spring.SpringMVCUtil;
|
||||
import cn.dev33.satoken.stp.SaLoginConfig;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
@@ -41,4 +43,13 @@ public class TestController {
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/getRequestPath
|
||||
@RequestMapping("getRequestPath")
|
||||
public SaResult getRequestPath() {
|
||||
System.out.println("------------ 测试访问路径获取 ");
|
||||
System.out.println("SpringMVCUtil.getRequest().getRequestURI() " + SpringMVCUtil.getRequest().getRequestURI());
|
||||
System.out.println("SaHolder.getRequest().getRequestPath() " + SaHolder.getRequest().getRequestPath());
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ sa-token:
|
||||
# 是否输出操作日志
|
||||
is-log: true
|
||||
|
||||
spring:
|
||||
spring:
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
+1
-2
@@ -1,10 +1,9 @@
|
||||
package com.pj;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
|
||||
/**
|
||||
* Sa-Token整合webflux 示例 (springboot3)
|
||||
*
|
||||
|
||||
@@ -19,7 +19,7 @@ sa-token:
|
||||
# 是否输出操作日志
|
||||
is-log: true
|
||||
|
||||
spring:
|
||||
spring:
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -19,7 +19,7 @@ sa-token:
|
||||
# 是否输出操作日志
|
||||
is-log: true
|
||||
|
||||
spring:
|
||||
spring:
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<!-- 定义 Sa-Token 版本号 -->
|
||||
<properties>
|
||||
<sa-token.version>1.36.0</sa-token.version>
|
||||
<sa-token.version>1.37.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<description>Sa-Token Dependencies</description>
|
||||
|
||||
<properties>
|
||||
<revision>1.36.0</revision>
|
||||
<revision>1.37.0</revision>
|
||||
|
||||
<!-- 统一定义依赖版本号 -->
|
||||
<springboot.version>2.5.15</springboot.version>
|
||||
@@ -24,8 +24,8 @@
|
||||
<jakarta-servlet-api.version>6.0.0</jakarta-servlet-api.version>
|
||||
<thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
|
||||
<solon.version>2.5.3</solon.version>
|
||||
<noear-redisx.version>1.4.8</noear-redisx.version>
|
||||
<noear-snack3.version>3.2.79</noear-snack3.version>
|
||||
<noear-redisx.version>1.6.0</noear-redisx.version>
|
||||
<noear-snack3.version>3.2.80</noear-snack3.version>
|
||||
<jfinal.version>4.9.17</jfinal.version>
|
||||
<jboot.version>3.14.4</jboot.version>
|
||||
<commons-pool2.version>2.5.0</commons-pool2.version>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<p align="center">
|
||||
<img alt="logo" src="https://sa-token.cc/logo.png" width="150" height="150">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.36.0</h1>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.37.0</h1>
|
||||
<h5 align="center">一个轻量级 Java 权限认证框架,让鉴权变得简单、优雅!</h5>
|
||||
<p align="center" class="badge-box">
|
||||
<a href="https://gitee.com/dromara/sa-token/stargazers"><img src="https://gitee.com/dromara/sa-token/badge/star.svg?theme=gvp"></a>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<div class="logo-box">
|
||||
<img src="logo.png" title="logo" />
|
||||
<h1 class="logo-text">Sa-Token</h1>
|
||||
<sub>v1.36.0</sub>
|
||||
<sub>v1.37.0</sub>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
@@ -28,6 +28,7 @@
|
||||
</div>
|
||||
<select class="select-version p-none" onchange="location.href=this.value">
|
||||
<option value="doc.html">最新版</option>
|
||||
<option value="v/v1.36.0/doc.html">v1.36.0</option>
|
||||
<option value="v/v1.35.0/doc.html">v1.35.0</option>
|
||||
<option value="v/v1.34.0/doc.html">v1.34.0</option>
|
||||
<option value="v/v1.33.0/doc.html">v1.33.0</option>
|
||||
@@ -103,7 +104,7 @@
|
||||
<!-- <a class="p-none wzi" href="#/more/blog">博客</a> -->
|
||||
<a class="p-none wzi" href="#/more/join-group">加入讨论群</a>
|
||||
<a class="p-none wzi" href="#/more/sa-token-donate">赞助</a>
|
||||
<a class="p-none wzi" href="#/sso/sso-pro">?? SSO商业版</a>
|
||||
<a class="p-none wzi" href="#/sso/sso-pro">🔥 SSO商业版</a>
|
||||
<div class="zk-box">
|
||||
<a class="wzi" href="javascript:;">
|
||||
<span>相关资源 </span>
|
||||
@@ -166,7 +167,7 @@
|
||||
<script src="./static/docsify-plugin.js?v=6"></script>
|
||||
<script src="./static/is-star-plugin.js?v=6"></script>
|
||||
<script>
|
||||
var saTokenTopVersion = '1.36.0'; // Sa-Token最新版本
|
||||
var saTokenTopVersion = '1.37.0'; // Sa-Token最新版本
|
||||
var name = '<img style="width: 60px; height: 60px; vertical-align: middle;" src="logo.png" alt="logo" /> ';
|
||||
name += '<b style="font-size: 28px; vertical-align: middle;">Sa-Token</b> <sub>v' + saTokenTopVersion + '</sub>';
|
||||
window.$docsify = {
|
||||
|
||||
+18
-9
@@ -67,7 +67,7 @@
|
||||
<!-- <a class="p-none wzi" href="doc.html#/more/blog">博客</a> -->
|
||||
<a class="p-none wzi" href="doc.html#/more/join-group">加入讨论群</a>
|
||||
<a class="p-none wzi" href="doc.html#/more/sa-token-donate">赞助</a>
|
||||
<a class="p-none wzi" href="doc.html#/sso/sso-pro">?? SSO商业版</a>
|
||||
<a class="p-none wzi" href="doc.html#/sso/sso-pro">🔥 SSO商业版</a>
|
||||
<div class="zk-box">
|
||||
<a class="wzi" href="javascript:;">
|
||||
<span>相关资源 </span>
|
||||
@@ -109,7 +109,7 @@
|
||||
<div class="main-box">
|
||||
<div class="content-box">
|
||||
<!-- <div class="fenge"></div> -->
|
||||
<h1>Sa-Token<small>v1.36.0</small></h1>
|
||||
<h1>Sa-Token<small>v1.37.0</small></h1>
|
||||
<div class="sub-title">一个轻量级 java 权限认证框架,让鉴权变得简单、优雅!</div>
|
||||
<div class="btn-box">
|
||||
<a class="abtn" href="https://github.com/dromara/sa-token" target="_blank">GitHub</a>
|
||||
@@ -147,7 +147,7 @@
|
||||
<p>多端登录、单端登录、同端互斥登录、七天免登录…… 多种登录策略只需改个配置即可完成</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h2>??️ 权限认证</h2>
|
||||
<h2>🔑️️ 权限认证</h2>
|
||||
<p>权限认证、角色认证、会话二级认证、注解鉴权、路由鉴权……多种姿势灵活鉴权</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
@@ -155,27 +155,27 @@
|
||||
<p>强制注销、踢人下线、账号封禁、身份切换、自动续签 …… 提供完善的会话管理方案</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h2>??️ Redis集成</h2>
|
||||
<h2>🔎 Redis集成</h2>
|
||||
<p>提供 Redis 集成方案、项目重启数据不丢失、多系统数据互通,可自定义数据持久化策略</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h2>??️ 前后端分离</h2>
|
||||
<h2>🚀️️ 前后端分离</h2>
|
||||
<p>内置多种 Token 读取策略,适配APP、小程序、SPA单页应用等前后端分离场景</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h2>️?? 单点登录</h2>
|
||||
<h2>️🍃 单点登录</h2>
|
||||
<p>同域、跨域、共享Redis、跨Redis、前后端一体、前后端分离……提供各种架构下的SSO接入方案</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h2>?? OAuth2.0</h2>
|
||||
<h2>🍂 OAuth2.0</h2>
|
||||
<p>轻松搭建 OAuth2.0 认证中心,支持四种授权模式,支持 openid 授权机制,支持二次扩展开发</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h2>??️ 微服务支持</h2>
|
||||
<h2>💦️ 微服务支持</h2>
|
||||
<p>分布式 Session 会话、网关统一鉴权、RPC调用鉴权……提供开箱即用的微服务认证方案</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<h2>??️ 开箱即用</h2>
|
||||
<h2>🗳️ 开箱即用</h2>
|
||||
<p>提供SpringMVC、WebFlux、Solon、jwt 等常见框架集成包,真正的开箱即用……</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -640,6 +640,15 @@
|
||||
msg="在 SpringBoot 中通过简单的方式将文件存储到 本地、阿里云 OSS、腾讯云 COS、七牛云 Kodo等">
|
||||
</a>
|
||||
|
||||
<a href="https://wemq.nicholasld.cn/" target="_blank">
|
||||
<img class="lazy" data-original="https://oss.dev33.cn/sa-token/link/wemq.png"
|
||||
msg="开源、高性能、安全、功能强大的物联网调试和管理解决方案。">
|
||||
</a>
|
||||
<a href="https://gitee.com/dromara/mayfly-go" target="_blank">
|
||||
<img class="lazy" data-original="https://oss.dev33.cn/sa-token/link/mayfly-go.png"
|
||||
msg="web 版 linux(终端[终端回放] 文件 脚本 进程 计划任务)、数据库(mysql postgres)、redis(单机 哨兵 集群)、mongo 统一管理操作平台">
|
||||
</a>
|
||||
|
||||
<a href="https://dromara.org/zh/projects/" target="_blank">
|
||||
<img class="lazy" data-original="https://oss.dev33.cn/sa-token/link/dromara.png"
|
||||
msg="让每一位开源爱好者,体会到开源的快乐。">
|
||||
|
||||
@@ -470,6 +470,60 @@ class MyConfiguration {
|
||||
[经验来源](https://gitee.com/dromara/sa-token/issues/I7EXIU)
|
||||
|
||||
|
||||
### Q:SpringBoot 3.x 路由拦截鉴权报错:No more pattern data allowed after {*...} or ** pattern element
|
||||
|
||||
|
||||
报错原因:SpringBoot3.x 版本默认将路由匹配机制由 `ant_path_matcher` 改为了 `path_pattern_parser` 模式,
|
||||
而此模式有一个规则,就是写路由匹配符的时候,不允许 `**` 之后再出现内容。例如:`/admin/**/info` 就是不允许的。
|
||||
|
||||
如果你的项目报了这个错,说明你写的路由匹配符出现了上述问题,有三种解决方案:
|
||||
1. 等待 SpringMVC 官方增强 `path_pattern_parser` 模式能力,使之可以支持 `**` 之后再出现内容。
|
||||
2. 在写路由匹配规则时,避免使 `**` 之后再出现内容。
|
||||
3. 将项目的路由匹配机制改为 `ant_path_matcher`。
|
||||
|
||||
先改项目的:
|
||||
``` yml
|
||||
spring:
|
||||
mvc:
|
||||
pathmatch:
|
||||
matching-strategy: ant_path_matcher
|
||||
```
|
||||
|
||||
再改 Sa-Token 的:
|
||||
``` java
|
||||
/**
|
||||
* 自定义 SaTokenContext 实现类,重写 matchPath 方法,切换为 ant_path_matcher 模式,使之可以支持 `**` 之后再出现内容
|
||||
*/
|
||||
@Primary
|
||||
@Component
|
||||
public class SaTokenContextByPatternsRequestCondition extends SaTokenContextForSpringInJakartaServlet {
|
||||
|
||||
@Override
|
||||
public boolean matchPath(String pattern, String path) {
|
||||
return SaPatternsRequestConditionHolder.match(pattern, path);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- ---------------------------- 常见疑问 ----------------------------- -->
|
||||
|
||||
|
||||
@@ -21,6 +21,18 @@ Sa-Token 采用 Apache-2.0 开源协议,**承诺框架本身与官网文档永
|
||||
|
||||
| 赞助人 | 赞助金额 | 留言 | 时间 |
|
||||
| :-------- | :-------- | :-------- | :-------- |
|
||||
| [时间很快](https://gitee.com/frsimple) | ¥ 220.0 | 感谢您的开源项目! | 2023-10-27 |
|
||||
| [立秋](https://gitee.com/code_wh) | ¥ 2.5 | 感谢您的开源项目! | 2023-10-27 |
|
||||
| [PotatoLoofah](https://gitee.com/PotatoLoofah) | ¥ 10.0 | 感谢您的开源项目! | 2023-10-27 |
|
||||
| [ly-chn](https://gitee.com/ly-chn) | ¥ 99.0 | 一定的资金支持有助于开源项目走的更加长远 | 2023-10-17 |
|
||||
| [yangs2w](https://gitee.com/yangs2w) | ¥ 10.0 | 感谢您的开源项目! | 2023-10-10 |
|
||||
| [lee](https://gitee.com/cngeeklee) | ¥ 10.0 | 真正的轻量级权限安全框架,希望继续更新 | 2023-10-06 |
|
||||
| [yang](https://gitee.com/hansdm) | ¥ 10.0 | 感谢您的开源项目! | 2023-09-27 |
|
||||
| [明道云](https://gitee.com/lunan-yn) | ¥ 200.0 | 明道云2023年伙伴大会,[报名链接](https://www.mingdao.com/event/mpc/2023) | 2023-09-25 |
|
||||
| [shenlicao](https://gitee.com/shenlicao) | ¥ 10.0 | 感谢您的开源项目! | 2023-09-15 |
|
||||
| [lostyue](https://gitee.com/lostyue) | ¥ 20.0 | 感谢您的开源项目! | 2023-09-14 |
|
||||
| [huni](https://gitee.com/simin_sizi) | ¥ 10.0 | 感谢您的开源项目! | 2023-09-11 |
|
||||
| [T_T](https://gitee.com/wm26hua) | ¥ 20.0 | 感谢您的开源项目! | 2023-09-07 |
|
||||
| [Meteor](https://gitee.com/meteoroc) | ¥ 2.5 | 感谢您的开源项目! | 2023-08-23 |
|
||||
| [刘斌](https://gitee.com/xuanfather) | ¥ 20.0 | 感谢您的开源项目! | 2023-08-17 |
|
||||
| [快快乐乐小码农](https://gitee.com/happy-little-farmer) | ¥ 1.0 | 感谢您的开源项目! | 2023-08-17 |
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# 更新日志
|
||||
|
||||
### v1.37.0 @2023-10-18
|
||||
- 修复:修复路由拦截鉴权可被绕过的问题。 **[漏洞修复]**
|
||||
- 重构:未登录时调用鉴权 API 抛出未登录异常而不再是无权限异常。
|
||||
- 优化:优化 SaTokenDao 组件更换时的逻辑。
|
||||
- 文档:提供 SpringBoot3.x 路由匹配出错的解决方案。
|
||||
|
||||
|
||||
### v1.36.0 @2023-9-22
|
||||
- sa-token-core:
|
||||
- 修复:API接口签名校验参数接口NPE问题,增加必须参数的非空校验处理。
|
||||
|
||||
@@ -103,7 +103,10 @@ public class SaTokenConfigure {
|
||||
|
||||
首先在 SaTokenConfigure 配置类中为 Thymeleaf 配置全局对象:
|
||||
|
||||
(注意: 如果`SaTokenConfigure`继承了`WebMvcConfigurer`等类, 可能会造成循环依赖, 如果遇到, 请新建一个其他配置类完成此项配置)
|
||||
|
||||
``` java
|
||||
@Configuration
|
||||
public class SaTokenConfigure{
|
||||
// ... 其它代码
|
||||
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
---
|
||||
|
||||
### 正式版本
|
||||
v1.36.0 正式版,可上生产:
|
||||
v1.37.0 正式版,可上生产:
|
||||
|
||||
``` xml
|
||||
<!-- Sa-Token 权限认证 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||
<version>1.36.0</version>
|
||||
<version>1.37.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
+2
-2
@@ -101,7 +101,7 @@ public class SaTokenDaoOfRedisBase64 implements SaTokenDao {
|
||||
*/
|
||||
@Override
|
||||
public Object getObject(String key) {
|
||||
return redisBucket.getAndDeserialize(key);
|
||||
return redisBucket.getAndDeserialize(key, Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,7 +156,7 @@ public class SaTokenDaoOfRedisBase64 implements SaTokenDao {
|
||||
@Override
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
|
||||
Set<String> keys = redisBucket.keys(prefix + "*" + keyword + "*");
|
||||
List<String> list = new ArrayList<>(keys);
|
||||
List<String> list = new ArrayList<String>(keys);
|
||||
return SaFoxUtil.searchList(list, start, size, sortType);
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -16,6 +16,7 @@
|
||||
package cn.dev33.satoken.servlet.model;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.application.ApplicationInfo;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.servlet.error.SaServletErrorCode;
|
||||
@@ -124,7 +125,7 @@ public class SaRequestForServlet implements SaRequest {
|
||||
*/
|
||||
@Override
|
||||
public String getRequestPath() {
|
||||
return request.getServletPath();
|
||||
return ApplicationInfo.cutPathPrefix(request.getRequestURI());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+5
-2
@@ -74,9 +74,12 @@ public class SaTokenActionHandler extends ActionHandler {
|
||||
// Controller controller = action.getControllerClass().newInstance();
|
||||
controller = controllerFactory.getController(action.getControllerClass());
|
||||
CPI._init_(controller, action, request, response, urlPara[0]);
|
||||
//加入SaToken上下文处理
|
||||
// if (resolveJson && controller.isJsonRequest()) {
|
||||
// // 注入 JsonRequest 包装对象接管 request
|
||||
// controller.setHttpServletRequest(jsonRequestFactory.apply(controller.getRawData(), controller.getRequest()));
|
||||
// }
|
||||
//加入SaToken上下文处理
|
||||
SaControllerContext.hold(controller);
|
||||
|
||||
if (devMode) {
|
||||
if (actionReporter.isReportAfterInvocation(request)) {
|
||||
new Invocation(action, controller).invoke();
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.reactor.filter;
|
||||
|
||||
import cn.dev33.satoken.exception.RequestPathInvalidException;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 校验请求 path 是否合法
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.37.0
|
||||
*/
|
||||
@Order(SaTokenConsts.PATH_CHECK_FILTER_ORDER)
|
||||
public class SaPathCheckFilterForReactor implements WebFilter {
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
|
||||
// 校验本次请求 path 是否合法
|
||||
try {
|
||||
SaStrategy.instance.checkRequestPath.run(exchange.getRequest().getPath().toString(), exchange, null);
|
||||
} catch (RequestPathInvalidException e) {
|
||||
if(SaStrategy.instance.requestPathInvalidHandle == null) {
|
||||
exchange.getResponse().getHeaders().set(SaTokenConsts.CONTENT_TYPE_KEY, SaTokenConsts.CONTENT_TYPE_TEXT_PLAIN);
|
||||
return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(e.getMessage().getBytes())));
|
||||
} else {
|
||||
SaStrategy.instance.requestPathInvalidHandle.run(e, exchange, null);
|
||||
}
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
// 向下执行
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -153,8 +153,8 @@ public class SaReactorFilter implements SaFilter, WebFilter {
|
||||
// 2. 写入输出流
|
||||
// 请注意此处默认 Content-Type 为 text/plain,如果需要返回 JSON 信息,需要在 return 前自行设置 Content-Type 为 application/json
|
||||
// 例如:SaHolder.getResponse().setHeader("Content-Type", "application/json;charset=UTF-8");
|
||||
if(exchange.getResponse().getHeaders().getFirst("Content-Type") == null) {
|
||||
exchange.getResponse().getHeaders().set("Content-Type", "text/plain; charset=utf-8");
|
||||
if(exchange.getResponse().getHeaders().getFirst(SaTokenConsts.CONTENT_TYPE_KEY) == null) {
|
||||
exchange.getResponse().getHeaders().set(SaTokenConsts.CONTENT_TYPE_KEY, SaTokenConsts.CONTENT_TYPE_TEXT_PLAIN);
|
||||
}
|
||||
return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(result.getBytes())));
|
||||
|
||||
|
||||
+2
-1
@@ -17,6 +17,7 @@ package cn.dev33.satoken.reactor.model;
|
||||
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.application.ApplicationInfo;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.reactor.context.SaReactorHolder;
|
||||
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
|
||||
@@ -112,7 +113,7 @@ public class SaRequestForReactor implements SaRequest {
|
||||
*/
|
||||
@Override
|
||||
public String getRequestPath() {
|
||||
return request.getURI().getPath();
|
||||
return ApplicationInfo.cutPathPrefix(request.getPath().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
package cn.dev33.satoken.reactor.spring;
|
||||
|
||||
import cn.dev33.satoken.context.SaTokenContextForThreadLocal;
|
||||
import cn.dev33.satoken.spring.SaPathPatternParserUtil;
|
||||
import cn.dev33.satoken.spring.pathmatch.SaPathPatternParserUtil;
|
||||
|
||||
/**
|
||||
* Sa-Token 上下文处理器 [ Spring Reactor 版本实现 ] ,基于 SaTokenContextForThreadLocal 定制
|
||||
|
||||
+11
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package cn.dev33.satoken.reactor.spring;
|
||||
|
||||
import cn.dev33.satoken.reactor.filter.SaPathCheckFilterForReactor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import cn.dev33.satoken.context.SaTokenContext;
|
||||
@@ -37,4 +38,14 @@ public class SaTokenContextRegister {
|
||||
return new SaTokenContextForSpringReactor();
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求 path 校验过滤器
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
@Bean
|
||||
public SaPathCheckFilterForReactor saPathCheckFilterForReactor() {
|
||||
return new SaPathCheckFilterForReactor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.reactor.filter;
|
||||
|
||||
import cn.dev33.satoken.exception.RequestPathInvalidException;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 校验请求 path 是否合法
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.37.0
|
||||
*/
|
||||
@Order(SaTokenConsts.PATH_CHECK_FILTER_ORDER)
|
||||
public class SaPathCheckFilterForReactor implements WebFilter {
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
|
||||
// 校验本次请求 path 是否合法
|
||||
try {
|
||||
SaStrategy.instance.checkRequestPath.run(exchange.getRequest().getPath().toString(), exchange, null);
|
||||
} catch (RequestPathInvalidException e) {
|
||||
if(SaStrategy.instance.requestPathInvalidHandle == null) {
|
||||
exchange.getResponse().getHeaders().set(SaTokenConsts.CONTENT_TYPE_KEY, SaTokenConsts.CONTENT_TYPE_TEXT_PLAIN);
|
||||
return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(e.getMessage().getBytes())));
|
||||
} else {
|
||||
SaStrategy.instance.requestPathInvalidHandle.run(e, exchange, null);
|
||||
}
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
// 向下执行
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
}
|
||||
+11
-12
@@ -15,19 +15,10 @@
|
||||
*/
|
||||
package cn.dev33.satoken.reactor.filter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import cn.dev33.satoken.filter.SaFilter;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
|
||||
import cn.dev33.satoken.exception.BackResultException;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.exception.StopMatchException;
|
||||
import cn.dev33.satoken.filter.SaFilter;
|
||||
import cn.dev33.satoken.filter.SaFilterAuthStrategy;
|
||||
import cn.dev33.satoken.filter.SaFilterErrorStrategy;
|
||||
import cn.dev33.satoken.reactor.context.SaReactorHolder;
|
||||
@@ -35,8 +26,16 @@ import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
|
||||
import cn.dev33.satoken.reactor.error.SaReactorSpringBootErrorCode;
|
||||
import cn.dev33.satoken.router.SaRouter;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Reactor 全局鉴权过滤器
|
||||
* <p>
|
||||
@@ -154,8 +153,8 @@ public class SaReactorFilter implements SaFilter, WebFilter {
|
||||
// 2. 写入输出流
|
||||
// 请注意此处默认 Content-Type 为 text/plain,如果需要返回 JSON 信息,需要在 return 前自行设置 Content-Type 为 application/json
|
||||
// 例如:SaHolder.getResponse().setHeader("Content-Type", "application/json;charset=UTF-8");
|
||||
if(exchange.getResponse().getHeaders().getFirst("Content-Type") == null) {
|
||||
exchange.getResponse().getHeaders().set("Content-Type", "text/plain; charset=utf-8");
|
||||
if(exchange.getResponse().getHeaders().getFirst(SaTokenConsts.CONTENT_TYPE_KEY) == null) {
|
||||
exchange.getResponse().getHeaders().set(SaTokenConsts.CONTENT_TYPE_KEY, SaTokenConsts.CONTENT_TYPE_TEXT_PLAIN);
|
||||
}
|
||||
return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(result.getBytes())));
|
||||
|
||||
|
||||
+6
-6
@@ -16,16 +16,16 @@
|
||||
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.SaManager;
|
||||
import cn.dev33.satoken.application.ApplicationInfo;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.reactor.context.SaReactorHolder;
|
||||
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -113,7 +113,7 @@ public class SaRequestForReactor implements SaRequest {
|
||||
*/
|
||||
@Override
|
||||
public String getRequestPath() {
|
||||
return request.getURI().getPath();
|
||||
return ApplicationInfo.cutPathPrefix(request.getPath().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
package cn.dev33.satoken.reactor.spring;
|
||||
|
||||
import cn.dev33.satoken.context.SaTokenContextForThreadLocal;
|
||||
import cn.dev33.satoken.spring.SaPathPatternParserUtil;
|
||||
import cn.dev33.satoken.spring.pathmatch.SaPathPatternParserUtil;
|
||||
|
||||
/**
|
||||
* Sa-Token 上下文处理器 [ Spring Reactor 版本实现 ] ,基于 SaTokenContextForThreadLocal 定制
|
||||
|
||||
+12
-2
@@ -15,9 +15,9 @@
|
||||
*/
|
||||
package cn.dev33.satoken.reactor.spring;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import cn.dev33.satoken.context.SaTokenContext;
|
||||
import cn.dev33.satoken.reactor.filter.SaPathCheckFilterForReactor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* 注册 Sa-Token 所需要的 Bean
|
||||
@@ -37,4 +37,14 @@ public class SaTokenContextRegister {
|
||||
return new SaTokenContextForSpringReactor();
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求 path 校验过滤器
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
@Bean
|
||||
public SaPathCheckFilterForReactor saPathCheckFilterForReactor() {
|
||||
return new SaPathCheckFilterForReactor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-12
@@ -15,19 +15,19 @@
|
||||
*/
|
||||
package cn.dev33.satoken.servlet.model;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.application.ApplicationInfo;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.servlet.error.SaServletErrorCode;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
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;
|
||||
import cn.dev33.satoken.servlet.error.SaServletErrorCode;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 对 SaRequest 包装类的实现(Servlet 版)
|
||||
@@ -41,15 +41,15 @@ public class SaRequestForServlet implements SaRequest {
|
||||
* 底层Request对象
|
||||
*/
|
||||
protected HttpServletRequest request;
|
||||
|
||||
|
||||
/**
|
||||
* 实例化
|
||||
* @param request request对象
|
||||
* @param request request对象
|
||||
*/
|
||||
public SaRequestForServlet(HttpServletRequest request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取底层源对象
|
||||
*/
|
||||
@@ -125,7 +125,7 @@ public class SaRequestForServlet implements SaRequest {
|
||||
*/
|
||||
@Override
|
||||
public String getRequestPath() {
|
||||
return request.getServletPath();
|
||||
return ApplicationInfo.cutPathPrefix(request.getRequestURI());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -102,7 +102,7 @@ public class SaTokenDaoOfRedisBase64 implements SaTokenDao {
|
||||
*/
|
||||
@Override
|
||||
public Object getObject(String key) {
|
||||
return redisBucket.getAndDeserialize(key);
|
||||
return redisBucket.getAndDeserialize(key, Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,7 +157,7 @@ public class SaTokenDaoOfRedisBase64 implements SaTokenDao {
|
||||
@Override
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
|
||||
Set<String> keys = redisBucket.keys(prefix + "*" + keyword + "*");
|
||||
List<String> list = new ArrayList<>(keys);
|
||||
List<String> list = new ArrayList<String>(keys);
|
||||
return SaFoxUtil.searchList(list, start, size, sortType);
|
||||
}
|
||||
}
|
||||
+1
@@ -17,6 +17,7 @@ package cn.dev33.satoken.spring;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import cn.dev33.satoken.spring.pathmatch.SaPathMatcherHolder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.util.PathMatcher;
|
||||
|
||||
+11
-1
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package cn.dev33.satoken.spring;
|
||||
|
||||
import cn.dev33.satoken.spring.context.path.ApplicationContextPathLoading;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@@ -50,5 +51,14 @@ public class SaBeanRegister {
|
||||
public SaJsonTemplate getSaJsonTemplateForJackson() {
|
||||
return new SaJsonTemplateForJackson();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 应用上下文路径加载器
|
||||
* @return /
|
||||
*/
|
||||
@Bean
|
||||
public ApplicationContextPathLoading getApplicationContextPathLoading() {
|
||||
return new ApplicationContextPathLoading();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.spring.context.path;
|
||||
|
||||
import cn.dev33.satoken.application.ApplicationInfo;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
|
||||
/**
|
||||
* 应用上下文路径加载器
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.37.0
|
||||
*/
|
||||
public class ApplicationContextPathLoading implements ApplicationRunner {
|
||||
|
||||
@Value("${server.servlet.context-path:}")
|
||||
String contextPath;
|
||||
|
||||
@Value("${spring.mvc.servlet.path:}")
|
||||
String servletPath;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
|
||||
String routePrefix = "";
|
||||
|
||||
if(SaFoxUtil.isNotEmpty(contextPath)) {
|
||||
if(! contextPath.startsWith("/")){
|
||||
contextPath = "/" + contextPath;
|
||||
}
|
||||
if (contextPath.endsWith("/")) {
|
||||
contextPath = contextPath.substring(0, contextPath.length() - 1);
|
||||
}
|
||||
routePrefix += contextPath;
|
||||
}
|
||||
|
||||
if(SaFoxUtil.isNotEmpty(servletPath)) {
|
||||
if(! servletPath.startsWith("/")){
|
||||
servletPath = "/" + servletPath;
|
||||
}
|
||||
if (servletPath.endsWith("/")) {
|
||||
servletPath = servletPath.substring(0, servletPath.length() - 1);
|
||||
}
|
||||
routePrefix += servletPath;
|
||||
}
|
||||
|
||||
if(SaFoxUtil.isNotEmpty(routePrefix) && ! routePrefix.equals("/") ){
|
||||
ApplicationInfo.routePrefix = routePrefix;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.spring;
|
||||
package cn.dev33.satoken.spring.pathmatch;
|
||||
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.spring;
|
||||
package cn.dev33.satoken.spring.pathmatch;
|
||||
|
||||
import org.springframework.http.server.PathContainer;
|
||||
import org.springframework.web.util.pattern.PathPattern;
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.spring;
|
||||
package cn.dev33.satoken.spring.pathmatch;
|
||||
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.filter;
|
||||
|
||||
import cn.dev33.satoken.exception.RequestPathInvalidException;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 校验请求 path 是否合法
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.37.0
|
||||
*/
|
||||
@Order(SaTokenConsts.PATH_CHECK_FILTER_ORDER)
|
||||
public class SaPathCheckFilterForServlet implements Filter {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
|
||||
// 校验本次请求 path 是否合法
|
||||
try {
|
||||
HttpServletRequest req = (HttpServletRequest) request;
|
||||
SaStrategy.instance.checkRequestPath.run(req.getRequestURI(), request, response);
|
||||
} catch (RequestPathInvalidException e) {
|
||||
if(SaStrategy.instance.requestPathInvalidHandle == null) {
|
||||
response.setContentType("text/plain; charset=utf-8");
|
||||
response.getWriter().print(e.getMessage());
|
||||
response.getWriter().flush();
|
||||
} else {
|
||||
SaStrategy.instance.requestPathInvalidHandle.run(e, request, response);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 向下执行
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
+8
-15
@@ -15,26 +15,19 @@
|
||||
*/
|
||||
package cn.dev33.satoken.filter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import cn.dev33.satoken.error.SaSpringBootErrorCode;
|
||||
import cn.dev33.satoken.exception.BackResultException;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.exception.StopMatchException;
|
||||
import cn.dev33.satoken.router.SaRouter;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import javax.servlet.*;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Servlet 全局鉴权过滤器
|
||||
@@ -147,7 +140,7 @@ public class SaServletFilter implements SaFilter, Filter {
|
||||
// 请注意此处默认 Content-Type 为 text/plain,如果需要返回 JSON 信息,需要在 return 前自行设置 Content-Type 为 application/json
|
||||
// 例如:SaHolder.getResponse().setHeader("Content-Type", "application/json;charset=UTF-8");
|
||||
if(response.getContentType() == null) {
|
||||
response.setContentType("text/plain; charset=utf-8");
|
||||
response.setContentType(SaTokenConsts.CONTENT_TYPE_TEXT_PLAIN);
|
||||
}
|
||||
response.getWriter().print(result);
|
||||
return;
|
||||
|
||||
+1
@@ -22,6 +22,7 @@ import cn.dev33.satoken.context.model.SaStorage;
|
||||
import cn.dev33.satoken.servlet.model.SaRequestForServlet;
|
||||
import cn.dev33.satoken.servlet.model.SaResponseForServlet;
|
||||
import cn.dev33.satoken.servlet.model.SaStorageForServlet;
|
||||
import cn.dev33.satoken.spring.pathmatch.SaPatternsRequestConditionHolder;
|
||||
|
||||
/**
|
||||
* Sa-Token 上下文处理器 [ SpringMVC版本实现 ]。在 SpringMVC、SpringBoot 中使用 Sa-Token 时,必须注入此实现类,否则会出现上下文无效异常
|
||||
|
||||
+12
-2
@@ -15,9 +15,9 @@
|
||||
*/
|
||||
package cn.dev33.satoken.spring;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import cn.dev33.satoken.context.SaTokenContext;
|
||||
import cn.dev33.satoken.filter.SaPathCheckFilterForServlet;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* 注册 Sa-Token 框架所需要的 Bean
|
||||
@@ -37,4 +37,14 @@ public class SaTokenContextRegister {
|
||||
return new SaTokenContextForSpring();
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求 path 校验过滤器
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
@Bean
|
||||
public SaPathCheckFilterForServlet saPathCheckFilterForServlet() {
|
||||
return new SaPathCheckFilterForServlet();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.filter;
|
||||
|
||||
import cn.dev33.satoken.exception.RequestPathInvalidException;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import jakarta.servlet.*;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 校验请求 path 是否合法
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.37.0
|
||||
*/
|
||||
@Order(SaTokenConsts.PATH_CHECK_FILTER_ORDER)
|
||||
public class SaPathCheckFilterForJakartaServlet implements Filter {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
|
||||
// 校验本次请求 path 是否合法
|
||||
try {
|
||||
HttpServletRequest req = (HttpServletRequest) request;
|
||||
SaStrategy.instance.checkRequestPath.run(req.getRequestURI(), request, response);
|
||||
} catch (RequestPathInvalidException e) {
|
||||
if(SaStrategy.instance.requestPathInvalidHandle == null) {
|
||||
response.setContentType("text/plain; charset=utf-8");
|
||||
response.getWriter().print(e.getMessage());
|
||||
response.getWriter().flush();
|
||||
} else {
|
||||
SaStrategy.instance.requestPathInvalidHandle.run(e, request, response);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 向下执行
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
+8
-14
@@ -15,25 +15,19 @@
|
||||
*/
|
||||
package cn.dev33.satoken.filter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import cn.dev33.satoken.error.SaSpringBootErrorCode;
|
||||
import cn.dev33.satoken.exception.BackResultException;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.exception.StopMatchException;
|
||||
import cn.dev33.satoken.router.SaRouter;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.FilterConfig;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.*;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Servlet 全局鉴权过滤器
|
||||
@@ -146,7 +140,7 @@ public class SaServletFilter implements SaFilter, Filter {
|
||||
// 请注意此处默认 Content-Type 为 text/plain,如果需要返回 JSON 信息,需要在 return 前自行设置 Content-Type 为 application/json
|
||||
// 例如:SaHolder.getResponse().setHeader("Content-Type", "application/json;charset=UTF-8");
|
||||
if(response.getContentType() == null) {
|
||||
response.setContentType("text/plain; charset=utf-8");
|
||||
response.setContentType(SaTokenConsts.CONTENT_TYPE_TEXT_PLAIN);
|
||||
}
|
||||
response.getWriter().print(result);
|
||||
return;
|
||||
|
||||
+1
@@ -22,6 +22,7 @@ import cn.dev33.satoken.context.model.SaStorage;
|
||||
import cn.dev33.satoken.servlet.model.SaRequestForServlet;
|
||||
import cn.dev33.satoken.servlet.model.SaResponseForServlet;
|
||||
import cn.dev33.satoken.servlet.model.SaStorageForServlet;
|
||||
import cn.dev33.satoken.spring.pathmatch.SaPathPatternParserUtil;
|
||||
|
||||
/**
|
||||
* Sa-Token 上下文处理器 [ SpringBoot3 Jakarta Servlet 版 ],在 SpringBoot3 中使用 Sa-Token 时,必须注入此实现类,否则会出现上下文无效异常
|
||||
|
||||
+12
-2
@@ -15,9 +15,9 @@
|
||||
*/
|
||||
package cn.dev33.satoken.spring;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import cn.dev33.satoken.context.SaTokenContext;
|
||||
import cn.dev33.satoken.filter.SaPathCheckFilterForJakartaServlet;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* 注册 Sa-Token 框架所需要的 Bean
|
||||
@@ -37,4 +37,14 @@ public class SaTokenContextRegister {
|
||||
return new SaTokenContextForSpringInJakartaServlet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求 path 校验过滤器
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
@Bean
|
||||
public SaPathCheckFilterForJakartaServlet saPathCheckFilterForJakartaServlet() {
|
||||
return new SaPathCheckFilterForJakartaServlet();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+21
-29
@@ -15,13 +15,22 @@
|
||||
*/
|
||||
package cn.dev33.satoken.springboot;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.context.SaTokenContext;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.exception.*;
|
||||
import cn.dev33.satoken.filter.SaServletFilter;
|
||||
import cn.dev33.satoken.json.SaJsonTemplate;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.spring.SpringMVCUtil;
|
||||
import cn.dev33.satoken.spring.pathmatch.SaPathMatcherHolder;
|
||||
import cn.dev33.satoken.stp.SaLoginConfig;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import cn.dev33.satoken.util.SoMap;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
@@ -31,28 +40,11 @@ import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.util.PathMatcher;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.context.SaTokenContext;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.exception.DisableServiceException;
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
import cn.dev33.satoken.exception.NotSafeException;
|
||||
import cn.dev33.satoken.exception.SaJsonConvertException;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.filter.SaServletFilter;
|
||||
import cn.dev33.satoken.json.SaJsonTemplate;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
import cn.dev33.satoken.spring.SaPathMatcherHolder;
|
||||
import cn.dev33.satoken.spring.SpringMVCUtil;
|
||||
import cn.dev33.satoken.stp.SaLoginConfig;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import cn.dev33.satoken.util.SoMap;
|
||||
import javax.servlet.ServletException;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Sa-Token 基础API测试
|
||||
|
||||
+3
-3
@@ -15,9 +15,9 @@
|
||||
*/
|
||||
package cn.dev33.satoken.springboot;
|
||||
|
||||
import cn.dev33.satoken.spring.SaPathMatcherHolder;
|
||||
import cn.dev33.satoken.spring.SaPathPatternParserUtil;
|
||||
import cn.dev33.satoken.spring.SaPatternsRequestConditionHolder;
|
||||
import cn.dev33.satoken.spring.pathmatch.SaPathMatcherHolder;
|
||||
import cn.dev33.satoken.spring.pathmatch.SaPathPatternParserUtil;
|
||||
import cn.dev33.satoken.spring.pathmatch.SaPatternsRequestConditionHolder;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
|
||||
Reference in New Issue
Block a user