Commit 70af2bc78c1dfbf516f6a8589597b251aac8466a
1 parent
fe94e459
1. 增加 API 访问日志、异常日志表
2. 调整过滤器的顺序
Showing
15 changed files
with
364 additions
and
112 deletions
ruoyi-common/src/main/resources/logback.xml deleted
| 1 | -<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | -<configuration> | |
| 3 | - <!-- 日志存放路径 --> | |
| 4 | - <property name="log.path" value="/home/ruoyi/logs" /> | |
| 5 | - <!-- 日志输出格式 --> | |
| 6 | - <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" /> | |
| 7 | - | |
| 8 | - <!-- 控制台输出 --> | |
| 9 | - <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> | |
| 10 | - <encoder> | |
| 11 | - <pattern>${log.pattern}</pattern> | |
| 12 | - </encoder> | |
| 13 | - </appender> | |
| 14 | - | |
| 15 | - <!-- 系统日志输出 --> | |
| 16 | - <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"> | |
| 17 | - <file>${log.path}/sys-info.log</file> | |
| 18 | - <!-- 循环政策:基于时间创建日志文件 --> | |
| 19 | - <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | |
| 20 | - <!-- 日志文件名格式 --> | |
| 21 | - <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern> | |
| 22 | - <!-- 日志最大的历史 60天 --> | |
| 23 | - <maxHistory>60</maxHistory> | |
| 24 | - </rollingPolicy> | |
| 25 | - <encoder> | |
| 26 | - <pattern>${log.pattern}</pattern> | |
| 27 | - </encoder> | |
| 28 | - <filter class="ch.qos.logback.classic.filter.LevelFilter"> | |
| 29 | - <!-- 过滤的级别 --> | |
| 30 | - <level>INFO</level> | |
| 31 | - <!-- 匹配时的操作:接收(记录) --> | |
| 32 | - <onMatch>ACCEPT</onMatch> | |
| 33 | - <!-- 不匹配时的操作:拒绝(不记录) --> | |
| 34 | - <onMismatch>DENY</onMismatch> | |
| 35 | - </filter> | |
| 36 | - </appender> | |
| 37 | - | |
| 38 | - <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"> | |
| 39 | - <file>${log.path}/sys-error.log</file> | |
| 40 | - <!-- 循环政策:基于时间创建日志文件 --> | |
| 41 | - <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | |
| 42 | - <!-- 日志文件名格式 --> | |
| 43 | - <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern> | |
| 44 | - <!-- 日志最大的历史 60天 --> | |
| 45 | - <maxHistory>60</maxHistory> | |
| 46 | - </rollingPolicy> | |
| 47 | - <encoder> | |
| 48 | - <pattern>${log.pattern}</pattern> | |
| 49 | - </encoder> | |
| 50 | - <filter class="ch.qos.logback.classic.filter.LevelFilter"> | |
| 51 | - <!-- 过滤的级别 --> | |
| 52 | - <level>ERROR</level> | |
| 53 | - <!-- 匹配时的操作:接收(记录) --> | |
| 54 | - <onMatch>ACCEPT</onMatch> | |
| 55 | - <!-- 不匹配时的操作:拒绝(不记录) --> | |
| 56 | - <onMismatch>DENY</onMismatch> | |
| 57 | - </filter> | |
| 58 | - </appender> | |
| 59 | - | |
| 60 | - <!-- 用户访问日志输出 --> | |
| 61 | - <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender"> | |
| 62 | - <file>${log.path}/sys-user.log</file> | |
| 63 | - <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | |
| 64 | - <!-- 按天回滚 daily --> | |
| 65 | - <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern> | |
| 66 | - <!-- 日志最大的历史 60天 --> | |
| 67 | - <maxHistory>60</maxHistory> | |
| 68 | - </rollingPolicy> | |
| 69 | - <encoder> | |
| 70 | - <pattern>${log.pattern}</pattern> | |
| 71 | - </encoder> | |
| 72 | - </appender> | |
| 73 | - | |
| 74 | - <!-- 系统模块日志级别控制 --> | |
| 75 | - <logger name="com.ruoyi" level="info" /> | |
| 76 | - <!-- Spring日志级别控制 --> | |
| 77 | - <logger name="org.springframework" level="warn" /> | |
| 78 | - | |
| 79 | - <root level="info"> | |
| 80 | - <appender-ref ref="console" /> | |
| 81 | - </root> | |
| 82 | - | |
| 83 | - <!--系统操作日志--> | |
| 84 | - <root level="info"> | |
| 85 | - <appender-ref ref="file_info" /> | |
| 86 | - <appender-ref ref="file_error" /> | |
| 87 | - </root> | |
| 88 | - | |
| 89 | - <!--系统用户操作日志--> | |
| 90 | - <logger name="sys-user" level="info"> | |
| 91 | - <appender-ref ref="sys-user"/> | |
| 92 | - </logger> | |
| 93 | -</configuration> | |
| 94 | 0 | \ No newline at end of file |
src/main/java/cn/iocoder/dashboard/common/enums/UserTypeEnum.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.common.enums; | |
| 2 | + | |
| 3 | +import lombok.AllArgsConstructor; | |
| 4 | +import lombok.Getter; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * 全局用户类型枚举 | |
| 8 | + */ | |
| 9 | +@AllArgsConstructor | |
| 10 | +@Getter | |
| 11 | +public enum UserTypeEnum { | |
| 12 | + | |
| 13 | + MEMBER(1, "会员"), // 面向 c 端,普通用户 | |
| 14 | + ADMIN(2, "管理员"); // 面向 b 端,管理后台 | |
| 15 | + | |
| 16 | + /** | |
| 17 | + * 类型 | |
| 18 | + */ | |
| 19 | + private final Integer value; | |
| 20 | + /** | |
| 21 | + * 类型名 | |
| 22 | + */ | |
| 23 | + private final String name; | |
| 24 | + | |
| 25 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/logger/apilog/config/ApiLogConfiguration.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.logger.apilog.config; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.logger.apilog.core.filter.ApiAccessLogFilter; | |
| 4 | +import cn.iocoder.dashboard.framework.web.config.WebProperties; | |
| 5 | +import cn.iocoder.dashboard.framework.web.core.enums.FilterOrderEnum; | |
| 6 | +import org.springframework.boot.web.servlet.FilterRegistrationBean; | |
| 7 | +import org.springframework.context.annotation.Bean; | |
| 8 | +import org.springframework.context.annotation.Configuration; | |
| 9 | + | |
| 10 | +import javax.servlet.Filter; | |
| 11 | + | |
| 12 | +@Configuration | |
| 13 | +public class ApiLogConfiguration { | |
| 14 | + | |
| 15 | + /** | |
| 16 | + * 创建 ApiAccessLogFilter Bean,记录 API 请求日志 | |
| 17 | + */ | |
| 18 | + @Bean | |
| 19 | + public FilterRegistrationBean<ApiAccessLogFilter> apiAccessLogFilter(WebProperties webProperties) { | |
| 20 | + ApiAccessLogFilter filter = new ApiAccessLogFilter(webProperties); | |
| 21 | + return createFilterBean(filter, FilterOrderEnum.API_ACCESS_LOG_FILTER); | |
| 22 | + } | |
| 23 | + | |
| 24 | + private static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) { | |
| 25 | + FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter); | |
| 26 | + bean.setOrder(order); | |
| 27 | + return bean; | |
| 28 | + } | |
| 29 | + | |
| 30 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/filter/ApiAccessLogFilter.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.logger.apilog.core.filter; | |
| 2 | + | |
| 3 | +import cn.hutool.extra.servlet.ServletUtil; | |
| 4 | +import cn.iocoder.dashboard.framework.logger.apilog.core.service.dto.ApiAccessLogCreateDTO; | |
| 5 | +import cn.iocoder.dashboard.framework.web.config.WebProperties; | |
| 6 | +import cn.iocoder.dashboard.util.servlet.ServletUtils; | |
| 7 | +import lombok.AllArgsConstructor; | |
| 8 | +import lombok.extern.slf4j.Slf4j; | |
| 9 | +import org.springframework.web.filter.OncePerRequestFilter; | |
| 10 | + | |
| 11 | +import javax.servlet.FilterChain; | |
| 12 | +import javax.servlet.ServletException; | |
| 13 | +import javax.servlet.http.HttpServletRequest; | |
| 14 | +import javax.servlet.http.HttpServletResponse; | |
| 15 | +import java.io.IOException; | |
| 16 | +import java.util.Date; | |
| 17 | +import java.util.Map; | |
| 18 | + | |
| 19 | +/** | |
| 20 | + * API 访问日志 Filter | |
| 21 | + * | |
| 22 | + * @author 芋道源码 | |
| 23 | + */ | |
| 24 | +@AllArgsConstructor | |
| 25 | +@Slf4j | |
| 26 | +public class ApiAccessLogFilter extends OncePerRequestFilter { | |
| 27 | + | |
| 28 | + private final WebProperties webProperties; | |
| 29 | + | |
| 30 | + @Override | |
| 31 | + protected boolean shouldNotFilter(HttpServletRequest request) { | |
| 32 | + return !request.getRequestURI().startsWith(webProperties.getApiPrefix()); | |
| 33 | + } | |
| 34 | + | |
| 35 | + @Override | |
| 36 | + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) | |
| 37 | + throws ServletException, IOException { | |
| 38 | + // 获得开始时间 | |
| 39 | + Date startTime = new Date(); | |
| 40 | + // 提前获得参数,避免 XssFilter 过滤处理 | |
| 41 | + Map<String, String> queryString = ServletUtil.getParamMap(request); | |
| 42 | + String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtil.getBody(request) : null; | |
| 43 | + | |
| 44 | + try { | |
| 45 | + // 继续过滤器 | |
| 46 | + filterChain.doFilter(request, response); | |
| 47 | + // 正常执行,记录日志 | |
| 48 | + createApiAccessLog(request, startTime, queryString, requestBody, null); | |
| 49 | + } catch (Exception ex) { | |
| 50 | + // 异常执行,记录日志 | |
| 51 | + createApiAccessLog(request, startTime, queryString, requestBody, ex); | |
| 52 | + throw ex; | |
| 53 | + } | |
| 54 | + } | |
| 55 | + | |
| 56 | + private void createApiAccessLog(HttpServletRequest request, Date startTime, | |
| 57 | + Map<String, String> queryString, String requestBody, Exception ex) { | |
| 58 | + try { | |
| 59 | + ApiAccessLogCreateDTO createDTO = this.buildApiAccessLogDTO(request, startTime, queryString, requestBody, ex); | |
| 60 | + | |
| 61 | + } catch (Exception e) { | |
| 62 | + log.error("[createApiAccessLog][url({}) 发生异常]", request.getRequestURI(), ex); | |
| 63 | + } | |
| 64 | + } | |
| 65 | + | |
| 66 | + private ApiAccessLogCreateDTO buildApiAccessLogDTO(HttpServletRequest request, Date startTime, | |
| 67 | + Map<String, String> queryString, String requestBody, Exception ex) { | |
| 68 | + return null; | |
| 69 | + } | |
| 70 | + | |
| 71 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/service/dto/ApiAccessLogCreateDTO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.logger.apilog.core.service.dto; | |
| 2 | + | |
| 3 | +import javax.validation.constraints.NotNull; | |
| 4 | +import java.util.Date; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * API 访问日志创建 DTO | |
| 8 | + * | |
| 9 | + * @author 芋道源码 | |
| 10 | + */ | |
| 11 | +public class ApiAccessLogCreateDTO { | |
| 12 | + | |
| 13 | + /** | |
| 14 | + * 链路追踪编号 | |
| 15 | + */ | |
| 16 | + private String traceId; | |
| 17 | + /** | |
| 18 | + * 用户编号 | |
| 19 | + */ | |
| 20 | + private Integer userId; | |
| 21 | + /** | |
| 22 | + * 用户类型 | |
| 23 | + */ | |
| 24 | + private Integer userType; | |
| 25 | + /** | |
| 26 | + * 应用名 | |
| 27 | + */ | |
| 28 | + @NotNull(message = "应用名不能为空") | |
| 29 | + private String applicationName; | |
| 30 | + | |
| 31 | + /** | |
| 32 | + * 请求方法名 | |
| 33 | + */ | |
| 34 | + @NotNull(message = "http 请求方法不能为空") | |
| 35 | + private String requestMethod; | |
| 36 | + /** | |
| 37 | + * 访问地址 | |
| 38 | + */ | |
| 39 | + @NotNull(message = "访问地址不能为空") | |
| 40 | + private String requestUrl; | |
| 41 | + /** | |
| 42 | + * 请求参数 | |
| 43 | + */ | |
| 44 | + @NotNull(message = "请求参数不能为空") | |
| 45 | + private String requestParams; | |
| 46 | + /** | |
| 47 | + * 用户 IP | |
| 48 | + */ | |
| 49 | + @NotNull(message = "ip 不能为空") | |
| 50 | + private String userIp; | |
| 51 | + /** | |
| 52 | + * 浏览器 UA | |
| 53 | + */ | |
| 54 | + @NotNull(message = "User-Agent 不能为空") | |
| 55 | + private String userAgent; | |
| 56 | + | |
| 57 | + /** | |
| 58 | + * 开始请求时间 | |
| 59 | + */ | |
| 60 | + @NotNull(message = "开始请求时间不能为空") | |
| 61 | + private Date startTime; | |
| 62 | + /** | |
| 63 | + * 结束请求时间 | |
| 64 | + */ | |
| 65 | + @NotNull(message = "结束请求时间不能为空") | |
| 66 | + private Date endTime; | |
| 67 | + /** | |
| 68 | + * 执行时长,单位:毫秒 | |
| 69 | + */ | |
| 70 | + @NotNull(message = "执行时长不能为空") | |
| 71 | + private Integer duration; | |
| 72 | + /** | |
| 73 | + * 结果码 | |
| 74 | + */ | |
| 75 | + @NotNull(message = "错误码不能为空") | |
| 76 | + private Integer resultCode; | |
| 77 | + /** | |
| 78 | + * 结果提示 | |
| 79 | + */ | |
| 80 | + private String resultMsg; | |
| 81 | + | |
| 82 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/service/package-info.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.logger.apilog.core.service; | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/security/core/handler/LogoutSuccessHandlerImpl.java
| ... | ... | @@ -37,6 +37,5 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { |
| 37 | 37 | } |
| 38 | 38 | // 返回成功 |
| 39 | 39 | ServletUtils.writeJSON(response, null); |
| 40 | -// ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(HttpStatus.OK.value(), "退出成功"))); | |
| 41 | 40 | } |
| 42 | 41 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/web/config/WebConfiguration.java
| 1 | 1 | package cn.iocoder.dashboard.framework.web.config; |
| 2 | 2 | |
| 3 | +import cn.iocoder.dashboard.framework.web.core.enums.FilterOrderEnum; | |
| 3 | 4 | import cn.iocoder.dashboard.framework.web.core.filter.RequestBodyCacheFilter; |
| 4 | 5 | import cn.iocoder.dashboard.framework.web.core.filter.XssFilter; |
| 5 | 6 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| 7 | +import org.springframework.boot.web.servlet.FilterRegistrationBean; | |
| 6 | 8 | import org.springframework.context.annotation.Bean; |
| 7 | 9 | import org.springframework.context.annotation.Configuration; |
| 8 | -import org.springframework.core.annotation.Order; | |
| 9 | 10 | import org.springframework.util.PathMatcher; |
| 10 | 11 | import org.springframework.web.bind.annotation.RestController; |
| 11 | 12 | import org.springframework.web.cors.CorsConfiguration; |
| ... | ... | @@ -15,10 +16,8 @@ import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; |
| 15 | 16 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
| 16 | 17 | |
| 17 | 18 | import javax.annotation.Resource; |
| 19 | +import javax.servlet.Filter; | |
| 18 | 20 | |
| 19 | -/** | |
| 20 | - * Web 配置类 | |
| 21 | - */ | |
| 22 | 21 | @Configuration |
| 23 | 22 | @EnableConfigurationProperties({WebProperties.class, XssProperties.class}) |
| 24 | 23 | public class WebConfiguration implements WebMvcConfigurer { |
| ... | ... | @@ -39,8 +38,7 @@ public class WebConfiguration implements WebMvcConfigurer { |
| 39 | 38 | * 创建 CorsFilter Bean,解决跨域问题 |
| 40 | 39 | */ |
| 41 | 40 | @Bean |
| 42 | - @Order(Integer.MIN_VALUE) | |
| 43 | - public CorsFilter corsFilter() { | |
| 41 | + public FilterRegistrationBean<CorsFilter> corsFilterBean() { | |
| 44 | 42 | // 创建 CorsConfiguration 对象 |
| 45 | 43 | CorsConfiguration config = new CorsConfiguration(); |
| 46 | 44 | config.setAllowCredentials(true); |
| ... | ... | @@ -50,25 +48,29 @@ public class WebConfiguration implements WebMvcConfigurer { |
| 50 | 48 | // 创建 UrlBasedCorsConfigurationSource 对象 |
| 51 | 49 | UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); |
| 52 | 50 | source.registerCorsConfiguration("/**", config); // 对接口配置跨域设置 |
| 53 | - return new CorsFilter(source); | |
| 51 | + return createFilterBean(new CorsFilter(source), FilterOrderEnum.CORS_FILTER); | |
| 54 | 52 | } |
| 55 | 53 | |
| 56 | 54 | /** |
| 57 | 55 | * 创建 RequestBodyCacheFilter Bean,可重复读取请求内容 |
| 58 | 56 | */ |
| 59 | 57 | @Bean |
| 60 | - @Order(Integer.MIN_VALUE) | |
| 61 | - public RequestBodyCacheFilter requestBodyCacheFilter() { | |
| 62 | - return new RequestBodyCacheFilter(); | |
| 58 | + public FilterRegistrationBean<RequestBodyCacheFilter> requestBodyCacheFilter() { | |
| 59 | + return createFilterBean(new RequestBodyCacheFilter(), FilterOrderEnum.REQUEST_BODY_CACHE_FILTER); | |
| 63 | 60 | } |
| 64 | 61 | |
| 65 | 62 | /** |
| 66 | 63 | * 创建 XssFilter Bean,解决 Xss 安全问题 |
| 67 | 64 | */ |
| 68 | 65 | @Bean |
| 69 | - @Order(Integer.MIN_VALUE + 1000) // 需要保证在 RequestBodyCacheFilter 后面 | |
| 70 | - public XssFilter xssFilter(XssProperties properties, PathMatcher pathMatcher) { | |
| 71 | - return new XssFilter(properties, pathMatcher); | |
| 66 | + public FilterRegistrationBean<XssFilter> xssFilter(XssProperties properties, PathMatcher pathMatcher) { | |
| 67 | + return createFilterBean(new XssFilter(properties, pathMatcher), FilterOrderEnum.XSS_FILTER); | |
| 68 | + } | |
| 69 | + | |
| 70 | + private static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) { | |
| 71 | + FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter); | |
| 72 | + bean.setOrder(order); | |
| 73 | + return bean; | |
| 72 | 74 | } |
| 73 | 75 | |
| 74 | 76 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/web/core/enums/FilterOrderEnum.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.web.core.enums; | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * 过滤器顺序的枚举类,保证过滤器按照符合我们的预期 | |
| 5 | + * | |
| 6 | + * @author 芋道源码 | |
| 7 | + */ | |
| 8 | +public interface FilterOrderEnum { | |
| 9 | + | |
| 10 | + int CORS_FILTER = Integer.MIN_VALUE; | |
| 11 | + | |
| 12 | + int REQUEST_BODY_CACHE_FILTER = Integer.MIN_VALUE + 500; | |
| 13 | + | |
| 14 | + // OrderedRequestContextFilter 默认为 -105,用于国际化上下文等等 | |
| 15 | + | |
| 16 | + int API_ACCESS_LOG_FILTER = -104; // 需要保证在 RequestBodyCacheFilter 后面 | |
| 17 | + | |
| 18 | + int XSS_FILTER = -103; // 需要保证在 RequestBodyCacheFilter 后面 | |
| 19 | + | |
| 20 | + // Spring Security Filter 默认为 -100,可见 SecurityProperties 配置属性类 | |
| 21 | + | |
| 22 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/web/core/filter/RequestBodyCacheFilter.java
| 1 | 1 | package cn.iocoder.dashboard.framework.web.core.filter; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.util.StrUtil; | |
| 4 | -import org.springframework.http.MediaType; | |
| 3 | +import cn.iocoder.dashboard.util.servlet.ServletUtils; | |
| 5 | 4 | import org.springframework.web.filter.OncePerRequestFilter; |
| 6 | 5 | import org.springframework.web.util.ContentCachingRequestWrapper; |
| 7 | 6 | |
| ... | ... | @@ -29,7 +28,7 @@ public class RequestBodyCacheFilter extends OncePerRequestFilter { |
| 29 | 28 | @Override |
| 30 | 29 | protected boolean shouldNotFilter(HttpServletRequest request) { |
| 31 | 30 | // 只处理 json 请求内容 |
| 32 | - return !StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE); | |
| 31 | + return !ServletUtils.isJsonRequest(request); | |
| 33 | 32 | } |
| 34 | 33 | |
| 35 | 34 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/web/core/filter/XssRequestWrapper.java
| ... | ... | @@ -6,6 +6,7 @@ import cn.hutool.core.util.ArrayUtil; |
| 6 | 6 | import cn.hutool.core.util.ReflectUtil; |
| 7 | 7 | import cn.hutool.core.util.StrUtil; |
| 8 | 8 | import cn.hutool.http.HTMLFilter; |
| 9 | +import cn.iocoder.dashboard.util.servlet.ServletUtils; | |
| 9 | 10 | import org.springframework.http.MediaType; |
| 10 | 11 | |
| 11 | 12 | import javax.servlet.ReadListener; |
| ... | ... | @@ -56,7 +57,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper { |
| 56 | 57 | @Override |
| 57 | 58 | public ServletInputStream getInputStream() throws IOException { |
| 58 | 59 | // 如果非 json 请求,不进行 Xss 处理 |
| 59 | - if (!StrUtil.startWithIgnoreCase(super.getContentType(), MediaType.APPLICATION_JSON_VALUE)) { | |
| 60 | + if (!ServletUtils.isJsonRequest(this)) { | |
| 60 | 61 | return super.getInputStream(); |
| 61 | 62 | } |
| 62 | 63 | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/infra/dal/dataobject/job/InfJobLogDO.java
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/logger/SysApiAccessLogDO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.dal.dataobject.logger; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.common.enums.UserTypeEnum; | |
| 4 | +import cn.iocoder.dashboard.common.pojo.CommonResult; | |
| 5 | +import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; | |
| 6 | +import com.baomidou.mybatisplus.annotation.TableId; | |
| 7 | +import com.baomidou.mybatisplus.annotation.TableName; | |
| 8 | +import lombok.*; | |
| 9 | + | |
| 10 | +import java.util.Date; | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * API 访问日志 | |
| 14 | + * | |
| 15 | + * @author 芋道源码 | |
| 16 | + */ | |
| 17 | +@TableName("sys_api_access_log") | |
| 18 | +@Data | |
| 19 | +@EqualsAndHashCode(callSuper = true) | |
| 20 | +@ToString(callSuper = true) | |
| 21 | +@Builder | |
| 22 | +@NoArgsConstructor | |
| 23 | +@AllArgsConstructor | |
| 24 | +public class SysApiAccessLogDO extends BaseDO { | |
| 25 | + | |
| 26 | + /** | |
| 27 | + * 编号 | |
| 28 | + */ | |
| 29 | + @TableId | |
| 30 | + private Integer id; | |
| 31 | + /** | |
| 32 | + * 链路追踪编号 | |
| 33 | + * | |
| 34 | + * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。 | |
| 35 | + */ | |
| 36 | + private String traceId; | |
| 37 | + /** | |
| 38 | + * 用户编号 | |
| 39 | + */ | |
| 40 | + private Integer userId; | |
| 41 | + /** | |
| 42 | + * 用户类型 | |
| 43 | + * | |
| 44 | + * 枚举 {@link UserTypeEnum} | |
| 45 | + */ | |
| 46 | + private Integer userType; | |
| 47 | + /** | |
| 48 | + * 应用名 | |
| 49 | + * | |
| 50 | + * 目前读取 `spring.application.name` 配置项 | |
| 51 | + */ | |
| 52 | + private String applicationName; | |
| 53 | + | |
| 54 | + /** | |
| 55 | + * 请求方法名 | |
| 56 | + */ | |
| 57 | + private String requestMethod; | |
| 58 | + /** | |
| 59 | + * 访问地址 | |
| 60 | + */ | |
| 61 | + private String requestUrl; | |
| 62 | + /** | |
| 63 | + * 请求参数 | |
| 64 | + * | |
| 65 | + * query: Query String | |
| 66 | + * body: Quest Body | |
| 67 | + */ | |
| 68 | + private String requestParams; | |
| 69 | + /** | |
| 70 | + * 用户 IP | |
| 71 | + */ | |
| 72 | + private String userIp; | |
| 73 | + /** | |
| 74 | + * 浏览器 UA | |
| 75 | + */ | |
| 76 | + private String userAgent; | |
| 77 | + | |
| 78 | + /** | |
| 79 | + * 开始请求时间 | |
| 80 | + */ | |
| 81 | + private Date startTime; | |
| 82 | + /** | |
| 83 | + * 结束请求时间 | |
| 84 | + */ | |
| 85 | + private Date endTime; | |
| 86 | + /** | |
| 87 | + * 执行时长,单位:毫秒 | |
| 88 | + */ | |
| 89 | + private Integer duration; | |
| 90 | + /** | |
| 91 | + * 结果码 | |
| 92 | + * | |
| 93 | + * 目前使用的 {@link CommonResult#getCode()} 属性 | |
| 94 | + */ | |
| 95 | + private Integer resultCode; | |
| 96 | + /** | |
| 97 | + * 结果提示 | |
| 98 | + * | |
| 99 | + * 目前使用的 {@link CommonResult#getMsg()} 属性 | |
| 100 | + */ | |
| 101 | + private String resultMsg; | |
| 102 | + | |
| 103 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/logger/SysApiErrorLogDO.java
0 → 100644
src/main/java/cn/iocoder/dashboard/util/servlet/ServletUtils.java
| 1 | 1 | package cn.iocoder.dashboard.util.servlet; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.io.IoUtil; |
| 4 | +import cn.hutool.core.util.StrUtil; | |
| 4 | 5 | import cn.hutool.extra.servlet.ServletUtil; |
| 5 | 6 | import cn.iocoder.dashboard.util.json.JsonUtils; |
| 6 | 7 | import org.springframework.http.MediaType; |
| ... | ... | @@ -8,6 +9,7 @@ import org.springframework.web.context.request.RequestAttributes; |
| 8 | 9 | import org.springframework.web.context.request.RequestContextHolder; |
| 9 | 10 | import org.springframework.web.context.request.ServletRequestAttributes; |
| 10 | 11 | |
| 12 | +import javax.servlet.ServletRequest; | |
| 11 | 13 | import javax.servlet.http.HttpServletRequest; |
| 12 | 14 | import javax.servlet.http.HttpServletResponse; |
| 13 | 15 | import java.io.IOException; |
| ... | ... | @@ -86,4 +88,8 @@ public class ServletUtils { |
| 86 | 88 | return ServletUtil.getClientIP(request); |
| 87 | 89 | } |
| 88 | 90 | |
| 91 | + public static boolean isJsonRequest(ServletRequest request) { | |
| 92 | + return StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE); | |
| 93 | + } | |
| 94 | + | |
| 89 | 95 | } | ... | ... |