修复xssbug

This commit is contained in:
开源oschina 2022-05-20 18:29:20 +08:00
parent 47993beebd
commit 219f28c3c1
8 changed files with 119 additions and 35 deletions

View File

@ -112,7 +112,6 @@
<artifactId>sa-token-dao-redis-jackson</artifactId> <artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.26.0</version> <version>1.26.0</version>
</dependency> </dependency>
<!-- thymeleaf模版 --> <!-- thymeleaf模版 -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

View File

@ -1,7 +1,8 @@
package com.fc.v2.common.conf.xss; package com.fc.v2.common.conf.xss;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter; import javax.servlet.Filter;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.FilterConfig; import javax.servlet.FilterConfig;
@ -10,35 +11,61 @@ import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import com.fc.v2.util.StringUtils;
import org.springframework.stereotype.Component;
@Component
public class SimpleCORSFilter implements Filter { public class SimpleCORSFilter implements Filter {
/**
* 排除链接
*/
public List<String> excludes = new ArrayList<>();
@Override @Override
public void doFilter(ServletRequest req, ServletResponse res, public void doFilter(ServletRequest req, ServletResponse res,FilterChain chain) throws IOException, ServletException {
FilterChain chain) throws IOException, ServletException {
// 在这里使用我们实现的XSS过滤器
XssHttpServletRequestWrapper request =
new XssHttpServletRequestWrapper((HttpServletRequest) req);
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods",
"POST, GET, PUT, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, token");
chain.doFilter(request, response);
// 在这里使用我们实现的XSS过滤器
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) req);
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
if (handleExcludeURL(request, response))
{
chain.doFilter(request, response);
return;
}
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods","POST, GET, PUT, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers","Origin, X-Requested-With, Content-Type, Accept, token");
chain.doFilter(xssRequest, response);
} }
public void init(FilterConfig filterConfig) { public void init(FilterConfig filterConfig) {
String tempExcludes = filterConfig.getInitParameter("exclusions");
if (StringUtils.isNotEmpty(tempExcludes))
{
String[] url = tempExcludes.split(",");
for (int i = 0; url != null && i < url.length; i++)
{
excludes.add(url[i]);
}
}
} }
private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response)
{
String url = request.getServletPath();
String method = request.getMethod();
// GET DELETE 不过滤
if (method == null || method.matches("GET") || method.matches("DELETE"))
{
return true;
}
return StringUtils.matches(url, excludes);
}
public void destroy() { public void destroy() {
} }
} }

View File

@ -3,12 +3,7 @@ package com.fc.v2.common.conf.xss;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fc.v2.common.conf.V2Config; import com.fc.v2.common.conf.V2Config;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import java.util.List; import java.util.List;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -29,8 +24,8 @@ public class XssFilterAtuoConfig {
* @return * @return
*/ */
@Bean @Bean
public FilterRegistrationBean xssFiltrRegister() { public FilterRegistrationBean<SimpleCORSFilter> xssFiltrRegister() {
FilterRegistrationBean registration = new FilterRegistrationBean(); FilterRegistrationBean<SimpleCORSFilter> registration = new FilterRegistrationBean<SimpleCORSFilter>();
//设置系统过滤器 (setFilter就是你所定义的过滤器filter类) //设置系统过滤器 (setFilter就是你所定义的过滤器filter类)
registration.setFilter(new SimpleCORSFilter()); registration.setFilter(new SimpleCORSFilter());
@ -45,12 +40,11 @@ public class XssFilterAtuoConfig {
if(xssnoturlList!=null&&xssnoturlList.size()>0) { if(xssnoturlList!=null&&xssnoturlList.size()>0) {
xssnot=String.join(",", xssnoturlList); xssnot=String.join(",", xssnoturlList);
} }
//添加忽略的格式以及链接请求 //添加忽略的格式以及链接请求
registration.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid2/*,"+xssnot); registration.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid2/*,"+xssnot);
//优先级 //优先级
registration.setOrder(1); registration.setOrder(1);
return registration; return registration;
} }

View File

@ -1,19 +1,24 @@
package com.fc.v2.common.exception; package com.fc.v2.common.exception;
import java.util.List;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.AntPathMatcher;
import org.springframework.validation.BindException; import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import com.fc.v2.common.conf.V2Config;
import com.fc.v2.common.domain.AjaxResult; import com.fc.v2.common.domain.AjaxResult;
import com.fc.v2.common.exception.demo.DemoModeException; import com.fc.v2.common.exception.demo.DemoModeException;
import com.fc.v2.util.ServletUtils; import com.fc.v2.util.ServletUtils;
import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException; import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException; import cn.dev33.satoken.exception.NotRoleException;
@ -29,6 +34,9 @@ import cn.dev33.satoken.exception.SaTokenException;
public class GlobalExceptionResolver{ public class GlobalExceptionResolver{
private static Logger logger = LoggerFactory.getLogger(GlobalExceptionResolver.class); private static Logger logger = LoggerFactory.getLogger(GlobalExceptionResolver.class);
@Autowired
private V2Config v2Config;
/** /**
* 权限校验失败 如果请求为ajax返回json普通请求跳转页面 * 权限校验失败 如果请求为ajax返回json普通请求跳转页面
*/ */
@ -45,10 +53,24 @@ public class GlobalExceptionResolver{
{ {
// 登录认证异常 // 登录认证异常
if(e instanceof NotLoginException){ if(e instanceof NotLoginException){
List<String> satoken_not_urls=v2Config.getSaTokenNotFilterUrl();
for (String nourl : satoken_not_urls) {
AntPathMatcher matcher = new AntPathMatcher();
if(matcher.match(nourl,request.getRequestURI())) {
return JSON.toJSONString(AjaxResult.error(886,e.getMessage()));
}
}
return new ModelAndView("/login"); return new ModelAndView("/login");
} }
// 权限认证异常 // 权限认证异常
else if (e instanceof NotPermissionException || e instanceof NotRoleException || e instanceof NotSafeException){ else if (e instanceof NotPermissionException || e instanceof NotRoleException || e instanceof NotSafeException){
List<String> satoken_not_urls=v2Config.getSaTokenNotFilterUrl();
for (String nourl : satoken_not_urls) {
AntPathMatcher matcher = new AntPathMatcher();
if(matcher.match(nourl,request.getRequestURI())) {
return JSON.toJSONString(AjaxResult.error(403,e.getMessage()));
}
}
return new ModelAndView("/error/403"); return new ModelAndView("/error/403");
} }
// 其它异常 // 其它异常

View File

@ -6,7 +6,6 @@ import com.fc.v2.common.domain.ResultTable;
import com.fc.v2.model.auto.GoviewProject; import com.fc.v2.model.auto.GoviewProject;
import com.fc.v2.model.auto.GoviewProjectData; import com.fc.v2.model.auto.GoviewProjectData;
import com.fc.v2.model.auto.GoviewProjectDataExample; import com.fc.v2.model.auto.GoviewProjectDataExample;
import com.fc.v2.model.auto.GoviewProjectExample;
import com.fc.v2.model.custom.Tablepar; import com.fc.v2.model.custom.Tablepar;
import com.fc.v2.service.GoviewProjectDataService; import com.fc.v2.service.GoviewProjectDataService;
import com.fc.v2.service.GoviewProjectService; import com.fc.v2.service.GoviewProjectService;
@ -14,9 +13,7 @@ import com.github.pagehelper.PageInfo;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import java.util.List; import java.util.List;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap; import org.springframework.ui.ModelMap;
@ -171,7 +168,7 @@ public class GoviewProjectAPi extends BaseController{
@ApiOperation(value = "保存项目数据", notes = "保存项目数据") @ApiOperation(value = "保存项目数据", notes = "保存项目数据")
@PostMapping("/save/data") @PostMapping("/save/data")
@ResponseBody @ResponseBody
public AjaxResult saveData(@RequestBody GoviewProjectData data) { public AjaxResult saveData(GoviewProjectData data) {
GoviewProject goviewProject= goviewProjectService.selectByPrimaryKey(data.getProjectId()); GoviewProject goviewProject= goviewProjectService.selectByPrimaryKey(data.getProjectId());
if(goviewProject==null) { if(goviewProject==null) {

View File

@ -86,6 +86,11 @@ public class SaTokenConfigure implements WebMvcConfigurer {
.setError(e -> { .setError(e -> {
// e.printStackTrace(); // e.printStackTrace();
if(e instanceof NotLoginException) { if(e instanceof NotLoginException) {
for (String nourl : satoken_not_urls) {
if(nourl.contains(SaHolder.getRequest().getUrl())) {
return JSON.toJSONString(AjaxResult.error(886,e.getMessage()));
}
}
SaHolder.getResponse().redirect("/admin/login"); SaHolder.getResponse().redirect("/admin/login");
} }
return JSON.toJSONString(AjaxResult.error(e.getMessage())); return JSON.toJSONString(AjaxResult.error(e.getMessage()));

View File

@ -1,9 +1,11 @@
package com.fc.v2.util; package com.fc.v2.util;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang.WordUtils; import org.apache.commons.lang.WordUtils;
import org.apache.commons.lang.text.StrBuilder; import org.apache.commons.lang.text.StrBuilder;
import org.springframework.util.AntPathMatcher;
@ -441,4 +443,42 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
} }
return columnToJava(tableName); return columnToJava(tableName);
} }
/**
* 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
*
* @param str 指定字符串
* @param strs 需要检查的字符串数组
* @return 是否匹配
*/
public static boolean matches(String str, List<String> strs)
{
if (isEmpty(str) || isEmpty(strs))
{
return false;
}
for (String pattern : strs)
{
if (isMatch(pattern, str))
{
return true;
}
}
return false;
}
/**
* 判断url是否与规则配置:
* ? 表示单个字符;
* * 表示一层路径内的任意字符串不可跨层级;
* ** 表示任意层路径;
*
* @param pattern 匹配规则
* @param url 需要匹配的url
* @return
*/
public static boolean isMatch(String pattern, String url)
{
AntPathMatcher matcher = new AntPathMatcher();
return matcher.match(pattern, url);
}
} }

View File

@ -20,7 +20,7 @@ fuce:
#漂亮得拖动验证码 默认false普通验证码、true滚动验证码 #漂亮得拖动验证码 默认false普通验证码、true滚动验证码
roll-verification: false roll-verification: false
#xss不拦截url #xss不拦截url
xss-not-filter-url: [/api/v1/token/api_token,/api/v1/yibaotong/save] xss-not-filter-url: [/api/v1/token/api_token,/api/goview/project/save/data]
#satoken不拦截url #satoken不拦截url
sa-token-not-filter-url: [/api/goview/sys/*,/api/goview/project/**] sa-token-not-filter-url: [/api/goview/sys/*,/api/goview/project/**]
#tomcat config #tomcat config