- 后端:替换 swagger bootstrap ui ,一下子接口文档好看了。
- 后端:增加 swagger AutoConfiguration 配置类 - 后端:统一访问日志的记录
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,4 +6,10 @@ spring:
|
||||
server:
|
||||
port: 18082
|
||||
servlet:
|
||||
context-path: /user-api/
|
||||
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());
|
||||
}
|
||||
}
|
||||
// 校验是否需要已授权
|
||||
|
||||
Reference in New Issue
Block a user