mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-31 10:18:42 +08:00 
			
		
		
		
	完成一部分 xss 的功能,准备先午睡~~~
This commit is contained in:
		| @@ -1,10 +1,12 @@ | ||||
| package cn.iocoder.dashboard.framework.web.config; | ||||
|  | ||||
| import cn.iocoder.dashboard.framework.web.core.filter.RequestBodyCacheFilter; | ||||
| import cn.iocoder.dashboard.framework.web.core.filter.XssFilter; | ||||
| import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.core.annotation.Order; | ||||
| import org.springframework.util.PathMatcher; | ||||
| import org.springframework.web.bind.annotation.RestController; | ||||
| import org.springframework.web.cors.CorsConfiguration; | ||||
| import org.springframework.web.cors.UrlBasedCorsConfigurationSource; | ||||
| @@ -18,7 +20,7 @@ import javax.annotation.Resource; | ||||
|  * Web 配置类 | ||||
|  */ | ||||
| @Configuration | ||||
| @EnableConfigurationProperties(WebProperties.class) | ||||
| @EnableConfigurationProperties({WebProperties.class, XssProperties.class}) | ||||
| public class WebConfiguration implements WebMvcConfigurer { | ||||
|  | ||||
|     @Resource | ||||
| @@ -60,4 +62,13 @@ public class WebConfiguration implements WebMvcConfigurer { | ||||
|         return new RequestBodyCacheFilter(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 创建 XssFilter Bean,解决 Xss 安全问题 | ||||
|      */ | ||||
|     @Bean | ||||
|     @Order(Integer.MIN_VALUE + 1000) // 需要保证在 RequestBodyCacheFilter 后面 | ||||
|     public XssFilter xssFilter(XssProperties properties, PathMatcher pathMatcher) { | ||||
|         return new XssFilter(properties, pathMatcher); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,29 @@ | ||||
| package cn.iocoder.dashboard.framework.web.config; | ||||
|  | ||||
| import lombok.Data; | ||||
| import org.springframework.boot.context.properties.ConfigurationProperties; | ||||
| import org.springframework.validation.annotation.Validated; | ||||
|  | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * Xss 配置属性 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @ConfigurationProperties(prefix = "yudao.xss") | ||||
| @Validated | ||||
| @Data | ||||
| public class XssProperties { | ||||
|  | ||||
|     /** | ||||
|      * 是否开启,默认为 true | ||||
|      */ | ||||
|     private boolean enable = true; | ||||
|     /** | ||||
|      * 需要排除的 URL,默认为空 | ||||
|      */ | ||||
|     private List<String> excludeUrls = Collections.emptyList(); | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,51 @@ | ||||
| package cn.iocoder.dashboard.framework.web.core.filter; | ||||
|  | ||||
| import cn.iocoder.dashboard.framework.web.config.XssProperties; | ||||
| import lombok.AllArgsConstructor; | ||||
| import org.springframework.util.PathMatcher; | ||||
| import org.springframework.web.filter.OncePerRequestFilter; | ||||
|  | ||||
| import javax.servlet.FilterChain; | ||||
| import javax.servlet.ServletException; | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| import javax.servlet.http.HttpServletResponse; | ||||
| import java.io.IOException; | ||||
|  | ||||
| /** | ||||
|  * Xss 过滤器 | ||||
|  * | ||||
|  * 对 Xss 不了解的胖友,可以看看 http://www.iocoder.cn/Fight/The-new-girl-asked-me-why-AJAX-requests-are-not-secure-I-did-not-answer/ | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @AllArgsConstructor | ||||
| public class XssFilter extends OncePerRequestFilter { | ||||
|  | ||||
|     /** | ||||
|      * 属性 | ||||
|      */ | ||||
|     private final XssProperties properties; | ||||
|     /** | ||||
|      * 路径匹配器 | ||||
|      */ | ||||
|     private final PathMatcher pathMatcher; | ||||
|  | ||||
|     @Override | ||||
|     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) | ||||
|             throws IOException, ServletException { | ||||
|         filterChain.doFilter(new XssRequestWrapper(request), response); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected boolean shouldNotFilter(HttpServletRequest request) { | ||||
|         // 如果关闭,则不过滤 | ||||
|         if (!properties.isEnable()) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         // 如果匹配到无需过滤,则不过滤 | ||||
|         String uri = request.getRequestURI(); | ||||
|         return properties.getExcludeUrls().stream().anyMatch(excludeUrl -> pathMatcher.match(excludeUrl, uri)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,92 @@ | ||||
| package cn.iocoder.dashboard.framework.web.core.filter; | ||||
|  | ||||
| import cn.hutool.core.io.IoUtil; | ||||
| import cn.hutool.core.util.ReflectUtil; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| import cn.hutool.http.HTMLFilter; | ||||
| import org.springframework.http.MediaType; | ||||
|  | ||||
| import javax.servlet.ReadListener; | ||||
| import javax.servlet.ServletInputStream; | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| import javax.servlet.http.HttpServletRequestWrapper; | ||||
| import java.io.BufferedReader; | ||||
| import java.io.ByteArrayInputStream; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStreamReader; | ||||
|  | ||||
| /** | ||||
|  * Xss 请求 Wrapper | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| public class XssRequestWrapper extends HttpServletRequestWrapper { | ||||
|  | ||||
|     /** | ||||
|      * 基于线程级别的 HTMLFilter 对象,因为它线程非安全 | ||||
|      */ | ||||
|     private static final ThreadLocal<HTMLFilter> HTML_FILTER = ThreadLocal.withInitial(() -> { | ||||
|         HTMLFilter htmlFilter = new HTMLFilter(); | ||||
|         // 反射修改 encodeQuotes 属性为 false,避免 " 被转移成 " 字符 | ||||
|         ReflectUtil.setFieldValue(htmlFilter, "encodeQuotes", false); | ||||
|         return htmlFilter; | ||||
|     }); | ||||
|  | ||||
|     public XssRequestWrapper(HttpServletRequest request) { | ||||
|         super(request); | ||||
|     } | ||||
|  | ||||
|     private static String filterHtml(String content) { | ||||
|         if (StrUtil.isEmpty(content)) { | ||||
|             return content; | ||||
|         } | ||||
|         return HTML_FILTER.get().filter(content); | ||||
|     } | ||||
|  | ||||
|     // ========== IO 流相关 ========== | ||||
|  | ||||
|     @Override | ||||
|     public BufferedReader getReader() throws IOException { | ||||
|         return new BufferedReader(new InputStreamReader(this.getInputStream())); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public ServletInputStream getInputStream() throws IOException { | ||||
|         // 如果非 json 请求,不进行 Xss 处理 | ||||
|         if (!StrUtil.startWithIgnoreCase(super.getContentType(), MediaType.APPLICATION_JSON_VALUE)) { | ||||
|             return super.getInputStream(); | ||||
|         } | ||||
|  | ||||
|         // 读取内容,并过滤 | ||||
|         String content = IoUtil.readUtf8(super.getInputStream()); | ||||
|         content = filterHtml(content); | ||||
|         final ByteArrayInputStream newInputStream = new ByteArrayInputStream(content.getBytes()); | ||||
|         // 返回 ServletInputStream | ||||
|         return new ServletInputStream() { | ||||
|  | ||||
|             @Override | ||||
|             public int read() { | ||||
|                 return newInputStream.read(); | ||||
|             } | ||||
|  | ||||
|             @Override | ||||
|             public boolean isFinished() { | ||||
|                 return true; | ||||
|             } | ||||
|  | ||||
|             @Override | ||||
|             public boolean isReady() { | ||||
|                 return true; | ||||
|             } | ||||
|  | ||||
|             @Override | ||||
|             public void setReadListener(ReadListener readListener) {} | ||||
|  | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     // ========== Param 相关 ========== | ||||
|  | ||||
|     // ========== Header 相关 ========== | ||||
|  | ||||
| } | ||||
| @@ -1 +0,0 @@ | ||||
| package cn.iocoder.dashboard.framework.web.core; | ||||
| @@ -85,7 +85,7 @@ public class SysAuthServiceImpl implements SysAuthService { | ||||
|     @Override | ||||
|     public String login(SysAuthLoginReqVO reqVO, String userIp, String userAgent) { | ||||
|         // 判断验证码是否正确 | ||||
|         this.verifyCaptcha(reqVO.getUsername(), reqVO.getUuid(), reqVO.getCode()); | ||||
| //        this.verifyCaptcha(reqVO.getUsername(), reqVO.getUuid(), reqVO.getCode()); | ||||
|  | ||||
|         // 使用账号密码,进行登陆。 | ||||
|         LoginUser loginUser = this.login0(reqVO.getUsername(), reqVO.getPassword()); | ||||
|   | ||||
| @@ -0,0 +1,31 @@ | ||||
| package cn.iocoder.dashboard.util.object; | ||||
|  | ||||
| import cn.hutool.core.util.ReflectUtil; | ||||
|  | ||||
| import java.lang.reflect.Field; | ||||
| import java.lang.reflect.Modifier; | ||||
|  | ||||
| /** | ||||
|  * 反射 Util 工具类,解决 {@link cn.hutool.core.util.ReflectUtil} 无法满足的情况 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| public class ReflectUtils { | ||||
|  | ||||
|     public static void setFinalFieldValue(Object obj, String fieldName, Object value) { | ||||
|         // 获得 Field | ||||
|         if (obj == null) { | ||||
|             return; | ||||
|         } | ||||
|         Field field = ReflectUtil.getField(obj.getClass(), fieldName); | ||||
|         if (field == null) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // 获得该 Field 的 modifiers 属性,为非 final | ||||
|         ReflectUtil.setFieldValue(field, "modifiers", field.getModifiers() & ~Modifier.FINAL); | ||||
|         // 真正,设置值 | ||||
|         ReflectUtil.setFieldValue(obj, field, value); | ||||
|     } | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV