安全漏洞和渗透修复
This commit is contained in:
parent
c79ade0cb8
commit
ca4a805c5d
|
|
@ -35,6 +35,11 @@ public class SysLogController {
|
||||||
*/
|
*/
|
||||||
@RequestMapping(value = "/queryByPage")
|
@RequestMapping(value = "/queryByPage")
|
||||||
public String queryByPage(SysLogs sysLogs, @RequestParam("page") Integer page, @RequestParam("limit") Integer pageSize) {
|
public String queryByPage(SysLogs sysLogs, @RequestParam("page") Integer page, @RequestParam("limit") Integer pageSize) {
|
||||||
|
String username = sysLogs.getUsername();
|
||||||
|
// 用正则匹配“是否包含非法字符”(若匹配到,则说明有非法字符)
|
||||||
|
if (username.matches(".*[^a-zA-Z0-9\u4e00-\u9fa5-].*")) {
|
||||||
|
return "{\"code\":1 , \"msg\":\"操作人仅允许输入中英文、数字和连字符\"}";
|
||||||
|
}
|
||||||
int count = sysLogDao.count(sysLogs);
|
int count = sysLogDao.count(sysLogs);
|
||||||
page = (page - 1) * pageSize;
|
page = (page - 1) * pageSize;
|
||||||
List<SysLogs> list = this.sysLogDao.queryAllByLimit(sysLogs, page, pageSize);
|
List<SysLogs> list = this.sysLogDao.queryAllByLimit(sysLogs, page, pageSize);
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,11 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand
|
||||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||||
|
import org.springframework.web.cors.CorsConfigurationSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* spring security配置
|
* spring security配置
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||||
public class BnsSecurityConfig extends WebSecurityConfigurerAdapter {
|
public class BnsSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
@ -37,6 +38,9 @@ public class BnsSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
@Autowired
|
@Autowired
|
||||||
private TokenFilter tokenFilter;
|
private TokenFilter tokenFilter;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CorsConfigurationSource corsConfigurationSource;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public BCryptPasswordEncoder bCryptPasswordEncoder() {
|
public BCryptPasswordEncoder bCryptPasswordEncoder() {
|
||||||
return new BCryptPasswordEncoder();
|
return new BCryptPasswordEncoder();
|
||||||
|
|
@ -45,7 +49,8 @@ public class BnsSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
@Override
|
@Override
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
http.csrf().disable();
|
http.csrf().disable();
|
||||||
|
// 使用新的跨域配置
|
||||||
|
http.cors(cors -> cors.configurationSource(corsConfigurationSource));
|
||||||
// 基于token,所以不需要session
|
// 基于token,所以不需要session
|
||||||
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
package com.bonus.boot.manager.manager.config;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
|
import org.springframework.web.cors.CorsConfigurationSource;
|
||||||
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
|
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跨域配置类
|
||||||
|
* 解决前后端不分离项目的跨域问题
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class CorsConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Value("${cors.allowed-origins}")
|
||||||
|
private String allowedOrigins;
|
||||||
|
|
||||||
|
@Value("${cors.allowed-methods}")
|
||||||
|
private String allowedMethods;
|
||||||
|
|
||||||
|
@Value("${cors.allowed-headers}")
|
||||||
|
private String allowedHeaders;
|
||||||
|
|
||||||
|
@Value("${cors.allow-credentials}")
|
||||||
|
private boolean allowCredentials;
|
||||||
|
|
||||||
|
@Value("${cors.max-age}")
|
||||||
|
private long maxAge;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addCorsMappings(CorsRegistry registry) {
|
||||||
|
registry.addMapping("/**")
|
||||||
|
.allowedOriginPatterns(getAllowedOriginPatterns().toArray(new String[0]))
|
||||||
|
.allowedMethods(getAllowedMethodArray())
|
||||||
|
.allowedHeaders(getAllowedHeaderArray())
|
||||||
|
.allowCredentials(allowCredentials)
|
||||||
|
.maxAge(maxAge)
|
||||||
|
.exposedHeaders("Content-Length", "Content-Type", "Token", "Authorization");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CorsConfigurationSource corsConfigurationSource() {
|
||||||
|
CorsConfiguration configuration = new CorsConfiguration();
|
||||||
|
configuration.setAllowedOriginPatterns(getAllowedOriginPatterns());
|
||||||
|
configuration.setAllowedMethods(Arrays.asList(getAllowedMethodArray()));
|
||||||
|
configuration.setAllowedHeaders(Arrays.asList(getAllowedHeaderArray()));
|
||||||
|
configuration.setExposedHeaders(Arrays.asList("Content-Length", "Content-Type", "Token", "Authorization"));
|
||||||
|
configuration.setAllowCredentials(allowCredentials);
|
||||||
|
configuration.setMaxAge(maxAge);
|
||||||
|
|
||||||
|
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||||
|
// 统一仅注册一套白名单策略,避免出现“*”
|
||||||
|
source.registerCorsConfiguration("/**", configuration);
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getAllowedOriginPatterns() {
|
||||||
|
if (allowedOrigins == null || allowedOrigins.trim().isEmpty()) {
|
||||||
|
return Arrays.asList(
|
||||||
|
"http://localhost:*",
|
||||||
|
"http://127.0.0.1:*",
|
||||||
|
"http://192.168.*.*:*",
|
||||||
|
"http://10.*.*.*:*"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Arrays.asList(allowedOrigins.split(","));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] getAllowedMethodArray() {
|
||||||
|
if (allowedMethods == null || allowedMethods.trim().isEmpty()) {
|
||||||
|
return new String[]{"GET", "POST", "PUT", "DELETE", "OPTIONS"};
|
||||||
|
}
|
||||||
|
return allowedMethods.split(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] getAllowedHeaderArray() {
|
||||||
|
if (allowedHeaders == null || allowedHeaders.trim().isEmpty()) {
|
||||||
|
return new String[]{"Content-Type", "X-Requested-With", "Token", "Authorization", "X-Custom-Header"};
|
||||||
|
}
|
||||||
|
return allowedHeaders.split(",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
package com.bonus.boot.manager.manager.config;
|
||||||
|
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.servlet.*;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 额外的安全头过滤器
|
||||||
|
* 用于设置更多的安全相关头信息
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Order(2)
|
||||||
|
public class SecurityHeadersFilter implements Filter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest request, ServletResponse response,
|
||||||
|
FilterChain chain) throws IOException, ServletException {
|
||||||
|
|
||||||
|
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||||
|
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||||
|
|
||||||
|
// 设置额外的安全头
|
||||||
|
setAdditionalSecurityHeaders(httpRequest, httpResponse);
|
||||||
|
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setAdditionalSecurityHeaders(HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
// 1) 缓存控制
|
||||||
|
response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
|
||||||
|
response.setHeader("Pragma", "no-cache");
|
||||||
|
response.setHeader("Expires", "0");
|
||||||
|
|
||||||
|
// 2) IE下载策略
|
||||||
|
response.setHeader("X-Download-Options", "noopen");
|
||||||
|
|
||||||
|
// 3) 跨域策略(条件化设置)
|
||||||
|
if (isPotentiallyTrustworthy(request)) {
|
||||||
|
response.setHeader("Cross-Origin-Opener-Policy", "same-origin");
|
||||||
|
response.setHeader("Cross-Origin-Resource-Policy", "same-origin");
|
||||||
|
response.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
|
||||||
|
} else {
|
||||||
|
response.setHeader("Cross-Origin-Opener-Policy", "");
|
||||||
|
response.setHeader("Cross-Origin-Resource-Policy", "");
|
||||||
|
response.setHeader("Cross-Origin-Embedder-Policy", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4) 不再设置已废弃的 Feature-Policy,避免与 Permissions-Policy 冲突
|
||||||
|
// Permissions-Policy 已在 CspFilter 中统一设置
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPotentiallyTrustworthy(HttpServletRequest request) {
|
||||||
|
boolean isSecure = request.isSecure();
|
||||||
|
String forwardedProto = request.getHeader("X-Forwarded-Proto");
|
||||||
|
if (!isSecure && forwardedProto != null) {
|
||||||
|
isSecure = "https".equalsIgnoreCase(forwardedProto);
|
||||||
|
}
|
||||||
|
String host = request.getServerName();
|
||||||
|
boolean isLocalhost = "localhost".equalsIgnoreCase(host) || "127.0.0.1".equals(host);
|
||||||
|
return isSecure || isLocalhost;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
// 清理资源
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,10 +19,10 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 跨域支持
|
* 跨域支持
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Bean
|
/*@Bean
|
||||||
public WebMvcConfigurer corsConfigurer() {
|
public WebMvcConfigurer corsConfigurer() {
|
||||||
return new WebMvcConfigurer() {
|
return new WebMvcConfigurer() {
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -30,11 +30,11 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
||||||
registry.addMapping("/**").allowedMethods("*");
|
registry.addMapping("/**").allowedMethods("*");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* datatable分页解析
|
* datatable分页解析
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,10 @@ public class UserController {
|
||||||
@ApiOperation(value = "当前登录用户")
|
@ApiOperation(value = "当前登录用户")
|
||||||
@GetMapping("/current")
|
@GetMapping("/current")
|
||||||
public SysUser currentUser() {
|
public SysUser currentUser() {
|
||||||
return UserUtil.getLoginUser();
|
//置空password
|
||||||
|
SysUser sysUser = UserUtil.getLoginUser();
|
||||||
|
sysUser.setPassword(null);
|
||||||
|
return sysUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/getTokenKey")
|
@GetMapping("/getTokenKey")
|
||||||
|
|
@ -155,7 +158,7 @@ public class UserController {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**-------------------------------------------以上为老代码,以下为layui新页面所使用的方法-----------------------------------------------------------------*/
|
/**-------------------------------------------以上为老代码,以下为layui新页面所使用的方法-----------------------------------------------------------------*/
|
||||||
|
|
||||||
@LogAnnotation
|
@LogAnnotation
|
||||||
@PostMapping("getMsgContent")
|
@PostMapping("getMsgContent")
|
||||||
@ApiOperation(value = "用户管理-列表")
|
@ApiOperation(value = "用户管理-列表")
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@ package com.bonus.boot.manager.manager.filter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.*;
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
|
@ -22,7 +21,7 @@ import com.bonus.boot.manager.manager.entity.LoginUser;
|
||||||
* Token过滤器
|
* Token过滤器
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class TokenFilter extends OncePerRequestFilter {
|
public class TokenFilter extends OncePerRequestFilter implements Filter {
|
||||||
|
|
||||||
public static final String TOKEN_KEY = "token";
|
public static final String TOKEN_KEY = "token";
|
||||||
|
|
||||||
|
|
@ -45,14 +44,18 @@ public class TokenFilter extends OncePerRequestFilter {
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 在这里设置 CSP 头或其他过滤逻辑
|
||||||
|
response.setHeader(
|
||||||
|
"Content-Security-Policy",
|
||||||
|
"default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline';"
|
||||||
|
);
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验时间<br>
|
* 校验时间<br>
|
||||||
* 过期时间与当前时间对比,临近过期10分钟内的话,自动刷新缓存
|
* 过期时间与当前时间对比,临近过期10分钟内的话,自动刷新缓存
|
||||||
*
|
*
|
||||||
* @param loginUser
|
* @param loginUser
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
|
@ -70,7 +73,7 @@ public class TokenFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据参数或者header获取token
|
* 根据参数或者header获取token
|
||||||
*
|
*
|
||||||
* @param request
|
* @param request
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
|
@ -82,5 +85,4 @@ public class TokenFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#\u8BBF\u95EE\u7AEF\u53E3
|
#\u8BBF\u95EE\u7AEF\u53E3
|
||||||
#\u6B63\u5F0F\u7AEF\u53E3
|
#\u6B63\u5F0F\u7AEF\u53E3
|
||||||
#server.port=18088
|
#server.port=18088
|
||||||
#\u672C\u5730\u7AEF\u53E3
|
#\u672C\u5730\u7AEF\u53E3
|
||||||
server.port=18088
|
server.port=18088
|
||||||
|
|
@ -73,4 +73,31 @@ files.path=/data/yn
|
||||||
#\u672C\u5730
|
#\u672C\u5730
|
||||||
#files.url=http://192.168.0.110:18088/YSpeaManager/statics
|
#files.url=http://192.168.0.110:18088/YSpeaManager/statics
|
||||||
#files.path=d:\\data\\yn
|
#files.path=d:\\data\\yn
|
||||||
#files.upload=d:\\files
|
#files.upload=d:\\files
|
||||||
|
# \u8DE8\u57DF\u914D\u7F6E
|
||||||
|
# \u5141\u8BB8\u7684\u6E90\uFF08\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694\uFF09
|
||||||
|
cors.allowed-origins=http://localhost:1616,http://127.0.0.1:1616,http://192.168.0.39:1616,http://192.168.0.14:1616,http://112.29.103.165:1616
|
||||||
|
|
||||||
|
# \u5141\u8BB8\u7684HTTP\u65B9\u6CD5
|
||||||
|
cors.allowed-methods=GET,POST,PUT,DELETE,OPTIONS
|
||||||
|
|
||||||
|
# \u5141\u8BB8\u7684\u8BF7\u6C42\u5934
|
||||||
|
cors.allowed-headers=Content-Type,X-Requested-With,Token,Authorization,X-Custom-Header
|
||||||
|
|
||||||
|
# \u662F\u5426\u5141\u8BB8\u643A\u5E26\u8BA4\u8BC1\u4FE1\u606F
|
||||||
|
cors.allow-credentials=true
|
||||||
|
|
||||||
|
# \u9884\u68C0\u8BF7\u6C42\u7F13\u5B58\u65F6\u95F4\uFF08\u79D2\uFF09
|
||||||
|
cors.max-age=3600
|
||||||
|
|
||||||
|
# \u5B89\u5168\u5934\u914D\u7F6E
|
||||||
|
# \u662F\u5426\u542F\u7528\u4E25\u683C\u7684\u5B89\u5168\u5934
|
||||||
|
security.headers.strict=true
|
||||||
|
|
||||||
|
# \u662F\u5426\u542F\u7528HSTS\uFF08HTTP\u4E25\u683C\u4F20\u8F93\u5B89\u5168\uFF09
|
||||||
|
security.hsts.enabled=true
|
||||||
|
|
||||||
|
# \u662F\u5426\u6E05\u9664\u670D\u52A1\u5668\u4FE1\u606F\u5934
|
||||||
|
security.headers.clear-server-info=true
|
||||||
|
|
||||||
|
management.endpoint.caches.enabled=false
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue