数据集成修改

This commit is contained in:
cwchen 2025-09-11 16:47:21 +08:00
parent e422008325
commit 95211bf2ce
14 changed files with 176 additions and 76 deletions

View File

@ -0,0 +1,35 @@
package com.bonus.web.controller.common;
import com.bonus.common.core.domain.AjaxResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* 会话检查控制器
*/
@RestController
@RequestMapping("/api/session")
public class SessionCheckController {
// @Autowired
// private SessionManagementService sessionManagementService;
/**
* 检查会话状态
*/
/*@GetMapping("/check")
public AjaxResult checkSession(HttpServletRequest request) {
String sessionId = request.getSession().getId();
if (sessionManagementService.isSessionForcedLogout(sessionId)) {
String message = sessionManagementService.getForceLogoutMessage(sessionId);
return AjaxResult.error(403, message).put("forceLogout", true);
}
return AjaxResult.success().put("forceLogout", false);
}*/
}

View File

@ -24,6 +24,9 @@ import com.bonus.framework.web.service.TokenService;
import com.bonus.system.service.ISysConfigService;
import com.bonus.system.service.ISysMenuService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 登录验证
*
@ -54,11 +57,11 @@ public class SysLoginController
* @return 结果
*/
@PostMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody)
public AjaxResult login(HttpServletRequest request, @RequestBody LoginBody loginBody)
{
AjaxResult ajax = AjaxResult.success();
// 生成令牌
String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
String token = loginService.login(request,loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
loginBody.getUuid());
ajax.put(Constants.TOKEN, token);
return ajax;

View File

@ -109,6 +109,7 @@ public class KyDataClassifyController extends BaseController {
* @return
*/
/*@PreAuthorize("@ss.hasPermi('data:classify:list')")*/
@RequiresPermissions("data:Collect:list")
@GetMapping("/listAll")
public TableDataInfo listAll(KyDataClassify kyDataClassify)
{

View File

@ -164,6 +164,11 @@
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -218,8 +218,8 @@ public class RequestCoverFilter implements Filter {
throw new CaptchaException("请求参数不正确");
}
String calculatedHash = Sm3Util.encrypt(query);
log.debug("计算出的哈希值: {}", calculatedHash);
log.debug("提供的哈希值: {}", providedHmac);
log.info("计算出的哈希值: {}", calculatedHash);
log.info("提供的哈希值: {}", providedHmac);
if (!calculatedHash.equals(providedHmac)) {
log.error("参数完整性校验失败");
throw new CaptchaException("请求参数不正确");
@ -279,6 +279,7 @@ public class RequestCoverFilter implements Filter {
public QueryStringRequestWrapper(HttpServletRequest request, String queryString) {
super(request);
this.queryString = queryString;
// log.info("QueryStringRequestWrapper 接收到的参数: {}", queryString);
}
@Override
@ -295,7 +296,7 @@ public class RequestCoverFilter implements Filter {
@Override
public Map<String, String[]> getParameterMap() {
if (cachedParameterMap == null) {
cachedParameterMap = parseNestedQueryString(queryString);
cachedParameterMap = parseQueryString(queryString);
}
return cachedParameterMap;
}
@ -311,52 +312,68 @@ public class RequestCoverFilter implements Filter {
}
/**
* 解析嵌套查询字符串为Map支持 params[beginTime] 格式
* 解析查询字符串支持普通格式和嵌套格式
*/
private Map<String, String[]> parseNestedQueryString(String queryString) {
private Map<String, String[]> parseQueryString(String queryString) {
Map<String, String[]> parameterMap = new HashMap<>();
if (queryString != null) {
try {
// 先URL解码
String decodedQueryString = java.net.URLDecoder.decode(queryString, StandardCharsets.UTF_8.name());
String[] pairs = decodedQueryString.split("&");
if (queryString == null || queryString.trim().isEmpty()) {
return parameterMap;
}
for (String pair : pairs) {
String[] keyValue = pair.split("=", 2);
if (keyValue.length == 2) {
String key = keyValue[0];
String value = keyValue[1];
// log.info("开始解析查询字符串: {}", queryString);
// 处理嵌套参数格式 params[beginTime]
if (key.startsWith("params[") && key.endsWith("]")) {
// 提取嵌套参数名如从 "params[beginTime]" 提取 "beginTime"
String nestedKey = key.substring(7, key.length() - 1);
try {
// 先URL解码
String decodedQueryString = java.net.URLDecoder.decode(queryString, StandardCharsets.UTF_8.name());
String[] pairs = decodedQueryString.split("&");
// 将嵌套参数转换为 params. 前缀的参数
String paramsKey = "params[" + nestedKey + "]";
parameterMap.put(paramsKey, new String[]{value});
} else {
// 普通参数处理
if (parameterMap.containsKey(key)) {
String[] existingValues = parameterMap.get(key);
String[] newValues = Arrays.copyOf(existingValues, existingValues.length + 1);
newValues[existingValues.length] = value;
parameterMap.put(key, newValues);
} else {
parameterMap.put(key, new String[]{value});
}
}
}
for (String pair : pairs) {
if (pair == null || pair.trim().isEmpty()) {
continue;
}
} catch (Exception e) {
log.warn("解析嵌套查询字符串失败,使用简单解析: {}", e.getMessage());
return parseSimpleQueryString(queryString);
String[] keyValue = pair.split("=", 2);
if (keyValue.length >= 1) {
String key = keyValue[0].trim();
String value = keyValue.length == 2 ? keyValue[1].trim() : "";
// 处理嵌套参数格式 params[beginTime]
if (key.startsWith("params[") && key.endsWith("]")) {
String nestedKey = key.substring(7, key.length() - 1);
String paramsKey = "params[" + nestedKey + "]";
addParameter(parameterMap, paramsKey, value);
} else {
// 处理普通参数 pageNum=1, pageSize=10
addParameter(parameterMap, key, value);
}
}
}
} catch (Exception e) {
log.warn("解析查询字符串失败: {}", e.getMessage());
// 失败时尝试简单解析
return parseSimpleQueryString(queryString);
}
// log.info("解析后的参数Map: {}", parameterMap);
return parameterMap;
}
/**
* 添加参数到Map支持多值参数
*/
private void addParameter(Map<String, String[]> parameterMap, String key, String value) {
if (parameterMap.containsKey(key)) {
String[] existingValues = parameterMap.get(key);
String[] newValues = Arrays.copyOf(existingValues, existingValues.length + 1);
newValues[existingValues.length] = value;
parameterMap.put(key, newValues);
} else {
parameterMap.put(key, new String[]{value});
}
// log.info("添加参数: {} = {}", key, value);
}
/**
* 简单解析查询字符串备用方案
*/
@ -369,15 +386,7 @@ public class RequestCoverFilter implements Filter {
if (keyValue.length == 2) {
String key = keyValue[0];
String value = keyValue[1];
if (parameterMap.containsKey(key)) {
String[] existingValues = parameterMap.get(key);
String[] newValues = Arrays.copyOf(existingValues, existingValues.length + 1);
newValues[existingValues.length] = value;
parameterMap.put(key, newValues);
} else {
parameterMap.put(key, new String[]{value});
}
addParameter(parameterMap, key, value);
}
}
}

View File

@ -10,6 +10,7 @@ import org.springframework.security.authentication.dao.DaoAuthenticationProvider
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@ -111,7 +112,7 @@ public class SecurityConfig
.authorizeHttpRequests((requests) -> {
permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
requests.antMatchers("/login", "/register", "/captchaImage").permitAll()
requests.antMatchers("/login", "/register", "/captchaImage","/session/check").permitAll()
// 静态资源可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()

View File

@ -0,0 +1,30 @@
package com.bonus.framework.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.session.HttpSessionEventPublisher;
/**
* Session管理配置
*/
@Configuration
public class SessionConfig {
/**
* 注册SessionRegistry bean
*/
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
/**
* 注册HttpSessionEventPublisher用于监听session创建和销毁事件
*/
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
}

View File

@ -38,6 +38,7 @@ public class ReplayAttackFilter implements Filter {
ignoreUrlPatterns.add("/smartArchives/logout");
ignoreUrlPatterns.add("/smartArchives/getInfo");
ignoreUrlPatterns.add("/smartArchives/getRouters");
ignoreUrlPatterns.add("/smartArchives/session/check");
}
@Autowired

View File

@ -5,6 +5,7 @@ import com.bonus.common.core.domain.AjaxResult;
import com.bonus.common.utils.SafeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
@ -33,6 +34,7 @@ public class ParamSecureHandler implements AsyncHandlerInterceptor {
ignoreUrlPatterns.add("/smartArchives/logout");
ignoreUrlPatterns.add("/smartArchives/getInfo");
ignoreUrlPatterns.add("/smartArchives/getRouters");
ignoreUrlPatterns.add("/smartArchives/session/check");
}
private String rnd = null;
@ -67,6 +69,7 @@ public class ParamSecureHandler implements AsyncHandlerInterceptor {
}
XssRequestWrapper requestWrapper = new XssRequestWrapper(request);
System.err.println(JSON.toJSONString(request.getParameterMap()));
/**
* 校验参数是否合法
*/

View File

@ -17,7 +17,6 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
@ -47,12 +46,12 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
}
String requestBody = stringBuilder.toString();
// 进行XSS清理
streamParam = xssClean(requestBody);
// 不变更请求体只做检测
streamParam = requestBody;
body = streamParam.getBytes(request.getCharacterEncoding() != null ?
request.getCharacterEncoding() : "UTF-8");
// 检查安全性
// 检查安全性仅校验不篡改
String queryStr = request.getQueryString();
setChecked(xssCleanNew(requestBody) && (queryStr == null || xssCleanNew(queryStr)));
@ -63,7 +62,8 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
setChecked(false);
}
queryString = xssClean(request.getQueryString());
// 不对查询串做篡改保持原样防止影响参数绑定
queryString = request.getQueryString();
}
@Override
@ -166,18 +166,14 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
private static final String regex9 = "vbscript:";
private static final String regex10 = "onload(.*?)=";
// 添加安全脚本模式
public static final String SAFE_SCRIPT_PATTERN = "(\\||;|\\$|'|\\'|0x0d|0x0a|\\%27|\\%3B|<>|\\[\\]|\\(\\)|\"|script|alert|svg|confirm|prompt|onload|%3c|%3e|%2b|@|!|img|src)";
// 安全模式移除过度严格的字符级别清理避免破坏 JSON/参数结构
// public static final String SAFE_SCRIPT_PATTERN = "(\\||;|\\$|'|\\'|0x0d|0x0a|\\%27|\\%3B|_|.)";
private String xssClean(String value) {
if (value == null) {
return null;
}
// 移除空字符串替换这行代码没有实际效果
// value = value.replaceAll("", "");
// 使用预编译的模式提高性能
value = Pattern.compile(regex1, Pattern.CASE_INSENSITIVE).matcher(value).replaceAll("");
value = Pattern.compile(regex2, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL).matcher(value).replaceAll("");
value = Pattern.compile(regex3, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL).matcher(value).replaceAll("");
@ -188,8 +184,8 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
value = Pattern.compile(regex8, Pattern.CASE_INSENSITIVE).matcher(value).replaceAll("");
value = Pattern.compile(regex9, Pattern.CASE_INSENSITIVE).matcher(value).replaceAll("");
value = Pattern.compile(regex10, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL).matcher(value).replaceAll("");
// 添加额外的安全过滤
value = Pattern.compile(SAFE_SCRIPT_PATTERN, Pattern.CASE_INSENSITIVE).matcher(value).replaceAll("");
// 移除对普通字符的全量替换防止破坏字段名/JSON结构
// value = Pattern.compile(SAFE_SCRIPT_PATTERN, Pattern.CASE_INSENSITIVE).matcher(value).replaceAll("");
return value;
}
@ -215,7 +211,8 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
patterns.add(Pattern.compile(regex8, Pattern.CASE_INSENSITIVE));
patterns.add(Pattern.compile(regex9, Pattern.CASE_INSENSITIVE));
patterns.add(Pattern.compile(regex10, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
patterns.add(Pattern.compile(SAFE_SCRIPT_PATTERN, Pattern.CASE_INSENSITIVE));
// 移除过度严格的 SAFE_SCRIPT_PATTERN 检查
// patterns.add(Pattern.compile(SAFE_SCRIPT_PATTERN, Pattern.CASE_INSENSITIVE));
for (Pattern pattern : patterns) {
if (match(pattern, value)) {
@ -332,7 +329,8 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
patterns.add(Pattern.compile(regex8, Pattern.CASE_INSENSITIVE));
patterns.add(Pattern.compile(regex9, Pattern.CASE_INSENSITIVE));
patterns.add(Pattern.compile(regex10, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
patterns.add(Pattern.compile(SAFE_SCRIPT_PATTERN, Pattern.CASE_INSENSITIVE));
// 移除过度严格的 SAFE_SCRIPT_PATTERN 检查
// patterns.add(Pattern.compile(SAFE_SCRIPT_PATTERN, Pattern.CASE_INSENSITIVE));
for (Pattern pattern : patterns) {
if (match(pattern, value)) {

View File

@ -4,6 +4,7 @@ import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
@ -30,6 +31,7 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler
@Autowired
private TokenService tokenService;
/**
* 退出处理
*
@ -43,6 +45,7 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler
if (StringUtils.isNotNull(loginUser))
{
String userName = loginUser.getUsername();
String sessionId = request.getSession().getId();
// 删除用户缓存记录
tokenService.delLoginUser(loginUser.getToken());
// 记录用户退出日志

View File

@ -1,11 +1,14 @@
package com.bonus.framework.web.service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import com.bonus.common.constant.CacheConstants;
import com.bonus.common.constant.Constants;
@ -52,6 +55,8 @@ public class SysLoginService
@Autowired
private ISysConfigService configService;
/**
* 登录验证
*
@ -61,7 +66,7 @@ public class SysLoginService
* @param uuid 唯一标识
* @return 结果
*/
public String login(String username, String password, String code, String uuid)
public String login(HttpServletRequest request,String username, String password, String code, String uuid)
{
// 验证码校验
validateCaptcha(username, code, uuid);

View File

@ -9,7 +9,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
from da_ky_data_collect_data
where del_flag = '1'
<if test="dataClassifyId!=null">
data_classify_id = #{dataClassifyId}
AND data_classify_id = #{dataClassifyId}
</if>
</select>
</mapper>

View File

@ -65,17 +65,23 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
SELECT COUNT(*)
FROM da_ky_sys_ip_whitelist
WHERE status = '0'
AND (
(ip_address = #{ip} AND ip_address IS NOT NULL)
OR
(ip_range_start IS NOT NULL AND ip_range_end IS NOT NULL
AND INET_ATON(#{ip}) BETWEEN INET_ATON(ip_range_start) AND INET_ATON(ip_range_end))
)
AND (
AND (
(TRIM(ip_address) = #{ip})
OR
(ip_range_start IS NOT NULL
AND ip_range_end IS NOT NULL
AND TRIM(ip_range_start) != ''
AND TRIM(ip_range_end) != ''
AND INET_ATON(#{ip}) BETWEEN
INET_ATON(TRIM(ip_range_start)) AND
INET_ATON(TRIM(ip_range_end)))
)
AND (
(access_start_time IS NULL AND access_end_time IS NULL)
OR
(#{currentTime} BETWEEN access_start_time AND access_end_time)
)
OR
(access_start_time &lt;= #{currentTime}
AND access_end_time &gt;= #{currentTime})
)
</select>
<select id="selectSysIpWhitelist" resultType="com.bonus.system.domain.SysIpWhitelist">
<include refid="selectSysIpWhitelistVo"/>