parent
70af2bc78c
commit
7a87fdbd79
|
@ -1,6 +1,7 @@
|
|||
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.service.ApiAccessLogFrameworkService;
|
||||
import cn.iocoder.dashboard.framework.web.config.WebProperties;
|
||||
import cn.iocoder.dashboard.framework.web.core.enums.FilterOrderEnum;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
|
@ -16,8 +17,9 @@ public class ApiLogConfiguration {
|
|||
* 创建 ApiAccessLogFilter Bean,记录 API 请求日志
|
||||
*/
|
||||
@Bean
|
||||
public FilterRegistrationBean<ApiAccessLogFilter> apiAccessLogFilter(WebProperties webProperties) {
|
||||
ApiAccessLogFilter filter = new ApiAccessLogFilter(webProperties);
|
||||
public FilterRegistrationBean<ApiAccessLogFilter> apiAccessLogFilter(WebProperties webProperties,
|
||||
ApiAccessLogFrameworkService apiAccessLogFrameworkService) {
|
||||
ApiAccessLogFilter filter = new ApiAccessLogFilter(webProperties, apiAccessLogFrameworkService);
|
||||
return createFilterBean(filter, FilterOrderEnum.API_ACCESS_LOG_FILTER);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,21 @@
|
|||
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.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.tracer.core.util.TracerUtils;
|
||||
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 lombok.AllArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
|
@ -21,14 +31,19 @@ import java.util.Map;
|
|||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class ApiAccessLogFilter extends OncePerRequestFilter {
|
||||
|
||||
private final WebProperties webProperties;
|
||||
private final ApiAccessLogFrameworkService apiAccessLogFrameworkService;
|
||||
|
||||
@Value("${spring.application.name}")
|
||||
private String applicationName;
|
||||
|
||||
@Override
|
||||
protected boolean shouldNotFilter(HttpServletRequest request) {
|
||||
// 只过滤 API 请求的地址
|
||||
return !request.getRequestURI().startsWith(webProperties.getApiPrefix());
|
||||
}
|
||||
|
||||
|
@ -56,8 +71,8 @@ public class ApiAccessLogFilter extends OncePerRequestFilter {
|
|||
private void createApiAccessLog(HttpServletRequest request, Date startTime,
|
||||
Map<String, String> queryString, String requestBody, Exception ex) {
|
||||
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) {
|
||||
log.error("[createApiAccessLog][url({}) 发生异常]", request.getRequestURI(), ex);
|
||||
}
|
||||
|
@ -65,7 +80,36 @@ public class ApiAccessLogFilter extends OncePerRequestFilter {
|
|||
|
||||
private ApiAccessLogCreateDTO buildApiAccessLogDTO(HttpServletRequest request, Date startTime,
|
||||
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;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Date;
|
||||
|
||||
|
@ -8,6 +10,7 @@ import java.util.Date;
|
|||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
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.enums.OperateTypeEnum;
|
||||
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.modules.system.controller.logger.vo.operatelog.SysOperateLogCreateReqVO;
|
||||
import cn.iocoder.dashboard.util.json.JsonUtils;
|
||||
|
@ -148,7 +148,7 @@ public class OperateLogAspect {
|
|||
}
|
||||
|
||||
private static void fillUserFields(SysOperateLogCreateReqVO operateLogVO) {
|
||||
operateLogVO.setUserId(SecurityUtils.getLoginUserId());
|
||||
operateLogVO.setUserId(SecurityFrameworkUtils.getLoginUserId());
|
||||
}
|
||||
|
||||
private static void fillModuleFields(SysOperateLogCreateReqVO operateLogVO,
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
package cn.iocoder.dashboard.framework.security.core.filter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.servlet.ServletUtil;
|
||||
import cn.iocoder.dashboard.common.pojo.CommonResult;
|
||||
import cn.iocoder.dashboard.framework.security.config.SecurityProperties;
|
||||
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.modules.system.service.auth.SysAuthService;
|
||||
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.web.filter.OncePerRequestFilter;
|
||||
|
||||
|
@ -42,7 +38,7 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
|||
@SuppressWarnings("NullableProblems")
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws ServletException, IOException {
|
||||
String token = SecurityUtils.obtainAuthorization(request, securityProperties.getTokenHeader());
|
||||
String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader());
|
||||
if (StrUtil.isNotEmpty(token)) {
|
||||
try {
|
||||
// 验证 token 有效性
|
||||
|
@ -53,7 +49,7 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
|||
}
|
||||
// 设置当前用户
|
||||
if (loginUser != null) {
|
||||
SecurityUtils.setLoginUser(loginUser, request);
|
||||
SecurityFrameworkUtils.setLoginUser(loginUser, request);
|
||||
}
|
||||
} catch (Throwable 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.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 lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
|
@ -35,7 +35,7 @@ public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
|
|||
throws IOException, ServletException {
|
||||
// 打印 warn 的原因是,不定期合并 warn,看看有没恶意破坏
|
||||
log.warn("[commence][访问 URL({}) 时,用户({}) 权限不够]", request.getRequestURI(),
|
||||
SecurityUtils.getLoginUser().getId(), e);
|
||||
SecurityFrameworkUtils.getLoginUser().getId(), e);
|
||||
// 返回 403
|
||||
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.iocoder.dashboard.framework.security.config.SecurityProperties;
|
||||
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 org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||
|
@ -31,7 +31,7 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
|
|||
@Override
|
||||
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)) {
|
||||
securityFrameworkService.logout(token);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package cn.iocoder.dashboard.framework.security.core.util;
|
||||
|
||||
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.core.context.SecurityContextHolder;
|
||||
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
|
||||
|
@ -45,7 +46,7 @@ public class SecurityUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* 获得当前用户的编号
|
||||
* 获得当前用户的编号,从上下文中
|
||||
*
|
||||
* @return 用户编号
|
||||
*/
|
||||
|
@ -53,6 +54,11 @@ public class SecurityUtils {
|
|||
return getLoginUser().getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得当前用户的角色编号数组
|
||||
*
|
||||
* @return 角色编号数组
|
||||
*/
|
||||
public static Set<Long> getLoginUserRoleIds() {
|
||||
return getLoginUser().getRoleIds();
|
||||
}
|
||||
|
@ -70,6 +76,9 @@ public class SecurityUtils {
|
|||
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
// 设置到上下文
|
||||
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;
|
||||
|
||||
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 org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
|
@ -55,8 +55,8 @@ public class WebConfiguration implements WebMvcConfigurer {
|
|||
* 创建 RequestBodyCacheFilter Bean,可重复读取请求内容
|
||||
*/
|
||||
@Bean
|
||||
public FilterRegistrationBean<RequestBodyCacheFilter> requestBodyCacheFilter() {
|
||||
return createFilterBean(new RequestBodyCacheFilter(), FilterOrderEnum.REQUEST_BODY_CACHE_FILTER);
|
||||
public FilterRegistrationBean<CacheRequestBodyFilter> requestBodyCacheFilter() {
|
||||
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 org.springframework.web.filter.OncePerRequestFilter;
|
||||
import org.springframework.web.util.ContentCachingRequestWrapper;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
|
@ -13,16 +12,14 @@ import java.io.IOException;
|
|||
/**
|
||||
* Request Body 缓存 Filter,实现它的可重复读取
|
||||
*
|
||||
* 基于 Spring 提供的 {@link org.springframework.web.util.ContentCachingRequestWrapper} 实现
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class RequestBodyCacheFilter extends OncePerRequestFilter {
|
||||
public class CacheRequestBodyFilter extends OncePerRequestFilter {
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws IOException, ServletException {
|
||||
filterChain.doFilter(new ContentCachingRequestWrapper(request), response);
|
||||
filterChain.doFilter(new CacheRequestBodyWrapper(request), response);
|
||||
}
|
||||
|
||||
@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.ServiceException;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
|
@ -26,6 +26,8 @@ import static cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstan
|
|||
|
||||
/**
|
||||
* 全局异常处理器,将 Exception 翻译成 CommonResult + 对应的异常编号
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
@Slf4j
|
||||
|
@ -183,7 +185,7 @@ public class GlobalExceptionHandler {
|
|||
*/
|
||||
@ExceptionHandler(value = AccessDeniedException.class)
|
||||
public CommonResult<?> accessDeniedExceptionHandler(HttpServletRequest req, AccessDeniedException ex) {
|
||||
log.warn("[accessDeniedExceptionHandler][userId({}) 无法访问 url({})]", SecurityUtils.getLoginUserId(),
|
||||
log.warn("[accessDeniedExceptionHandler][userId({}) 无法访问 url({})]", SecurityFrameworkUtils.getLoginUserId(),
|
||||
req.getRequestURL(), ex);
|
||||
return CommonResult.error(FORBIDDEN);
|
||||
}
|
||||
|
@ -275,7 +277,7 @@ public class GlobalExceptionHandler {
|
|||
// // 设置其它字段
|
||||
// exceptionLog.setTraceId(MallUtils.getTraceId())
|
||||
// .setApplicationName(applicationName)
|
||||
// .setUri(request.getRequestURI()) // TODO 提升:如果想要优化,可以使用 Swagger 的 @ApiOperation 注解。
|
||||
// .setUri(request.getRequestURI())
|
||||
// .setQueryString(HttpUtil.buildQueryString(request))
|
||||
// .setMethod(request.getMethod())
|
||||
// .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 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.SecurityUtils.getLoginUserRoleIds;
|
||||
import static cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
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.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.util.ArrayUtil;
|
||||
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.SysUserRoleMapper;
|
||||
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)) {
|
||||
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)) {
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue