parent
70af2bc78c
commit
7a87fdbd79
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue