初始代码

This commit is contained in:
wangmingwei
2026-04-21 17:41:09 +08:00
parent 186ec6683a
commit b686ecac5f
493 changed files with 52349 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
<?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/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>yunzhupaas-boot-common</artifactId>
<groupId>com.yunzhupaas</groupId>
<version>5.2.0-RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>yunzhupaas-common-security</artifactId>
<dependencies>
<dependency>
<groupId>com.yunzhupaas</groupId>
<artifactId>yunzhupaas-common-auth</artifactId>
</dependency>
<dependency>
<groupId>com.yunzhupaas</groupId>
<artifactId>yunzhupaas-common-database</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,150 @@
package com.yunzhupaas.config;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.fun.strategy.SaCheckElementAnnotationFunction;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.same.SaSameUtil;
import cn.dev33.satoken.strategy.SaStrategy;
import com.yunzhupaas.annotation.SaCheckSame;
import com.yunzhupaas.base.UserInfo;
import com.yunzhupaas.consts.AuthConsts;
import com.yunzhupaas.filter.ClearThreadContextFilter;
import com.yunzhupaas.filter.RequestWrapperFilter;
import com.yunzhupaas.filter.SecurityFilter;
import com.yunzhupaas.handler.IRestHandler;
import com.yunzhupaas.encrypt.EncryptRestInterceptor;
import com.yunzhupaas.properties.SecurityProperties;
import com.yunzhupaas.util.StringUtil;
import com.yunzhupaas.util.UserProvider;
import com.yunzhupaas.util.context.SpringContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import jakarta.servlet.*;
import java.util.List;
import java.util.function.BiFunction;
/**
*
* @author 云筑产品开发平台组
* @copyright 深圳市乐程软件有限公司
*/
@Slf4j
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class SecurityConfiguration implements WebMvcConfigurer {
@Autowired
private SecurityProperties securityProperties;
/**
* 注册sa-token的拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
initSaInterfaceAuth(registry);
initEncryptRestInterceptor(registry);
}
/**
* 请求封装过滤器
*/
@Bean("myRequestWrapperFilter")
@ConditionalOnMissingBean(name = "myRequestWrapperFilter")
public Filter getRequestWrapperFilter(List<IRestHandler> handlers){
return new RequestWrapperFilter(handlers, securityProperties);
}
/**
* 线程变量清除过滤器
*/
@Bean("myClearThreadContextFilter")
@ConditionalOnMissingBean(name = "myClearThreadContextFilter")
public Filter getClearThreadContextFilter(){
return new ClearThreadContextFilter();
}
/**
* 来源验证、用户、租户设置过滤器
*/
@Bean("mySecurityFilter")
@ConditionalOnMissingBean(name = "mySecurityFilter")
public Filter getSecurityFilter(SecurityProperties securityProperties, ConfigValueUtil configValueUtil){
return new SecurityFilter(securityProperties, configValueUtil).addInclude("/**");
}
/**
* 传输加密
*/
@Bean("myEncryptRestInterceptor")
@ConditionalOnMissingBean(name = "myEncryptRestInterceptor")
@ConditionalOnProperty(prefix = "security", name = "enable-rest-encrypt", havingValue = "true")
public HandlerInterceptor getEncryptRestInterceptor(){
return new EncryptRestInterceptor();
}
protected void initEncryptRestInterceptor(InterceptorRegistry registry){
if(securityProperties.isEnableRestEncrypt()){
registry.addInterceptor(SpringContext.getBean("myEncryptRestInterceptor"));
}
}
protected void initSaInterfaceAuth(InterceptorRegistry registry){
if(securityProperties.isEnablePreAuth()) {
// 旧注解校验方法
SaCheckElementAnnotationFunction oldCheckElementAnnotation = SaStrategy.instance.checkElementAnnotation;
SaStrategy.instance.checkElementAnnotation = (element) -> {
// 添加内部请求校验
SaCheckSame checkRole = (SaCheckSame) SaStrategy.instance.getAnnotation.apply(element, SaCheckSame.class);
if(checkRole != null){
SaSameUtil.checkToken(SaHolder.getRequest().getHeader(AuthConsts.INNER_TOKEN_KEY));
}
// 调用旧注解校验
oldCheckElementAnnotation.accept(element);
};
// 开启接口请求权限控制
registry.addInterceptor(new SaInterceptor((handler) -> {
}).isAnnotation(securityProperties.isEnablePreAuth())).addPathPatterns("/**");
}
//接口鉴权忽略管理员、内部请求
BiFunction<List<String>, String, Boolean> oldCheckFunc = SaStrategy.instance.hasElement;
SaStrategy.instance.hasElement = (list, element) -> {
//启用之后才验证
if (securityProperties.isEnablePreAuth()) {
UserInfo userInfo = UserProvider.getUser();
//未获取到用户信息返回false
if (StringUtil.isEmpty(userInfo.getUserId())) {
return false;
}
//管理员返回true
if (userInfo.getIsAdministrator()) {
return true;
}
boolean result = oldCheckFunc.apply(list, element);
//如果鉴权失败, 检测是否来自内部请求
if (!result) {
String innerToken = SaHolder.getRequest().getHeader(AuthConsts.INNER_TOKEN_KEY);
//来自内部请求(非网关) 无需鉴权
if (UserProvider.isValidInnerToken(innerToken)) {
result = true;
}
}
return result;
}
return true;
};
}
}

View File

@@ -0,0 +1,27 @@
package com.yunzhupaas.encrypt;
import com.yunzhupaas.handler.IRestHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Slf4j
@Order(0)
@Component
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnProperty(prefix = "security", name = "enable-rest-encrypt", havingValue = "true")
public class EmptyEncryptRestRequestHandler implements IRestHandler {
@Override
public boolean supportParameter() {
return true;
}
@Override
public boolean supportBodyJson() {
return true;
}
}

View File

@@ -0,0 +1,43 @@
package com.yunzhupaas.encrypt;
import cn.hutool.core.util.HexUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yunzhupaas.annotation.EncryptApi;
import com.yunzhupaas.properties.SecurityProperties;
import com.yunzhupaas.util.DesUtil;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@ControllerAdvice
public class EncryptResponseAdvice implements ResponseBodyAdvice<Object> {
private ObjectMapper objectMapper;
public EncryptResponseAdvice(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
EncryptApi methodAnnotation = returnType.getMethodAnnotation(EncryptApi.class);
return methodAnnotation != null && methodAnnotation.encryptResponse();
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
try {
byte[] bytes = objectMapper.writeValueAsBytes(body);
return HexUtil.encodeHexStr(DesUtil.aesOrDecode(bytes, true, true, SecurityProperties.getInstance().getSecurityKey()));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,154 @@
package com.yunzhupaas.encrypt;
import cn.hutool.core.net.url.UrlQuery;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import com.yunzhupaas.annotation.EncryptApi;
import com.yunzhupaas.constant.GlobalConst;
import com.yunzhupaas.constant.MsgCode;
import com.yunzhupaas.exception.EncryptFailException;
import com.yunzhupaas.util.DesUtil;
import com.yunzhupaas.util.StringUtil;
import com.yunzhupaas.wrapper.MyRequestWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import java.net.URLDecoder;
import java.util.*;
/**
* 接口传输加密
* 支持请求类型:
* application/json
* application/x-www-form-urlencoded
*/
@Slf4j
public class EncryptRestInterceptor implements AsyncHandlerInterceptor {
private static final String ENCRYPT_KEY = "encryptData";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
try {
if (handler instanceof HandlerMethod && request instanceof MyRequestWrapper) {
HandlerMethod method = (HandlerMethod) handler;
EncryptApi methodAnnotation = method.getMethodAnnotation(EncryptApi.class);
if (methodAnnotation != null && methodAnnotation.encryptRequest()) {
MyRequestWrapper myRequest = (MyRequestWrapper) request;
// 需要对数据进行加密解密
// application/json
// application/x-www-form-urlencoded
String contentType = request.getContentType();
if (contentType != null) {
String requestBody = null;
boolean canEncrypt = false;
if ((StringUtils.substringMatch(contentType, 0,
MediaType.APPLICATION_FORM_URLENCODED_VALUE))
|| "get".equalsIgnoreCase(request.getMethod())) {
// 1.application/x-www-form-urlencoded 支持参数在body或者在param
canEncrypt = true;
requestBody = convertFormToString(request);
if ("{}".equals(requestBody)) {
requestBody = URLDecoder.decode(myRequest.getRequestBody());
Map<CharSequence, CharSequence> uriToListToMap = new UrlQuery()
.parse(requestBody, GlobalConst.DEFAULT_CHARSET).getQueryMap();
requestBody = JSONObject.toJSONString(uriToListToMap);
}
} else if (StringUtils.substringMatch(contentType, 0, MediaType.APPLICATION_JSON_VALUE)) {
// application/json 支持加密参数在body
canEncrypt = true;
requestBody = myRequest.getRequestBody();
}
if (canEncrypt) {
if (requestBody != null && !"{}".equals(requestBody)) {
JSONObject jsonBody = JSON.parseObject(requestBody);
JSON result = decodeApi(jsonBody);
if (result != null) {
myRequest.setRequestBody(result.toJSONString());
if (result instanceof JSONObject) {
myRequest.addAllParameters((Map<String, Object>) result);
}
return true;
}
}
}
}
throw encryptFailException();
}
}
} catch (EncryptFailException eex) {
throw eex;
} catch (Exception e) {
log.error("解密失败, 异常地址:{}", request.getServletPath(), e);
throw encryptFailException();
}
return true;
}
/**
* Pamams参数转JSON字符串
*
* @param request
* @return
*/
private String convertFormToString(HttpServletRequest request) {
Map<String, String> result = new HashMap<>(8);
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String name = parameterNames.nextElement();
result.put(name, request.getParameter(name));
}
try {
return JSON.toJSONString(result);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
/**
* 请求内容解密
*
* @param body
* @return
*/
public JSON decodeApi(JSON body) {
try {
JSONObject jsonObject = (JSONObject) body;
String content = jsonObject.getOrDefault(ENCRYPT_KEY, "").toString();
if (!StringUtil.isEmpty(content)) {
content = decryptData(content);
return (JSON) JSON.parse(content);
}
} catch (Exception e) {
log.error("解密失败, 文本: {}", body, e);
}
return null;
}
/**
* 文本解密
*
* @param data
* @return
*/
protected String decryptData(String data) {
if (StringUtil.isEmpty(data)) {
return data;
}
if (Objects.equals(data.charAt(0), '"') && Objects.equals(data.charAt(data.length() - 1), '"')) {
data = data.substring(1, data.length() - 1);
}
return DesUtil.aesOrDecode(data, false, true);
}
private EncryptFailException encryptFailException() {
throw new EncryptFailException(MsgCode.FA051.get());
}
}

View File

@@ -0,0 +1,41 @@
package com.yunzhupaas.filter;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.yunzhupaas.database.util.IgnoreLogicDeleteHolder;
import com.yunzhupaas.database.util.NotTenantPluginHolder;
import com.yunzhupaas.database.util.TenantDataSourceUtil;
import com.yunzhupaas.util.TenantProvider;
import com.yunzhupaas.util.UserProvider;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 线程缓存清理
*/
@Order(-99)
public class ClearThreadContextFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
filterChain.doFilter(request, response);
}finally {
//清除线程缓存
UserProvider.clearLocalUser();
TenantProvider.clearBaseSystemIfo();
TenantDataSourceUtil.clearLocalTenantInfo();
DynamicDataSourceContextHolder.clear();
NotTenantPluginHolder.clearNotSwitchFlag();
IgnoreLogicDeleteHolder.clear();
}
}
}

View File

@@ -0,0 +1,48 @@
package com.yunzhupaas.filter;
import cn.dev33.satoken.context.SaHolder;
import com.yunzhupaas.handler.IRestHandler;
import com.yunzhupaas.properties.SecurityProperties;
import com.yunzhupaas.util.PathUtil;
import com.yunzhupaas.wrapper.MyRequestWrapper;
import com.yunzhupaas.wrapper.MyResponseWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* 请求内容
*/
@Slf4j
@Order(-97)
public class RequestWrapperFilter extends OncePerRequestFilter {
private List<IRestHandler> handlers;
private SecurityProperties securityProperties;
public RequestWrapperFilter(List<IRestHandler> handlers, SecurityProperties securityProperties) {
this.handlers = handlers;
this.securityProperties = securityProperties;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if(handlers.isEmpty()){
filterChain.doFilter(request, response);
} else {
MyRequestWrapper myRequest = new MyRequestWrapper(request, handlers);
MyResponseWrapper wrapResponse = new MyResponseWrapper(response, handlers);
filterChain.doFilter(myRequest, wrapResponse);
wrapResponse.doFinal();
}
}
}

View File

@@ -0,0 +1,83 @@
package com.yunzhupaas.filter;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.router.SaRouter;
import com.yunzhupaas.base.UserInfo;
import com.yunzhupaas.config.ConfigValueUtil;
import com.yunzhupaas.constant.MsgCode;
import com.yunzhupaas.consts.AuthConsts;
import com.yunzhupaas.database.util.TenantDataSourceUtil;
import com.yunzhupaas.properties.SecurityProperties;
import com.yunzhupaas.util.StringUtil;
import com.yunzhupaas.util.UserProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.core.annotation.Order;
import jakarta.annotation.PostConstruct;
import java.util.function.Predicate;
@Slf4j
@Order(-98)
public class SecurityFilter extends SaServletFilter {
private SecurityProperties securityProperties;
private ConfigValueUtil configValueUtil;
public SecurityFilter(SecurityProperties securityProperties, ConfigValueUtil configValueUtil) {
this.securityProperties = securityProperties;
this.configValueUtil = configValueUtil;
setAuth(o -> {
initAuthenticationInfo();
});
setBeforeAuth(o -> {
checkRequestOrigin.test(null);
});
}
/**
* 请求来源验证
*/
private Predicate<Object> checkRequestOrigin = t -> true;
protected void initAuthenticationInfo(){
//执行Token续期, 存用户信息至本地缓存后续无需重新获取
UserProvider.renewTimeout();
//设置租户信息
if(configValueUtil.isMultiTenancy()) {
UserInfo userInfo = UserProvider.getUser();
if (StringUtil.isNotEmpty(userInfo.getTenantId())) {
TenantDataSourceUtil.switchTenant(userInfo.getTenantId());
}
}
}
/**
* 微服务才验证请求来源
*/
@ConditionalOnClass(name = "org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient")
@PostConstruct
public void onCloudCheckRequestOrigin(){
checkRequestOrigin = t -> {
//验证请求来源, 网关, Feign
if(securityProperties.isEnableInnerAuth()) {
SaRouter.match("/favicon.ico").stop();
SaRouter.match("/**").match(r->{
String innerToken = SaHolder.getRequest().getHeader(AuthConsts.INNER_TOKEN_KEY);
String innerGatewayToken = SaHolder.getRequest().getHeader(AuthConsts.INNER_GATEWAY_TOKEN_KEY);
if(!UserProvider.isValidInnerToken(innerGatewayToken) && !UserProvider.isValidInnerToken(innerToken)){
log.error("非法请求: {}, {}", SaHolder.getRequest().getRequestPath(), innerToken);
return true;
}
return false;
}).back(MsgCode.FA034.get());
}
return true;
};
}
}

View File

@@ -0,0 +1,59 @@
package com.yunzhupaas.handler;
import com.alibaba.fastjson.JSON;
import java.util.Map;
public interface IRestHandler {
/**
* 是否处理Header
*/
default boolean supportHeader(){
return false;
}
/**
* 是否处理Form表单数据
*/
default boolean supportParameter(){
return false;
}
/**
* 是否处理Body JSON
*/
default boolean supportBodyJson(){
return false;
}
/**
* 是否处理返回结果
*/
default boolean supportResponse(){
return false;
}
/**
* 初始化Body JSON
*/
default JSON initBodyJson(JSON jsonContent){
return jsonContent;
}
/**
* 初始化Form表单数据
*/
default Map<String, String[]> initParameter(Map<String, String[]> parameter){
return parameter;
}
/**
* 处理返回结果
*/
default String processResponse(String data) {
return data;
}
}

View File

@@ -0,0 +1,131 @@
package com.yunzhupaas.permissions;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpUtil;
import com.yunzhupaas.base.UserInfo;
import com.yunzhupaas.model.BaseSystemInfo;
import com.yunzhupaas.properties.SecurityProperties;
import com.yunzhupaas.util.TenantProvider;
import com.yunzhupaas.util.UserProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.*;
import static com.yunzhupaas.util.Constants.ADMIN_KEY;
/**
* 权限认证接口实现
*
* @author 云筑产品开发平台组
* @copyright 深圳市乐程软件有限公司
*/
@Component
public class PermissionInterfaceImpl implements StpInterface {
public static final String PERMISSION_KEY = "user_permission";
public static final String ROLE_KEY = "user_roles";
public static final String COLUMN_KEY = "user_columns";
public static final String FORM_KEY = "user_forms";
@Autowired
private SecurityProperties securityProperties;
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
if (!securityProperties.isEnablePreAuth()) {
return Collections.emptyList();
}
UserInfo userInfo = UserProvider.getUser();
String account = userInfo.getIsAdministrator() ? ADMIN_KEY : userInfo.getUserId();
account = UserProvider.splicingLoginId(account);
SaSession saSession = StpUtil.getSessionByLoginId(account, false);
if (saSession == null) {
return Collections.emptyList();
}
return saSession.get(PERMISSION_KEY, Collections.emptyList());
}
@Override
public List<String> getRoleList(Object loginId, String loginType) {
if (!securityProperties.isEnablePreAuth()) {
return Collections.emptyList();
}
UserInfo userInfo = UserProvider.getUser();
String account = userInfo.getIsAdministrator() ? ADMIN_KEY : userInfo.getUserId();
account = UserProvider.splicingLoginId(account);
SaSession saSession = StpUtil.getSessionByLoginId(account, false);
if (saSession == null) {
return Collections.emptyList();
}
return saSession.get(ROLE_KEY, Collections.emptyList());
}
public static Map<String, Object> getColumnMap() {
UserInfo userInfo = UserProvider.getUser();
String account = userInfo.getIsAdministrator() ? ADMIN_KEY : userInfo.getUserId();
account = UserProvider.splicingLoginId(account);
SaSession saSession = StpUtil.getSessionByLoginId(account, false);
if (saSession == null) {
return Collections.emptyMap();
}
return saSession.get(COLUMN_KEY, Collections.emptyMap());
}
public static Map<String, Object> getFormMap() {
UserInfo userInfo = UserProvider.getUser();
String account = userInfo.getIsAdministrator() ? ADMIN_KEY : userInfo.getUserId();
account = UserProvider.splicingLoginId(account);
SaSession saSession = StpUtil.getSessionByLoginId(account, false);
if (saSession == null) {
return Collections.emptyMap();
}
return saSession.get(FORM_KEY, Collections.emptyMap());
}
public static void setAuthorityList(String userAccount, Set<String> authority, BaseSystemInfo baseSystemInfo) {
userAccount = UserProvider.splicingLoginId(userAccount);
try {
TenantProvider.setBaseSystemInfo(baseSystemInfo);
StpUtil.getSessionByLoginId(userAccount, true).set(PERMISSION_KEY, new ArrayList<>(authority));
} finally {
TenantProvider.clearBaseSystemIfo();
}
}
public static void setRoleList(String userAccount, Set<String> role, BaseSystemInfo baseSystemInfo) {
userAccount = UserProvider.splicingLoginId(userAccount);
try {
TenantProvider.setBaseSystemInfo(baseSystemInfo);
StpUtil.getSessionByLoginId(userAccount, true).set(ROLE_KEY, new ArrayList<>(role));
} finally {
TenantProvider.clearBaseSystemIfo();
}
}
public static void setColumnMap(String userAccount, Map<String, List<Map<String, Object>>> columnList, BaseSystemInfo baseSystemInfo) {
userAccount = UserProvider.splicingLoginId(userAccount);
try {
TenantProvider.setBaseSystemInfo(baseSystemInfo);
StpUtil.getSessionByLoginId(userAccount, true).set(COLUMN_KEY, columnList);
} finally {
TenantProvider.clearBaseSystemIfo();
}
}
public static void setFormMap(String userAccount, Map<String, List<Map<String, Object>>> formList, BaseSystemInfo baseSystemInfo) {
userAccount = UserProvider.splicingLoginId(userAccount);
try {
TenantProvider.setBaseSystemInfo(baseSystemInfo);
StpUtil.getSessionByLoginId(userAccount, true).set(FORM_KEY, formList);
} finally {
TenantProvider.clearBaseSystemIfo();
}
}
}

View File

@@ -0,0 +1,240 @@
package com.yunzhupaas.wrapper;
import cn.dev33.satoken.context.SaHolder;
import cn.hutool.core.net.url.UrlQuery;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yunzhupaas.constant.GlobalConst;
import com.yunzhupaas.handler.IRestHandler;
import com.yunzhupaas.util.StringUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
/**
* Request封装
* 处理类型:
* application/json
* application/x-www-form-urlencoded
*/
@Slf4j
public class MyRequestWrapper extends HttpServletRequestWrapper {
protected Map<String, String[]> paramHashValues;
@Getter
@Setter
protected String requestBody = null;
protected HttpServletRequest req;
private final List<IRestHandler> handlers;
private final String[] EMPTY_ARRAY = new String[0];
/**
* 是否需要处理Header, Body, Form
*/
private boolean supportHeader, supportBody, supportParameter;
public MyRequestWrapper(HttpServletRequest request, List<IRestHandler> handlers) throws IOException {
super(request);
this.req = request;
this.handlers = handlers == null ? Collections.emptyList() : handlers;
if(!handlers.isEmpty()){
try {
if (isJsonBodyRequest()) {
processBody();
} else if (isParameterRequest()) {
processParameter();
processFormBody();
}
} catch (Exception e){
log.error("请求解析失败:{}", SaHolder.getRequest().getRequestPath());
throw e;
}
}
}
private void processBody() throws IOException {
List<IRestHandler> bodyHandlers = this.handlers.stream().filter(IRestHandler::supportBodyJson).collect(Collectors.toList());
if (!bodyHandlers.isEmpty()) {
this.requestBody = convertInputStreamToString(req.getInputStream());
if (StringUtil.isNotEmpty(this.requestBody)) {
JSON jsonData = (JSON) JSON.parse(this.requestBody);
for (IRestHandler bodyHandler : bodyHandlers) {
jsonData = bodyHandler.initBodyJson(jsonData);
}
requestBody = jsonData.toJSONString();
supportBody = true;
}
}
}
private void processParameter(){
List<IRestHandler> parameterHandlers = this.handlers.stream().filter(IRestHandler::supportParameter).collect(Collectors.toList());
if (!parameterHandlers.isEmpty()) {
paramHashValues = new HashMap<>();
paramHashValues.putAll(req.getParameterMap());
/*
//解除锁定直接添加
if(paramHashValues instanceof ParameterMap){
((ParameterMap<String, String[]>) paramHashValues).setLocked(false);
}
*/
parameterHandlers.forEach(h -> paramHashValues = h.initParameter(paramHashValues));
supportParameter = true;
}
}
private void processFormBody() throws IOException {
List<IRestHandler> bodyHandlers = this.handlers.stream().filter(IRestHandler::supportBodyJson).collect(Collectors.toList());
if (!bodyHandlers.isEmpty()) {
this.requestBody = convertInputStreamToString(req.getInputStream());
if(StringUtil.isNotEmpty(this.requestBody)){
Map<CharSequence, CharSequence> uriToListToMap = new UrlQuery().parse(this.requestBody, GlobalConst.DEFAULT_CHARSET).getQueryMap();
requestBody = JSONObject.toJSONString(uriToListToMap);
}
if (StringUtil.isNotEmpty(this.requestBody)) {
JSON jsonData = (JSON) JSON.parse(this.requestBody);
for (IRestHandler bodyHandler : bodyHandlers) {
jsonData = bodyHandler.initBodyJson(jsonData);
}
Map<String, Object> formJson = (JSONObject) jsonData;
UrlQuery urlQuery = new UrlQuery();
for (Map.Entry<String, Object> entry : formJson.entrySet()) {
urlQuery.add(entry.getKey(), entry.getValue());
}
requestBody = urlQuery.build(GlobalConst.DEFAULT_CHARSET);
supportBody = true;
}
}
}
@Override
public BufferedReader getReader() throws IOException {
if(supportBody){
return new BufferedReader(new StringReader(requestBody));
}else{
return super.getReader();
}
}
@Override
public ServletInputStream getInputStream() throws IOException {
if(supportBody){
return new ServletInputStream() {
private InputStream in = new ByteArrayInputStream(
requestBody.getBytes(req.getCharacterEncoding()));
@Override
public int read() throws IOException {
return in.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}else {
return super.getInputStream();
}
}
@Override
public Map<String, String[]> getParameterMap() {
if(supportParameter){
return this.paramHashValues;
}else {
return super.getParameterMap();
}
}
@Override
public String getParameter(String name) {
if(supportParameter){
String[] parameter = this.paramHashValues.getOrDefault(name, EMPTY_ARRAY);
return parameter.length == 0 ? null: parameter[0];
}else {
return super.getParameter(name);
}
}
@Override
public String[] getParameterValues(String name) {
if(supportParameter){
return this.paramHashValues.get(name);
}else {
return super.getParameterValues(name);
}
}
@Override
public Enumeration<String> getParameterNames() {
if(supportParameter){
return Collections.enumeration(this.paramHashValues.keySet());
}else {
return super.getParameterNames();
}
}
public void addAllParameters(Map<String, Object> otherParams) {// 增加多个参数
for (Map.Entry<String, Object> entry : otherParams.entrySet()) {
addParameter(entry.getKey(), entry.getValue());
}
}
public void addParameter(String name, Object value) {// 增加参数
if (value != null && paramHashValues != null) {
if (value instanceof String[]) {
paramHashValues.put(name, (String[]) value);
} else if (value instanceof String) {
paramHashValues.put(name, new String[]{(String) value});
} else {
paramHashValues.put(name, new String[]{String.valueOf(value)});
}
}
}
protected boolean isParameterRequest(){
String contentType = this.req.getContentType();
if(StringUtil.isNotEmpty(contentType)) {
if (StringUtils.substringMatch(contentType, 0, MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
return true;
}
}
return false;
}
protected boolean isJsonBodyRequest(){
String contentType = this.req.getContentType();
if(StringUtil.isNotEmpty(contentType)) {
if (StringUtils.substringMatch(contentType, 0, MediaType.APPLICATION_JSON_VALUE)) {
return true;
}
}
return false;
}
protected String convertInputStreamToString(InputStream inputStream) throws IOException {
return StreamUtils.copyToString(inputStream, GlobalConst.DEFAULT_CHARSET);
}
}

View File

@@ -0,0 +1,159 @@
package com.yunzhupaas.wrapper;
import com.yunzhupaas.constant.GlobalConst;
import com.yunzhupaas.handler.IRestHandler;
import com.yunzhupaas.util.StringUtil;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.WriteListener;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponseWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* Response封装
* 处理类型:
* application/json
*/
public class MyResponseWrapper extends HttpServletResponseWrapper {
private ByteArrayOutputStream buffer;
private ServletOutputStream out = null;
private PrintWriter writer = null;
private List<IRestHandler> handlers;
private boolean supportResponse;
public MyResponseWrapper(HttpServletResponse resp, List<IRestHandler> handlers) throws IOException {
super(resp);
this.handlers = handlers.stream().filter(IRestHandler::supportResponse).collect(Collectors.toList());
supportResponse = !handlers.isEmpty();
//返回处理器顺序翻转
Collections.reverse(handlers);
if(supportResponse) {
buffer = new ByteArrayOutputStream();
}
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
if(supportResponse) {
if (out == null) {
out = new WapperedOutputStream(buffer);
}
return out;
}else{
return super.getOutputStream();
}
}
@Override
public PrintWriter getWriter() throws IOException {
if(supportResponse) {
if (writer == null) {
writer = new PrintWriter(new OutputStreamWriter(buffer, this.getCharacterEncoding()));
}
return writer;
}else{
return super.getWriter();
}
}
@Override
public void flushBuffer() throws IOException {
if(supportResponse) {
if (out != null) {
out.flush();
}
if (writer != null) {
writer.flush();
}
}else{
super.flushBuffer();
}
}
@Override
public void reset() {
if(supportResponse) {
buffer.reset();
}else{
super.reset();
}
}
public void doFinal() throws IOException{
if(supportResponse) {
flushBuffer();
if (buffer.size() > 0) {
byte[] byteArray = buffer.toByteArray();
if (supportResponse && isJsonBodyRequest()) {
getResponse().setContentLength(-1);
getResponse().setCharacterEncoding(GlobalConst.DEFAULT_CHARSET_STR);
String data = new String(byteArray, GlobalConst.DEFAULT_CHARSET);
for (IRestHandler handler : handlers) {
data = handler.processResponse(data);
}
writeResponse(data);
return;
}
writeResponse(byteArray);
}
}
}
private void writeResponse(String responseString)
throws IOException {
PrintWriter out = getResponse().getWriter();
out.write(responseString);
out.flush();
out.close();
}
private void writeResponse(byte[] responseData) throws IOException {
ServletOutputStream outputStream = getResponse().getOutputStream();
outputStream.write(responseData);
outputStream.flush();
outputStream.close();
}
private class WapperedOutputStream extends ServletOutputStream {
private ByteArrayOutputStream bos = null;
public WapperedOutputStream(ByteArrayOutputStream stream) throws IOException {
bos = stream;
}
@Override
public void write(int b) throws IOException {
bos.write(b);
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener listener) {
}
}
protected boolean isJsonBodyRequest(){
String contentType = getResponse().getContentType();
if(StringUtil.isNotEmpty(contentType)) {
if (StringUtils.substringMatch(contentType, 0, MediaType.APPLICATION_JSON_VALUE)) {
return true;
}
}
return false;
}
}