- 后端:替换 swagger bootstrap ui ,一下子接口文档好看了。
- 后端:增加 swagger AutoConfiguration 配置类 - 后端:统一访问日志的记录
This commit is contained in:
parent
3ff9f1b326
commit
53fff39a6c
@ -90,6 +90,12 @@
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.skywalking</groupId>
|
||||
<artifactId>apm-toolkit-trace</artifactId>
|
||||
<version>6.1.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
@ -0,0 +1,42 @@
|
||||
package cn.iocoder.common.framework.constant;
|
||||
|
||||
/**
|
||||
* Mall 全局枚举
|
||||
*/
|
||||
public interface MallConstants {
|
||||
|
||||
// 全局请求路径枚举类,用于定义不同用户类型的根请求路径
|
||||
/**
|
||||
* 根路径 - 用户
|
||||
*/
|
||||
String ROOT_PATH_USER = "/users";
|
||||
/**
|
||||
* 根路径 - 管理员
|
||||
*/
|
||||
String ROOT_PATH_ADMIN = "/admins";
|
||||
|
||||
// 用户类型
|
||||
/**
|
||||
* 用户类型 - 用户
|
||||
*/
|
||||
Integer USER_TYPE_USER = 1;
|
||||
/**
|
||||
* 用户类型 - 管理员
|
||||
*/
|
||||
Integer USER_TYPE_ADMIN = 2;
|
||||
|
||||
// HTTP Request Attr
|
||||
/**
|
||||
* HTTP Request Attr - 用户编号
|
||||
*/
|
||||
String REQUEST_ATTR_USER_ID_KEY = "mall_user_id";
|
||||
/**
|
||||
* HTTP Request Attr - 用户类型
|
||||
*/
|
||||
String REQUEST_ATTR_USER_TYPE_KEY = "mall_user_type";
|
||||
/**
|
||||
* HTTP Request Attr - Controller 执行返回
|
||||
*/
|
||||
String REQUEST_ATTR_COMMON_RESULT = "mall_common_result";
|
||||
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package cn.iocoder.common.framework.config;
|
||||
package cn.iocoder.common.framework.exception;
|
||||
|
||||
import cn.iocoder.common.framework.constant.SysErrorCodeEnum;
|
||||
import cn.iocoder.common.framework.exception.ServiceException;
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
@ -5,6 +5,11 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Cors 过滤器
|
||||
*
|
||||
* 未来使用 {@link org.springframework.web.filter.CorsFilter} 替换
|
||||
*/
|
||||
public class CorsFilter implements Filter {
|
||||
|
||||
@Override
|
||||
|
@ -44,7 +44,7 @@ public class HttpUtil {
|
||||
*/
|
||||
public static final String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1";
|
||||
|
||||
public static String obtainAccess(HttpServletRequest request) {
|
||||
public static String obtainAuthorization(HttpServletRequest request) {
|
||||
String authorization = request.getHeader("Authorization");
|
||||
if (!StringUtils.hasText(authorization)) {
|
||||
return null;
|
||||
|
@ -0,0 +1,53 @@
|
||||
package cn.iocoder.common.framework.util;
|
||||
|
||||
import cn.iocoder.common.framework.constant.MallConstants;
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import org.apache.skywalking.apm.toolkit.trace.TraceContext;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import java.util.UUID;
|
||||
|
||||
public class MallUtil {
|
||||
|
||||
public static Integer getUserId(ServletRequest request) {
|
||||
return (Integer) request.getAttribute(MallConstants.REQUEST_ATTR_USER_ID_KEY);
|
||||
}
|
||||
|
||||
public static void setUserId(ServletRequest request, Integer userId) {
|
||||
request.setAttribute(MallConstants.REQUEST_ATTR_USER_ID_KEY, userId);
|
||||
}
|
||||
|
||||
public static Integer getUserType(ServletRequest request) {
|
||||
return (Integer) request.getAttribute(MallConstants.REQUEST_ATTR_USER_TYPE_KEY);
|
||||
}
|
||||
|
||||
public static void setUserType(ServletRequest request, Integer userType) {
|
||||
request.setAttribute(MallConstants.REQUEST_ATTR_USER_TYPE_KEY, userType);
|
||||
}
|
||||
|
||||
public static CommonResult getCommonResult(ServletRequest request) {
|
||||
return (CommonResult) request.getAttribute(MallConstants.REQUEST_ATTR_COMMON_RESULT);
|
||||
}
|
||||
|
||||
public static void setCommonResult(ServletRequest request, CommonResult result) {
|
||||
request.setAttribute(MallConstants.REQUEST_ATTR_COMMON_RESULT, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得链路追踪编号
|
||||
*
|
||||
* 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
|
||||
*
|
||||
* 默认情况下,我们使用 Apache SkyWalking 的 traceId 作为链路追踪编号。当然,可能会存在并未引入 Skywalking 的情况,此时使用 UUID 。
|
||||
*
|
||||
* @return 链路追踪编号
|
||||
*/
|
||||
public static String getTraceId() {
|
||||
String traceId = TraceContext.traceId();
|
||||
if (StringUtil.hasText(traceId)) {
|
||||
return traceId;
|
||||
}
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
}
|
@ -5,7 +5,7 @@ import org.springframework.util.Assert;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class CommonResult<T> implements Serializable {
|
||||
public final class CommonResult<T> implements Serializable {
|
||||
|
||||
public static Integer CODE_SUCCESS = 0;
|
||||
|
||||
|
@ -47,6 +47,17 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>swagger-bootstrap-ui</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
@ -1,17 +0,0 @@
|
||||
package cn.iocoder.mall.spring.boot.constant;
|
||||
|
||||
/**
|
||||
* 全局请求路径枚举类,用于定义不同用户类型的根请求路径
|
||||
*/
|
||||
public interface RootRequestPath {
|
||||
|
||||
/**
|
||||
* 管理员
|
||||
*/
|
||||
String ADMIN = "/admins";
|
||||
/**
|
||||
* 用户
|
||||
*/
|
||||
String USER = "/users";
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package cn.iocoder.mall.spring.boot.swagger;
|
||||
|
||||
import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
/**
|
||||
* 简单的 Swagger2 自动配置类
|
||||
*
|
||||
* 较为完善的,可以了解 https://mvnrepository.com/artifact/com.spring4all/spring-boot-starter-swagger
|
||||
*/
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
@EnableSwaggerBootstrapUI
|
||||
@ConditionalOnClass({Docket.class, ApiInfoBuilder.class})
|
||||
@ConditionalOnProperty(prefix = "swagger", value = "enable", matchIfMissing = true) // 允许使用 swagger.enable=false 禁用 Swagger
|
||||
@EnableConfigurationProperties(SwaggerProperties.class)
|
||||
public class SwaggerAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SwaggerProperties swaggerProperties() {
|
||||
return new SwaggerProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Docket createRestApi() {
|
||||
SwaggerProperties properties = swaggerProperties();
|
||||
// 创建 Docket 对象
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.apiInfo(apiInfo(properties))
|
||||
.select()
|
||||
.apis(RequestHandlerSelectors.basePackage(properties.getBasePackage()))
|
||||
.paths(PathSelectors.any())
|
||||
.build();
|
||||
}
|
||||
|
||||
private ApiInfo apiInfo(SwaggerProperties properties) {
|
||||
return new ApiInfoBuilder()
|
||||
.title(properties.getTitle())
|
||||
.description(properties.getDescription())
|
||||
.version(properties.getVersion())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package cn.iocoder.mall.spring.boot.swagger;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@Data
|
||||
@ConfigurationProperties("swagger")
|
||||
public class SwaggerProperties {
|
||||
|
||||
private String title;
|
||||
private String description;
|
||||
private String version;
|
||||
private String basePackage;
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package cn.iocoder.mall.spring.boot.web;
|
||||
|
||||
import cn.iocoder.common.framework.constant.MallConstants;
|
||||
import cn.iocoder.common.framework.servlet.CorsFilter;
|
||||
import cn.iocoder.mall.spring.boot.web.interceptor.AccessLogInterceptor;
|
||||
import cn.iocoder.mall.admin.sdk.interceptor.AdminSecurityInterceptor;
|
||||
import cn.iocoder.mall.spring.boot.web.handler.GlobalExceptionHandler;
|
||||
import cn.iocoder.mall.spring.boot.web.handler.GlobalResponseBodyHandler;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) // TODO 芋艿,未来可能考虑 REACTIVE
|
||||
@ConditionalOnClass({DispatcherServlet.class, WebMvcConfigurer.class, // 有 Spring MVC 容器
|
||||
AdminSecurityInterceptor.class, AccessLogInterceptor.class}) // 有引入 system-sdk
|
||||
public class AdminMVCAutoConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Bean
|
||||
// @ConditionalOnMissingBean(AccessLogInterceptor.class)
|
||||
public AccessLogInterceptor adminAccessLogInterceptor() {
|
||||
return new AccessLogInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AdminSecurityInterceptor.class)
|
||||
public AdminSecurityInterceptor adminSecurityInterceptor() {
|
||||
return new AdminSecurityInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(GlobalResponseBodyHandler.class)
|
||||
public GlobalResponseBodyHandler globalReturnValueHandler() {
|
||||
return new GlobalResponseBodyHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(GlobalResponseBodyHandler.class)
|
||||
public GlobalExceptionHandler globalExceptionHandler() {
|
||||
return new GlobalExceptionHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(adminAccessLogInterceptor()).addPathPatterns(MallConstants.ROOT_PATH_ADMIN + "/**");
|
||||
registry.addInterceptor(adminSecurityInterceptor()).addPathPatterns(MallConstants.ROOT_PATH_ADMIN + "/**");
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public FilterRegistrationBean<CorsFilter> corsFilter() {
|
||||
FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>();
|
||||
registrationBean.setFilter(new CorsFilter());
|
||||
registrationBean.addUrlPatterns("/*");
|
||||
return registrationBean;
|
||||
}
|
||||
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
package cn.iocoder.mall.spring.boot.web;
|
||||
|
||||
import cn.iocoder.mall.admin.sdk.interceptor.AdminAccessLogInterceptor;
|
||||
import cn.iocoder.mall.admin.sdk.interceptor.AdminSecurityInterceptor;
|
||||
import cn.iocoder.mall.spring.boot.constant.RootRequestPath;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) // TODO 芋艿,未来可能考虑 REACTIVE
|
||||
@ConditionalOnClass({DispatcherServlet.class, WebMvcConfigurer.class, // 有 Spring MVC 容器
|
||||
AdminSecurityInterceptor.class, AdminAccessLogInterceptor.class}) // 有引入 system-sdk
|
||||
public class AdminMVCConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AdminSecurityInterceptor.class)
|
||||
public AdminSecurityInterceptor adminSecurityInterceptor() {
|
||||
return new AdminSecurityInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AdminAccessLogInterceptor.class)
|
||||
public AdminAccessLogInterceptor adminAccessLogInterceptor() {
|
||||
return new AdminAccessLogInterceptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(adminAccessLogInterceptor()).addPathPatterns(RootRequestPath.ADMIN + "/**");
|
||||
registry.addInterceptor(adminSecurityInterceptor()).addPathPatterns(RootRequestPath.ADMIN + "/**");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping(RootRequestPath.USER + "/**")
|
||||
.allowedOrigins("*")
|
||||
.allowedMethods("*")
|
||||
.allowedHeaders("*")
|
||||
.allowCredentials(true).maxAge(1800);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package cn.iocoder.mall.spring.boot.web;
|
||||
|
||||
import cn.iocoder.common.framework.constant.MallConstants;
|
||||
import cn.iocoder.common.framework.servlet.CorsFilter;
|
||||
import cn.iocoder.mall.spring.boot.web.interceptor.AccessLogInterceptor;
|
||||
import cn.iocoder.mall.spring.boot.web.handler.GlobalExceptionHandler;
|
||||
import cn.iocoder.mall.spring.boot.web.handler.GlobalResponseBodyHandler;
|
||||
import cn.iocoder.mall.user.sdk.interceptor.UserSecurityInterceptor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) // TODO 芋艿,未来可能考虑 REACTIVE
|
||||
@ConditionalOnClass({DispatcherServlet.class, WebMvcConfigurer.class, // 有 Spring MVC 容器
|
||||
UserSecurityInterceptor.class, // 有引入 user-sdk
|
||||
AccessLogInterceptor.class}) // 有引入 system-sdk
|
||||
public class UserMVCAutoConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Bean
|
||||
// @ConditionalOnMissingBean(AccessLogInterceptor.class)
|
||||
public AccessLogInterceptor userAccessLogInterceptor() {
|
||||
return new AccessLogInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(UserSecurityInterceptor.class)
|
||||
public UserSecurityInterceptor userSecurityInterceptor() {
|
||||
return new UserSecurityInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(GlobalResponseBodyHandler.class)
|
||||
public GlobalResponseBodyHandler globalReturnValueHandler() {
|
||||
return new GlobalResponseBodyHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(GlobalExceptionHandler.class)
|
||||
public GlobalExceptionHandler globalExceptionHandler() {
|
||||
return new GlobalExceptionHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(userAccessLogInterceptor()).addPathPatterns(MallConstants.ROOT_PATH_USER + "/**");
|
||||
registry.addInterceptor(userSecurityInterceptor()).addPathPatterns(MallConstants.ROOT_PATH_USER + "/**");
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public FilterRegistrationBean<CorsFilter> corsFilter() {
|
||||
FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>();
|
||||
registrationBean.setFilter(new CorsFilter());
|
||||
registrationBean.addUrlPatterns("/*");
|
||||
return registrationBean;
|
||||
}
|
||||
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
package cn.iocoder.mall.spring.boot.web;
|
||||
|
||||
import cn.iocoder.mall.spring.boot.constant.RootRequestPath;
|
||||
import cn.iocoder.mall.user.sdk.interceptor.UserAccessLogInterceptor;
|
||||
import cn.iocoder.mall.user.sdk.interceptor.UserSecurityInterceptor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) // TODO 芋艿,未来可能考虑 REACTIVE
|
||||
@ConditionalOnClass({DispatcherServlet.class, WebMvcConfigurer.class, // 有 Spring MVC 容器
|
||||
UserSecurityInterceptor.class, UserAccessLogInterceptor.class}) // 有引入 system-sdk
|
||||
public class UserMVCConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(UserAccessLogInterceptor.class)
|
||||
public UserAccessLogInterceptor userAccessLogInterceptor() {
|
||||
return new UserAccessLogInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(UserSecurityInterceptor.class)
|
||||
public UserSecurityInterceptor userSecurityInterceptor() {
|
||||
return new UserSecurityInterceptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(userAccessLogInterceptor()).addPathPatterns(RootRequestPath.USER + "/**");
|
||||
registry.addInterceptor(userSecurityInterceptor()).addPathPatterns(RootRequestPath.USER + "/**");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping(RootRequestPath.USER + "/**")
|
||||
.allowedOrigins("*")
|
||||
.allowedMethods("*")
|
||||
.allowedHeaders("*")
|
||||
.allowCredentials(true).maxAge(1800);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package cn.iocoder.mall.spring.boot.web.handler;
|
||||
|
||||
import cn.iocoder.common.framework.constant.SysErrorCodeEnum;
|
||||
import cn.iocoder.common.framework.exception.ServiceException;
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
|
||||
@ControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
// 逻辑异常
|
||||
@ResponseBody
|
||||
@ExceptionHandler(value = ServiceException.class)
|
||||
public CommonResult serviceExceptionHandler(HttpServletRequest req, ServiceException ex) {
|
||||
logger.debug("[serviceExceptionHandler]", ex);
|
||||
return CommonResult.error(ex.getCode(), ex.getMessage());
|
||||
}
|
||||
|
||||
// Spring MVC 参数不正确
|
||||
@ResponseBody
|
||||
@ExceptionHandler(value = MissingServletRequestParameterException.class)
|
||||
public CommonResult missingServletRequestParameterExceptionHandler(HttpServletRequest req, MissingServletRequestParameterException ex) {
|
||||
logger.warn("[missingServletRequestParameterExceptionHandler]", ex);
|
||||
return CommonResult.error(SysErrorCodeEnum.MISSING_REQUEST_PARAM_ERROR.getCode(), SysErrorCodeEnum.MISSING_REQUEST_PARAM_ERROR.getMessage() + ":" + ex.getMessage());
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@ExceptionHandler(value = ConstraintViolationException.class)
|
||||
public CommonResult constraintViolationExceptionHandler(HttpServletRequest req, ConstraintViolationException ex) {
|
||||
logger.info("[constraintViolationExceptionHandler]", ex);
|
||||
// TODO 芋艿,后续要想一个更好的方式。
|
||||
// 拼接详细报错
|
||||
StringBuilder detailMessage = new StringBuilder("\n\n详细错误如下:");
|
||||
ex.getConstraintViolations().forEach(constraintViolation -> detailMessage.append("\n").append(constraintViolation.getMessage()));
|
||||
return CommonResult.error(SysErrorCodeEnum.VALIDATION_REQUEST_PARAM_ERROR.getCode(), SysErrorCodeEnum.VALIDATION_REQUEST_PARAM_ERROR.getMessage()
|
||||
+ detailMessage.toString());
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@ExceptionHandler(value = Exception.class)
|
||||
public CommonResult resultExceptionHandler(HttpServletRequest req, Exception e) {
|
||||
logger.error("[resultExceptionHandler]", e);
|
||||
// 返回
|
||||
try {
|
||||
addExceptionLog();
|
||||
} catch (Throwable th) {
|
||||
// TODO
|
||||
}
|
||||
return CommonResult.error(SysErrorCodeEnum.SYS_ERROR.getCode(), SysErrorCodeEnum.SYS_ERROR.getMessage());
|
||||
}
|
||||
|
||||
// TODO 芋艿,应该还有其它的异常,需要进行翻译
|
||||
|
||||
@Async
|
||||
public void addExceptionLog() {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package cn.iocoder.mall.spring.boot.web.handler;
|
||||
|
||||
import cn.iocoder.common.framework.util.MallUtil;
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
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;
|
||||
|
||||
@ControllerAdvice
|
||||
public class GlobalResponseBodyHandler implements ResponseBodyAdvice {
|
||||
|
||||
@Override
|
||||
public boolean supports(MethodParameter returnType, Class converterType) {
|
||||
if (returnType.getMethod() == null) {
|
||||
return false;
|
||||
}
|
||||
return returnType.getMethod().getReturnType().isAssignableFrom(CommonResult.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType,
|
||||
ServerHttpRequest request, ServerHttpResponse response) {
|
||||
MallUtil.setCommonResult(((ServletServerHttpRequest) request).getServletRequest(), (CommonResult) body);
|
||||
return body;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package cn.iocoder.mall.spring.boot.web.interceptor;
|
||||
|
||||
import cn.iocoder.common.framework.util.HttpUtil;
|
||||
import cn.iocoder.common.framework.util.MallUtil;
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.admin.api.SystemLogService;
|
||||
import cn.iocoder.mall.admin.api.dto.AccessLogAddDTO;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.apache.dubbo.config.annotation.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 访问日志拦截器
|
||||
*/
|
||||
@Component
|
||||
public class AccessLogInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private static final ThreadLocal<Date> START_TIME = new ThreadLocal<>();
|
||||
|
||||
@Reference(validation = "true", version = "${dubbo.consumer.AdminAccessLogService.version:1.0.0}")
|
||||
private SystemLogService adminAccessLogService;
|
||||
|
||||
@Value("${spring.application.name}")
|
||||
private String applicationName;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
// 记录当前时间
|
||||
START_TIME.set(new Date());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
|
||||
AccessLogAddDTO accessLog = new AccessLogAddDTO();
|
||||
try {
|
||||
// 设置用户编号
|
||||
accessLog.setUserId(MallUtil.getUserId(request));
|
||||
if (accessLog.getUserId() == null) {
|
||||
accessLog.setUserId(AccessLogAddDTO.USER_ID_NULL);
|
||||
}
|
||||
accessLog.setUserType(MallUtil.getUserType(request));
|
||||
// 设置访问结果
|
||||
CommonResult result = MallUtil.getCommonResult(request);
|
||||
Assert.isTrue(result != null, "result 必须非空");
|
||||
accessLog.setErrorCode(result.getCode())
|
||||
.setErrorMessage(result.getMessage());
|
||||
// 设置其它字段
|
||||
accessLog.setTraceId(MallUtil.getTraceId())
|
||||
.setApplicationName(applicationName)
|
||||
.setUri(request.getRequestURI()) // TODO 提升:如果想要优化,可以使用 Swagger 的 @ApiOperation 注解。
|
||||
.setQueryString(HttpUtil.buildQueryString(request))
|
||||
.setMethod(request.getMethod())
|
||||
.setUserAgent(HttpUtil.getUserAgent(request))
|
||||
.setIp(HttpUtil.getIp(request))
|
||||
.setStartTime(START_TIME.get())
|
||||
.setResponseTime((int) (System.currentTimeMillis() - accessLog.getStartTime().getTime())); // 默认响应时间设为 0
|
||||
// 执行插入
|
||||
addAccessLog(accessLog);
|
||||
// TODO 提升:暂时不考虑 ELK 的方案。而是基于 MySQL 存储。如果访问日志比较多,需要定期归档。
|
||||
} catch (Throwable th) {
|
||||
logger.error("[afterCompletion][插入访问日志({}) 发生异常({})", JSON.toJSONString(accessLog), ExceptionUtils.getRootCauseMessage(th));
|
||||
} finally {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Async // 异步入库
|
||||
public void addAccessLog(AccessLogAddDTO accessLog) {
|
||||
try {
|
||||
adminAccessLogService.addAccessLog(accessLog);
|
||||
} catch (Throwable th) {
|
||||
logger.error("[addAccessLog][插入访问日志({}) 发生异常({})", JSON.toJSONString(accessLog), ExceptionUtils.getRootCauseMessage(th));
|
||||
}
|
||||
}
|
||||
|
||||
private static void clear() {
|
||||
START_TIME.remove();
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
cn.iocoder.mall.spring.boot.web.AdminMVCConfiguration, \
|
||||
cn.iocoder.mall.spring.boot.web.UserMVCConfiguration
|
||||
cn.iocoder.mall.spring.boot.web.AdminMVCAutoConfiguration, \
|
||||
cn.iocoder.mall.spring.boot.web.UserMVCAutoConfiguration, \
|
||||
cn.iocoder.mall.spring.boot.swagger.SwaggerAutoConfiguration
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.mall.order.application.config;
|
||||
|
||||
import cn.iocoder.common.framework.config.GlobalExceptionHandler;
|
||||
import cn.iocoder.common.framework.exception.GlobalExceptionHandler;
|
||||
import cn.iocoder.common.framework.servlet.CorsFilter;
|
||||
import cn.iocoder.mall.admin.sdk.interceptor.AdminSecurityInterceptor;
|
||||
import cn.iocoder.mall.user.sdk.interceptor.UserAccessLogInterceptor;
|
||||
@ -49,12 +49,4 @@ public class MVCConfiguration implements WebMvcConfigurer {
|
||||
return registrationBean;
|
||||
}
|
||||
|
||||
// TODO 芋艿,允许跨域
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedHeaders("*")
|
||||
.allowedMethods("*")
|
||||
.allowedOrigins("*");
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,11 @@
|
||||
<artifactId>common-framework</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<artifactId>mall-spring-boot</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<artifactId>pay-service-impl</artifactId>
|
||||
@ -63,8 +68,9 @@
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>swagger-bootstrap-ui</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
@ -2,8 +2,10 @@ package cn.iocoder.mall.pay.application;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
||||
@SpringBootApplication(scanBasePackages = {"cn.iocoder.mall.pay"})
|
||||
@EnableAsync(proxyTargetClass = true)
|
||||
public class PayApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
@ -1,39 +0,0 @@
|
||||
package cn.iocoder.mall.pay.application.config;
|
||||
|
||||
import cn.iocoder.common.framework.config.GlobalExceptionHandler;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@EnableWebMvc
|
||||
@Configuration
|
||||
@Import(value = {GlobalExceptionHandler.class, // 统一全局返回
|
||||
// AdminSecurityInterceptor.class
|
||||
})
|
||||
public class MVCConfiguration implements WebMvcConfigurer {
|
||||
|
||||
// @Autowired
|
||||
// private UserSecurityInterceptor securityInterceptor;
|
||||
|
||||
// @Autowired
|
||||
// private AdminSecurityInterceptor adminSecurityInterceptor;
|
||||
////
|
||||
// @Override
|
||||
// public void addInterceptors(InterceptorRegistry registry) {
|
||||
//// registry.addInterceptor(securityInterceptor).addPathPatterns("/user/**", "/admin/**"); // 只拦截我们定义的接口
|
||||
// registry.addInterceptor(adminSecurityInterceptor).addPathPatterns("/admins/**")
|
||||
// .excludePathPatterns("/admins/passport/login"); // 排除登陆接口
|
||||
// }
|
||||
|
||||
// TODO 芋艿,允许跨域
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedHeaders("*")
|
||||
.allowedMethods("*")
|
||||
.allowedOrigins("*");
|
||||
}
|
||||
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package cn.iocoder.mall.pay.application.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
@Configuration
|
||||
@EnableSwagger2 // TODO 生产环境时,禁用掉。
|
||||
public class SwaggerConfiguration {
|
||||
|
||||
@Bean
|
||||
public Docket createRestApi() {
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.apiInfo(apiInfo())
|
||||
.select()
|
||||
.apis(RequestHandlerSelectors.basePackage("cn.iocoder.mall.pay.application.controller"))
|
||||
.paths(PathSelectors.any())
|
||||
.build();
|
||||
}
|
||||
|
||||
private ApiInfo apiInfo() {
|
||||
return new ApiInfoBuilder()
|
||||
.title("支付子系统")
|
||||
.description("支付子系统")
|
||||
.termsOfServiceUrl("http://www.iocoder.cn")
|
||||
.version("1.0.0")
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
@ -7,3 +7,9 @@ server:
|
||||
port: 18084
|
||||
servlet:
|
||||
context-path: /pay-api/
|
||||
|
||||
swagger:
|
||||
title: 支付子系统
|
||||
description: 支付子系统
|
||||
version: 1.0.0
|
||||
base-package: cn.iocoder.mall.pay.application.controller
|
||||
|
14
pom.xml
14
pom.xml
@ -37,6 +37,7 @@
|
||||
<!-- <curator.version>4.0.1</curator.version>-->
|
||||
<!-- <zookeeper.version>3.4.14</zookeeper.version>-->
|
||||
<springfox-swagger.version>2.9.2</springfox-swagger.version>
|
||||
<swagger-bootstrap-ui.version>1.9.3</swagger-bootstrap-ui.version>
|
||||
<mybatis-spring-boot-starter.version>2.0.0</mybatis-spring-boot-starter.version>
|
||||
<xxl-job.version>2.0.1</xxl-job.version>
|
||||
<guava.version>27.0.1-jre</guava.version>
|
||||
@ -129,6 +130,11 @@
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<version>${springfox-swagger.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>swagger-bootstrap-ui</artifactId>
|
||||
<version>${swagger-bootstrap-ui.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
@ -184,6 +190,14 @@
|
||||
<version>${qiniu.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<!-- <scope>provided</scope>-->
|
||||
<version>2.5</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
@ -17,6 +17,11 @@
|
||||
<artifactId>common-framework</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<artifactId>mall-spring-boot</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<artifactId>product-service-api</artifactId>
|
||||
@ -60,11 +65,10 @@
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>swagger-bootstrap-ui</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -2,8 +2,10 @@ package cn.iocoder.mall.product.application;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
||||
@SpringBootApplication(scanBasePackages = {"cn.iocoder.mall.product"})
|
||||
@EnableAsync(proxyTargetClass = true)
|
||||
public class ProductApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
@ -1,42 +0,0 @@
|
||||
package cn.iocoder.mall.product.application.config;
|
||||
|
||||
import cn.iocoder.common.framework.config.GlobalExceptionHandler;
|
||||
import cn.iocoder.common.framework.servlet.CorsFilter;
|
||||
import cn.iocoder.mall.admin.sdk.interceptor.AdminAccessLogInterceptor;
|
||||
import cn.iocoder.mall.admin.sdk.interceptor.AdminSecurityInterceptor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@EnableWebMvc
|
||||
@Configuration
|
||||
@Import(value = {GlobalExceptionHandler.class, // 统一全局返回
|
||||
AdminSecurityInterceptor.class, AdminAccessLogInterceptor.class})
|
||||
public class MVCConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Autowired
|
||||
private AdminSecurityInterceptor adminSecurityInterceptor;
|
||||
@Autowired
|
||||
private AdminAccessLogInterceptor adminAccessLogInterceptor;
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// registry.addInterceptor(securityInterceptor);
|
||||
registry.addInterceptor(adminAccessLogInterceptor).addPathPatterns("/admins/**");
|
||||
registry.addInterceptor(adminSecurityInterceptor).addPathPatterns("/admins/**");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean<CorsFilter> corsFilter() {
|
||||
FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>();
|
||||
registrationBean.setFilter(new CorsFilter());
|
||||
registrationBean.addUrlPatterns("/*");
|
||||
return registrationBean;
|
||||
}
|
||||
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package cn.iocoder.mall.product.application.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
public class SwaggerConfiguration {
|
||||
|
||||
@Bean
|
||||
public Docket createRestApi() {
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.apiInfo(apiInfo())
|
||||
.select()
|
||||
.apis(RequestHandlerSelectors.basePackage("cn.iocoder.mall.product.application.controller"))
|
||||
.paths(PathSelectors.any())
|
||||
.build();
|
||||
}
|
||||
|
||||
private ApiInfo apiInfo() {
|
||||
return new ApiInfoBuilder()
|
||||
.title("商品子系统")
|
||||
.description("商品子系统")
|
||||
.termsOfServiceUrl("http://www.iocoder.cn")
|
||||
.version("1.0.0")
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
@ -7,3 +7,9 @@ server:
|
||||
port: 18081
|
||||
servlet:
|
||||
context-path: /product-api/
|
||||
|
||||
swagger:
|
||||
title: 商品子系统
|
||||
description: 商品子系统
|
||||
version: 1.0.0
|
||||
base-package: cn.iocoder.mall.product.application.controller
|
||||
|
@ -31,6 +31,8 @@ dubbo:
|
||||
version: 1.0.0
|
||||
ProductSpuService:
|
||||
version: 1.0.0
|
||||
OAuth2Service:
|
||||
version: 1.0.0
|
||||
|
||||
# rocketmq
|
||||
rocketmq:
|
||||
|
@ -18,6 +18,11 @@
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<artifactId>product-service-api</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<artifactId>promotion-service-api</artifactId>
|
||||
@ -59,14 +64,8 @@
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<artifactId>product-service-api</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>swagger-bootstrap-ui</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
@ -1,56 +0,0 @@
|
||||
package cn.iocoder.mall.promotion.application.config;
|
||||
|
||||
import cn.iocoder.common.framework.config.GlobalExceptionHandler;
|
||||
import cn.iocoder.common.framework.servlet.CorsFilter;
|
||||
import cn.iocoder.mall.admin.sdk.interceptor.AdminAccessLogInterceptor;
|
||||
import cn.iocoder.mall.admin.sdk.interceptor.AdminSecurityInterceptor;
|
||||
import cn.iocoder.mall.user.sdk.interceptor.UserAccessLogInterceptor;
|
||||
import cn.iocoder.mall.user.sdk.interceptor.UserSecurityInterceptor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@EnableWebMvc
|
||||
@Configuration
|
||||
@Import(value = {GlobalExceptionHandler.class, // 统一全局返回
|
||||
AdminSecurityInterceptor.class, UserAccessLogInterceptor.class,
|
||||
UserSecurityInterceptor.class, AdminAccessLogInterceptor.class,
|
||||
})
|
||||
public class MVCConfiguration implements WebMvcConfigurer {
|
||||
|
||||
// @Autowired
|
||||
// private UserSecurityInterceptor securityInterceptor;
|
||||
|
||||
@Autowired
|
||||
private UserSecurityInterceptor userSecurityInterceptor;
|
||||
@Autowired
|
||||
private UserAccessLogInterceptor userAccessLogInterceptor;
|
||||
@Autowired
|
||||
private AdminSecurityInterceptor adminSecurityInterceptor;
|
||||
@Autowired
|
||||
private AdminAccessLogInterceptor adminAccessLogInterceptor;
|
||||
//
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 用户
|
||||
registry.addInterceptor(userAccessLogInterceptor).addPathPatterns("/users/**");
|
||||
registry.addInterceptor(userSecurityInterceptor).addPathPatterns("/users/**"); // 只拦截我们定义的接口
|
||||
// 管理员
|
||||
// registry.addInterceptor(adminAccessLogInterceptor).addPathPatterns("/admins/**");
|
||||
registry.addInterceptor(adminSecurityInterceptor).addPathPatterns("/admins/**");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean<CorsFilter> corsFilter() {
|
||||
FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>();
|
||||
registrationBean.setFilter(new CorsFilter());
|
||||
registrationBean.addUrlPatterns("/*");
|
||||
return registrationBean;
|
||||
}
|
||||
|
||||
}
|
@ -7,3 +7,9 @@ server:
|
||||
port: 18085
|
||||
servlet:
|
||||
context-path: /promotion-api/
|
||||
|
||||
swagger:
|
||||
title: 营销子系统
|
||||
description: 营销子系统
|
||||
version: 1.0.0
|
||||
base-package: cn.iocoder.mall.promotion.application.controller
|
||||
|
@ -17,6 +17,11 @@
|
||||
<artifactId>common-framework</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<artifactId>mall-spring-boot</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<artifactId>user-sdk</artifactId>
|
||||
@ -48,8 +53,8 @@
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>swagger-bootstrap-ui</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
@ -2,8 +2,10 @@ package cn.iocoder.mall.search.application;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
||||
@SpringBootApplication(scanBasePackages = {"cn.iocoder.mall.search"})
|
||||
@EnableAsync(proxyTargetClass = true)
|
||||
public class SearchApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
@ -1,51 +0,0 @@
|
||||
package cn.iocoder.mall.search.application.config;
|
||||
|
||||
import cn.iocoder.common.framework.config.GlobalExceptionHandler;
|
||||
import cn.iocoder.common.framework.servlet.CorsFilter;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@EnableWebMvc
|
||||
@Configuration
|
||||
@Import(value = {GlobalExceptionHandler.class, // 统一全局返回
|
||||
// AdminSecurityInterceptor.class, UserAccessLogInterceptor.class,
|
||||
// UserSecurityInterceptor.class, AdminAccessLogInterceptor.class,
|
||||
})
|
||||
public class MVCConfiguration implements WebMvcConfigurer {
|
||||
|
||||
// @Autowired
|
||||
// private UserSecurityInterceptor securityInterceptor;
|
||||
|
||||
// @Autowired
|
||||
// private UserSecurityInterceptor userSecurityInterceptor;
|
||||
// @Autowired
|
||||
// private UserAccessLogInterceptor userAccessLogInterceptor;
|
||||
// @Autowired
|
||||
// private AdminSecurityInterceptor adminSecurityInterceptor;
|
||||
// @Autowired
|
||||
// private AdminAccessLogInterceptor adminAccessLogInterceptor;
|
||||
//
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// // 用户
|
||||
// registry.addInterceptor(userAccessLogInterceptor).addPathPatterns("/users/**");
|
||||
// registry.addInterceptor(userSecurityInterceptor).addPathPatterns("/users/**"); // 只拦截我们定义的接口
|
||||
// // 管理员
|
||||
// registry.addInterceptor(adminAccessLogInterceptor).addPathPatterns("/admins/**");
|
||||
// registry.addInterceptor(adminSecurityInterceptor).addPathPatterns("/admins/**");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean<CorsFilter> corsFilter() {
|
||||
FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>();
|
||||
registrationBean.setFilter(new CorsFilter());
|
||||
registrationBean.addUrlPatterns("/*");
|
||||
return registrationBean;
|
||||
}
|
||||
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package cn.iocoder.mall.search.application.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
@Configuration
|
||||
@EnableSwagger2 // TODO 生产环境时,禁用掉。
|
||||
public class SwaggerConfiguration {
|
||||
|
||||
@Bean
|
||||
public Docket createRestApi() {
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.apiInfo(apiInfo())
|
||||
.select()
|
||||
.apis(RequestHandlerSelectors.basePackage("cn.iocoder.mall.search.application.controller"))
|
||||
.paths(PathSelectors.any())
|
||||
.build();
|
||||
}
|
||||
|
||||
private ApiInfo apiInfo() {
|
||||
return new ApiInfoBuilder()
|
||||
.title("搜索子系统")
|
||||
.description("搜索子系统")
|
||||
.termsOfServiceUrl("http://www.iocoder.cn")
|
||||
.version("1.0.0")
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
@ -7,3 +7,9 @@ server:
|
||||
port: 18086
|
||||
servlet:
|
||||
context-path: /search-api/
|
||||
|
||||
swagger:
|
||||
title: 搜索子系统
|
||||
description: 搜索子系统
|
||||
version: 1.0.0
|
||||
base-package: cn.iocoder.mall.search.application.controller
|
||||
|
@ -50,8 +50,9 @@
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>swagger-bootstrap-ui</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- <dependency>-->
|
||||
|
@ -3,9 +3,10 @@ package cn.iocoder.mall.admin.application;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
||||
@SpringBootApplication(scanBasePackages = {"cn.iocoder.mall.admin"})
|
||||
//@EnableAdminServer
|
||||
@EnableAsync(proxyTargetClass = true)
|
||||
public class AdminApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
@ -1,36 +0,0 @@
|
||||
package cn.iocoder.mall.admin.application.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
@Configuration
|
||||
@EnableSwagger2 // TODO 生产环境时,禁用掉。
|
||||
public class SwaggerConfiguration {
|
||||
|
||||
@Bean
|
||||
public Docket createRestApi() {
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.apiInfo(apiInfo())
|
||||
.select()
|
||||
.apis(RequestHandlerSelectors.basePackage("cn.iocoder.mall.admin.application.controller"))
|
||||
.paths(PathSelectors.any())
|
||||
.build();
|
||||
}
|
||||
|
||||
private ApiInfo apiInfo() {
|
||||
return new ApiInfoBuilder()
|
||||
.title("管理员子系统")
|
||||
.description("管理员子系统")
|
||||
.termsOfServiceUrl("http://www.iocoder.cn")
|
||||
.version("1.0.0")
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
@ -18,7 +18,7 @@ import cn.iocoder.mall.admin.application.vo.AdminPageVO;
|
||||
import cn.iocoder.mall.admin.application.vo.AdminRoleVO;
|
||||
import cn.iocoder.mall.admin.application.vo.AdminVO;
|
||||
import cn.iocoder.mall.admin.sdk.context.AdminSecurityContextHolder;
|
||||
import cn.iocoder.mall.spring.boot.constant.RootRequestPath;
|
||||
import cn.iocoder.common.framework.constant.MallConstants;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
@ -30,7 +30,7 @@ import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(RootRequestPath.ADMIN + "/admin")
|
||||
@RequestMapping(MallConstants.ROOT_PATH_ADMIN + "/admin")
|
||||
@Api("管理员模块")
|
||||
public class AdminController {
|
||||
|
||||
|
@ -13,3 +13,6 @@ management:
|
||||
include: "*"
|
||||
server:
|
||||
port: 19083 # 配置独立端口。而该端口,不使用 nginx 对外暴露,从而不配置安全认证。也就是说,内网环境可访问,外网环境不可访问。当然,这么做的前提是,认为内网安全。
|
||||
|
||||
swagger:
|
||||
enable: true # 暂时不去掉
|
||||
|
@ -17,3 +17,9 @@ qiniu:
|
||||
access-key: YldfyUC7OewoWM63TPYTairqnq8GMJvNek9EGoID
|
||||
secret-key: zZ7Q8wwZRyaklVvkyLmVydA4WygOBqtc_gTYzalS
|
||||
bucket: onemall
|
||||
|
||||
swagger:
|
||||
title: 管理员子系统
|
||||
description: 管理员子系统
|
||||
version: 1.0.0
|
||||
base-package: cn.iocoder.mall.admin.application.controller
|
||||
|
@ -1,81 +0,0 @@
|
||||
package cn.iocoder.mall.admin.sdk.interceptor;
|
||||
|
||||
import cn.iocoder.common.framework.util.HttpUtil;
|
||||
import cn.iocoder.mall.admin.api.AdminAccessLogService;
|
||||
import cn.iocoder.mall.admin.api.dto.AdminAccessLogAddDTO;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.apache.dubbo.config.annotation.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 访问日志拦截器
|
||||
*/
|
||||
@Component
|
||||
public class AdminAccessLogInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private static final ThreadLocal<Date> START_TIME = new ThreadLocal<>();
|
||||
/**
|
||||
* 管理员编号
|
||||
*/
|
||||
private static final ThreadLocal<Integer> ADMIN_ID = new ThreadLocal<>();
|
||||
|
||||
@Reference(validation = "true", version = "${dubbo.consumer.AdminAccessLogService.version:1.0.0}")
|
||||
private AdminAccessLogService adminAccessLogService;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
// 记录当前时间
|
||||
START_TIME.set(new Date());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
|
||||
if (adminAccessLogService == null) {
|
||||
throw new IllegalStateException("AdminAccessLogService 服务未引入成功");
|
||||
}
|
||||
AdminAccessLogAddDTO accessLog = new AdminAccessLogAddDTO();
|
||||
try {
|
||||
accessLog.setAdminId(ADMIN_ID.get());
|
||||
if (accessLog.getAdminId() == null) {
|
||||
accessLog.setAdminId(AdminAccessLogAddDTO.ADMIN_ID_NULL);
|
||||
}
|
||||
accessLog.setUri(request.getRequestURI()); // TODO 提升:如果想要优化,可以使用 Swagger 的 @ApiOperation 注解。
|
||||
accessLog.setQueryString(HttpUtil.buildQueryString(request));
|
||||
accessLog.setMethod(request.getMethod());
|
||||
accessLog.setUserAgent(HttpUtil.getUserAgent(request));
|
||||
accessLog.setIp(HttpUtil.getIp(request));
|
||||
accessLog.setStartTime(START_TIME.get());
|
||||
accessLog.setResponseTime((int) (System.currentTimeMillis() - accessLog.getStartTime().getTime()));// 默认响应时间设为0
|
||||
adminAccessLogService.addAdminAccessLog(accessLog);
|
||||
// TODO 提升:暂时不考虑 ELK 的方案。而是基于 MySQL 存储。如果访问日志比较多,需要定期归档。
|
||||
} catch (Throwable th) {
|
||||
logger.error("[afterCompletion][插入管理员访问日志({}) 发生异常({})", JSON.toJSONString(accessLog), ExceptionUtils.getRootCauseMessage(th));
|
||||
} finally {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setAdminId(Integer adminId) {
|
||||
ADMIN_ID.set(adminId);
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
START_TIME.remove();
|
||||
ADMIN_ID.remove();
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
package cn.iocoder.mall.admin.sdk.interceptor;
|
||||
|
||||
import cn.iocoder.common.framework.constant.MallConstants;
|
||||
import cn.iocoder.common.framework.exception.ServiceException;
|
||||
import cn.iocoder.common.framework.util.HttpUtil;
|
||||
import cn.iocoder.common.framework.util.MallUtil;
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.admin.api.OAuth2Service;
|
||||
import cn.iocoder.mall.admin.api.bo.OAuth2AuthenticationBO;
|
||||
@ -39,8 +41,10 @@ public class AdminSecurityInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
// 设置当前访问的用户类型。注意,即使未登陆,我们也认为是管理员
|
||||
MallUtil.setUserType(request, MallConstants.USER_TYPE_ADMIN);
|
||||
// 校验访问令牌是否正确。若正确,返回授权信息
|
||||
String accessToken = HttpUtil.obtainAccess(request);
|
||||
String accessToken = HttpUtil.obtainAuthorization(request);
|
||||
OAuth2AuthenticationBO authentication = null;
|
||||
if (accessToken != null) {
|
||||
CommonResult<OAuth2AuthenticationBO> result = oauth2Service.checkToken(accessToken);
|
||||
@ -60,7 +64,7 @@ public class AdminSecurityInterceptor extends HandlerInterceptorAdapter {
|
||||
// AdminSecurityInterceptor 执行后,会移除 AdminSecurityContext 信息,这就导致 AdminAccessLogInterceptor 无法获得管理员编号
|
||||
// 因此,这里需要进行记录
|
||||
if (authentication.getAdminId() != null) {
|
||||
AdminAccessLogInterceptor.setAdminId(authentication.getAdminId());
|
||||
MallUtil.setUserId(request, authentication.getAdminId());
|
||||
}
|
||||
} else {
|
||||
String url = request.getRequestURI();
|
||||
|
@ -1,13 +0,0 @@
|
||||
package cn.iocoder.mall.admin.api;
|
||||
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.admin.api.dto.AdminAccessLogAddDTO;
|
||||
|
||||
/**
|
||||
* 管理员访问日志 Service 接口
|
||||
*/
|
||||
public interface AdminAccessLogService {
|
||||
|
||||
CommonResult<Boolean> addAdminAccessLog(AdminAccessLogAddDTO adminAccessLogAddDTO);
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package cn.iocoder.mall.admin.api;
|
||||
|
||||
import cn.iocoder.mall.admin.api.dto.AccessLogAddDTO;
|
||||
|
||||
/**
|
||||
* 系统日志 Service 接口
|
||||
*
|
||||
* 例如说,访问日志、错误日志、操作日志等等
|
||||
*/
|
||||
public interface SystemLogService {
|
||||
|
||||
void addAccessLog(AccessLogAddDTO accessLogAddDTO);
|
||||
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
package cn.iocoder.mall.admin.api.dto;
|
||||
|
||||
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 访问日志添加 DTO
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class AccessLogAddDTO implements Serializable {
|
||||
|
||||
/**
|
||||
* 用户编号 - 空
|
||||
*/
|
||||
public static final Integer USER_ID_NULL = 0;
|
||||
/**
|
||||
* 链路追踪编号
|
||||
*
|
||||
* 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
|
||||
*/
|
||||
@NotNull(message = "链路追踪编号不能为空")
|
||||
private String traceId;
|
||||
/**
|
||||
* 用户编号.
|
||||
*
|
||||
* 当管理员为空时,该值为 {@link #USER_ID_NULL}
|
||||
*/
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Integer userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
@NotNull(message = "用户类型不能为空")
|
||||
private Integer userType;
|
||||
/**
|
||||
* 应用名
|
||||
*
|
||||
* 目前读取 spring.application.name
|
||||
*/
|
||||
@NotNull(message = "应用名不能为空")
|
||||
private String applicationName;
|
||||
/**
|
||||
* 访问地址
|
||||
*/
|
||||
@NotNull(message = "访问地址不能为空")
|
||||
private String uri;
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
@NotNull(message = "请求参数不能为空")
|
||||
private String queryString;
|
||||
/**
|
||||
* http 方法
|
||||
*/
|
||||
@NotNull(message = "http 请求方法不能为空")
|
||||
private String method;
|
||||
/**
|
||||
* User Agent
|
||||
*/
|
||||
@NotNull(message = "User-Agent 不能为空")
|
||||
private String userAgent;
|
||||
/**
|
||||
* ip
|
||||
*/
|
||||
@NotNull(message = "ip 不能为空")
|
||||
private String ip;
|
||||
/**
|
||||
* 请求时间
|
||||
*/
|
||||
@NotNull(message = "请求时间不能为空")
|
||||
private Date startTime;
|
||||
/**
|
||||
* 响应时长 -- 毫秒级
|
||||
*/
|
||||
@NotNull(message = "响应时长不能为空")
|
||||
private Integer responseTime;
|
||||
/**
|
||||
* 错误码
|
||||
*
|
||||
* 目前的结果,是使用 {@link CommonResult#getCode()} 属性
|
||||
*/
|
||||
@NotNull(message = "错误码不能为空")
|
||||
private Integer errorCode;
|
||||
/**
|
||||
* 错误提示
|
||||
*
|
||||
* 目前的结果,是使用 {@link CommonResult#getMessage()} 属性
|
||||
*/
|
||||
private String errorMessage;
|
||||
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
package cn.iocoder.mall.admin.api.dto;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 管理员访问日志添加 DTO
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class AdminAccessLogAddDTO implements Serializable {
|
||||
|
||||
/**
|
||||
* 管理员编号 - 空
|
||||
*/
|
||||
public static final Integer ADMIN_ID_NULL = 0;
|
||||
|
||||
/**
|
||||
* 管理员编号.
|
||||
*
|
||||
* 当管理员为空时,该值为0
|
||||
*/
|
||||
@NotNull(message = "管理员编号不能为空")
|
||||
private Integer adminId;
|
||||
/**
|
||||
* 访问地址
|
||||
*/
|
||||
@NotNull(message = "访问地址不能为空")
|
||||
private String uri;
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
@NotNull(message = "请求参数不能为空")
|
||||
private String queryString;
|
||||
/**
|
||||
* http 方法
|
||||
*/
|
||||
@NotNull(message = "http 请求方法不能为空")
|
||||
private String method;
|
||||
/**
|
||||
* User Agent
|
||||
*/
|
||||
@NotNull(message = "User-Agent 不能为空")
|
||||
private String userAgent;
|
||||
/**
|
||||
* ip
|
||||
*/
|
||||
@NotNull(message = "ip 不能为空")
|
||||
private String ip;
|
||||
/**
|
||||
* 请求时间
|
||||
*/
|
||||
@NotNull(message = "请求时间不能为空")
|
||||
private Date startTime;
|
||||
/**
|
||||
* 响应时长 -- 毫秒级
|
||||
*/
|
||||
@NotNull(message = "响应时长不能为空")
|
||||
private Integer responseTime;
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package cn.iocoder.mall.admin.convert;
|
||||
|
||||
import cn.iocoder.mall.admin.api.dto.AccessLogAddDTO;
|
||||
import cn.iocoder.mall.admin.dataobject.AccessLogDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface AccessLogConvert {
|
||||
|
||||
AccessLogConvert INSTANCE = Mappers.getMapper(AccessLogConvert.class);
|
||||
|
||||
@Mappings({})
|
||||
AccessLogDO convert(AccessLogAddDTO accessLogAddDTO);
|
||||
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package cn.iocoder.mall.admin.convert;
|
||||
|
||||
import cn.iocoder.mall.admin.api.dto.AdminAccessLogAddDTO;
|
||||
import cn.iocoder.mall.admin.dataobject.AdminAccessLogDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface AdminAccessLogConvert {
|
||||
|
||||
AdminAccessLogConvert INSTANCE = Mappers.getMapper(AdminAccessLogConvert.class);
|
||||
|
||||
@Mappings({})
|
||||
AdminAccessLogDO convert(AdminAccessLogAddDTO adminAccessLogAddDTO);
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package cn.iocoder.mall.admin.dao;
|
||||
|
||||
import cn.iocoder.mall.admin.dataobject.AccessLogDO;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface AccessLogMapper {
|
||||
|
||||
void insert(AccessLogDO entity);
|
||||
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package cn.iocoder.mall.admin.dao;
|
||||
|
||||
import cn.iocoder.mall.admin.dataobject.AdminAccessLogDO;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface AdminAccessLogMapper {
|
||||
|
||||
void insert(AdminAccessLogDO entity);
|
||||
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
package cn.iocoder.mall.admin.dataobject;
|
||||
|
||||
import cn.iocoder.common.framework.dataobject.DeletableDO;
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 管理员访问日志 DO
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class AccessLogDO extends DeletableDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
private Integer id;
|
||||
/**
|
||||
* 链路追踪编号
|
||||
*
|
||||
* 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
|
||||
*/
|
||||
private String traceId;
|
||||
/**
|
||||
* 用户编号.
|
||||
*
|
||||
* 当管理员为空时,该值为 {@link cn.iocoder.mall.admin.api.dto.AccessLogAddDTO#USER_ID_NULL}
|
||||
*/
|
||||
private Integer userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private Integer userType;
|
||||
/**
|
||||
* 应用名
|
||||
*
|
||||
* 目前读取 spring.application.name
|
||||
*/
|
||||
private String applicationName;
|
||||
/**
|
||||
* 访问地址
|
||||
*/
|
||||
private String uri;
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
private String queryString;
|
||||
/**
|
||||
* http 方法
|
||||
*/
|
||||
private String method;
|
||||
/**
|
||||
* userAgent
|
||||
*/
|
||||
private String userAgent;
|
||||
/**
|
||||
* ip
|
||||
*/
|
||||
private String ip;
|
||||
/**
|
||||
* 请求时间
|
||||
*/
|
||||
private Date startTime;
|
||||
/**
|
||||
* 响应时长 -- 毫秒级
|
||||
*/
|
||||
private Integer responseTime;
|
||||
/**
|
||||
* 错误码
|
||||
*
|
||||
* 目前的结果,是使用 {@link CommonResult#getCode()} 属性
|
||||
*/
|
||||
private Integer errorCode;
|
||||
/**
|
||||
* 错误提示
|
||||
*
|
||||
* 目前的结果,是使用 {@link CommonResult#getMessage()} 属性
|
||||
*/
|
||||
private String errorMessage;
|
||||
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
package cn.iocoder.mall.admin.dataobject;
|
||||
|
||||
import cn.iocoder.common.framework.dataobject.DeletableDO;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 管理员访问日志 DO
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class AdminAccessLogDO extends DeletableDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
private Integer id;
|
||||
/**
|
||||
* 管理员编号.
|
||||
*
|
||||
* 当管理员为空时,该值为0
|
||||
*/
|
||||
private Integer adminId;
|
||||
/**
|
||||
* 访问地址
|
||||
*/
|
||||
private String uri;
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
private String queryString;
|
||||
/**
|
||||
* http 方法
|
||||
*/
|
||||
private String method;
|
||||
/**
|
||||
* userAgent
|
||||
*/
|
||||
private String userAgent;
|
||||
/**
|
||||
* ip
|
||||
*/
|
||||
private String ip;
|
||||
/**
|
||||
* 请求时间
|
||||
*/
|
||||
private Date startTime;
|
||||
/**
|
||||
* 响应时长 -- 毫秒级
|
||||
*/
|
||||
private Integer responseTime;
|
||||
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
package cn.iocoder.mall.admin.service;
|
||||
|
||||
import cn.iocoder.common.framework.util.StringUtil;
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.admin.api.AdminAccessLogService;
|
||||
import cn.iocoder.mall.admin.api.dto.AdminAccessLogAddDTO;
|
||||
import cn.iocoder.mall.admin.convert.AdminAccessLogConvert;
|
||||
import cn.iocoder.mall.admin.dao.AdminAccessLogMapper;
|
||||
import cn.iocoder.mall.admin.dataobject.AdminAccessLogDO;
|
||||
import cn.iocoder.mall.admin.api.SystemLogService;
|
||||
import cn.iocoder.mall.admin.api.dto.AccessLogAddDTO;
|
||||
import cn.iocoder.mall.admin.convert.AccessLogConvert;
|
||||
import cn.iocoder.mall.admin.dao.AccessLogMapper;
|
||||
import cn.iocoder.mall.admin.dataobject.AccessLogDO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -14,7 +13,7 @@ import java.util.Date;
|
||||
|
||||
@Service
|
||||
@org.apache.dubbo.config.annotation.Service(validation = "true", version = "${dubbo.provider.AdminAccessLogService.version}")
|
||||
public class AdminAccessLogServiceImpl implements AdminAccessLogService {
|
||||
public class SystemLogServiceImpl implements SystemLogService {
|
||||
|
||||
/**
|
||||
* 请求参数最大长度。
|
||||
@ -30,12 +29,12 @@ public class AdminAccessLogServiceImpl implements AdminAccessLogService {
|
||||
private static final Integer USER_AGENT_MAX_LENGTH = 1024;
|
||||
|
||||
@Autowired
|
||||
private AdminAccessLogMapper adminAccessLogMapper;
|
||||
private AccessLogMapper accessLogMapper;
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> addAdminAccessLog(AdminAccessLogAddDTO adminAccessLogAddDTO) {
|
||||
public void addAccessLog(AccessLogAddDTO adminAccessLogAddDTO) {
|
||||
// 创建 AdminAccessLogDO
|
||||
AdminAccessLogDO accessLog = AdminAccessLogConvert.INSTANCE.convert(adminAccessLogAddDTO);
|
||||
AccessLogDO accessLog = AccessLogConvert.INSTANCE.convert(adminAccessLogAddDTO);
|
||||
accessLog.setCreateTime(new Date());
|
||||
// 截取最大长度
|
||||
if (accessLog.getUri().length() > URI_MAX_LENGTH) {
|
||||
@ -48,9 +47,7 @@ public class AdminAccessLogServiceImpl implements AdminAccessLogService {
|
||||
accessLog.setUserAgent(StringUtil.substring(accessLog.getUserAgent(), USER_AGENT_MAX_LENGTH));
|
||||
}
|
||||
// 插入
|
||||
adminAccessLogMapper.insert(accessLog);
|
||||
// 返回成功
|
||||
return CommonResult.success(true);
|
||||
accessLogMapper.insert(accessLog);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.mall.admin.dao.AccessLogMapper">
|
||||
|
||||
<!--<sql id="FIELDS">-->
|
||||
<!--id, username, nickname, password, status,-->
|
||||
<!--create_time-->
|
||||
<!--</sql>-->
|
||||
|
||||
<insert id="insert" parameterType="AccessLogDO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
|
||||
INSERT INTO access_log (
|
||||
trace_id, user_id, user_type, uri, query_string, method, user_agent,
|
||||
ip, start_time, response_time, error_code, error_message, create_time
|
||||
) VALUES (
|
||||
#{traceId}, #{userId}, #{userType}, #{uri}, #{queryString}, #{method}, #{userAgent},
|
||||
#{ip}, #{startTime}, #{responseTime}, #{errorCode}, #{errorMessage}, #{createTime}
|
||||
)
|
||||
</insert>
|
||||
|
||||
</mapper>
|
@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.mall.admin.dao.AdminAccessLogMapper">
|
||||
|
||||
<!--<sql id="FIELDS">-->
|
||||
<!--id, username, nickname, password, status,-->
|
||||
<!--create_time-->
|
||||
<!--</sql>-->
|
||||
|
||||
<insert id="insert" parameterType="AdminAccessLogDO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
|
||||
INSERT INTO admin_access_log (
|
||||
admin_id, uri, query_string, method, user_agent,
|
||||
ip, start_time, response_time, create_time
|
||||
) VALUES (
|
||||
#{adminId}, #{uri}, #{queryString}, #{method}, #{userAgent},
|
||||
#{ip}, #{startTime}, #{responseTime}, #{createTime}
|
||||
)
|
||||
</insert>
|
||||
|
||||
</mapper>
|
@ -74,8 +74,8 @@
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>swagger-bootstrap-ui</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
@ -2,9 +2,10 @@ package cn.iocoder.mall.user.application;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
||||
@SpringBootApplication(scanBasePackages = {"cn.iocoder.mall.user"})
|
||||
@EnableAsync(proxyTargetClass = true)
|
||||
public class UserApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
@ -1,36 +0,0 @@
|
||||
package cn.iocoder.mall.user.application.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
public class SwaggerConfiguration {
|
||||
|
||||
@Bean
|
||||
public Docket createRestApi() {
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.apiInfo(apiInfo())
|
||||
.select()
|
||||
.apis(RequestHandlerSelectors.basePackage("cn.iocoder.mall.user.application.controller"))
|
||||
.paths(PathSelectors.any())
|
||||
.build();
|
||||
}
|
||||
|
||||
private ApiInfo apiInfo() {
|
||||
return new ApiInfoBuilder()
|
||||
.title("用户子系统")
|
||||
.description("用户子系统")
|
||||
.termsOfServiceUrl("http://www.iocoder.cn")
|
||||
.version("1.0.0")
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
@ -7,3 +7,9 @@ server:
|
||||
port: 18082
|
||||
servlet:
|
||||
context-path: /user-api/
|
||||
|
||||
swagger:
|
||||
title: 用户子系统
|
||||
description: 用户子系统
|
||||
version: 1.0.0
|
||||
base-package: cn.iocoder.mall.user.application.controller
|
||||
|
@ -1,84 +0,0 @@
|
||||
package cn.iocoder.mall.user.sdk.interceptor;
|
||||
|
||||
import cn.iocoder.common.framework.util.HttpUtil;
|
||||
import cn.iocoder.mall.user.api.UserAccessLogService;
|
||||
import cn.iocoder.mall.user.api.dto.UserAccessLogAddDTO;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.apache.dubbo.config.annotation.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 访问日志拦截器
|
||||
*/
|
||||
@Component
|
||||
public class UserAccessLogInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private static final ThreadLocal<Date> START_TIME = new ThreadLocal<>();
|
||||
/**
|
||||
* 管理员编号
|
||||
*/
|
||||
private static final ThreadLocal<Integer> USER_ID = new ThreadLocal<>();
|
||||
|
||||
@Reference(validation = "true", version = "${dubbo.provider.UserAccessLogService.version:1.0.0}")
|
||||
private UserAccessLogService userAccessLogService;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
// TODO 芋艿,临时拿来处理 vue axios options 请求的问题。
|
||||
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
|
||||
|
||||
return false; // 通过这样的方式,让前端知道允许的 header 等等。
|
||||
}
|
||||
// 记录当前时间
|
||||
START_TIME.set(new Date());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
|
||||
UserAccessLogAddDTO accessLog = new UserAccessLogAddDTO();
|
||||
try {
|
||||
accessLog.setUserId(USER_ID.get());
|
||||
if (accessLog.getUserId() == null) {
|
||||
accessLog.setUserId(UserAccessLogAddDTO.USER_ID_NULL);
|
||||
}
|
||||
accessLog.setUri(request.getRequestURI()); // TODO 提升:如果想要优化,可以使用 Swagger 的 @ApiOperation 注解。
|
||||
accessLog.setQueryString(HttpUtil.buildQueryString(request));
|
||||
accessLog.setMethod(request.getMethod());
|
||||
accessLog.setUserAgent(HttpUtil.getUserAgent(request));
|
||||
accessLog.setIp(HttpUtil.getIp(request));
|
||||
accessLog.setStartTime(START_TIME.get());
|
||||
accessLog.setResponseTime((int) (System.currentTimeMillis() - accessLog.getStartTime().getTime()));// 默认响应时间设为0
|
||||
userAccessLogService.addUserAccessLog(accessLog);
|
||||
// TODO 提升:暂时不考虑 ELK 的方案。而是基于 MySQL 存储。如果访问日志比较多,需要定期归档。
|
||||
} catch (Throwable th) {
|
||||
logger.error("[afterCompletion][插入管理员访问日志({}) 发生异常({})", JSON.toJSONString(accessLog), ExceptionUtils.getRootCauseMessage(th));
|
||||
} finally {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setUserId(Integer userId) {
|
||||
USER_ID.set(userId);
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
START_TIME.remove();
|
||||
USER_ID.remove();
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
package cn.iocoder.mall.user.sdk.interceptor;
|
||||
|
||||
import cn.iocoder.common.framework.constant.MallConstants;
|
||||
import cn.iocoder.common.framework.exception.ServiceException;
|
||||
import cn.iocoder.common.framework.util.HttpUtil;
|
||||
import cn.iocoder.common.framework.util.MallUtil;
|
||||
import cn.iocoder.mall.user.api.OAuth2Service;
|
||||
import cn.iocoder.mall.user.api.bo.OAuth2AuthenticationBO;
|
||||
import cn.iocoder.mall.user.sdk.annotation.PermitAll;
|
||||
@ -26,8 +28,10 @@ public class UserSecurityInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
// 设置当前访问的用户类型。注意,即使未登陆,我们也认为是用户
|
||||
MallUtil.setUserType(request, MallConstants.USER_TYPE_USER);
|
||||
// 校验访问令牌是否正确。若正确,返回授权信息
|
||||
String accessToken = HttpUtil.obtainAccess(request);
|
||||
String accessToken = HttpUtil.obtainAuthorization(request);
|
||||
OAuth2AuthenticationBO authentication = null;
|
||||
if (accessToken != null) {
|
||||
authentication = oauth2Service.checkToken(accessToken); // TODO 芋艿,如果访问的地址无需登录,这里也不用抛异常
|
||||
@ -39,7 +43,7 @@ public class UserSecurityInterceptor extends HandlerInterceptorAdapter {
|
||||
// AdminSecurityInterceptor 执行后,会移除 AdminSecurityContext 信息,这就导致 AdminAccessLogInterceptor 无法获得管理员编号
|
||||
// 因此,这里需要进行记录
|
||||
if (authentication.getUserId() != null) {
|
||||
UserAccessLogInterceptor.setUserId(authentication.getUserId());
|
||||
MallUtil.setUserId(request, authentication.getUserId());
|
||||
}
|
||||
}
|
||||
// 校验是否需要已授权
|
||||
|
Loading…
Reference in New Issue
Block a user