适配 springboot3

This commit is contained in:
click33
2023-01-05 19:20:21 +08:00
parent d09366602a
commit 1feca73e2a
134 changed files with 3238 additions and 974 deletions
@@ -0,0 +1,12 @@
target/
node_modules/
bin/
.settings/
unpackage/
.classpath
.project
.factorypath
.idea/
@@ -0,0 +1,77 @@
<?xml version='1.0' encoding='utf-8'?>
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-starter</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<packaging>jar</packaging>
<name>sa-token-spring-boot3-starter</name>
<artifactId>sa-token-spring-boot3-starter</artifactId>
<description>springboot3 integrate sa-token</description>
<properties>
<springboot3.version>3.0.1</springboot3.version>
</properties>
<dependencies>
<!-- spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- config (optional) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- sa-token-jakarta-servlet -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jakarta-servlet</artifactId>
</dependency>
<!-- sa-token-spring-boot-autoconfig -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-autoconfig</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- spring-boot-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${springboot3.version}</version>
</dependency>
<!-- spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${springboot3.version}</version>
</dependency>
<!-- config (optional) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${springboot3.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
@@ -0,0 +1,191 @@
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;
/**
* Servlet全局过滤器
* @author kong
*
*/
@Order(SaTokenConsts.ASSEMBLY_ORDER)
public class SaServletFilter implements Filter {
// ------------------------ 设置此过滤器 拦截 & 放行 的路由
/**
* 拦截路由
*/
private List<String> includeList = new ArrayList<>();
/**
* 放行路由
*/
private List<String> excludeList = new ArrayList<>();
/**
* 添加 [拦截路由]
* @param paths 路由
* @return 对象自身
*/
public SaServletFilter addInclude(String... paths) {
includeList.addAll(Arrays.asList(paths));
return this;
}
/**
* 添加 [放行路由]
* @param paths 路由
* @return 对象自身
*/
public SaServletFilter addExclude(String... paths) {
excludeList.addAll(Arrays.asList(paths));
return this;
}
/**
* 写入 [拦截路由] 集合
* @param pathList 路由集合
* @return 对象自身
*/
public SaServletFilter setIncludeList(List<String> pathList) {
includeList = pathList;
return this;
}
/**
* 写入 [放行路由] 集合
* @param pathList 路由集合
* @return 对象自身
*/
public SaServletFilter setExcludeList(List<String> pathList) {
excludeList = pathList;
return this;
}
/**
* 获取 [拦截路由] 集合
* @return see note
*/
public List<String> getIncludeList() {
return includeList;
}
/**
* 获取 [放行路由] 集合
* @return see note
*/
public List<String> getExcludeList() {
return excludeList;
}
// ------------------------ 钩子函数
/**
* 认证函数:每次请求执行
*/
public SaFilterAuthStrategy auth = r -> {};
/**
* 异常处理函数:每次[认证函数]发生异常时执行此函数
*/
public SaFilterErrorStrategy error = e -> {
throw new SaTokenException(e).setCode(SaSpringBootErrorCode.CODE_20105);
};
/**
* 前置函数:在每次[认证函数]之前执行
*/
public SaFilterAuthStrategy beforeAuth = r -> {};
/**
* 写入[认证函数]: 每次请求执行
* @param auth see note
* @return 对象自身
*/
public SaServletFilter setAuth(SaFilterAuthStrategy auth) {
this.auth = auth;
return this;
}
/**
* 写入[异常处理函数]:每次[认证函数]发生异常时执行此函数
* @param error see note
* @return 对象自身
*/
public SaServletFilter setError(SaFilterErrorStrategy error) {
this.error = error;
return this;
}
/**
* 写入[前置函数]:在每次[认证函数]之前执行
* @param beforeAuth see note
* @return 对象自身
*/
public SaServletFilter setBeforeAuth(SaFilterAuthStrategy beforeAuth) {
this.beforeAuth = beforeAuth;
return this;
}
// ------------------------ doFilter
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
// 执行全局过滤器
SaRouter.match(includeList).notMatch(excludeList).check(r -> {
beforeAuth.run(null);
auth.run(null);
});
} catch (StopMatchException e) {
} catch (Throwable e) {
// 1. 获取异常处理策略结果
String result = (e instanceof BackResultException) ? e.getMessage() : String.valueOf(error.run(e));
// 2. 写入输出流
if(response.getContentType() == null) {
response.setContentType("text/plain; charset=utf-8");
}
response.getWriter().print(result);
return;
}
// 执行
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
@@ -0,0 +1,113 @@
package cn.dev33.satoken.interceptor;
import java.lang.reflect.Method;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.exception.BackResultException;
import cn.dev33.satoken.exception.StopMatchException;
import cn.dev33.satoken.fun.SaParamFunction;
import cn.dev33.satoken.strategy.SaStrategy;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* Sa-Token 综合拦截器,提供注解鉴权和路由拦截鉴权能力
*
* @author kong
* @since: 2022-8-21
*/
public class SaInterceptor implements HandlerInterceptor {
/**
* 是否打开注解鉴权
*/
public boolean isAnnotation = true;
/**
* 认证函数:每次请求执行
* <p> 参数:路由处理函数指针
*/
public SaParamFunction<Object> auth = handler -> {};
/**
* 创建一个 Sa-Token 综合拦截器,默认带有注解鉴权能力
*/
public SaInterceptor() {
}
/**
* 创建一个 Sa-Token 综合拦截器,默认带有注解鉴权能力
* @param auth 认证函数,每次请求执行
*/
public SaInterceptor(SaParamFunction<Object> auth) {
this.auth = auth;
}
/**
* 设置是否打开注解鉴权
* @param isAnnotation /
* @return 对象自身
*/
public SaInterceptor isAnnotation(boolean isAnnotation) {
this.isAnnotation = isAnnotation;
return this;
}
/**
* 写入[认证函数]: 每次请求执行
* @param auth /
* @return 对象自身
*/
public SaInterceptor setAuth(SaParamFunction<Object> auth) {
this.auth = auth;
return this;
}
// ----------------- 验证方法 -----------------
/**
* 每次请求之前触发的方法
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
try {
if(isAnnotation && handler instanceof HandlerMethod) {
// 获取此请求对应的 Method 处理函数
Method method = ((HandlerMethod) handler).getMethod();
// 如果此 Method 或其所属 Class 标注了 @SaIgnore,则忽略掉鉴权
if(SaStrategy.me.isAnnotationPresent.apply(method, SaIgnore.class)) {
return true;
}
// 注解校验
SaStrategy.me.checkMethodAnnotation.accept(method);
}
// Auth 校验
auth.run(handler);
} catch (StopMatchException e) {
// 停止匹配,进入Controller
} catch (BackResultException e) {
// 停止匹配,向前端输出结果
if(response.getContentType() == null) {
response.setContentType("text/plain; charset=utf-8");
}
response.getWriter().print(e.getMessage());
return false;
}
// 通过验证
return true;
}
}
@@ -0,0 +1,4 @@
/**
* sa-token集成SpringBoot的各个组件
*/
package cn.dev33.satoken;
@@ -0,0 +1,59 @@
package cn.dev33.satoken.spring;
import cn.dev33.satoken.context.SaTokenContext;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.context.model.SaResponse;
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;
/**
* Sa-Token 上下文处理器 [ SpringBoot3 Jakarta Servlet 版 ]
*
* @author kong
*
*/
public class SaTokenContextForSpringInJakartaServlet implements SaTokenContext {
/**
* 获取当前请求的Request对象
*/
@Override
public SaRequest getRequest() {
return new SaRequestForServlet(SpringMVCUtil.getRequest());
}
/**
* 获取当前请求的Response对象
*/
@Override
public SaResponse getResponse() {
return new SaResponseForServlet(SpringMVCUtil.getResponse());
}
/**
* 获取当前请求的 [存储器] 对象
*/
@Override
public SaStorage getStorage() {
return new SaStorageForServlet(SpringMVCUtil.getRequest());
}
/**
* 校验指定路由匹配符是否可以匹配成功指定路径
*/
@Override
public boolean matchPath(String pattern, String path) {
return SaPathMatcherHolder.getPathMatcher().match(pattern, path);
}
/**
* 此上下文是否有效
*/
@Override
public boolean isValid() {
return SpringMVCUtil.isWeb();
}
}
@@ -0,0 +1,26 @@
package cn.dev33.satoken.spring;
import org.springframework.context.annotation.Bean;
import cn.dev33.satoken.context.SaTokenContext;
/**
* SaTokenContext 上下文注册
*
* @author kong
* @since 2023年1月1日
*
*/
public class SaTokenContextRegister {
/**
* 获取上下文Bean [ SpringBoot3 Jakarta Servlet 版 ]
*
* @return /
*/
@Bean
public SaTokenContext getSaTokenContextForSpringInJakartaServlet() {
return new SaTokenContextForSpringInJakartaServlet();
}
}
@@ -0,0 +1,53 @@
package cn.dev33.satoken.spring;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import cn.dev33.satoken.error.SaSpringBootErrorCode;
import cn.dev33.satoken.exception.NotWebContextException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* SpringMVC相关操作
* @author kong
*
*/
public class SpringMVCUtil {
private SpringMVCUtil() {
}
/**
* 获取当前会话的 request
* @return request
*/
public static HttpServletRequest getRequest() {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(servletRequestAttributes == null) {
throw new NotWebContextException("非Web上下文无法获取Request").setCode(SaSpringBootErrorCode.CODE_20101);
}
return servletRequestAttributes.getRequest();
}
/**
* 获取当前会话的 response
* @return response
*/
public static HttpServletResponse getResponse() {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(servletRequestAttributes == null) {
throw new NotWebContextException("非Web上下文无法获取Response").setCode(SaSpringBootErrorCode.CODE_20101);
}
return servletRequestAttributes.getResponse();
}
/**
* 判断当前是否处于 Web 上下文中
* @return request
*/
public static boolean isWeb() {
return RequestContextHolder.getRequestAttributes() != null;
}
}