mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-22 23:31:52 +08:00
完成一部分 xss 的功能,准备先午睡~~~
This commit is contained in:
parent
183bb5855a
commit
8605cc35c9
5
pom.xml
5
pom.xml
@ -192,6 +192,11 @@
|
|||||||
<artifactId>hutool-captcha</artifactId>
|
<artifactId>hutool-captcha</artifactId>
|
||||||
<version>${hutool.version}</version>
|
<version>${hutool.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-http</artifactId>
|
||||||
|
<version>${hutool.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
package com.ruoyi.framework.config;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
|
||||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
|
||||||
import org.springframework.web.filter.CorsFilter;
|
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
|
||||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
||||||
import com.ruoyi.common.config.RuoYiConfig;
|
|
||||||
import com.ruoyi.common.constant.Constants;
|
|
||||||
import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通用配置
|
|
||||||
*
|
|
||||||
* @author ruoyi
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
public class ResourcesConfig implements WebMvcConfigurer {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RepeatSubmitInterceptor repeatSubmitInterceptor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自定义拦截规则
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void addInterceptors(InterceptorRegistry registry) {
|
|
||||||
registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
package com.ruoyi.framework.config;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import com.ruoyi.common.utils.ServletUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 服务相关配置
|
|
||||||
*
|
|
||||||
* @author ruoyi
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class ServerConfig {
|
|
||||||
/**
|
|
||||||
* 获取完整的请求路径,包括:域名,端口,上下文访问路径
|
|
||||||
*
|
|
||||||
* @return 服务地址
|
|
||||||
*/
|
|
||||||
public String getUrl() {
|
|
||||||
HttpServletRequest request = ServletUtils.getRequest();
|
|
||||||
return getDomain(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getDomain(HttpServletRequest request) {
|
|
||||||
StringBuffer url = request.getRequestURL();
|
|
||||||
String contextPath = request.getServletContext().getContextPath();
|
|
||||||
return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,104 +0,0 @@
|
|||||||
package com.ruoyi.common.filter;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import javax.servlet.ReadListener;
|
|
||||||
import javax.servlet.ServletInputStream;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletRequestWrapper;
|
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
|
||||||
import com.ruoyi.common.utils.html.EscapeUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* XSS过滤处理
|
|
||||||
*
|
|
||||||
* @author ruoyi
|
|
||||||
*/
|
|
||||||
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param request
|
|
||||||
*/
|
|
||||||
public XssHttpServletRequestWrapper(HttpServletRequest request)
|
|
||||||
{
|
|
||||||
super(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] getParameterValues(String name)
|
|
||||||
{
|
|
||||||
String[] values = super.getParameterValues(name);
|
|
||||||
if (values != null)
|
|
||||||
{
|
|
||||||
int length = values.length;
|
|
||||||
String[] escapseValues = new String[length];
|
|
||||||
for (int i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
// 防xss攻击和过滤前后空格
|
|
||||||
escapseValues[i] = EscapeUtil.clean(values[i]).trim();
|
|
||||||
}
|
|
||||||
return escapseValues;
|
|
||||||
}
|
|
||||||
return super.getParameterValues(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ServletInputStream getInputStream() throws IOException
|
|
||||||
{
|
|
||||||
// 非json类型,直接返回
|
|
||||||
if (!isJsonRequest())
|
|
||||||
{
|
|
||||||
return super.getInputStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 为空,直接返回
|
|
||||||
String json = IOUtils.toString(super.getInputStream(), "utf-8");
|
|
||||||
if (StringUtils.isEmpty(json))
|
|
||||||
{
|
|
||||||
return super.getInputStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
// xss过滤
|
|
||||||
json = EscapeUtil.clean(json).trim();
|
|
||||||
final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes("utf-8"));
|
|
||||||
return new ServletInputStream()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public boolean isFinished()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isReady()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setReadListener(ReadListener readListener)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read() throws IOException
|
|
||||||
{
|
|
||||||
return bis.read();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否是Json请求
|
|
||||||
*
|
|
||||||
* @param request
|
|
||||||
*/
|
|
||||||
public boolean isJsonRequest()
|
|
||||||
{
|
|
||||||
String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
|
|
||||||
return MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(header);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
#错误消息
|
|
||||||
not.null=* 必须填写
|
|
||||||
user.jcaptcha.error=验证码错误
|
|
||||||
user.jcaptcha.expire=验证码已失效
|
|
||||||
user.not.exists=用户不存在/密码错误
|
|
||||||
user.password.not.match=用户不存在/密码错误
|
|
||||||
user.password.retry.limit.count=密码输入错误{0}次
|
|
||||||
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定10分钟
|
|
||||||
user.password.delete=对不起,您的账号已被删除
|
|
||||||
user.blocked=用户已封禁,请联系管理员
|
|
||||||
role.blocked=角色已封禁,请联系管理员
|
|
||||||
user.logout.success=退出成功
|
|
||||||
|
|
||||||
length.not.valid=长度必须在{min}到{max}个字符之间
|
|
||||||
|
|
||||||
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
|
|
||||||
user.password.not.valid=* 5-50个字符
|
|
||||||
|
|
||||||
user.email.not.valid=邮箱格式错误
|
|
||||||
user.mobile.phone.number.not.valid=手机号格式错误
|
|
||||||
user.login.success=登录成功
|
|
||||||
user.notfound=请重新登录
|
|
||||||
user.forcelogout=管理员强制退出,请重新登录
|
|
||||||
user.unknown.error=未知错误,请重新登录
|
|
||||||
|
|
||||||
##文件上传消息
|
|
||||||
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
|
|
||||||
upload.filename.exceed.length=上传的文件名最长{0}个字符
|
|
||||||
|
|
||||||
##权限
|
|
||||||
no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
|
|
||||||
no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
|
|
||||||
no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
|
|
||||||
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
|
|
||||||
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
|
|
||||||
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
|
|
File diff suppressed because one or more lines are too long
@ -1,10 +1,12 @@
|
|||||||
package cn.iocoder.dashboard.framework.web.config;
|
package cn.iocoder.dashboard.framework.web.config;
|
||||||
|
|
||||||
import cn.iocoder.dashboard.framework.web.core.filter.RequestBodyCacheFilter;
|
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.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.util.PathMatcher;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
@ -18,7 +20,7 @@ import javax.annotation.Resource;
|
|||||||
* Web 配置类
|
* Web 配置类
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableConfigurationProperties(WebProperties.class)
|
@EnableConfigurationProperties({WebProperties.class, XssProperties.class})
|
||||||
public class WebConfiguration implements WebMvcConfigurer {
|
public class WebConfiguration implements WebMvcConfigurer {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@ -60,4 +62,13 @@ public class WebConfiguration implements WebMvcConfigurer {
|
|||||||
return new RequestBodyCacheFilter();
|
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
|
@Override
|
||||||
public String login(SysAuthLoginReqVO reqVO, String userIp, String userAgent) {
|
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());
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user