目录结构重构完成

This commit is contained in:
shengzhang
2021-05-05 01:02:20 +08:00
parent 5cde169e7f
commit 9965821b51
115 changed files with 185 additions and 33 deletions

24
sa-token-starter/pom.xml Normal file
View File

@@ -0,0 +1,24 @@
<?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-parent</artifactId>
<version>1.18.0</version>
</parent>
<packaging>pom</packaging>
<name>sa-token-starter</name>
<artifactId>sa-token-starter</artifactId>
<description>sa-token starters</description>
<modules>
<module>sa-token-servlet</module>
<module>sa-token-spring-boot-starter</module>
<module>sa-token-reactor-spring-boot-starter</module>
</modules>
</project>

View File

@@ -0,0 +1,12 @@
target/
node_modules/
bin/
.settings/
unpackage/
.classpath
.project
.factorypath
.idea/

View File

@@ -0,0 +1,63 @@
<?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>1.18.0</version>
</parent>
<packaging>jar</packaging>
<name>sa-token-reactor-spring-boot-starter</name>
<artifactId>sa-token-reactor-spring-boot-starter</artifactId>
<description>springboot reactor integrate sa-token</description>
<dependencies>
<!-- sa-token-core -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-core</artifactId>
<version>${sa-token-version}</version>
</dependency>
<!-- spring-boot-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.0.0.RELEASE</version>
<scope>compile</scope>
</dependency>
<!-- spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.4.RELEASE</version>
<scope>compile</scope>
</dependency>
<!-- reactor-core -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.1.4.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.0.0.RELEASE</version>
<optional>true</optional>
</dependency>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.0.0.RELEASE</version>
</dependency> -->
</dependencies>
</project>

View File

@@ -0,0 +1,44 @@
package cn.dev33.satoken.reactor.context;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* Reactor上下文操作 [异步]
* @author kong
*
*/
public class SaReactorHolder {
/**
* key
*/
public static final Class<ServerWebExchange> CONTEXT_KEY = ServerWebExchange.class;
/**
* 获取上下文对象
* @return see note
*/
public static Mono<ServerWebExchange> getContent() {
// 从全局 Mono<Context> 获取
return Mono.subscriberContext().map(ctx -> ctx.get(CONTEXT_KEY));
}
/**
* 获取上下文对象, 并设置到同步上下文中
* @return see note
*/
public static Mono<ServerWebExchange> getContentAndSetSync() {
// 从全局 Mono<Context> 获取
return Mono.subscriberContext().map(ctx -> {
// 设置到sync中
SaReactorSyncHolder.setContent(ctx.get(CONTEXT_KEY));
return ctx.get(CONTEXT_KEY);
}).doFinally(r->{
// 从sync中清除
SaReactorSyncHolder.clearContent();
});
}
}

View File

@@ -0,0 +1,63 @@
package cn.dev33.satoken.reactor.context;
import org.springframework.web.server.ServerWebExchange;
import cn.dev33.satoken.context.SaTokenContextForThreadLocalStorage;
import cn.dev33.satoken.context.SaTokenContextForThreadLocalStorage.Box;
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.fun.SaFunction;
import cn.dev33.satoken.reactor.model.SaRequestForReactor;
import cn.dev33.satoken.reactor.model.SaResponseForReactor;
import cn.dev33.satoken.reactor.model.SaStorageForReactor;
/**
* Reactor上下文操作 [同步]
* @author kong
*
*/
public class SaReactorSyncHolder {
/**
* 写入上下文对象
* @param exchange see note
*/
public static void setContent(ServerWebExchange exchange) {
SaRequest request = new SaRequestForReactor(exchange.getRequest());
SaResponse response = new SaResponseForReactor(exchange.getResponse());
SaStorage storage = new SaStorageForReactor(exchange);
SaTokenContextForThreadLocalStorage.setBox(request, response, storage);
}
/**
* 获取上下文对象
* @return see note
*/
public static ServerWebExchange getContent() {
Box box = SaTokenContextForThreadLocalStorage.getBoxNotNull();
return (ServerWebExchange)box.getStorage().getSource();
}
/**
* 清除上下文对象
*/
public static void clearContent() {
SaTokenContextForThreadLocalStorage.clearBox();
}
/**
* 写入上下文对象, 并在执行函数后将其清除
* @param exchange see note
* @param fun see note
*/
public static void setContent(ServerWebExchange exchange, SaFunction fun) {
try {
setContent(exchange);
fun.run();
} finally {
clearContent();
}
}
}

View File

@@ -0,0 +1,195 @@
package cn.dev33.satoken.reactor.filter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
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.SaTokenException;
import cn.dev33.satoken.filter.SaFilterAuthStrategy;
import cn.dev33.satoken.filter.SaFilterErrorStrategy;
import cn.dev33.satoken.reactor.context.SaReactorHolder;
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
import cn.dev33.satoken.router.SaRouterUtil;
import cn.dev33.satoken.util.SaTokenConsts;
import reactor.core.publisher.Mono;
/**
* Reactor全局过滤器
* @author kong
*
*/
@Order(SaTokenConsts.ASSEMBLY_ORDER)
public class SaReactorFilter implements WebFilter {
// ------------------------ 设置此过滤器 拦截 & 放行 的路由
/**
* 拦截路由
*/
private List<String> includeList = new ArrayList<>();
/**
* 放行路由
*/
private List<String> excludeList = new ArrayList<>();
/**
* 添加 [拦截路由]
* @param paths 路由
* @return 对象自身
*/
public SaReactorFilter addInclude(String... paths) {
includeList.addAll(Arrays.asList(paths));
return this;
}
/**
* 添加 [放行路由]
* @param paths 路由
* @return 对象自身
*/
public SaReactorFilter addExclude(String... paths) {
excludeList.addAll(Arrays.asList(paths));
return this;
}
/**
* 写入 [拦截路由] 集合
* @param pathList 路由集合
* @return 对象自身
*/
public SaReactorFilter setIncludeList(List<String> pathList) {
includeList = pathList;
return this;
}
/**
* 写入 [放行路由] 集合
* @param pathList 路由集合
* @return 对象自身
*/
public SaReactorFilter 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);
};
/**
* 前置函数:在每次[认证函数]之前执行
*/
public SaFilterAuthStrategy beforeAuth = r -> {};
/**
* 写入[认证函数]: 每次请求执行
* @param auth see note
* @return 对象自身
*/
public SaReactorFilter setAuth(SaFilterAuthStrategy auth) {
this.auth = auth;
return this;
}
/**
* 写入[异常处理函数]:每次[认证函数]发生异常时执行此函数
* @param error see note
* @return 对象自身
*/
public SaReactorFilter setError(SaFilterErrorStrategy error) {
this.error = error;
return this;
}
/**
* 写入[前置函数]:在每次[认证函数]之前执行
* @param beforeAuth see note
* @return 对象自身
*/
public SaReactorFilter setBeforeAuth(SaFilterAuthStrategy beforeAuth) {
this.beforeAuth = beforeAuth;
return this;
}
// ------------------------ filter
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
// ---------- 全局认证处理
try {
// 写入全局上下文 (同步)
SaReactorSyncHolder.setContent(exchange);
// 执行全局过滤器
SaRouterUtil.match(includeList, excludeList, () -> {
beforeAuth.run(null);
auth.run(null);
});
} catch (Throwable e) {
// 1. 获取异常处理策略结果
Object result = error.run(e);
String resultString = String.valueOf(result);
// 2. 写入输出流
if(exchange.getResponse().getHeaders().getFirst("Content-Type") == null) {
exchange.getResponse().getHeaders().set("Content-Type", "text/plain; charset=utf-8");
}
return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(resultString.getBytes())));
} finally {
// 清除上下文
SaReactorSyncHolder.clearContent();
}
// ---------- 执行
// 写入全局上下文 (同步)
SaReactorSyncHolder.setContent(exchange);
// 执行
return chain.filter(exchange).subscriberContext(ctx -> {
// 写入全局上下文 (异步)
ctx = ctx.put(SaReactorHolder.CONTEXT_KEY, exchange);
return ctx;
}).doFinally(r -> {
// 清除上下文
SaReactorSyncHolder.clearContent();
});
}
}

View File

@@ -0,0 +1,80 @@
package cn.dev33.satoken.reactor.model;
import org.springframework.http.HttpCookie;
import org.springframework.http.server.reactive.ServerHttpRequest;
import cn.dev33.satoken.context.model.SaRequest;
/**
* Request for Reactor
* @author kong
*
*/
public class SaRequestForReactor implements SaRequest {
/**
* 底层Request对象
*/
ServerHttpRequest request;
/**
* 实例化
* @param request request对象
*/
public SaRequestForReactor(ServerHttpRequest request) {
this.request = request;
}
/**
* 获取底层源对象
*/
@Override
public Object getSource() {
return request;
}
/**
* 在 [请求体] 里获取一个值
*/
@Override
public String getParameter(String name) {
return request.getQueryParams().getFirst(name);
}
/**
* 在 [请求头] 里获取一个值
*/
@Override
public String getHeader(String name) {
return request.getHeaders().getFirst(name);
}
/**
* 在 [Cookie作用域] 里获取一个值
*/
@Override
public String getCookieValue(String name) {
HttpCookie cookie = request.getCookies().getFirst(name);
if(cookie == null) {
return null;
}
return cookie.getValue();
}
/**
* 返回当前请求path (不包括上下文名称)
*/
@Override
public String getRequestPath() {
return request.getURI().getPath();
}
/**
* 返回当前请求的类型
*/
@Override
public String getMethod() {
return request.getMethodValue();
}
}

View File

@@ -0,0 +1,83 @@
package cn.dev33.satoken.reactor.model;
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseCookie.ResponseCookieBuilder;
import org.springframework.http.server.reactive.ServerHttpResponse;
import cn.dev33.satoken.context.model.SaResponse;
import cn.dev33.satoken.util.SaFoxUtil;
/**
* Response for Reactor
* @author kong
*
*/
public class SaResponseForReactor implements SaResponse {
/**
* 底层Response对象
*/
ServerHttpResponse response;
/**
* 实例化
* @param response response对象
*/
public SaResponseForReactor(ServerHttpResponse response) {
this.response = response;
}
/**
* 获取底层源对象
*/
@Override
public Object getSource() {
return response;
}
/**
* 删除指定Cookie
*/
@Override
public void deleteCookie(String name) {
addCookie(name, null, null, null, 0);
}
/**
* 写入指定Cookie
*/
@Override
public void addCookie(String name, String value, String path, String domain, int timeout) {
// 构建CookieBuilder
ResponseCookieBuilder builder = ResponseCookie.from(name, value)
.domain(domain)
.path(path)
.maxAge(timeout)
;
// set path
if(SaFoxUtil.isEmpty(path) == true) {
path = "/";
}
builder.path(path);
// set domain
if(SaFoxUtil.isEmpty(domain) == false) {
builder.domain(domain);
}
// 写入Cookie
response.addCookie(builder.build());
}
/**
* 在响应头里写入一个值
*/
@Override
public SaResponse setHeader(String name, String value) {
response.getHeaders().set(name, value);
return this;
}
}

View File

@@ -0,0 +1,59 @@
package cn.dev33.satoken.reactor.model;
import org.springframework.web.server.ServerWebExchange;
import cn.dev33.satoken.context.model.SaStorage;
/**
* Storage for Reactor
* @author kong
*
*/
public class SaStorageForReactor implements SaStorage {
/**
* 底层Request对象
*/
ServerWebExchange exchange;
/**
* 实例化
* @param exchange exchange对象
*/
public SaStorageForReactor(ServerWebExchange exchange) {
this.exchange = exchange;
}
/**
* 获取底层源对象
*/
@Override
public Object getSource() {
return exchange;
}
/**
* 在 [Request作用域] 里写入一个值
*/
@Override
public void set(String key, Object value) {
exchange.getAttributes().put(key, value);
}
/**
* 在 [Request作用域] 里获取一个值
*/
@Override
public Object get(String key) {
return exchange.getAttributes().get(key);
}
/**
* 在 [Request作用域] 里删除一个值
*/
@Override
public void delete(String key) {
exchange.getAttributes().remove(key);
}
}

View File

@@ -0,0 +1,4 @@
/**
* sa-token集成Reactor响应式编程的各个组件
*/
package cn.dev33.satoken.reactor;

View File

@@ -0,0 +1,37 @@
package cn.dev33.satoken.reactor.spring;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
/**
*
* @author kong
*
*/
public class SaPathMatcherHolder {
/**
* 路由匹配器
*/
public static PathMatcher pathMatcher;
/**
* 获取路由匹配器
* @return 路由匹配器
*/
public static PathMatcher getPathMatcher() {
if(pathMatcher == null) {
pathMatcher = new AntPathMatcher();
}
return pathMatcher;
}
/**
* 写入路由匹配器
* @param pathMatcher 路由匹配器
*/
public static void setPathMatcher(PathMatcher pathMatcher) {
SaPathMatcherHolder.pathMatcher = pathMatcher;
}
}

View File

@@ -0,0 +1,127 @@
package cn.dev33.satoken.reactor.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.util.PathMatcher;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.action.SaTokenAction;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.context.SaTokenContext;
import cn.dev33.satoken.context.SaTokenContextForThreadLocal;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.stp.StpInterface;
/**
* 利用spring的自动装配来加载开发者重写的Bean
*
* @author kong
*
*/
@Component
public class SaTokenSpringAutowired {
/**
* 获取配置Bean
*
* @return 配置对象
*/
@Bean
@ConfigurationProperties(prefix = "spring.sa-token")
public SaTokenConfig getSaTokenConfig() {
return new SaTokenConfig();
}
/**
* 注入配置Bean
*
* @param saTokenConfig 配置对象
*/
@Autowired
public void setConfig(SaTokenConfig saTokenConfig) {
SaManager.setConfig(saTokenConfig);
}
/**
* 注入持久化Bean
*
* @param saTokenDao SaTokenDao对象
*/
@Autowired(required = false)
public void setSaTokenDao(SaTokenDao saTokenDao) {
SaManager.setSaTokenDao(saTokenDao);
}
/**
* 注入权限认证Bean
*
* @param stpInterface StpInterface对象
*/
@Autowired(required = false)
public void setStpInterface(StpInterface stpInterface) {
SaManager.setStpInterface(stpInterface);
}
/**
* 注入框架行为Bean
*
* @param saTokenAction SaTokenAction对象
*/
@Autowired(required = false)
public void setSaTokenAction(SaTokenAction saTokenAction) {
SaManager.setSaTokenAction(saTokenAction);
}
/**
* 获取容器交互Bean (ThreadLocal版)
*
* @return 容器交互Bean (ThreadLocal版)
*/
@Bean
public SaTokenContext getSaTokenContext() {
return new SaTokenContextForThreadLocal() {
/**
* 重写路由匹配方法
*/
@Override
public boolean matchPath(String pattern, String path) {
return SaPathMatcherHolder.getPathMatcher().match(pattern, path);
}
};
}
/**
* 注入容器交互Bean
*
* @param saTokenContext SaTokenContext对象
*/
@Autowired
public void setSaTokenContext(SaTokenContext saTokenContext) {
SaManager.setSaTokenContext(saTokenContext);
}
/**
* 注入侦听器Bean
*
* @param saTokenListener saTokenListener对象
*/
@Autowired(required = false)
public void setSaTokenListener(SaTokenListener saTokenListener) {
SaManager.setSaTokenListener(saTokenListener);
}
/**
* 利用自动匹配特性获取SpringMVC框架内部使用的路由匹配器
*
* @param pathMatcher 要设置的 pathMatcher
*/
@Autowired(required = false)
public void setPathMatcher(PathMatcher pathMatcher) {
SaPathMatcherHolder.setPathMatcher(pathMatcher);
}
}

View File

@@ -0,0 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.dev33.satoken.reactor.spring.SaTokenSpringAutowired

View File

@@ -0,0 +1,12 @@
target/
node_modules/
bin/
.settings/
unpackage/
.classpath
.project
.factorypath
.idea/

View File

@@ -0,0 +1,36 @@
<?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>1.18.0</version>
</parent>
<packaging>jar</packaging>
<name>sa-token-servlet</name>
<artifactId>sa-token-servlet</artifactId>
<description>sa-token authentication by Sservlet API</description>
<dependencies>
<!-- sa-token-core -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-core</artifactId>
<version>${sa-token-version}</version>
</dependency>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,84 @@
package cn.dev33.satoken.servlet.model;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import cn.dev33.satoken.context.model.SaRequest;
/**
* Request for Servlet
* @author kong
*
*/
public class SaRequestForServlet implements SaRequest {
/**
* 底层Request对象
*/
HttpServletRequest request;
/**
* 实例化
* @param request request对象
*/
public SaRequestForServlet(HttpServletRequest request) {
this.request = request;
}
/**
* 获取底层源对象
*/
@Override
public Object getSource() {
return request;
}
/**
* 在 [请求体] 里获取一个值
*/
@Override
public String getParameter(String name) {
return request.getParameter(name);
}
/**
* 在 [请求头] 里获取一个值
*/
@Override
public String getHeader(String name) {
return request.getHeader(name);
}
/**
* 在 [Cookie作用域] 里获取一个值
*/
@Override
public String getCookieValue(String name) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie != null && name.equals(cookie.getName())) {
return cookie.getValue();
}
}
}
return null;
}
/**
* 返回当前请求path (不包括上下文名称)
*/
@Override
public String getRequestPath() {
return request.getServletPath();
}
/**
* 返回当前请求的类型
*/
@Override
public String getMethod() {
return request.getMethod();
}
}

View File

@@ -0,0 +1,72 @@
package cn.dev33.satoken.servlet.model;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import cn.dev33.satoken.context.model.SaResponse;
import cn.dev33.satoken.util.SaFoxUtil;
/**
* Response for Servlet
* @author kong
*
*/
public class SaResponseForServlet implements SaResponse {
/**
* 底层Request对象
*/
HttpServletResponse response;
/**
* 实例化
* @param response response对象
*/
public SaResponseForServlet(HttpServletResponse response) {
this.response = response;
}
/**
* 获取底层源对象
*/
@Override
public Object getSource() {
return response;
}
/**
* 删除指定Cookie
*/
@Override
public void deleteCookie(String name) {
addCookie(name, null, null, null, 0);
}
/**
* 写入指定Cookie
*/
@Override
public void addCookie(String name, String value, String path, String domain, int timeout) {
Cookie cookie = new Cookie(name, value);
if(SaFoxUtil.isEmpty(path) == true) {
path = "/";
}
if(SaFoxUtil.isEmpty(domain) == false) {
cookie.setDomain(domain);
}
cookie.setPath(path);
cookie.setMaxAge(timeout);
response.addCookie(cookie);
}
/**
* 在响应头里写入一个值
*/
@Override
public SaResponse setHeader(String name, String value) {
response.setHeader(name, value);
return this;
}
}

View File

@@ -0,0 +1,59 @@
package cn.dev33.satoken.servlet.model;
import javax.servlet.http.HttpServletRequest;
import cn.dev33.satoken.context.model.SaStorage;
/**
* Storage for Servlet
* @author kong
*
*/
public class SaStorageForServlet implements SaStorage {
/**
* 底层Request对象
*/
HttpServletRequest request;
/**
* 实例化
* @param request request对象
*/
public SaStorageForServlet(HttpServletRequest request) {
this.request = request;
}
/**
* 获取底层源对象
*/
@Override
public Object getSource() {
return request;
}
/**
* 在 [Request作用域] 里写入一个值
*/
@Override
public void set(String key, Object value) {
request.setAttribute(key, value);
}
/**
* 在 [Request作用域] 里获取一个值
*/
@Override
public Object get(String key) {
return request.getAttribute(key);
}
/**
* 在 [Request作用域] 里删除一个值
*/
@Override
public void delete(String key) {
request.removeAttribute(key);
}
}

View File

@@ -0,0 +1,4 @@
/**
* Sa-Token对接ServletAPI容器所需要的实现类接口包
*/
package cn.dev33.satoken.servlet;

View File

@@ -0,0 +1,12 @@
target/
node_modules/
bin/
.settings/
unpackage/
.classpath
.project
.factorypath
.idea/

View File

@@ -0,0 +1,39 @@
<?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>1.18.0</version>
</parent>
<packaging>jar</packaging>
<name>sa-token-spring-boot-starter</name>
<artifactId>sa-token-spring-boot-starter</artifactId>
<description>springboot integrate sa-token</description>
<dependencies>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-servlet</artifactId>
<version>${sa-token-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.0.0.RELEASE</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,186 @@
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.exception.SaTokenException;
import cn.dev33.satoken.router.SaRouterUtil;
import cn.dev33.satoken.util.SaTokenConsts;
/**
* 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);
};
/**
* 前置函数:在每次[认证函数]之前执行
*/
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 {
// 执行全局过滤器
SaRouterUtil.match(includeList, excludeList, () -> {
beforeAuth.run(null);
auth.run(null);
});
} catch (Throwable e) {
// 1. 获取异常处理策略结果
Object result = error.run(e);
String resultString = String.valueOf(result);
// 2. 写入输出流
response.setContentType("text/plain; charset=utf-8");
response.getWriter().print(resultString);
return;
}
// 执行
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}

View File

@@ -0,0 +1,46 @@
package cn.dev33.satoken.interceptor;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import cn.dev33.satoken.SaManager;
/**
* 注解式鉴权 - 拦截器
*
* @author kong
*/
public class SaAnnotationInterceptor implements HandlerInterceptor {
/**
* 构建: 注解式鉴权 - 拦截器
*/
public SaAnnotationInterceptor() {
}
/**
* 每次请求之前触发的方法
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 获取处理method
if (handler instanceof HandlerMethod == false) {
return true;
}
Method method = ((HandlerMethod) handler).getMethod();
// 进行验证
SaManager.getSaTokenAction().checkMethodAnnotation(method);
// 通过验证
return true;
}
}

View File

@@ -0,0 +1,70 @@
package cn.dev33.satoken.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import cn.dev33.satoken.router.SaRouteFunction;
import cn.dev33.satoken.servlet.model.SaRequestForServlet;
import cn.dev33.satoken.servlet.model.SaResponseForServlet;
import cn.dev33.satoken.stp.StpUtil;
/**
* sa-token基于路由的拦截式鉴权
* @author kong
*/
public class SaRouteInterceptor implements HandlerInterceptor {
/**
* 每次进入拦截器的[执行函数]
*/
public SaRouteFunction function;
/**
* 创建一个路由拦截器
*/
public SaRouteInterceptor() {
}
/**
* 创建, 并指定[执行函数]
* @param function [执行函数]
*/
public SaRouteInterceptor(SaRouteFunction function) {
this.function = function;
}
/**
* 静态方法快速构建一个
* @param function 自定义模式下的执行函数
* @return sa路由拦截器
*/
public static SaRouteInterceptor newInstance(SaRouteFunction function) {
return new SaRouteInterceptor(function);
}
// ----------------- 验证方法 -----------------
/**
* 每次请求之前触发的方法
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 如果未提供function默认进行登录验证
if(function == null) {
StpUtil.checkLogin();
} else {
// 否则执行函数
function.run(new SaRequestForServlet(request), new SaResponseForServlet(response), handler);
}
// 通过验证
return true;
}
}

View File

@@ -0,0 +1,4 @@
/**
* sa-token集成SpringBoot的各个组件
*/
package cn.dev33.satoken;

View File

@@ -0,0 +1,37 @@
package cn.dev33.satoken.spring;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
/**
*
* @author kong
*
*/
public class SaPathMatcherHolder {
/**
* 路由匹配器
*/
public static PathMatcher pathMatcher;
/**
* 获取路由匹配器
* @return 路由匹配器
*/
public static PathMatcher getPathMatcher() {
if(pathMatcher == null) {
pathMatcher = new AntPathMatcher();
}
return pathMatcher;
}
/**
* 写入路由匹配器
* @param pathMatcher 路由匹配器
*/
public static void setPathMatcher(PathMatcher pathMatcher) {
SaPathMatcherHolder.pathMatcher = pathMatcher;
}
}

View File

@@ -0,0 +1,53 @@
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 对Cookie的相关操作 接口实现类
*
* @author kong
*
*/
public class SaTokenContextForSpring 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);
}
}

View File

@@ -0,0 +1,118 @@
package cn.dev33.satoken.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.util.PathMatcher;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.action.SaTokenAction;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.context.SaTokenContext;
import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.stp.StpInterface;
/**
* 利用spring的自动装配来加载开发者重写的Bean
*
* @author kong
*
*/
@Component
public class SaTokenSpringAutowired {
/**
* 获取配置Bean
*
* @return 配置对象
*/
@Bean
@ConfigurationProperties(prefix = "spring.sa-token")
public SaTokenConfig getSaTokenConfig() {
return new SaTokenConfig();
}
/**
* 注入配置Bean
*
* @param saTokenConfig 配置对象
*/
@Autowired
public void setConfig(SaTokenConfig saTokenConfig) {
SaManager.setConfig(saTokenConfig);
}
/**
* 注入持久化Bean
*
* @param saTokenDao SaTokenDao对象
*/
@Autowired(required = false)
public void setSaTokenDao(SaTokenDao saTokenDao) {
SaManager.setSaTokenDao(saTokenDao);
}
/**
* 注入权限认证Bean
*
* @param stpInterface StpInterface对象
*/
@Autowired(required = false)
public void setStpInterface(StpInterface stpInterface) {
SaManager.setStpInterface(stpInterface);
}
/**
* 注入框架行为Bean
*
* @param saTokenAction SaTokenAction对象
*/
@Autowired(required = false)
public void setSaTokenAction(SaTokenAction saTokenAction) {
SaManager.setSaTokenAction(saTokenAction);
}
/**
* 获取容器交互Bean (Spring版)
*
* @return 容器交互Bean (Spring版)
*/
@Bean
public SaTokenContext getSaTokenContext() {
return new SaTokenContextForSpring();
}
/**
* 注入容器交互Bean
*
* @param saTokenContext SaTokenContext对象
*/
@Autowired
public void setSaTokenContext(SaTokenContext saTokenContext) {
SaManager.setSaTokenContext(saTokenContext);
}
/**
* 注入侦听器Bean
*
* @param saTokenListener saTokenListener对象
*/
@Autowired(required = false)
public void setSaTokenListener(SaTokenListener saTokenListener) {
SaManager.setSaTokenListener(saTokenListener);
}
/**
* 利用自动匹配特性获取SpringMVC框架内部使用的路由匹配器
*
* @param pathMatcher 要设置的 pathMatcher
*/
@Autowired(required = false)
public void setPathMatcher(PathMatcher pathMatcher) {
SaPathMatcherHolder.setPathMatcher(pathMatcher);
}
}

View File

@@ -0,0 +1,42 @@
package cn.dev33.satoken.spring;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import cn.dev33.satoken.exception.SaTokenException;
/**
* SpringMVC相关操作
* @author kong
*
*/
public class SpringMVCUtil {
/**
* 获取当前会话的 request
* @return request
*/
public static HttpServletRequest getRequest() {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(servletRequestAttributes == null) {
throw new SaTokenException("非Web上下文无法获取Request");
}
return servletRequestAttributes.getRequest();
}
/**
* 获取当前会话的 response
* @return response
*/
public static HttpServletResponse getResponse() {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(servletRequestAttributes == null) {
throw new SaTokenException("非Web上下文无法获取Response");
}
return servletRequestAttributes.getResponse();
}
}

View File

@@ -0,0 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.dev33.satoken.spring.SaTokenSpringAutowired