mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-31 02:08:43 +08:00 
			
		
		
		
	1. 修复 request body 缓存的 bug
2. 进一步完善 api 访问日志的实现
This commit is contained in:
		| @@ -1,6 +1,7 @@ | |||||||
| package cn.iocoder.dashboard.framework.logger.apilog.config; | package cn.iocoder.dashboard.framework.logger.apilog.config; | ||||||
|  |  | ||||||
| import cn.iocoder.dashboard.framework.logger.apilog.core.filter.ApiAccessLogFilter; | import cn.iocoder.dashboard.framework.logger.apilog.core.filter.ApiAccessLogFilter; | ||||||
|  | import cn.iocoder.dashboard.framework.logger.apilog.core.service.ApiAccessLogFrameworkService; | ||||||
| import cn.iocoder.dashboard.framework.web.config.WebProperties; | import cn.iocoder.dashboard.framework.web.config.WebProperties; | ||||||
| import cn.iocoder.dashboard.framework.web.core.enums.FilterOrderEnum; | import cn.iocoder.dashboard.framework.web.core.enums.FilterOrderEnum; | ||||||
| import org.springframework.boot.web.servlet.FilterRegistrationBean; | import org.springframework.boot.web.servlet.FilterRegistrationBean; | ||||||
| @@ -16,8 +17,9 @@ public class ApiLogConfiguration { | |||||||
|      * 创建 ApiAccessLogFilter Bean,记录 API 请求日志 |      * 创建 ApiAccessLogFilter Bean,记录 API 请求日志 | ||||||
|      */ |      */ | ||||||
|     @Bean |     @Bean | ||||||
|     public FilterRegistrationBean<ApiAccessLogFilter> apiAccessLogFilter(WebProperties webProperties) { |     public FilterRegistrationBean<ApiAccessLogFilter> apiAccessLogFilter(WebProperties webProperties, | ||||||
|         ApiAccessLogFilter filter = new ApiAccessLogFilter(webProperties); |                                                                          ApiAccessLogFrameworkService apiAccessLogFrameworkService) { | ||||||
|  |         ApiAccessLogFilter filter = new ApiAccessLogFilter(webProperties, apiAccessLogFrameworkService); | ||||||
|         return createFilterBean(filter, FilterOrderEnum.API_ACCESS_LOG_FILTER); |         return createFilterBean(filter, FilterOrderEnum.API_ACCESS_LOG_FILTER); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,11 +1,21 @@ | |||||||
| package cn.iocoder.dashboard.framework.logger.apilog.core.filter; | package cn.iocoder.dashboard.framework.logger.apilog.core.filter; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.exceptions.ExceptionUtil; | ||||||
|  | import cn.hutool.core.map.MapUtil; | ||||||
| import cn.hutool.extra.servlet.ServletUtil; | import cn.hutool.extra.servlet.ServletUtil; | ||||||
|  | import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | ||||||
|  | import cn.iocoder.dashboard.common.pojo.CommonResult; | ||||||
|  | import cn.iocoder.dashboard.framework.logger.apilog.core.service.ApiAccessLogFrameworkService; | ||||||
| import cn.iocoder.dashboard.framework.logger.apilog.core.service.dto.ApiAccessLogCreateDTO; | import cn.iocoder.dashboard.framework.logger.apilog.core.service.dto.ApiAccessLogCreateDTO; | ||||||
|  | import cn.iocoder.dashboard.framework.tracer.core.util.TracerUtils; | ||||||
| import cn.iocoder.dashboard.framework.web.config.WebProperties; | import cn.iocoder.dashboard.framework.web.config.WebProperties; | ||||||
|  | import cn.iocoder.dashboard.framework.web.core.util.WebFrameworkUtils; | ||||||
|  | import cn.iocoder.dashboard.util.date.DateUtils; | ||||||
|  | import cn.iocoder.dashboard.util.json.JsonUtils; | ||||||
| import cn.iocoder.dashboard.util.servlet.ServletUtils; | import cn.iocoder.dashboard.util.servlet.ServletUtils; | ||||||
| import lombok.AllArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
|  | import org.springframework.beans.factory.annotation.Value; | ||||||
| import org.springframework.web.filter.OncePerRequestFilter; | import org.springframework.web.filter.OncePerRequestFilter; | ||||||
|  |  | ||||||
| import javax.servlet.FilterChain; | import javax.servlet.FilterChain; | ||||||
| @@ -21,14 +31,19 @@ import java.util.Map; | |||||||
|  * |  * | ||||||
|  * @author 芋道源码 |  * @author 芋道源码 | ||||||
|  */ |  */ | ||||||
| @AllArgsConstructor | @RequiredArgsConstructor | ||||||
| @Slf4j | @Slf4j | ||||||
| public class ApiAccessLogFilter extends OncePerRequestFilter { | public class ApiAccessLogFilter extends OncePerRequestFilter { | ||||||
|  |  | ||||||
|     private final WebProperties webProperties; |     private final WebProperties webProperties; | ||||||
|  |     private final ApiAccessLogFrameworkService apiAccessLogFrameworkService; | ||||||
|  |  | ||||||
|  |     @Value("${spring.application.name}") | ||||||
|  |     private String applicationName; | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     protected boolean shouldNotFilter(HttpServletRequest request) { |     protected boolean shouldNotFilter(HttpServletRequest request) { | ||||||
|  |         // 只过滤 API 请求的地址 | ||||||
|         return !request.getRequestURI().startsWith(webProperties.getApiPrefix()); |         return !request.getRequestURI().startsWith(webProperties.getApiPrefix()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -56,8 +71,8 @@ public class ApiAccessLogFilter extends OncePerRequestFilter { | |||||||
|     private void createApiAccessLog(HttpServletRequest request, Date startTime, |     private void createApiAccessLog(HttpServletRequest request, Date startTime, | ||||||
|                                     Map<String, String> queryString, String requestBody, Exception ex) { |                                     Map<String, String> queryString, String requestBody, Exception ex) { | ||||||
|         try { |         try { | ||||||
|             ApiAccessLogCreateDTO createDTO = this.buildApiAccessLogDTO(request, startTime, queryString, requestBody, ex); |             ApiAccessLogCreateDTO accessLog = this.buildApiAccessLogDTO(request, startTime, queryString, requestBody, ex); | ||||||
|  |             apiAccessLogFrameworkService.createApiAccessLogAsync(accessLog); | ||||||
|         } catch (Exception e) { |         } catch (Exception e) { | ||||||
|             log.error("[createApiAccessLog][url({}) 发生异常]", request.getRequestURI(), ex); |             log.error("[createApiAccessLog][url({}) 发生异常]", request.getRequestURI(), ex); | ||||||
|         } |         } | ||||||
| @@ -65,7 +80,36 @@ public class ApiAccessLogFilter extends OncePerRequestFilter { | |||||||
|  |  | ||||||
|     private ApiAccessLogCreateDTO buildApiAccessLogDTO(HttpServletRequest request, Date startTime, |     private ApiAccessLogCreateDTO buildApiAccessLogDTO(HttpServletRequest request, Date startTime, | ||||||
|                                                        Map<String, String> queryString, String requestBody, Exception ex) { |                                                        Map<String, String> queryString, String requestBody, Exception ex) { | ||||||
|         return null; |         ApiAccessLogCreateDTO accessLog = new ApiAccessLogCreateDTO(); | ||||||
|  |         // 处理用户信息 | ||||||
|  |         accessLog.setUserId(WebFrameworkUtils.getLoginUserId(request)); | ||||||
|  |         accessLog.setUserType(WebFrameworkUtils.getUsrType(request)); | ||||||
|  |         // 设置访问结果 | ||||||
|  |         CommonResult<?> result = WebFrameworkUtils.getCommonResult(request); | ||||||
|  |         if (result != null) { | ||||||
|  |             accessLog.setResultCode(result.getCode()); | ||||||
|  |             accessLog.setResultMsg(result.getMsg()); | ||||||
|  |         } else if (ex != null) { | ||||||
|  |             accessLog.setResultCode(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode()); | ||||||
|  |             accessLog.setResultMsg(ExceptionUtil.getRootCauseMessage(ex)); | ||||||
|  |         } else { | ||||||
|  |             accessLog.setResultCode(0); | ||||||
|  |             accessLog.setResultMsg(""); | ||||||
|  |         } | ||||||
|  |         // 设置其它字段 | ||||||
|  |         accessLog.setTraceId(TracerUtils.getTraceId()); | ||||||
|  |         accessLog.setApplicationName(applicationName); | ||||||
|  |         accessLog.setRequestUrl(request.getRequestURI()); | ||||||
|  |         Map<String, Object> requestParams = MapUtil.<String, Object>builder().put("query", queryString).put("body", requestBody).build(); | ||||||
|  |         accessLog.setRequestParams(JsonUtils.toJsonString(requestParams)); | ||||||
|  |         accessLog.setRequestMethod(request.getMethod()); | ||||||
|  |         accessLog.setUserAgent(ServletUtils.getUserAgent(request)); | ||||||
|  |         accessLog.setUserIp(ServletUtil.getClientIP(request)); | ||||||
|  |         // 持续时间 | ||||||
|  |         accessLog.setStartTime(startTime); | ||||||
|  |         accessLog.setEndTime(new Date()); | ||||||
|  |         accessLog.setDuration((int) DateUtils.diff(accessLog.getEndTime(), accessLog.getStartTime())); | ||||||
|  |         return accessLog; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,21 @@ | |||||||
|  | package cn.iocoder.dashboard.framework.logger.apilog.core.service; | ||||||
|  |  | ||||||
|  | import cn.iocoder.dashboard.framework.logger.apilog.core.service.dto.ApiAccessLogCreateDTO; | ||||||
|  |  | ||||||
|  | import javax.validation.Valid; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * API 访问日志 Framework Service 接口 | ||||||
|  |  * | ||||||
|  |  * @author 芋道源码 | ||||||
|  |  */ | ||||||
|  | public interface ApiAccessLogFrameworkService { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 创建 API 访问日志 | ||||||
|  |      * | ||||||
|  |      * @param createDTO 创建信息 | ||||||
|  |      */ | ||||||
|  |     void createApiAccessLogAsync(@Valid ApiAccessLogCreateDTO createDTO); | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -1,5 +1,7 @@ | |||||||
| package cn.iocoder.dashboard.framework.logger.apilog.core.service.dto; | package cn.iocoder.dashboard.framework.logger.apilog.core.service.dto; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  |  | ||||||
| import javax.validation.constraints.NotNull; | import javax.validation.constraints.NotNull; | ||||||
| import java.util.Date; | import java.util.Date; | ||||||
|  |  | ||||||
| @@ -8,6 +10,7 @@ import java.util.Date; | |||||||
|  * |  * | ||||||
|  * @author 芋道源码 |  * @author 芋道源码 | ||||||
|  */ |  */ | ||||||
|  | @Data | ||||||
| public class ApiAccessLogCreateDTO { | public class ApiAccessLogCreateDTO { | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -17,7 +20,7 @@ public class ApiAccessLogCreateDTO { | |||||||
|     /** |     /** | ||||||
|      * 用户编号 |      * 用户编号 | ||||||
|      */ |      */ | ||||||
|     private Integer userId; |     private Long userId; | ||||||
|     /** |     /** | ||||||
|      * 用户类型 |      * 用户类型 | ||||||
|      */ |      */ | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ import cn.iocoder.dashboard.common.pojo.CommonResult; | |||||||
| import cn.iocoder.dashboard.framework.logger.operatelog.core.annotations.OperateLog; | import cn.iocoder.dashboard.framework.logger.operatelog.core.annotations.OperateLog; | ||||||
| import cn.iocoder.dashboard.framework.logger.operatelog.core.enums.OperateTypeEnum; | import cn.iocoder.dashboard.framework.logger.operatelog.core.enums.OperateTypeEnum; | ||||||
| import cn.iocoder.dashboard.framework.logger.operatelog.core.service.OperateLogFrameworkService; | import cn.iocoder.dashboard.framework.logger.operatelog.core.service.OperateLogFrameworkService; | ||||||
| import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils; | import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils; | ||||||
| import cn.iocoder.dashboard.framework.tracer.core.util.TracerUtils; | import cn.iocoder.dashboard.framework.tracer.core.util.TracerUtils; | ||||||
| import cn.iocoder.dashboard.modules.system.controller.logger.vo.operatelog.SysOperateLogCreateReqVO; | import cn.iocoder.dashboard.modules.system.controller.logger.vo.operatelog.SysOperateLogCreateReqVO; | ||||||
| import cn.iocoder.dashboard.util.json.JsonUtils; | import cn.iocoder.dashboard.util.json.JsonUtils; | ||||||
| @@ -148,7 +148,7 @@ public class OperateLogAspect { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     private static void fillUserFields(SysOperateLogCreateReqVO operateLogVO) { |     private static void fillUserFields(SysOperateLogCreateReqVO operateLogVO) { | ||||||
|         operateLogVO.setUserId(SecurityUtils.getLoginUserId()); |         operateLogVO.setUserId(SecurityFrameworkUtils.getLoginUserId()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private static void fillModuleFields(SysOperateLogCreateReqVO operateLogVO, |     private static void fillModuleFields(SysOperateLogCreateReqVO operateLogVO, | ||||||
|   | |||||||
| @@ -1,17 +1,13 @@ | |||||||
| package cn.iocoder.dashboard.framework.security.core.filter; | package cn.iocoder.dashboard.framework.security.core.filter; | ||||||
|  |  | ||||||
| import cn.hutool.core.util.StrUtil; | import cn.hutool.core.util.StrUtil; | ||||||
| import cn.hutool.extra.servlet.ServletUtil; |  | ||||||
| import cn.iocoder.dashboard.common.pojo.CommonResult; | import cn.iocoder.dashboard.common.pojo.CommonResult; | ||||||
| import cn.iocoder.dashboard.framework.security.config.SecurityProperties; | import cn.iocoder.dashboard.framework.security.config.SecurityProperties; | ||||||
| import cn.iocoder.dashboard.framework.security.core.LoginUser; | import cn.iocoder.dashboard.framework.security.core.LoginUser; | ||||||
| import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils; | import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils; | ||||||
| import cn.iocoder.dashboard.framework.web.core.handler.GlobalExceptionHandler; | import cn.iocoder.dashboard.framework.web.core.handler.GlobalExceptionHandler; | ||||||
| import cn.iocoder.dashboard.modules.system.service.auth.SysAuthService; | import cn.iocoder.dashboard.modules.system.service.auth.SysAuthService; | ||||||
| import cn.iocoder.dashboard.util.servlet.ServletUtils; | import cn.iocoder.dashboard.util.servlet.ServletUtils; | ||||||
| import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; |  | ||||||
| import org.springframework.security.core.context.SecurityContextHolder; |  | ||||||
| import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; |  | ||||||
| import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||||
| import org.springframework.web.filter.OncePerRequestFilter; | import org.springframework.web.filter.OncePerRequestFilter; | ||||||
|  |  | ||||||
| @@ -42,7 +38,7 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { | |||||||
|     @SuppressWarnings("NullableProblems") |     @SuppressWarnings("NullableProblems") | ||||||
|     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) |     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) | ||||||
|             throws ServletException, IOException { |             throws ServletException, IOException { | ||||||
|         String token = SecurityUtils.obtainAuthorization(request, securityProperties.getTokenHeader()); |         String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader()); | ||||||
|         if (StrUtil.isNotEmpty(token)) { |         if (StrUtil.isNotEmpty(token)) { | ||||||
|             try { |             try { | ||||||
|                 // 验证 token 有效性 |                 // 验证 token 有效性 | ||||||
| @@ -53,7 +49,7 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { | |||||||
|                 } |                 } | ||||||
|                 // 设置当前用户 |                 // 设置当前用户 | ||||||
|                 if (loginUser != null) { |                 if (loginUser != null) { | ||||||
|                     SecurityUtils.setLoginUser(loginUser, request); |                     SecurityFrameworkUtils.setLoginUser(loginUser, request); | ||||||
|                 } |                 } | ||||||
|             } catch (Throwable ex) { |             } catch (Throwable ex) { | ||||||
|                 CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex); |                 CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex); | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ package cn.iocoder.dashboard.framework.security.core.handler; | |||||||
|  |  | ||||||
| import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | ||||||
| import cn.iocoder.dashboard.common.pojo.CommonResult; | import cn.iocoder.dashboard.common.pojo.CommonResult; | ||||||
| import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils; | import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils; | ||||||
| import cn.iocoder.dashboard.util.servlet.ServletUtils; | import cn.iocoder.dashboard.util.servlet.ServletUtils; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| import org.springframework.security.access.AccessDeniedException; | import org.springframework.security.access.AccessDeniedException; | ||||||
| @@ -35,7 +35,7 @@ public class AccessDeniedHandlerImpl implements AccessDeniedHandler { | |||||||
|             throws IOException, ServletException { |             throws IOException, ServletException { | ||||||
|         // 打印 warn 的原因是,不定期合并 warn,看看有没恶意破坏 |         // 打印 warn 的原因是,不定期合并 warn,看看有没恶意破坏 | ||||||
|         log.warn("[commence][访问 URL({}) 时,用户({}) 权限不够]", request.getRequestURI(), |         log.warn("[commence][访问 URL({}) 时,用户({}) 权限不够]", request.getRequestURI(), | ||||||
|                 SecurityUtils.getLoginUser().getId(), e); |                 SecurityFrameworkUtils.getLoginUser().getId(), e); | ||||||
|         // 返回 403 |         // 返回 403 | ||||||
|         ServletUtils.writeJSON(response, CommonResult.error(UNAUTHORIZED)); |         ServletUtils.writeJSON(response, CommonResult.error(UNAUTHORIZED)); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ package cn.iocoder.dashboard.framework.security.core.handler; | |||||||
| import cn.hutool.core.util.StrUtil; | import cn.hutool.core.util.StrUtil; | ||||||
| import cn.iocoder.dashboard.framework.security.config.SecurityProperties; | import cn.iocoder.dashboard.framework.security.config.SecurityProperties; | ||||||
| import cn.iocoder.dashboard.framework.security.core.service.SecurityAuthFrameworkService; | import cn.iocoder.dashboard.framework.security.core.service.SecurityAuthFrameworkService; | ||||||
| import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils; | import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils; | ||||||
| import cn.iocoder.dashboard.util.servlet.ServletUtils; | import cn.iocoder.dashboard.util.servlet.ServletUtils; | ||||||
| import org.springframework.security.core.Authentication; | import org.springframework.security.core.Authentication; | ||||||
| import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; | import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; | ||||||
| @@ -31,7 +31,7 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { | |||||||
|     @Override |     @Override | ||||||
|     public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { |     public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { | ||||||
|         // 执行退出 |         // 执行退出 | ||||||
|         String token = SecurityUtils.obtainAuthorization(request, securityProperties.getTokenHeader()); |         String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader()); | ||||||
|         if (StrUtil.isNotBlank(token)) { |         if (StrUtil.isNotBlank(token)) { | ||||||
|             securityFrameworkService.logout(token); |             securityFrameworkService.logout(token); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| package cn.iocoder.dashboard.framework.security.core.util; | package cn.iocoder.dashboard.framework.security.core.util; | ||||||
| 
 | 
 | ||||||
| import cn.iocoder.dashboard.framework.security.core.LoginUser; | import cn.iocoder.dashboard.framework.security.core.LoginUser; | ||||||
|  | import cn.iocoder.dashboard.framework.web.core.util.WebFrameworkUtils; | ||||||
| import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||||||
| import org.springframework.security.core.context.SecurityContextHolder; | import org.springframework.security.core.context.SecurityContextHolder; | ||||||
| import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; | import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; | ||||||
| @@ -12,11 +13,11 @@ import java.util.Set; | |||||||
| /** | /** | ||||||
|  * 安全服务工具类 |  * 安全服务工具类 | ||||||
|  * |  * | ||||||
|  * @author ruoyi |  * @author 芋道源码 | ||||||
|  */ |  */ | ||||||
| public class SecurityUtils { | public class SecurityFrameworkUtils { | ||||||
| 
 | 
 | ||||||
|     private SecurityUtils() {} |     private SecurityFrameworkUtils() {} | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 从请求中,获得认证 Token |      * 从请求中,获得认证 Token | ||||||
| @@ -45,7 +46,7 @@ public class SecurityUtils { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 获得当前用户的编号 |      * 获得当前用户的编号,从上下文中 | ||||||
|      * |      * | ||||||
|      * @return 用户编号 |      * @return 用户编号 | ||||||
|      */ |      */ | ||||||
| @@ -53,6 +54,11 @@ public class SecurityUtils { | |||||||
|         return getLoginUser().getId(); |         return getLoginUser().getId(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * 获得当前用户的角色编号数组 | ||||||
|  |      * | ||||||
|  |      * @return 角色编号数组 | ||||||
|  |      */ | ||||||
|     public static Set<Long> getLoginUserRoleIds() { |     public static Set<Long> getLoginUserRoleIds() { | ||||||
|         return getLoginUser().getRoleIds(); |         return getLoginUser().getRoleIds(); | ||||||
|     } |     } | ||||||
| @@ -70,6 +76,9 @@ public class SecurityUtils { | |||||||
|         authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); |         authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); | ||||||
|         // 设置到上下文 |         // 设置到上下文 | ||||||
|         SecurityContextHolder.getContext().setAuthentication(authenticationToken); |         SecurityContextHolder.getContext().setAuthentication(authenticationToken); | ||||||
|  |         // 额外设置到 request 中,用于 ApiAccessLogFilter 可以获取到用户编号; | ||||||
|  |         // 原因是,Spring Security 的 Filter 在 ApiAccessLogFilter 后面,在它记录访问日志时,线上上下文已经没有用户编号等信息 | ||||||
|  |         WebFrameworkUtils.setLoginUserId(request, loginUser.getId()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| package cn.iocoder.dashboard.framework.web.config; | package cn.iocoder.dashboard.framework.web.config; | ||||||
|  |  | ||||||
| import cn.iocoder.dashboard.framework.web.core.enums.FilterOrderEnum; | import cn.iocoder.dashboard.framework.web.core.enums.FilterOrderEnum; | ||||||
| import cn.iocoder.dashboard.framework.web.core.filter.RequestBodyCacheFilter; | import cn.iocoder.dashboard.framework.web.core.filter.CacheRequestBodyFilter; | ||||||
| import cn.iocoder.dashboard.framework.web.core.filter.XssFilter; | import cn.iocoder.dashboard.framework.web.core.filter.XssFilter; | ||||||
| import org.springframework.boot.context.properties.EnableConfigurationProperties; | import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||||||
| import org.springframework.boot.web.servlet.FilterRegistrationBean; | import org.springframework.boot.web.servlet.FilterRegistrationBean; | ||||||
| @@ -55,8 +55,8 @@ public class WebConfiguration implements WebMvcConfigurer { | |||||||
|      * 创建 RequestBodyCacheFilter Bean,可重复读取请求内容 |      * 创建 RequestBodyCacheFilter Bean,可重复读取请求内容 | ||||||
|      */ |      */ | ||||||
|     @Bean |     @Bean | ||||||
|     public FilterRegistrationBean<RequestBodyCacheFilter> requestBodyCacheFilter() { |     public FilterRegistrationBean<CacheRequestBodyFilter> requestBodyCacheFilter() { | ||||||
|         return createFilterBean(new RequestBodyCacheFilter(), FilterOrderEnum.REQUEST_BODY_CACHE_FILTER); |         return createFilterBean(new CacheRequestBodyFilter(), FilterOrderEnum.REQUEST_BODY_CACHE_FILTER); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ package cn.iocoder.dashboard.framework.web.core.filter; | |||||||
| 
 | 
 | ||||||
| import cn.iocoder.dashboard.util.servlet.ServletUtils; | import cn.iocoder.dashboard.util.servlet.ServletUtils; | ||||||
| import org.springframework.web.filter.OncePerRequestFilter; | import org.springframework.web.filter.OncePerRequestFilter; | ||||||
| import org.springframework.web.util.ContentCachingRequestWrapper; |  | ||||||
| 
 | 
 | ||||||
| import javax.servlet.FilterChain; | import javax.servlet.FilterChain; | ||||||
| import javax.servlet.ServletException; | import javax.servlet.ServletException; | ||||||
| @@ -13,16 +12,14 @@ import java.io.IOException; | |||||||
| /** | /** | ||||||
|  * Request Body 缓存 Filter,实现它的可重复读取 |  * Request Body 缓存 Filter,实现它的可重复读取 | ||||||
|  * |  * | ||||||
|  * 基于 Spring 提供的 {@link org.springframework.web.util.ContentCachingRequestWrapper} 实现 |  | ||||||
|  * |  | ||||||
|  * @author 芋道源码 |  * @author 芋道源码 | ||||||
|  */ |  */ | ||||||
| public class RequestBodyCacheFilter extends OncePerRequestFilter { | public class CacheRequestBodyFilter extends OncePerRequestFilter { | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) |     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) | ||||||
|             throws IOException, ServletException { |             throws IOException, ServletException { | ||||||
|         filterChain.doFilter(new ContentCachingRequestWrapper(request), response); |         filterChain.doFilter(new CacheRequestBodyWrapper(request), response); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
| @@ -0,0 +1,63 @@ | |||||||
|  | package cn.iocoder.dashboard.framework.web.core.filter; | ||||||
|  |  | ||||||
|  | import cn.hutool.extra.servlet.ServletUtil; | ||||||
|  |  | ||||||
|  | import javax.servlet.ReadListener; | ||||||
|  | import javax.servlet.ServletInputStream; | ||||||
|  | import javax.servlet.http.HttpServletRequest; | ||||||
|  | import javax.servlet.http.HttpServletRequestWrapper; | ||||||
|  | import java.io.BufferedReader; | ||||||
|  | import java.io.ByteArrayInputStream; | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStreamReader; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  *  Request Body 缓存 Wrapper | ||||||
|  |  * | ||||||
|  |  * @author 芋道源码 | ||||||
|  |  */ | ||||||
|  | public class CacheRequestBodyWrapper extends HttpServletRequestWrapper { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 缓存的内容 | ||||||
|  |      */ | ||||||
|  |     private final byte[] body; | ||||||
|  |  | ||||||
|  |     public CacheRequestBodyWrapper(HttpServletRequest request) { | ||||||
|  |         super(request); | ||||||
|  |         body = ServletUtil.getBodyBytes(request); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public BufferedReader getReader() throws IOException { | ||||||
|  |         return new BufferedReader(new InputStreamReader(this.getInputStream())); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public ServletInputStream getInputStream() throws IOException { | ||||||
|  |         final ByteArrayInputStream inputStream = new ByteArrayInputStream(body); | ||||||
|  |         // 返回 ServletInputStream | ||||||
|  |         return new ServletInputStream() { | ||||||
|  |  | ||||||
|  |             @Override | ||||||
|  |             public int read() { | ||||||
|  |                 return inputStream.read(); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             @Override | ||||||
|  |             public boolean isFinished() { | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             @Override | ||||||
|  |             public boolean isReady() { | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             @Override | ||||||
|  |             public void setReadListener(ReadListener readListener) {} | ||||||
|  |  | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -3,7 +3,7 @@ package cn.iocoder.dashboard.framework.web.core.handler; | |||||||
| import cn.iocoder.dashboard.common.exception.GlobalException; | import cn.iocoder.dashboard.common.exception.GlobalException; | ||||||
| import cn.iocoder.dashboard.common.exception.ServiceException; | import cn.iocoder.dashboard.common.exception.ServiceException; | ||||||
| import cn.iocoder.dashboard.common.pojo.CommonResult; | import cn.iocoder.dashboard.common.pojo.CommonResult; | ||||||
| import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils; | import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils; | ||||||
| import io.github.resilience4j.ratelimiter.RequestNotPermitted; | import io.github.resilience4j.ratelimiter.RequestNotPermitted; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| import org.springframework.security.access.AccessDeniedException; | import org.springframework.security.access.AccessDeniedException; | ||||||
| @@ -26,6 +26,8 @@ import static cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstan | |||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 全局异常处理器,将 Exception 翻译成 CommonResult + 对应的异常编号 |  * 全局异常处理器,将 Exception 翻译成 CommonResult + 对应的异常编号 | ||||||
|  |  * | ||||||
|  |  * @author 芋道源码 | ||||||
|  */ |  */ | ||||||
| @RestControllerAdvice | @RestControllerAdvice | ||||||
| @Slf4j | @Slf4j | ||||||
| @@ -183,7 +185,7 @@ public class GlobalExceptionHandler { | |||||||
|      */ |      */ | ||||||
|     @ExceptionHandler(value = AccessDeniedException.class) |     @ExceptionHandler(value = AccessDeniedException.class) | ||||||
|     public CommonResult<?> accessDeniedExceptionHandler(HttpServletRequest req, AccessDeniedException ex) { |     public CommonResult<?> accessDeniedExceptionHandler(HttpServletRequest req, AccessDeniedException ex) { | ||||||
|         log.warn("[accessDeniedExceptionHandler][userId({}) 无法访问 url({})]", SecurityUtils.getLoginUserId(), |         log.warn("[accessDeniedExceptionHandler][userId({}) 无法访问 url({})]", SecurityFrameworkUtils.getLoginUserId(), | ||||||
|                 req.getRequestURL(), ex); |                 req.getRequestURL(), ex); | ||||||
|         return CommonResult.error(FORBIDDEN); |         return CommonResult.error(FORBIDDEN); | ||||||
|     } |     } | ||||||
| @@ -275,7 +277,7 @@ public class GlobalExceptionHandler { | |||||||
| //        // 设置其它字段 | //        // 设置其它字段 | ||||||
| //        exceptionLog.setTraceId(MallUtils.getTraceId()) | //        exceptionLog.setTraceId(MallUtils.getTraceId()) | ||||||
| //                .setApplicationName(applicationName) | //                .setApplicationName(applicationName) | ||||||
| //                .setUri(request.getRequestURI()) // TODO 提升:如果想要优化,可以使用 Swagger 的 @ApiOperation 注解。 | //                .setUri(request.getRequestURI()) | ||||||
| //                .setQueryString(HttpUtil.buildQueryString(request)) | //                .setQueryString(HttpUtil.buildQueryString(request)) | ||||||
| //                .setMethod(request.getMethod()) | //                .setMethod(request.getMethod()) | ||||||
| //                .setUserAgent(HttpUtil.getUserAgent(request)) | //                .setUserAgent(HttpUtil.getUserAgent(request)) | ||||||
|   | |||||||
| @@ -0,0 +1,45 @@ | |||||||
|  | package cn.iocoder.dashboard.framework.web.core.handler; | ||||||
|  |  | ||||||
|  | import cn.iocoder.dashboard.common.pojo.CommonResult; | ||||||
|  | import cn.iocoder.dashboard.framework.web.core.util.WebFrameworkUtils; | ||||||
|  | import org.springframework.core.MethodParameter; | ||||||
|  | import org.springframework.http.MediaType; | ||||||
|  | import org.springframework.http.server.ServerHttpRequest; | ||||||
|  | import org.springframework.http.server.ServerHttpResponse; | ||||||
|  | import org.springframework.http.server.ServletServerHttpRequest; | ||||||
|  | import org.springframework.web.bind.annotation.ControllerAdvice; | ||||||
|  | import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 全局响应结果(ResponseBody)处理器 | ||||||
|  |  * | ||||||
|  |  * 不同于在网上看到的很多文章,会选择自动将 Controller 返回结果包上 {@link CommonResult}, | ||||||
|  |  * 在 onemall 中,是 Controller 在返回时,主动自己包上 {@link CommonResult}。 | ||||||
|  |  * 原因是,GlobalResponseBodyHandler 本质上是 AOP,它不应该改变 Controller 返回的数据结构 | ||||||
|  |  * | ||||||
|  |  * 目前,GlobalResponseBodyHandler 的主要作用是,记录 Controller 的返回结果, | ||||||
|  |  * 方便 {@link cn.iocoder.dashboard.framework.logger.apilog.core.filter.ApiAccessLogFilter} 记录访问日志 | ||||||
|  |  */ | ||||||
|  | @ControllerAdvice | ||||||
|  | public class GlobalResponseBodyHandler implements ResponseBodyAdvice { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     @SuppressWarnings("NullableProblems") // 避免 IDEA 警告 | ||||||
|  |     public boolean supports(MethodParameter returnType, Class converterType) { | ||||||
|  |         if (returnType.getMethod() == null) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         // 只拦截返回结果为 CommonResult 类型 | ||||||
|  |         return returnType.getMethod().getReturnType() == CommonResult.class; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     @SuppressWarnings("NullableProblems") // 避免 IDEA 警告 | ||||||
|  |     public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, | ||||||
|  |                                   ServerHttpRequest request, ServerHttpResponse response) { | ||||||
|  |         // 记录 Controller 结果 | ||||||
|  |         WebFrameworkUtils.setCommonResult(((ServletServerHttpRequest) request).getServletRequest(), (CommonResult<?>) body); | ||||||
|  |         return body; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,46 @@ | |||||||
|  | package cn.iocoder.dashboard.framework.web.core.util; | ||||||
|  |  | ||||||
|  | import cn.iocoder.dashboard.common.enums.UserTypeEnum; | ||||||
|  | import cn.iocoder.dashboard.common.pojo.CommonResult; | ||||||
|  |  | ||||||
|  | import javax.servlet.ServletRequest; | ||||||
|  | import javax.servlet.http.HttpServletRequest; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 专属于 web 包的工具类 | ||||||
|  |  * | ||||||
|  |  * @author 芋道源码 | ||||||
|  |  */ | ||||||
|  | public class WebFrameworkUtils { | ||||||
|  |  | ||||||
|  |     private static final String REQUEST_ATTRIBUTE_LOGIN_USER_ID = "login_user_id"; | ||||||
|  |  | ||||||
|  |     private static final String REQUEST_ATTRIBUTE_COMMON_RESULT = "common_result"; | ||||||
|  |  | ||||||
|  |     public static void setLoginUserId(ServletRequest request, Long userId) { | ||||||
|  |         request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_ID, userId); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获得当前用户的编号,从请求中 | ||||||
|  |      * | ||||||
|  |      * @param request 请求 | ||||||
|  |      * @return 用户编号 | ||||||
|  |      */ | ||||||
|  |     public static Long getLoginUserId(HttpServletRequest request) { | ||||||
|  |         return (Long) request.getAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_ID); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static Integer getUsrType(HttpServletRequest request) { | ||||||
|  |         return UserTypeEnum.ADMIN.getValue(); // TODO 芋艿:等后续优化 | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static void setCommonResult(ServletRequest request, CommonResult<?> result) { | ||||||
|  |         request.setAttribute(REQUEST_ATTRIBUTE_COMMON_RESULT, result); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static CommonResult<?> getCommonResult(ServletRequest request) { | ||||||
|  |         return (CommonResult<?>) request.getAttribute(REQUEST_ATTRIBUTE_COMMON_RESULT); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -26,8 +26,8 @@ import javax.validation.Valid; | |||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| import static cn.iocoder.dashboard.common.pojo.CommonResult.success; | import static cn.iocoder.dashboard.common.pojo.CommonResult.success; | ||||||
| import static cn.iocoder.dashboard.framework.security.core.util.SecurityUtils.getLoginUserId; | import static cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | ||||||
| import static cn.iocoder.dashboard.framework.security.core.util.SecurityUtils.getLoginUserRoleIds; | import static cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils.getLoginUserRoleIds; | ||||||
| import static cn.iocoder.dashboard.util.servlet.ServletUtils.getClientIP; | import static cn.iocoder.dashboard.util.servlet.ServletUtils.getClientIP; | ||||||
| import static cn.iocoder.dashboard.util.servlet.ServletUtils.getUserAgent; | import static cn.iocoder.dashboard.util.servlet.ServletUtils.getUserAgent; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,11 @@ | |||||||
|  | package cn.iocoder.dashboard.modules.system.service.logger; | ||||||
|  |  | ||||||
|  | import cn.iocoder.dashboard.framework.logger.apilog.core.service.ApiAccessLogFrameworkService; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * API 访问日志 Service 接口 | ||||||
|  |  * | ||||||
|  |  * @author 芋道源码 | ||||||
|  |  */ | ||||||
|  | public interface SysApiAccessLogService extends ApiAccessLogFrameworkService { | ||||||
|  | } | ||||||
| @@ -0,0 +1,26 @@ | |||||||
|  | package cn.iocoder.dashboard.modules.system.service.logger.impl; | ||||||
|  |  | ||||||
|  | import cn.iocoder.dashboard.framework.logger.apilog.core.service.dto.ApiAccessLogCreateDTO; | ||||||
|  | import cn.iocoder.dashboard.modules.system.service.logger.SysApiAccessLogService; | ||||||
|  | import org.springframework.stereotype.Service; | ||||||
|  | import org.springframework.validation.annotation.Validated; | ||||||
|  |  | ||||||
|  | import javax.validation.Valid; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * API 访问日志 Service 实现类 | ||||||
|  |  * | ||||||
|  |  * @author 芋道源码 | ||||||
|  |  */ | ||||||
|  | @Service | ||||||
|  | @Validated | ||||||
|  | public class SysApiAccessLogServiceImpl implements SysApiAccessLogService { | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void createApiAccessLogAsync(@Valid ApiAccessLogCreateDTO createDTO) { | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -4,7 +4,7 @@ import cn.hutool.core.collection.CollUtil; | |||||||
| import cn.hutool.core.collection.CollectionUtil; | import cn.hutool.core.collection.CollectionUtil; | ||||||
| import cn.hutool.core.util.ArrayUtil; | import cn.hutool.core.util.ArrayUtil; | ||||||
| import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; | import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; | ||||||
| import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils; | import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils; | ||||||
| import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysRoleMenuMapper; | import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysRoleMenuMapper; | ||||||
| import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysUserRoleMapper; | import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysUserRoleMapper; | ||||||
| import cn.iocoder.dashboard.modules.system.dal.dataobject.permission.SysMenuDO; | import cn.iocoder.dashboard.modules.system.dal.dataobject.permission.SysMenuDO; | ||||||
| @@ -262,7 +262,7 @@ public class SysPermissionServiceImpl implements SysPermissionService { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 获得当前登陆的角色。如果为空,说明没有权限 |         // 获得当前登陆的角色。如果为空,说明没有权限 | ||||||
|         Set<Long> roleIds = SecurityUtils.getLoginUserRoleIds(); |         Set<Long> roleIds = SecurityFrameworkUtils.getLoginUserRoleIds(); | ||||||
|         if (CollUtil.isEmpty(roleIds)) { |         if (CollUtil.isEmpty(roleIds)) { | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
| @@ -297,7 +297,7 @@ public class SysPermissionServiceImpl implements SysPermissionService { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 获得当前登陆的角色。如果为空,说明没有权限 |         // 获得当前登陆的角色。如果为空,说明没有权限 | ||||||
|         Set<Long> roleIds = SecurityUtils.getLoginUserRoleIds(); |         Set<Long> roleIds = SecurityFrameworkUtils.getLoginUserRoleIds(); | ||||||
|         if (CollUtil.isEmpty(roleIds)) { |         if (CollUtil.isEmpty(roleIds)) { | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV