From 591f68b998f7cd35987f759a1eed8a8f51b58a75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E7=90=A6=E6=B6=9B?= Date: Thu, 6 Apr 2023 10:58:19 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E6=8F=90=E5=8F=96=E5=88=86=E6=94=AF--?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A1=B9=E7=9B=AE=E5=AE=89=E5=85=A8=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E8=AE=A4=E8=AF=81=E4=B8=8E=E6=8B=A6=E6=88=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/fallback/SysBaseAPIFallback.java | 10 + .../java/com/jero/common/api/CommonAPI.java | 19 + .../jero/common/aspect/TranslationAspect.java | 181 +++++++ .../common/aspect/annotation/Translation.java | 14 + .../common/system/vo/SysDictItemCore.java | 87 ++++ .../main/java/com/jero/config/CsrfFilter.java | 126 ----- .../main/java/com/jero/config/WebConfig.java | 62 +++ .../com/jero/config/WebMvcConfiguration.java | 17 - .../jero/config/filter/cors/CorsFilter.java | 92 ++++ .../jero/config/filter/csrf/CsrfFilter.java | 127 +++++ .../jero/config/filter/xss/HTMLFilter.java | 446 ++++++++++++++++++ .../com/jero/config/filter/xss/SqlFilter.java | 33 ++ .../com/jero/config/filter/xss/XssFilter.java | 57 +++ .../xss/XssHttpServletRequestWrapper.java | 134 ++++++ .../com/jero/config/shiro/ShiroConfig.java | 4 +- .../system/entity/SysDictItemCore.java | 87 ++++ .../system/mapper/SysDictItemMapper.java | 10 + .../system/mapper/xml/SysDictItemMapper.xml | 5 + .../system/mapper/xml/SysDictMapper.xml | 24 +- .../system/service/ISysDictItemService.java | 9 + .../system/service/impl/SysBaseApiImpl.java | 22 + .../service/impl/SysDictItemServiceImpl.java | 6 + .../src/main/resources/application-dev.yml | 8 +- .../src/main/resources/application-prod.yml | 6 + .../src/main/resources/application-test.yml | 8 +- 25 files changed, 1438 insertions(+), 156 deletions(-) create mode 100644 jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/aspect/TranslationAspect.java create mode 100644 jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/aspect/annotation/Translation.java create mode 100644 jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/system/vo/SysDictItemCore.java delete mode 100644 jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/CsrfFilter.java create mode 100644 jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/WebConfig.java create mode 100644 jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/cors/CorsFilter.java create mode 100644 jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/csrf/CsrfFilter.java create mode 100644 jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/xss/HTMLFilter.java create mode 100644 jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/xss/SqlFilter.java create mode 100644 jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/xss/XssFilter.java create mode 100644 jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/xss/XssHttpServletRequestWrapper.java create mode 100644 jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/entity/SysDictItemCore.java diff --git a/jero-boot/jero-boot-base/jero-boot-base-api/jero-system-cloud-api/src/main/java/com/jero/common/system/api/fallback/SysBaseAPIFallback.java b/jero-boot/jero-boot-base/jero-boot-base-api/jero-system-cloud-api/src/main/java/com/jero/common/system/api/fallback/SysBaseAPIFallback.java index 64f4c190..308dfd4d 100644 --- a/jero-boot/jero-boot-base/jero-boot-base-api/jero-system-cloud-api/src/main/java/com/jero/common/system/api/fallback/SysBaseAPIFallback.java +++ b/jero-boot/jero-boot-base/jero-boot-base-api/jero-system-cloud-api/src/main/java/com/jero/common/system/api/fallback/SysBaseAPIFallback.java @@ -279,6 +279,16 @@ public class SysBaseAPIFallback implements ISysBaseAPI { return Collections.emptyList(); } + @Override + public List getDictItemAll() { + return Collections.emptyList(); + } + + @Override + public String queryTableDictTextByKey(String table, String text, String code, String key) { + return null; + } + @Override public void sendEmailMsg(String email,String title,String content) { // do other diff --git a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/api/CommonAPI.java b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/api/CommonAPI.java index bf7df1f2..e91300de 100644 --- a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/api/CommonAPI.java +++ b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/api/CommonAPI.java @@ -119,4 +119,23 @@ public interface CommonAPI { * @return */ List translateDictFromTableByKeys(String table, String text, String code, String keys); + + /** + * 14获取全部字典 + * @author lqt + * @date 2022/8/16 8:54 + * @return JSONObject + */ + List getDictItemAll();; + /** + * 15通过查询指定table的 text code 获取字典值text + * @author lqt + * @date 2022/8/16 8:54 + * @param table 表名 + * @param text 文本 + * @param code 编码 + * @param key 键 + * @return JSONObject + */ + String queryTableDictTextByKey(String table,String text,String code, String key); } diff --git a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/aspect/TranslationAspect.java b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/aspect/TranslationAspect.java new file mode 100644 index 00000000..9d09adc6 --- /dev/null +++ b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/aspect/TranslationAspect.java @@ -0,0 +1,181 @@ +package com.jero.common.aspect; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jero.common.api.CommonAPI; +import com.jero.common.api.vo.Results; +import com.jero.common.aspect.annotation.Dict; +import com.jero.common.constant.CommonConstant; +import com.jero.common.system.vo.SysDictItemCore; +import com.jero.common.util.oConvertUtils; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.*; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.lang.reflect.Field; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @Description 对controller接口返回值翻译,不支持多层嵌套翻译 + * @Author lqt + * @Date 2022/6/7 17:08 + * @Version 1.0 + */ +@Slf4j +@Aspect +@Component +@SuppressWarnings({"unused"}) +public class TranslationAspect { + + @Resource + private CommonAPI commonAPI; + + + @Pointcut("@annotation(com.jero.common.aspect.annotation.Translation)") + public void annotationPointcut() { + // do other + } + + @Before("annotationPointcut()") + public void beforePointcut(JoinPoint joinPoint) { + // 此处进入到方法前 可以实现一些业务逻辑 + } + + @Around("annotationPointcut()") + public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { + Object result = joinPoint.proceed(); + this.parseDictText(result); + return result; + } + + /** + * 在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理) + * @param joinPoint + */ + @AfterReturning(returning="rvt",pointcut = "annotationPointcut()") + public void doAfterReturning(JoinPoint joinPoint, Object rvt) { + + } + + private void parseDictText(Object result) { + List listDict = commonAPI.getDictItemAll(); + if(CollectionUtils.isEmpty(listDict)){ + return; + } + if (result instanceof Results) { + if (((Results) result).getResult() instanceof IPage) { + List items = new ArrayList<>(); + + for (Object record : ((IPage) ((Results) result).getResult()).getRecords()) { + Object item = translateObject(record,listDict); + items.add(item); + } + ((IPage) ((Results) result).getResult()).setRecords(items); + }else if (((Results) result).getResult() instanceof List) { + List items = new ArrayList<>(); + for (Object record : ((List) ((Results) result).getResult())) { + Object item = translateObject(record,listDict); + items.add(item); + } + ((Results) result).setResult(items); + }else{ + Object record = (Object) ((Results) result).getResult(); + Object item = translateObject(record,listDict); + ((Results) result).setResult(item); + } + } + } + + private Object translateObject(Object record,List listDict){ + ObjectMapper mapper = new ObjectMapper(); + String json; + try { + //解决@JsonFormat注解解析不了的问题详见SysAnnouncement类的@JsonFormat + json = mapper.writeValueAsString(record); + } catch (JsonProcessingException e) { + log.error("json解析失败" + e.getMessage(), e); + return record; + } + JSONObject item; + try { + item = JSONObject.parseObject(json); + //update-begin--Author:scott -- Date:20190603 ----for:解决继承实体字段无法翻译问题------ + //for (Field field : record.getClass().getDeclaredFields()) { + + for (Field field : oConvertUtils.getAllFields(record)) { + //update-end--Author:scott -- Date:20190603 ----for:解决继承实体字段无法翻译问题------ + if (field.getAnnotation(Dict.class) != null) { + String code = field.getAnnotation(Dict.class).dicCode(); + String text = field.getAnnotation(Dict.class).dicText(); + String table = field.getAnnotation(Dict.class).dictTable(); + String key = String.valueOf(item.get(field.getName())); + + //翻译字典值对应的txt + String textValue = translateDictValue(code, text, table, key, listDict); + item.put(field.getName() + CommonConstant.DICT_TEXT_SUFFIX, textValue); + } + //date类型默认转换string格式化日期 + if (Objects.equals(field.getType().getName(),"java.util.Date") && field.getAnnotation(JsonFormat.class) == null && item.get(field.getName()) != null) { + SimpleDateFormat aDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + item.put(field.getName(), aDate.format(new Date((Long) item.get(field.getName())))); + } + } + }catch (Exception e){ + log.info("########################此返回类型不支持字典翻译########################"); + return record; + } + return item; + } + + /** + * 翻译字典文本 + * @param code 编码 + * @param text 值 + * @param table 表名 + * @param key 键 + * @param listDict 字典集合 + */ + private String translateDictValue(String code, String text, String table, String key, List listDict) { + if(oConvertUtils.isEmpty(key)) { + return null; + } + StringBuilder textValue = new StringBuilder(); + String[] keys = key.split(","); + for (String k : keys) { + String tmpValue = null; + if (k.trim().length() == 0) { + continue; //跳过循环 + } + if (!StringUtils.isEmpty(table)){ + tmpValue = commonAPI.queryTableDictTextByKey(table,text,code,k.trim()); + }else { + // tmpValue = dictService.queryDictTextByKey(code, k.trim()); + List listNew = listDict.stream().filter(o-> Objects.equals(o.getDictCode(),code) && Objects.equals(o.getItemValue(),k.trim())).collect(Collectors.toList()); + if(!CollectionUtils.isEmpty(listNew)){ + tmpValue = listNew.get(0).getItemText(); + } + } + + if (tmpValue != null) { + if (!"".equals(textValue.toString())) { + textValue.append(","); + } + textValue.append(tmpValue); + } + } + return textValue.toString(); + } +} diff --git a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/aspect/annotation/Translation.java b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/aspect/annotation/Translation.java new file mode 100644 index 00000000..959e354e --- /dev/null +++ b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/aspect/annotation/Translation.java @@ -0,0 +1,14 @@ +package com.jero.common.aspect.annotation; + +import java.lang.annotation.*; + +/** + * 只适用controller层,在controller上添加此注解,该注解对返回结果进行翻译,不支持多层嵌套 + * @author LQT + */ +@Target({ElementType.METHOD,ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Translation { + +} diff --git a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/system/vo/SysDictItemCore.java b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/system/vo/SysDictItemCore.java new file mode 100644 index 00000000..b6087f31 --- /dev/null +++ b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/system/vo/SysDictItemCore.java @@ -0,0 +1,87 @@ +package com.jero.common.system.vo; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.jero.common.aspect.annotation.Dict; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecgframework.poi.excel.annotation.Excel; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * + *

+ * + * @Author zhangweijian + * @since 2018-12-28 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +public class SysDictItemCore implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_ID) + private String id; + + /** + * 字典id + */ + private String dictId; + + /** + * 字典编码 + */ + @TableField(exist = false) + private String dictCode; + + /** + * 字典项文本 + */ + @Excel(name = "字典项文本", width = 20) + private String itemText; + + /** + * 字典项值 + */ + @Excel(name = "字典项值", width = 30) + private String itemValue; + + /** + * 描述 + */ + @Excel(name = "描述", width = 40) + private String description; + + /** + * 排序 + */ + @Excel(name = "排序", width = 15,type=4) + private Integer sortOrder; + + + /** + * 状态(1启用 0不启用) + */ + @Dict(dicCode = "dict_item_status") + private Integer status; + + private String createBy; + + private Date createTime; + + private String updateBy; + + private Date updateTime; + + +} diff --git a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/CsrfFilter.java b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/CsrfFilter.java deleted file mode 100644 index ca354e15..00000000 --- a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/CsrfFilter.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.jero.config; - -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; -import com.jero.common.api.vo.Results; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import javax.servlet.*; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.PrintWriter; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; - -/** - * 描述:跨站过滤器 - * - * @Author: 马志朝 - * @Date: 2021/4/16 14:09 - */ - -@Component -public class CsrfFilter implements Filter { - - private static final String UNKNOWN_CONSTANT = "unknown"; - /** - * LOGGER - */ - private static final Log LOGGER = LogFactory.getLog(CsrfFilter.class); - - /** - * 白名单 - */ - @Value("${jero.whiteUrls}") - private List whiteUrls; - - /** - * size - */ - private int size = 0; - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - size = whiteUrls.size(); - } - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { - try { - HttpServletRequest req = (HttpServletRequest) request; - HttpServletResponse res = (HttpServletResponse) response; - // 获取请求url地址 - String url = req.getRequestURL().toString(); - // 获取来源 - String referurl = req.getHeader("Referer"); - if(isWhiteReq(referurl)){ - chain.doFilter(request, response); - }else{ - String log = ""; - String date = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); - String ip = getIp(req); - log = "跨站请求---->>>" + ip + "||" + date + "||" + referurl + "||" + url; - LOGGER.warn(log); - //发送错误信息 - JSONObject json = JSONUtil.parseObj(Results.error("监测到跨站请求,请求失败"), false); - res.setCharacterEncoding("UTF-8"); - res.setContentType("application/json; charset=utf-8"); - PrintWriter out = res.getWriter(); - out.append(json.toStringPretty()); - } - } catch (Exception e) { - LOGGER.error("doFilter", e); - } - } - /** - * 判断是否是白名单 - */ - private boolean isWhiteReq(String referUrl) { - if (referUrl == null || "".equals(referUrl) || size == 0) { - return true; - } else { - String refHost = ""; - referUrl = referUrl.toLowerCase(); - if (referUrl.startsWith("http://")) { - refHost = referUrl.substring(7); - } else if (referUrl.startsWith("https://")) { - refHost = referUrl.substring(8); - } - for (String urlTemp : whiteUrls) { - if (refHost.contains(urlTemp.toLowerCase())) { - return true; - } - } - } - return false; - } - /** - * 获取登录用户IP地址 - * @param request - * @return - */ - public String getIp(HttpServletRequest request) { - String ip = request.getHeader("x-forwarded-for"); - if (ip == null || ip.length() == 0 || UNKNOWN_CONSTANT.equalsIgnoreCase(ip)) { - ip = request.getHeader("Proxy-Client-IP"); - } - if (ip == null || ip.length() == 0 || UNKNOWN_CONSTANT.equalsIgnoreCase(ip)) { - ip = request.getHeader("WL-Proxy-Client-IP"); - } - if (ip == null || ip.length() == 0 || UNKNOWN_CONSTANT.equalsIgnoreCase(ip)) { - ip = request.getRemoteAddr(); - } - if (ip.equals("0:0:0:0:0:0:0:1")) { - ip = "localhost"; - } - return ip; - } - @Override - public void destroy() { - // do other - } -} diff --git a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/WebConfig.java b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/WebConfig.java new file mode 100644 index 00000000..f1406645 --- /dev/null +++ b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/WebConfig.java @@ -0,0 +1,62 @@ +package com.jero.config; + +import com.jero.config.filter.cors.CorsFilter; +import com.jero.config.filter.csrf.CsrfFilter; +import com.jero.config.filter.xss.XssFilter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +/** + * @author hzwl + */ +@Configuration +public class WebConfig { + @Value("${jero.xssExcludedPages}") + private List xssExcluded; + + @Value("${jero.notFilter}") + private List notFilter; + + @Value("${jero.originIp}") + private String originIp; + + /** + * 白名单 + */ + @Value("${jero.whiteUrls}") + private List whiteUrls; + + @Bean + public FilterRegistrationBean csrfFilter() { + FilterRegistrationBean registration = new FilterRegistrationBean<>(); + registration.setFilter(new CsrfFilter(whiteUrls)); + registration.addUrlPatterns("/*"); + registration.setName("csrfFilter"); + registration.setOrder(1); + return registration; + } + + @Bean + public FilterRegistrationBean corsFilter() { + FilterRegistrationBean registration = new FilterRegistrationBean<>(); + registration.setFilter(new CorsFilter(originIp,notFilter)); + registration.addUrlPatterns("/*"); + registration.setName("corsFilter"); + registration.setOrder(2); + return registration; + } + + @Bean + public FilterRegistrationBean xssFilter() { + FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(); + filterRegistrationBean.setFilter(new XssFilter(xssExcluded)); + filterRegistrationBean.addUrlPatterns("/*"); + filterRegistrationBean.setOrder(3); + return filterRegistrationBean; + } + +} \ No newline at end of file diff --git a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/WebMvcConfiguration.java b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/WebMvcConfiguration.java index 2118630b..d1088c4f 100644 --- a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/WebMvcConfiguration.java +++ b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/WebMvcConfiguration.java @@ -53,23 +53,6 @@ public class WebMvcConfiguration implements WebMvcConfigurer { registry.addViewController("/").setViewName("doc.html"); } - @Bean - @Conditional(CorsFilterCondition.class) - public CorsFilter corsFilter() { - final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); - final CorsConfiguration corsConfiguration = new CorsConfiguration(); - //是否允许请求带有验证信息 - corsConfiguration.setAllowCredentials(true); - // 允许访问的客户端域名 - corsConfiguration.addAllowedOrigin("*"); - // 允许服务端访问的客户端请求头 - corsConfiguration.addAllowedHeader("*"); - // 允许访问的方法名,GET POST等 - corsConfiguration.addAllowedMethod("*"); - urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); - return new CorsFilter(urlBasedCorsConfigurationSource); - } - /** * 添加Long转json精度丢失的配置 * @Return: void diff --git a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/cors/CorsFilter.java b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/cors/CorsFilter.java new file mode 100644 index 00000000..ecddc8c6 --- /dev/null +++ b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/cors/CorsFilter.java @@ -0,0 +1,92 @@ +package com.jero.config.filter.cors; + + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.servlet.*; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Objects; + +/** + * @author hzwl + * 设置响应信息 + */ +@WebFilter(urlPatterns = {"/verifyCode/**"}) +public class CorsFilter implements Filter { + + private static final Log LOGGER = LogFactory.getLog(CorsFilter.class); + + private final String originIp; + + private final List notFilter; + + public CorsFilter(String originIp, List notFilter) { + this.originIp = originIp; + this.notFilter = notFilter; + } + + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) request; + + final String origin = ((HttpServletRequest) request).getHeader("Origin"); + //请求头与系统的origin不通则咔嚓 + String[] split = originIp.split(","); + if (Objects.nonNull(origin) && !ArrayUtils.contains(split,origin)){ + return; + } + //获取请求路径 + String url = req.getRequestURL().toString(); + if (!isMSBrowser(req)){ + for (String name : notFilter) { + //如果包含,不需要判断Origin是否合法 + if(url.contains(name)){ + responseInfo(request, response, chain); + return; + } + } + } + responseInfo(request, response, chain); + } + public boolean isMSBrowser(HttpServletRequest request) { + String[] IEBrowserSignals = {"MSIE", "Trident"}; + String userAgent = request.getHeader("User-Agent"); + for (String signal : IEBrowserSignals) { + if (userAgent.contains(signal)){ + return true; + } + } + return false; + } + private void responseInfo(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + httpServletResponse.setHeader("Access-Control-Allow-Origin", originIp); + httpServletResponse.addHeader("Access-Control-Allow-Headers", "Authorization"); + httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); + httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST,GET"); + httpServletResponse.setHeader("Access-Control-Max-Age", "3600"); + httpServletResponse.setHeader("Content-Security-Policy", "upgrade-insecure-requests;connect-src *"); + httpServletResponse.setHeader("X-Content-Type-Options", "nosniff"); + httpServletResponse.setHeader("X-XSS-Protection", "1;mode=block"); + httpServletResponse.setHeader("Access-Control-Allow-Headers", "Origin, Accept, x-auth-token, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, authorization"); + chain.doFilter(request, response); + } + + @Override + public void destroy() { + } + +} diff --git a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/csrf/CsrfFilter.java b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/csrf/CsrfFilter.java new file mode 100644 index 00000000..b4a11611 --- /dev/null +++ b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/csrf/CsrfFilter.java @@ -0,0 +1,127 @@ +package com.jero.config.filter.csrf; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.jero.common.api.vo.Results; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +/** + * 描述:跨站过滤器 + * + * @Author: 马志朝 + * @Date: 2021/4/16 14:09 + */ + +public class CsrfFilter implements Filter { + /** + * LOGGER + */ + private static final Log LOGGER = LogFactory.getLog(CsrfFilter.class); + + private final List whiteUrls; + + /** + * size + */ + private int size = 0; + + public CsrfFilter(List whiteUrls) { + this.whiteUrls = whiteUrls; + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + size = whiteUrls.size(); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse res = (HttpServletResponse) response; + // 获取请求url地址 + String url = req.getRequestURL().toString(); + // 获取来源 + String referurl = req.getHeader("Referer"); + if(isWhiteReq(referurl)){ + chain.doFilter(request, response); + }else{ + String log = ""; + String date = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); + String ip = getIp(req); + log = "跨站请求---->>>" + ip + "||" + date + "||" + referurl + "||" + url; + LOGGER.warn(log); + //发送错误信息 + JSONObject json = JSONUtil.parseObj(Results.error("监测到跨站请求,请求失败"), false); + res.setCharacterEncoding("UTF-8"); + res.setContentType("application/json; charset=utf-8"); + PrintWriter out = res.getWriter(); + out.append(json.toStringPretty()); + } + } + /** + * 判断是否是白名单 + */ + private boolean isWhiteReq(String referUrl) { + try { + if (referUrl == null || "".equals(referUrl) || size == 0) { + return false; + } else { + String refHost = ""; + referUrl = referUrl.toLowerCase(); + if (referUrl.startsWith("http://")) { + int i = referUrl.substring(7).indexOf("/"); + refHost = referUrl.substring(7,i+7); + } else if (referUrl.startsWith("https://")) { + int i = referUrl.substring(8).indexOf("/"); + refHost = referUrl.substring(8,i+8); + } + + for (String urlTemp : whiteUrls) { + if (refHost.equals(urlTemp.toLowerCase())) { + return true; + } + } + } + return false; + }catch (Exception e){ + LOGGER.error("doFilter", e); + return false; + } + } + /** + * 获取登录用户IP地址 + * @param request + * @return + */ + public String getIp(HttpServletRequest request) { + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + if (ip.equals("0:0:0:0:0:0:0:1")) { + ip = "localhost"; + } + return ip; + } + @Override + public void destroy() { + + } +} diff --git a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/xss/HTMLFilter.java b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/xss/HTMLFilter.java new file mode 100644 index 00000000..e067d1d9 --- /dev/null +++ b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/xss/HTMLFilter.java @@ -0,0 +1,446 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by FernFlower decompiler) +// + +package com.jero.config.filter.xss; + +import cn.hutool.core.lang.Console; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author hzwl + */ +public final class HTMLFilter { + private static final int REGEX_FLAGS_SI = 34; + private static final Pattern P_COMMENTS = Pattern.compile("", 32); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", 34); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", 32); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", 34); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", 34); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", 34); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", 34); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", 34); + private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", 32); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']"); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap(); + private final Map> vAllowed; + private final Map vTagCounts; + private final String[] vSelfClosingTags; + private final String[] vNeedClosingTags; + private final String[] vDisallowed; + private final String[] vProtocolAtts; + private final String[] vAllowedProtocols; + private final String[] vRemoveBlanks; + private final String[] vAllowedEntities; + private final boolean stripComment; + private final boolean encodeQuotes; + private boolean vDebug; + private final boolean alwaysMakeTags; + + public HTMLFilter() { + this.vTagCounts = new HashMap(); + this.vDebug = false; + this.vAllowed = new HashMap(); + ArrayList a_atts = new ArrayList(); + a_atts.add("href"); + a_atts.add("target"); + this.vAllowed.put("a", a_atts); + ArrayList img_atts = new ArrayList(); + img_atts.add("src"); + img_atts.add("width"); + img_atts.add("height"); + img_atts.add("alt"); + this.vAllowed.put("img", img_atts); + ArrayList no_atts = new ArrayList(); + this.vAllowed.put("b", no_atts); + this.vAllowed.put("strong", no_atts); + this.vAllowed.put("i", no_atts); + this.vAllowed.put("em", no_atts); + this.vSelfClosingTags = new String[]{"img"}; + this.vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"}; + this.vDisallowed = new String[0]; + this.vAllowedProtocols = new String[]{"http", "mailto", "https"}; + this.vProtocolAtts = new String[]{"src", "href"}; + this.vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"}; + this.vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"}; + this.stripComment = true; + this.encodeQuotes = true; + this.alwaysMakeTags = true; + } + + public HTMLFilter(boolean debug) { + this(); + this.vDebug = debug; + } + + public HTMLFilter(Map conf) { + this.vTagCounts = new HashMap(); + this.vDebug = false; + + assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; + + assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; + + assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; + + assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; + + assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; + + assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; + + assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; + + assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; + + this.vAllowed = Collections.unmodifiableMap((HashMap)conf.get("vAllowed")); + this.vSelfClosingTags = (String[])((String[])conf.get("vSelfClosingTags")); + this.vNeedClosingTags = (String[])((String[])conf.get("vNeedClosingTags")); + this.vDisallowed = (String[])((String[])conf.get("vDisallowed")); + this.vAllowedProtocols = (String[])((String[])conf.get("vAllowedProtocols")); + this.vProtocolAtts = (String[])((String[])conf.get("vProtocolAtts")); + this.vRemoveBlanks = (String[])((String[])conf.get("vRemoveBlanks")); + this.vAllowedEntities = (String[])((String[])conf.get("vAllowedEntities")); + this.stripComment = conf.containsKey("stripComment") ? (Boolean)conf.get("stripComment") : true; + this.encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean)conf.get("encodeQuotes") : true; + this.alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean)conf.get("alwaysMakeTags") : true; + } + + private void reset() { + this.vTagCounts.clear(); + } + + private void debug(String msg) { + if (this.vDebug) { + Console.log(msg); + } + + } + + public static String chr(int decimal) { + return String.valueOf((char)decimal); + } + + public static String htmlSpecialChars(String s) { +// String result = regexReplace(P_AMP, "&", s); + String result = regexReplace(P_QUOTE, """, s); + result = regexReplace(P_LEFT_ARROW, "<", result); + result = regexReplace(P_RIGHT_ARROW, ">", result); + return result; + } + + public String filter(String input) { + this.reset(); + this.debug("************************************************"); + this.debug(" INPUT: " + input); + String s = this.escapeComments(input); + this.debug(" escapeComments: " + s); + s = this.balanceHTML(s); + this.debug(" balanceHTML: " + s); + s = this.checkTags(s); + this.debug(" checkTags: " + s); + s = this.processRemoveBlanks(s); + this.debug("processRemoveBlanks: " + s); +// s = this.validateEntities(s); +// this.debug(" validateEntites: " + s); + this.debug("************************************************\n\n"); + return s; + } + + public boolean isAlwaysMakeTags() { + return this.alwaysMakeTags; + } + + public boolean isStripComments() { + return this.stripComment; + } + + private String escapeComments(String s) { + Matcher m = P_COMMENTS.matcher(s); + StringBuffer buf = new StringBuffer(); + if (m.find()) { + String match = m.group(1); + m.appendReplacement(buf, Matcher.quoteReplacement("")); + } + + m.appendTail(buf); + return buf.toString(); + } + + private String balanceHTML(String s) { + if (this.alwaysMakeTags) { +/* s = regexReplace(P_END_ARROW, "", s); + s = regexReplace(P_BODY_TO_END, "<$1>", s); + s = regexReplace(P_XML_CONTENT, "$1<$2", s);*/ + } else { + s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); + s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); + s = regexReplace(P_BOTH_ARROWS, "", s); + } + + return s; + } + + private String checkTags(String s) { + Matcher m = P_TAGS.matcher(s); + StringBuffer buf = new StringBuffer(); + + while(m.find()) { + String replaceStr = m.group(1); + replaceStr = this.processTag(replaceStr); + m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); + } + + m.appendTail(buf); + StringBuilder sBuilder = new StringBuilder(buf.toString()); + Iterator var5 = this.vTagCounts.keySet().iterator(); + + while(var5.hasNext()) { + String key = (String)var5.next(); + + for(int ii = 0; ii < (Integer)this.vTagCounts.get(key); ++ii) { + sBuilder.append(""); + } + } + + s = sBuilder.toString(); + return s; + } + + private String processRemoveBlanks(String s) { + String result = s; + String[] var3 = this.vRemoveBlanks; + int var4 = var3.length; + + for(int var5 = 0; var5 < var4; ++var5) { + String tag = var3[var5]; + if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) { + P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); + } + + result = regexReplace((Pattern)P_REMOVE_PAIR_BLANKS.get(tag), "", result); + if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) { + P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); + } + + result = regexReplace((Pattern)P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private static String regexReplace(Pattern regex_pattern, String replacement, String s) { + Matcher m = regex_pattern.matcher(s); + return m.replaceAll(replacement); + } + + private String processTag(String s) { + Matcher m = P_END_TAG.matcher(s); + String name; + if (m.find()) { + name = m.group(1).toLowerCase(); + if (this.allowed(name) && !inArray(name, this.vSelfClosingTags) && this.vTagCounts.containsKey(name)) { + this.vTagCounts.put(name, (Integer)this.vTagCounts.get(name) - 1); + return ""; + } + } + + m = P_START_TAG.matcher(s); + if (!m.find()) { + m = P_COMMENT.matcher(s); + return !this.stripComment && m.find() ? "<" + m.group() + ">" : ""; + } else { + name = m.group(1).toLowerCase(); + String body = m.group(2); + String ending = m.group(3); + if (!this.allowed(name)) { + return ""; + } else { + StringBuilder params = new StringBuilder(); + Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); + Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); + List paramNames = new ArrayList(); + ArrayList paramValues = new ArrayList(); + + while(m2.find()) { + paramNames.add(m2.group(1)); + paramValues.add(m2.group(3)); + } + + while(m3.find()) { + paramNames.add(m3.group(1)); + paramValues.add(m3.group(3)); + } + + for(int ii = 0; ii < paramNames.size(); ++ii) { + String paramName = ((String)paramNames.get(ii)).toLowerCase(); + String paramValue = (String)paramValues.get(ii); + if (this.allowedAttribute(name, paramName)) { + if (inArray(paramName, this.vProtocolAtts)) { + paramValue = this.processParamProtocol(paramValue); + } + + params.append(' ').append(paramName).append("=\"").append(paramValue).append("\""); + } + } + + if (inArray(name, this.vSelfClosingTags)) { + ending = " /"; + } + + if (inArray(name, this.vNeedClosingTags)) { + ending = ""; + } + + if (ending != null && ending.length() >= 1) { + ending = " /"; + } else if (this.vTagCounts.containsKey(name)) { + this.vTagCounts.put(name, (Integer)this.vTagCounts.get(name) + 1); + } else { + this.vTagCounts.put(name, 1); + } + + return "<" + name + params + ending + ">"; + } + } + } + + private String processParamProtocol(String s) { + s = this.decodeEntities(s); + Matcher m = P_PROTOCOL.matcher(s); + if (m.find()) { + String protocol = m.group(1); + if (!inArray(protocol, this.vAllowedProtocols)) { + s = "#" + s.substring(protocol.length() + 1); + if (s.startsWith("#//")) { + s = "#" + s.substring(3); + } + } + } + + return s; + } + + private String decodeEntities(String s) { + StringBuffer buf = new StringBuffer(); + Matcher m = P_ENTITY.matcher(s); + + String match; + int decimal; + while(m.find()) { + match = m.group(1); + decimal = Integer.decode(match); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + + m.appendTail(buf); + s = buf.toString(); + buf = new StringBuffer(); + m = P_ENTITY_UNICODE.matcher(s); + + while(m.find()) { + match = m.group(1); + decimal = Integer.valueOf(match, 16); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + + m.appendTail(buf); + s = buf.toString(); + buf = new StringBuffer(); +// m = P_ENCODE.matcher(s); + + while(m.find()) { + match = m.group(1); + decimal = Integer.valueOf(match, 16); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + + m.appendTail(buf); + s = buf.toString(); + s = this.validateEntities(s); + return s; + } + + private String validateEntities(String s) { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_ENTITIES.matcher(s); + + while(m.find()) { + String one = m.group(1); + String two = m.group(2); + m.appendReplacement(buf, Matcher.quoteReplacement(this.checkEntity(one, two))); + } + + m.appendTail(buf); + return this.encodeQuotes(buf.toString()); + } + + private String encodeQuotes(String s) { + if (!this.encodeQuotes) { + return s; + } else { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_QUOTES.matcher(s); + + while(m.find()) { + String one = m.group(1); + String two = m.group(2); + String three = m.group(3); + m.appendReplacement(buf, Matcher.quoteReplacement(one + regexReplace(P_QUOTE, """, two) + three)); + } + + m.appendTail(buf); + return buf.toString(); + } + } + + private String checkEntity(String preamble, String term) { + return ";".equals(term) && this.isValidEntity(preamble) ? '&' + preamble : "&" + preamble; + } + + private boolean isValidEntity(String entity) { + return inArray(entity, this.vAllowedEntities); + } + + private static boolean inArray(String s, String[] array) { + String[] var2 = array; + int var3 = array.length; + + for(int var4 = 0; var4 < var3; ++var4) { + String item = var2[var4]; + if (item != null && item.equals(s)) { + return true; + } + } + + return false; + } + + private boolean allowed(String name) { + return (this.vAllowed.isEmpty() || this.vAllowed.containsKey(name)) && !inArray(name, this.vDisallowed); + } + + private boolean allowedAttribute(String name, String paramName) { + return this.allowed(name) && (this.vAllowed.isEmpty() || ((List)this.vAllowed.get(name)).contains(paramName)); + } +} diff --git a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/xss/SqlFilter.java b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/xss/SqlFilter.java new file mode 100644 index 00000000..6d8d320a --- /dev/null +++ b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/xss/SqlFilter.java @@ -0,0 +1,33 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by FernFlower decompiler) +// + +package com.jero.config.filter.xss; + +import org.apache.commons.lang.StringUtils; + +public class SqlFilter { + public SqlFilter() { + } + + public static String sqlInject(String str) { + if (StringUtils.isBlank(str)) { + return null; + } else { + str = StringUtils.replace(str, "\\n", "Line_Break"); + String[] keywords = new String[]{"master", "truncate", "insert", "select", "delete", "update", "declare", "alter", "drop"}; + String[] var2 = keywords; + int var3 = keywords.length; + + for(int var4 = 0; var4 < var3; ++var4) { + String keyword = var2[var4]; + if (StringUtils.indexOfIgnoreCase(str, keyword + " ") != -1) { + throw new RuntimeException("包含非法字符"); + } + } + str = StringUtils.replace(str, "Line_Break", "\\n"); + return str; + } + } +} diff --git a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/xss/XssFilter.java b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/xss/XssFilter.java new file mode 100644 index 00000000..dca9b096 --- /dev/null +++ b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/xss/XssFilter.java @@ -0,0 +1,57 @@ +package com.jero.config.filter.xss; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.List; + +/** + * @author hzwl + */ +public class XssFilter implements Filter { + private final List excludedPages; + + public XssFilter(List excludedPages) { + this.excludedPages=excludedPages; + } + + @Override + public void init(FilterConfig config) throws ServletException { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + boolean isExcludedPage = false; + for (String excludedPage : excludedPages) { + if (((HttpServletRequest)request).getRequestURI().contains(excludedPage)) { + isExcludedPage = true; + break; + } + } + if (isExcludedPage) { + chain.doFilter(request, response); + } else { + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest)request); + chain.doFilter(xssRequest, response); + } + + } + + @Override + public void destroy() { + } + + + public static String filterNull(Object o) { + return o != null && !"null".equals(o.toString()) ? o.toString().trim() : ""; + } + + public static boolean isNotEmpty(Object o) { + if (o == null) { + return false; + } else { + return !"".equals(filterNull(o.toString())); + } + } +} diff --git a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/xss/XssHttpServletRequestWrapper.java b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/xss/XssHttpServletRequestWrapper.java new file mode 100644 index 00000000..7e554c1f --- /dev/null +++ b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/filter/xss/XssHttpServletRequestWrapper.java @@ -0,0 +1,134 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by FernFlower decompiler) +// + +package com.jero.config.filter.xss; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author hzwl + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { + HttpServletRequest orgRequest; + private static final HTMLFilter HTML_FILTER = new HTMLFilter(); + private static final SqlFilter sqlFilter = new SqlFilter(); + + public XssHttpServletRequestWrapper(HttpServletRequest request) { + super(request); + this.orgRequest = request; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + String type = super.getHeader("Content-Type"); + if (StringUtils.indexOfIgnoreCase(type, "application/json") < 0) { + return super.getInputStream(); + } else { + String json = IOUtils.toString(super.getInputStream(), "utf-8"); + if (StringUtils.isBlank(json)) { + return super.getInputStream(); + } else { + json = this.xssSqlEncode(json); + 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(); + } + }; + } + } + } + + @Override + public String getParameter(String name) { + String value = super.getParameter(this.xssSqlEncode(name)); + if (StringUtils.isNotBlank(value)) { + value = this.xssSqlEncode(value); + } + + return value; + } + + @Override + public String[] getParameterValues(String name) { + String[] parameters = super.getParameterValues(name); + if (parameters != null && parameters.length != 0) { + for(int i = 0; i < parameters.length; ++i) { + parameters[i] = this.xssSqlEncode(parameters[i]); + } + + return parameters; + } else { + return null; + } + } + + @Override + public Map getParameterMap() { + Map map = new LinkedHashMap(); + Map parameters = super.getParameterMap(); + Iterator var3 = parameters.keySet().iterator(); + + while(var3.hasNext()) { + String key = (String)var3.next(); + String[] values = (String[])parameters.get(key); + + for(int i = 0; i < values.length; ++i) { + values[i] = this.xssSqlEncode(values[i]); + } + + map.put(key, values); + } + + return map; + } + + @Override + public String getHeader(String name) { + String value = super.getHeader(this.xssSqlEncode(name)); + if (StringUtils.isNotBlank(value)) { + value = this.xssSqlEncode(value); + } + + return value; + } + + private String xssSqlEncode(String input) { + input=input.replaceAll("%5b","[").replaceAll("%5d","]"); + String htmlOutput= HTML_FILTER.filter(input); + return SqlFilter.sqlInject(htmlOutput); + } + + public HttpServletRequest getOrgRequest() { + return this.orgRequest; + } + + public static HttpServletRequest getOrgRequest(HttpServletRequest request) { + return request instanceof XssHttpServletRequestWrapper ? ((XssHttpServletRequestWrapper)request).getOrgRequest() : request; + } +} diff --git a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/shiro/ShiroConfig.java b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/shiro/ShiroConfig.java index 80e3dff8..b9ce1a3d 100644 --- a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/shiro/ShiroConfig.java +++ b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/shiro/ShiroConfig.java @@ -125,8 +125,8 @@ public class ShiroConfig { //filterChainDefinitionMap.put("/test/JeroDemo/html", "anon"); //模板页面 //filterChainDefinitionMap.put("/test/JeroDemo/redis/**", "anon"); //redis测试 - //websocket排除 - filterChainDefinitionMap.put("/websocket/**", "anon");//系统通知和公告 + // websocket排除 + filterChainDefinitionMap.put("/websocket/**", "anon"); filterChainDefinitionMap.put("/newsWebsocket/**", "anon");//CMS模块 filterChainDefinitionMap.put("/vxeSocket/**", "anon");//JVxeTable无痕刷新示例 diff --git a/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/entity/SysDictItemCore.java b/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/entity/SysDictItemCore.java new file mode 100644 index 00000000..85d55a3d --- /dev/null +++ b/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/entity/SysDictItemCore.java @@ -0,0 +1,87 @@ +package com.jero.modules.system.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.jero.common.aspect.annotation.Dict; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecgframework.poi.excel.annotation.Excel; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * + *

+ * + * @Author zhangweijian + * @since 2018-12-28 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +public class SysDictItemCore implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_ID) + private String id; + + /** + * 字典id + */ + private String dictId; + + /** + * 字典编码 + */ + @TableField(exist = false) + private String dictCode; + + /** + * 字典项文本 + */ + @Excel(name = "字典项文本", width = 20) + private String itemText; + + /** + * 字典项值 + */ + @Excel(name = "字典项值", width = 30) + private String itemValue; + + /** + * 描述 + */ + @Excel(name = "描述", width = 40) + private String description; + + /** + * 排序 + */ + @Excel(name = "排序", width = 15,type=4) + private Integer sortOrder; + + + /** + * 状态(1启用 0不启用) + */ + @Dict(dicCode = "dict_item_status") + private Integer status; + + private String createBy; + + private Date createTime; + + private String updateBy; + + private Date updateTime; + + +} diff --git a/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/mapper/SysDictItemMapper.java b/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/mapper/SysDictItemMapper.java index fd73fb93..b60540b7 100644 --- a/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/mapper/SysDictItemMapper.java +++ b/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/mapper/SysDictItemMapper.java @@ -17,4 +17,14 @@ import java.util.List; public interface SysDictItemMapper extends BaseMapper { @Select("SELECT * FROM sys_dict_item WHERE DICT_ID = #{mainId} order by sort_order asc, item_value asc") public List selectItemsByMainId(String mainId); + + +// /** +// * 获取字典所有数据 +// * @author LQT +// * @Date 2022/5/13 14:22 +// * @param +// * @return java.util.List +// */ +// List getDictItemAll(); } diff --git a/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/mapper/xml/SysDictItemMapper.xml b/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/mapper/xml/SysDictItemMapper.xml index b8652495..38da105e 100644 --- a/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/mapper/xml/SysDictItemMapper.xml +++ b/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/mapper/xml/SysDictItemMapper.xml @@ -2,4 +2,9 @@ + + + + + diff --git a/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/mapper/xml/SysDictMapper.xml b/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/mapper/xml/SysDictMapper.xml index fe5108d9..5183b785 100644 --- a/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/mapper/xml/SysDictMapper.xml +++ b/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/mapper/xml/SysDictMapper.xml @@ -36,7 +36,7 @@ @@ -66,7 +66,7 @@ - + - + - + SELECT COUNT(*) FROM ${tableName} WHERE ${fieldName} = #{fieldVal} and id <> #{dataId} + + and del_flag = 0 + - + - + - + - + - +