diff --git a/pom.xml b/pom.xml
index 5bb6512..a178321 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,6 +41,11 @@
org.springframework.boot
spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
diff --git a/src/main/java/org/anyin/gitee/shiro/advisor/ApiMessageAdvisor.java b/src/main/java/org/anyin/gitee/shiro/advisor/ApiMessageAdvisor.java
new file mode 100644
index 0000000..e50bc04
--- /dev/null
+++ b/src/main/java/org/anyin/gitee/shiro/advisor/ApiMessageAdvisor.java
@@ -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);
+ }
+}
diff --git a/src/main/java/org/anyin/gitee/shiro/base/BusinessCodeEnum.java b/src/main/java/org/anyin/gitee/shiro/base/BusinessCodeEnum.java
index 2a7cf06..33ee71c 100644
--- a/src/main/java/org/anyin/gitee/shiro/base/BusinessCodeEnum.java
+++ b/src/main/java/org/anyin/gitee/shiro/base/BusinessCodeEnum.java
@@ -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", "用户账号密码错误"),
diff --git a/src/main/java/org/anyin/gitee/shiro/base/BusinessException.java b/src/main/java/org/anyin/gitee/shiro/base/BusinessException.java
index b92e5d7..22a97b1 100644
--- a/src/main/java/org/anyin/gitee/shiro/base/BusinessException.java
+++ b/src/main/java/org/anyin/gitee/shiro/base/BusinessException.java
@@ -16,6 +16,6 @@ public class BusinessException extends RuntimeException{
public BusinessException(String code, String msg){
this.code = code;
- this.msg = code;
+ this.msg = msg;
}
}
diff --git a/src/main/java/org/anyin/gitee/shiro/base/Response.java b/src/main/java/org/anyin/gitee/shiro/base/Response.java
index 9ce86ff..cd409e2 100644
--- a/src/main/java/org/anyin/gitee/shiro/base/Response.java
+++ b/src/main/java/org/anyin/gitee/shiro/base/Response.java
@@ -7,20 +7,25 @@ import lombok.Data;
public class Response {
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){
diff --git a/src/main/java/org/anyin/gitee/shiro/config/AppConfig.java b/src/main/java/org/anyin/gitee/shiro/config/AppConfig.java
new file mode 100644
index 0000000..fe1b69f
--- /dev/null
+++ b/src/main/java/org/anyin/gitee/shiro/config/AppConfig.java
@@ -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();
+ }
+}
diff --git a/src/main/java/org/anyin/gitee/shiro/controller/LogTestController.java b/src/main/java/org/anyin/gitee/shiro/controller/LogTestController.java
new file mode 100644
index 0000000..7cd1474
--- /dev/null
+++ b/src/main/java/org/anyin/gitee/shiro/controller/LogTestController.java
@@ -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 success(){
+ return new Response<>("success");
+ }
+
+ @GetMapping("/business-exception")
+ public Response businessException(){
+ throw BusinessCodeEnum.PWD_NOT_MATCH.getException();
+ }
+
+ @GetMapping("/system-exception")
+ public Response systemException(){
+ throw new NullPointerException("空指针异常");
+ }
+}
diff --git a/src/main/java/org/anyin/gitee/shiro/controller/LoginController.java b/src/main/java/org/anyin/gitee/shiro/controller/LoginController.java
index b4eb792..3f8eeee 100644
--- a/src/main/java/org/anyin/gitee/shiro/controller/LoginController.java
+++ b/src/main/java/org/anyin/gitee/shiro/controller/LoginController.java
@@ -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;
diff --git a/src/main/java/org/anyin/gitee/shiro/shiro/TokenFilter.java b/src/main/java/org/anyin/gitee/shiro/shiro/TokenFilter.java
index 91ec11d..789a837 100644
--- a/src/main/java/org/anyin/gitee/shiro/shiro/TokenFilter.java
+++ b/src/main/java/org/anyin/gitee/shiro/shiro/TokenFilter.java
@@ -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));
}
diff --git a/src/main/java/org/anyin/gitee/shiro/utils/RequestIdUtils.java b/src/main/java/org/anyin/gitee/shiro/utils/RequestIdUtils.java
new file mode 100644
index 0000000..8d09735
--- /dev/null
+++ b/src/main/java/org/anyin/gitee/shiro/utils/RequestIdUtils.java
@@ -0,0 +1,26 @@
+package org.anyin.gitee.shiro.utils;
+
+import java.util.UUID;
+
+public class RequestIdUtils {
+ private static final ThreadLocal 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();
+ }
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 458884f..9097c01 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -1,3 +1,9 @@
spring:
application:
- name: shiro-to-token
\ No newline at end of file
+ name: shiro-to-token
+
+logging:
+ level:
+ root: info
+ file:
+ path: ./logs/shiro-to-token.log
\ No newline at end of file
diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000..3878a19
--- /dev/null
+++ b/src/main/resources/logback-spring.xml
@@ -0,0 +1,34 @@
+
+
+
+ logback
+
+
+
+
+
+ System.out
+
+ DEBUG
+
+
+ %d{yyyy-MM-dd HH:mm:ss.SSS} [%X{REQUEST_ID}] [%thread] [%-5level] [%logger{0}:%L] : %msg%n
+
+
+
+
+ ${path}
+
+ ${path}.%d{yyyy-MM-dd}.zip
+
+
+ %d{yyyy-MM-dd HH:mm:ss.SSS} [%X{REQUEST_ID}] [%thread] [%-5level] [%logger{0}:%L] : %msg%n
+
+
+
+
+
+
+
+
+
\ No newline at end of file