fix 完成request-id

master
陈景阳 4 years ago
parent f688a44e77
commit 614d3fc273
  1. 5
      pom.xml
  2. 150
      src/main/java/org/anyin/gitee/shiro/advisor/ApiMessageAdvisor.java
  3. 1
      src/main/java/org/anyin/gitee/shiro/base/BusinessCodeEnum.java
  4. 2
      src/main/java/org/anyin/gitee/shiro/base/BusinessException.java
  5. 5
      src/main/java/org/anyin/gitee/shiro/base/Response.java
  6. 14
      src/main/java/org/anyin/gitee/shiro/config/AppConfig.java
  7. 27
      src/main/java/org/anyin/gitee/shiro/controller/LogTestController.java
  8. 1
      src/main/java/org/anyin/gitee/shiro/controller/LoginController.java
  9. 24
      src/main/java/org/anyin/gitee/shiro/shiro/TokenFilter.java
  10. 26
      src/main/java/org/anyin/gitee/shiro/utils/RequestIdUtils.java
  11. 8
      src/main/resources/application.yml
  12. 34
      src/main/resources/logback-spring.xml

@ -41,6 +41,11 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
<dependencyManagement>

@ -0,0 +1,150 @@
package org.anyin.gitee.shiro.advisor;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.anyin.gitee.shiro.base.BusinessCodeEnum;
import org.anyin.gitee.shiro.base.BusinessException;
import org.anyin.gitee.shiro.base.Response;
import org.anyin.gitee.shiro.utils.RequestIdUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.MDC;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Optional;
import java.util.UUID;
@Aspect
@Order
@Slf4j
public class ApiMessageAdvisor {
@Around("execution(public * org.anyin.gitee.shiro.controller..*Controller.*(..))")
public Object invokeAPI(ProceedingJoinPoint pjp) {
String apiName = this.getApiName(pjp);
String requestId = this.getRequestId();
// 配置日志文件打印 REQUEST_ID
MDC.put("REQUEST_ID", requestId);
Object returnValue = null;
try{
this.printRequestParam(apiName, pjp);
returnValue = pjp.proceed();
this.handleRequestId(returnValue);
}catch (BusinessException ex){
returnValue = this.handleBusinessException(apiName, ex);
}catch (Throwable ex){
returnValue = this.handleSystemException(apiName, ex);
}finally {
this.printResponse(apiName, returnValue);
RequestIdUtils.removeRequestId();
MDC.clear();
}
return returnValue;
}
/**
* 处理系统异常
* @param apiName 接口名称
* @param ex 系统异常
* @return 返回参数
*/
private Response handleSystemException(String apiName, Throwable ex){
log.error("@Meet unknown error when do " + apiName + ":" + ex.getMessage(), ex);
Response response = new Response(BusinessCodeEnum.UNKNOWN_ERROR.getCode(), BusinessCodeEnum.UNKNOWN_ERROR.getMsg());
response.setRequestId(RequestIdUtils.getRequestId().toString());
return response;
}
/**
* 处理业务异常
* @param apiName 接口名称
* @param ex 业务异常
* @return 返回参数
*/
private Response handleBusinessException(String apiName, BusinessException ex){
log.error("@Meet error when do " + apiName + "[" + ex.getCode() + "]:" + ex.getMsg(), ex);
Response response = new Response(ex.getCode(), ex.getMsg());
response.setRequestId(RequestIdUtils.getRequestId().toString());
return response;
}
/**
* 填充RequestId
* @param returnValue 返回参数
*/
private void handleRequestId(Object returnValue){
if(returnValue instanceof Response){
Response response = (Response)returnValue;
response.setRequestId(RequestIdUtils.getRequestId().toString());
}
}
/**
* 打印响应参数信息
* @param apiName 接口名称
* @param returnValue 返回值
*/
private void printResponse(String apiName, Object returnValue){
if (log.isInfoEnabled()) {
log.info("@@{} done, response: {}", apiName, JSONUtil.toJsonStr(returnValue));
}
}
/**
* 打印请求参数信息
* @param apiName 接口名称
* @param pjp 切点
*/
private void printRequestParam(String apiName, ProceedingJoinPoint pjp){
Object[] args = pjp.getArgs();
if(log.isInfoEnabled() && args != null&& args.length > 0){
for(Object o : args) {
if(!(o instanceof HttpServletRequest) && !(o instanceof HttpServletResponse) && !(o instanceof CommonsMultipartFile)) {
log.info("@@{} started, request: {}", apiName, JSONUtil.toJsonStr(o));
}
}
}
}
/**
* 获取RequestId
* 优先从header头获取如果没有则自己生成
* @return RequestId
*/
private String getRequestId(){
UUID existUUID = RequestIdUtils.getRequestId();
if(existUUID != null){
return existUUID.toString();
}
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(attributes == null || !StringUtils.hasText(attributes.getRequest().getHeader("x-request-id"))) {
RequestIdUtils.generateRequestId();
return RequestIdUtils.getRequestId().toString();
}
// 因为如果有网关,则一般会从网关传递过来,所以优先从header头获取
HttpServletRequest request = attributes.getRequest();
String requestId = request.getHeader("x-request-id");
UUID uuid = UUID.fromString(requestId);
RequestIdUtils.generateRequestId(uuid);
return requestId;
}
/**
* 获取当前接口对应的类名和方法名
* @param pjp 切点
* @return apiName
*/
private String getApiName(ProceedingJoinPoint pjp){
String apiClassName = pjp.getTarget().getClass().getSimpleName();
String methodName = pjp.getSignature().getName();
return apiClassName.concat(":").concat(methodName);
}
}

@ -4,6 +4,7 @@ import lombok.Getter;
@Getter
public enum BusinessCodeEnum {
UNKNOWN_ERROR("1000", "未知错误"),
USER_UN_LOGIN("1008", "用户未登录"),
USER_NOT_FOUND("B1001", "用户账号密码错误"),
PWD_NOT_MATCH("B1002", "用户账号密码错误"),

@ -16,6 +16,6 @@ public class BusinessException extends RuntimeException{
public BusinessException(String code, String msg){
this.code = code;
this.msg = code;
this.msg = msg;
}
}

@ -7,20 +7,25 @@ import lombok.Data;
public class Response<T> {
private static final String SUCCESS_CODE = "200";
private static final String SUCCESS_MSG = "处理成功";
private String code;
private String msg;
private String requestId;
private T data;
public Response(){
this.code = SUCCESS_CODE;
this.msg = SUCCESS_MSG;
}
public Response(T data){
this.data = data;
this.code = SUCCESS_CODE;
this.msg = SUCCESS_MSG;
}
public Response(String code, String msg){

@ -0,0 +1,14 @@
package org.anyin.gitee.shiro.config;
import org.anyin.gitee.shiro.advisor.ApiMessageAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public ApiMessageAdvisor apiMessageAdvisor(){
return new ApiMessageAdvisor();
}
}

@ -0,0 +1,27 @@
package org.anyin.gitee.shiro.controller;
import org.anyin.gitee.shiro.base.BusinessCodeEnum;
import org.anyin.gitee.shiro.base.Response;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/log-test")
public class LogTestController {
@GetMapping("/success")
public Response<String> success(){
return new Response<>("success");
}
@GetMapping("/business-exception")
public Response<String> businessException(){
throw BusinessCodeEnum.PWD_NOT_MATCH.getException();
}
@GetMapping("/system-exception")
public Response<String> systemException(){
throw new NullPointerException("空指针异常");
}
}

@ -2,7 +2,6 @@ package org.anyin.gitee.shiro.controller;
import org.anyin.gitee.shiro.base.Response;
import org.anyin.gitee.shiro.controller.form.LoginForm;
import org.anyin.gitee.shiro.model.UserInfo;
import org.anyin.gitee.shiro.service.ILoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;

@ -1,11 +1,15 @@
package org.anyin.gitee.shiro.shiro;
import cn.hutool.json.JSONUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.anyin.gitee.shiro.base.BusinessCodeEnum;
import org.anyin.gitee.shiro.base.BusinessException;
import org.anyin.gitee.shiro.base.Response;
import org.anyin.gitee.shiro.base.SpringContext;
import org.anyin.gitee.shiro.service.ITokenService;
import org.anyin.gitee.shiro.utils.RequestIdUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.springframework.util.ObjectUtils;
@ -16,6 +20,7 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.UUID;
@Slf4j
public class TokenFilter extends AuthenticatingFilter {
@ -52,7 +57,7 @@ public class TokenFilter extends AuthenticatingFilter {
String token = this.getToken(request);
if(ObjectUtils.isEmpty(token)){
this.respUnLogin(request, response);
this.responseException(request, response, BusinessCodeEnum.USER_UN_LOGIN.getException());
return false;
}
@ -62,19 +67,28 @@ public class TokenFilter extends AuthenticatingFilter {
}
if(!tokenService.check(token)){
this.respUnLogin(request, response);
this.responseException(request, response, BusinessCodeEnum.USER_UN_LOGIN.getException());
}
// 根据token获取用户信息,会执行 TokenRealm#doGetAuthenticationInfo 方法
return executeLogin(servletRequest, servletResponse);
}
private void respUnLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
@SneakyThrows
@Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest servletRequest, ServletResponse servletResponse) {
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
this.responseException(request, response, BusinessCodeEnum.TOKEN_INVALID.getException());
return false;
}
private void responseException(HttpServletRequest request, HttpServletResponse response, BusinessException ex) throws IOException {
response.setContentType("application/json;charset=utf-8");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
Response resp = new Response(BusinessCodeEnum.USER_UN_LOGIN.getCode(), BusinessCodeEnum.USER_UN_LOGIN.getMsg());
Response resp = new Response(ex.getCode(), ex.getMsg());
resp.setRequestId(UUID.randomUUID().toString());
response.getWriter().print(JSONUtil.toJsonStr(resp));
}

@ -0,0 +1,26 @@
package org.anyin.gitee.shiro.utils;
import java.util.UUID;
public class RequestIdUtils {
private static final ThreadLocal<UUID> requestIdHolder = new ThreadLocal<>();
public RequestIdUtils() {
}
public static void generateRequestId() {
requestIdHolder.set(UUID.randomUUID());
}
public static void generateRequestId(UUID uuid) {
requestIdHolder.set(uuid);
}
public static UUID getRequestId() {
return (UUID)requestIdHolder.get();
}
public static void removeRequestId() {
requestIdHolder.remove();
}
}

@ -1,3 +1,9 @@
spring:
application:
name: shiro-to-token
name: shiro-to-token
logging:
level:
root: info
file:
path: ./logs/shiro-to-token.log

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="true" scanPeriod="1 seconds">
<contextName>logback</contextName>
<springProperty scope="context" name="level" source="logging.level.root"/>
<springProperty scope="context" name="path" source="logging.file.path"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<Target>System.out</Target>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter" >
<level>DEBUG</level>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{REQUEST_ID}] [%thread] [%-5level] [%logger{0}:%L] : %msg%n</pattern>
</encoder>
</appender>
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${path}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${path}.%d{yyyy-MM-dd}.zip</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{REQUEST_ID}] [%thread] [%-5level] [%logger{0}:%L] : %msg%n</pattern>
</encoder>
</appender>
<root level="${level}">
<appender-ref ref="console"/>
<appender-ref ref="file"/>
</root>
</configuration>
Loading…
Cancel
Save