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/LockMethodAspect.java b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/aspect/LockMethodAspect.java new file mode 100644 index 00000000..ee0b7be8 --- /dev/null +++ b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/aspect/LockMethodAspect.java @@ -0,0 +1,57 @@ +package com.jero.common.aspect; + +import com.jero.common.aspect.annotation.RedisLock; +import com.jero.common.exception.JeroBootException; +import com.jero.common.util.RedisLockHelper; +import com.jero.common.util.RedisUtil; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.lang.reflect.Method; +import java.util.UUID; + +/** + * 分布式部署定时任务 + * @Author lqt + */ +@Slf4j +@Aspect +@Component +public class LockMethodAspect { + @Resource + private RedisLockHelper redisLockHelper; + @Resource + private RedisUtil redisUtis; + + @Around("@annotation(com.jero.common.aspect.annotation.RedisLock)") + public Object around(ProceedingJoinPoint joinPoint) { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + RedisLock redisLock = method.getAnnotation(RedisLock.class); + String value = UUID.randomUUID().toString(); + log.info("====value=====" + value + "==========="); + String key = redisLock.key(); + log.info("====key======" + key + "==========="); + try { + final boolean islock = redisLockHelper.lock(redisUtis, key, value, redisLock.expire(), redisLock.timeUnit()); + log.info("isLock : {}", islock); + if (!islock) { + log.error("获取锁失败"); + throw new JeroBootException("获取锁失败"); + } + try { + return joinPoint.proceed(); + } catch (Throwable throwable) { + throw new JeroBootException("系统异常"); + } + } finally { + log.info("释放锁"); + redisLockHelper.unlock(redisUtis, key, value); + } + } +} 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/RedisLock.java b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/aspect/annotation/RedisLock.java new file mode 100644 index 00000000..60ece522 --- /dev/null +++ b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/aspect/annotation/RedisLock.java @@ -0,0 +1,24 @@ +package com.jero.common.aspect.annotation; + +import java.lang.annotation.*; +import java.util.concurrent.TimeUnit; +/** + * @author LQT + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface RedisLock { + /*** 业务键** @return*/ + String key(); + + /*** 锁的过期秒数,默认是5秒** @return*/ + int expire() default 5; + + /*** 尝试加锁,最多等待时间** @return*/ + long waitTime() default Long.MIN_VALUE; + + /*** 锁的超时时间单位** @return*/ + TimeUnit timeUnit() default TimeUnit.SECONDS; +} 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/common/util/RedisLockHelper.java b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/util/RedisLockHelper.java new file mode 100644 index 00000000..fda1c243 --- /dev/null +++ b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/common/util/RedisLockHelper.java @@ -0,0 +1,116 @@ +package com.jero.common.util; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +@Slf4j +@Component +public class RedisLockHelper { + + private long sleepTime = 100; + /** + * 直接使用setnx + expire方式获取分布式锁 + * 非原子性 + * + * @param key + * @param value + * @param timeout + * @return + */ + public boolean lockSetNx(RedisUtil redisTemplate, String key, String value, int timeout) { + if ( redisTemplate.setnx(key, value)) { + return redisTemplate.expire(key, timeout); + } else { + return false; + } + } + + /** + * 使用Lua脚本,脚本中使用setnex+expire命令进行加锁操作 + * + * @param redisTemplate + * @param key + * @param uniqueId + * @param seconds + * @return + */ + public boolean lockWithLua(RedisUtil redisTemplate, String key, String uniqueId, int seconds) { + if(!Objects.isNull(seconds)){ + log.info("未使用参数:" + seconds); + } + String luaScript = "if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then" + + "redis.call('expire',KEYS[1],ARGV[2]) return 1 else return 0 end"; + Object result = redisTemplate.eval(RedisScript.of(luaScript), Collections.singletonList(key), uniqueId); + //判断是否成功 + return result.equals(1L); + } + + /** + * 在Redis的2.6.12及以后中,使用 set key value [NX] [EX] 命令 + * + * @param key + * @param value + * @param timeout + * @return + */ + public boolean lock(RedisUtil redisTemplate, String key, String value, int timeout, TimeUnit timeUnit) { + long seconds = timeUnit.toSeconds(timeout); + return redisTemplate.setnx(key, value, seconds); + } + + /** + * 自定义获取锁的超时时间 + * + * @param redisTemplate + * @param key + * @param value + * @param timeout + * @param waitTime + * @param timeUnit + * @return + * @throws InterruptedException + */ + public boolean lockWithWaitTime(RedisUtil redisTemplate, String key, String value, int timeout, long waitTime, TimeUnit timeUnit) throws InterruptedException { + long seconds = timeUnit.toSeconds(timeout); + while (waitTime >= 0) { + if (redisTemplate.setnx(key, value, seconds)) { + return true; + } + waitTime -= sleepTime; + Thread.sleep(sleepTime); + } + return false; + } + /** + * 错误的解锁方法—直接删除key + * + * @param key + */ + public void unlockWithDel(RedisUtil redisTemplate, String key) { + redisTemplate.del(key); + } + + /** + * 使用Lua脚本进行解锁操纵,解锁的时候验证value值 + * + * @param redisTemplate + * @param key + * @param value + * @return + */ + public boolean unlock(RedisUtil redisTemplate, String key, String value) { + String luaScript = "if redis.call('get',KEYS[1]) == ARGV[1] then " + + "return redis.call('del',KEYS[1]) else return 0 end"; + DefaultRedisScript redisScript =new DefaultRedisScript<> (); + redisScript.setScriptText(luaScript); + // 这个值类型要跟lua返回值类型一致才行,否则就会报 java.lang.IllegalStateException + redisScript.setResultType(Long.class); + return redisTemplate.eval(redisScript, Collections.singletonList(key), value); + } +} 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..29ad141d --- /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,89 @@ +package com.jero.config.filter.cors; + + +import org.apache.commons.lang3.ArrayUtils; + +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 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 { + // do other + } + + @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() { + // do other + } + +} 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..c40e84f3 --- /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,131 @@ +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; +import java.util.Objects; + +/** + * 描述:跨站过滤器 + * + * @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; + // 获取是否为websocket链接 + String upgrade = req.getHeader("Upgrade"); + // 获取来源 + String referurl = req.getHeader("Referer"); + if(isWhiteReq(referurl) || Objects.equals(upgrade,"websocket")){ + chain.doFilter(request, response); + }else{ + // 获取请求url地址 + String url = req.getRequestURL().toString(); + 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.indexOf("/", 7) - 7; + refHost = referUrl.substring(7,i+7); + } else if (referUrl.startsWith("https://")) { + int i = referUrl.indexOf("/", 8) - 8; + 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 unknown = "unknown"; + 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() { + // do other + } +} 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..652b60b7 --- /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,398 @@ +// +// 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 org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +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 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_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+)(<|$)", 32); + 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_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() { + String strong = "strong"; + this.vTagCounts = new HashMap<>(); + this.vDebug = false; + this.vAllowed = new HashMap<>(); + ArrayList aAtts = new ArrayList<>(); + aAtts.add("href"); + aAtts.add("target"); + this.vAllowed.put("a", aAtts); + ArrayList imgAtts = new ArrayList<>(); + imgAtts.add("src"); + imgAtts.add("width"); + imgAtts.add("height"); + imgAtts.add("alt"); + this.vAllowed.put("img", imgAtts); + ArrayList noAtts = new ArrayList<>(); + this.vAllowed.put("b", noAtts); + this.vAllowed.put(strong, noAtts); + this.vAllowed.put("i", noAtts); + this.vAllowed.put("em", noAtts); + 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; + } + + 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_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); + this.debug("************************************************\n\n"); + return s; + } + + 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) { + // do other + } 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()); + + for (Map.Entry entry : this.vTagCounts.entrySet()) { + 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(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(P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private static String regexReplace(Pattern regexPattern, String replacement, String s) { + Matcher m = regexPattern.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, 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<>(); + + getAppend(name, params, m2, m3, paramNames, paramValues); + + ending = getString(name, ending); + + return "<" + name + params + ending + ">"; + } + } + } + + @Nullable + private String getString(String name, String ending) { + 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, this.vTagCounts.get(name) + 1); + } else { + this.vTagCounts.put(name, 1); + } + return ending; + } + + private void getAppend(String name, StringBuilder params, Matcher m2, Matcher m3, List paramNames, ArrayList paramValues) { + 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 = paramNames.get(ii).toLowerCase(); + String paramValue = 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("\""); + } + } + } + + 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); + buf = new StringBuffer(); + + 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() || 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..280b2703 --- /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,35 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by FernFlower decompiler) +// + +package com.jero.config.filter.xss; + +import com.jero.common.exception.JeroBootException; +import org.apache.commons.lang.StringUtils; + +public class SqlFilter { + private 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 JeroBootException("包含非法字符"); + } + } + 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..7f4d2dfa --- /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,58 @@ +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 { + // do other + } + + @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() { + // do other + } + + + 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..d73678b8 --- /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,130 @@ +// +// 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.nio.charset.StandardCharsets; +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(); + + 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(), StandardCharsets.UTF_8); + if (StringUtils.isBlank(json)) { + return super.getInputStream(); + } else { + json = this.xssSqlEncode(json); + final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)); + return new ServletInputStream() { + @Override + public boolean isFinished() { + return true; + } + @Override + public boolean isReady() { + return true; + } + @Override + public void setReadListener(ReadListener readListener) { + // do other + } + @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 new String[0]; + } + } + + @Override + public Map getParameterMap() { + Map map = new LinkedHashMap<>(); + Map parameters = super.getParameterMap(); + + for (Map.Entry entry : parameters.entrySet()) { + String[] values = entry.getValue(); + for (int i = 0; i < values.length; ++i) { + values[i] = this.xssSqlEncode(values[i]); + } + map.put(entry.getKey(), 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.replace("%5b","[").replace("%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-base/jero-boot-base-tools/src/main/java/com/jero/common/util/RedisUtil.java b/jero-boot/jero-boot-base/jero-boot-base-tools/src/main/java/com/jero/common/util/RedisUtil.java index ce8c468d..43719482 100644 --- a/jero-boot/jero-boot-base/jero-boot-base-tools/src/main/java/com/jero/common/util/RedisUtil.java +++ b/jero-boot/jero-boot-base/jero-boot-base-tools/src/main/java/com/jero/common/util/RedisUtil.java @@ -1,10 +1,12 @@ package com.jero.common.util; +import java.time.Duration; import java.util.*; import java.util.concurrent.TimeUnit; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.*; +import org.springframework.data.redis.core.script.RedisScript; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; @@ -609,4 +611,37 @@ public class RedisUtil { e.printStackTrace(); } } + + /** + * setnx + */ + public boolean setnx(String key, String value) { + try { + return redisTemplate.opsForValue().setIfAbsent(key, value); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * setnx + */ + public boolean setnx(String key, String value,long time) { + try { + return redisTemplate.opsForValue().setIfAbsent(key,value, Duration.ofSeconds(time)); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * eval + */ + public Boolean eval(RedisScript luaScripts, List keys, String values) { + Long flag = redisTemplate.execute(luaScripts, keys, values); + //判断是不是为1 + return flag == 1L; + } } diff --git a/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/quartz/job/SampleParamJob.java b/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/quartz/job/SampleParamJob.java index 78ad9036..6c98a7ef 100644 --- a/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/quartz/job/SampleParamJob.java +++ b/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/quartz/job/SampleParamJob.java @@ -1,12 +1,16 @@ package com.jero.modules.quartz.job; +import com.jero.common.aspect.annotation.RedisLock; import com.jero.common.util.DateUtils; +import org.quartz.DisallowConcurrentExecution; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import lombok.extern.slf4j.Slf4j; +import java.util.concurrent.TimeUnit; + /** * 示例带参定时任务 * @@ -25,6 +29,7 @@ public class SampleParamJob implements Job { } @Override + @RedisLock(key = "lock----TestJob",expire=60,timeUnit= TimeUnit.SECONDS) public void execute(JobExecutionContext jobExecutionContext) { log.info(" Job Execution key:"+jobExecutionContext.getJobDetail().getKey()); log.info("welcome " + this.parameter + " Jero-Boot 带参数定时任务 SampleParamJob ! 时间:" + DateUtils.now()); 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 + - + - + - + - + - +