Prechádzať zdrojové kódy

增加 审计日志 功能

wuhongshuang 3 rokov pred
rodič
commit
52958fb168

+ 52 - 0
src/main/java/com/szwl/annotation/Audit.java

@@ -0,0 +1,52 @@
+package com.szwl.annotation;
+
+import com.szwl.constant.AuditEnum;
+
+import java.lang.annotation.*;
+
+/**
+ * 审计日志
+ *
+ * @author PT-ZHOUYUHENG
+ * @date 2022-05-17 11:40
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Audit {
+
+    /**
+     * 日志内容,支持SPEL,使用 #作为标识符
+     * 使用说明:
+     * 一、获取登录用户:
+     *  1、#loginUser 即可获取登录的DefaultUserDetails对象,如果使用了@IgnoreToken,则获取到一个new DefaultUserDetails()
+     *
+     * 二、获取方法参数:
+     *  1、接口参数为int、String等基本数据类型的,参数名为id,直接使用 #id则可以获取到值
+     *  2、接口参数为对象的,如 #obj.propertyA、#obj.propertyB
+     *
+     *  三、获取方法返回值对象:
+     *  1、#returnObj 为该方法的返回值对象
+     *
+     * 四、引用Bean
+     *  1、使用@beanId可以获取spring中注册了的bean,例如:@sysUserServiceImpl.getById(#loginUser.userId)可以获取到SysUserService对象,使用里面的方法
+     *
+     *  五、综合使用:
+     *  1、@Audit(content = "#loginUser.userRealName + '禁用了用户' + #banUserId")
+     *  解析结果为:测试用户禁用了用户202205180000001
+     *  2、@Audit(content = "#loginUser.userRealName + '给用户组[' + @sysUserGroupServiceImpl.getById(#req.userGroupId).userGroupName + ']添加了用户:' + #req.userIds")
+     *  解析结果为:张三给用户组[测试组]添加了用户:202109151538040291a2d37fb9ef641398
+     */
+    String content();
+
+    /**
+     * 业务编号,支持SPEL
+     */
+    String bizNo() default "";
+
+    /**
+     * 业务类型
+     */
+    AuditEnum type();
+
+}

+ 235 - 0
src/main/java/com/szwl/aspect/AuditAspect.java

@@ -0,0 +1,235 @@
+package com.szwl.aspect;
+
+import com.alibaba.fastjson.JSON;
+import com.szwl.annotation.Audit;
+import com.szwl.manager.TokenManager;
+import com.szwl.model.bo.UserDetailBO;
+import com.szwl.model.entity.AuditLog;
+import com.szwl.service.AuditLogService;
+import com.szwl.util.SpringUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.expression.BeanFactoryResolver;
+import org.springframework.core.DefaultParameterNameDiscoverer;
+import org.springframework.core.ParameterNameDiscoverer;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.Expression;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author PT-ZHOUYUHENG
+ * @date 2022-05-17 11:45
+ */
+@Aspect
+@Component
+@Slf4j
+public class AuditAspect {
+    @Autowired
+    AuditLogService auditLogService;
+    @Autowired
+    private TokenManager securityManager;
+
+    @Value("${spring.application.name}")
+    private String appName;
+
+    /**
+     * 解析SPEL,例如#obj.name
+     */
+    private final ExpressionParser parser = new SpelExpressionParser();
+    private final String spElFlag = "#";
+    private static final BeanFactoryResolver BEAN_FACTORY_RESOLVER = new BeanFactoryResolver(SpringUtils.getApplicationContext());
+
+    private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(4, 8, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(500));
+
+
+    @Around(value = "@annotation(com.szwl.annotation.Audit)")
+    public Object around(ProceedingJoinPoint point) throws Throwable {
+        boolean success = false;
+        Exception exception = null;
+        Object[] reqValue = null;
+        Object respValue = null;
+        try {
+            Object[] args = point.getArgs();
+            reqValue = args;
+            Object result = point.proceed(args);
+            respValue = result;
+            success = true;
+            return result;
+        } catch (Exception e) {
+            exception = e;
+            throw e;
+        } finally {
+            UserDetailBO userDetails;
+            try {
+                userDetails = securityManager.getLoginUserDetails();
+            } catch (Exception e) {
+                userDetails = new UserDetailBO();
+            }
+            EXECUTOR.execute(new AuditThread(userDetails, success, point, exception, reqValue, respValue));
+        }
+    }
+
+    private String getReqValue(Object[] args) {
+        try {
+            return JSON.toJSONString(args);
+        } catch (Exception e) {
+            log.info("--解析请求参数失败:{}", e.getMessage());
+            return null;
+        }
+    }
+
+    private String getRespValue(Object result) {
+        try {
+            return JSON.toJSONString(result);
+        } catch (Exception e) {
+            log.info("--解析响应参数失败:{}", e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 创建审计日志
+     *
+     * @param userDetails
+     * @param success
+     * @param point
+     * @param exception
+     */
+    private void buildAuditLog(UserDetailBO userDetails, boolean success, ProceedingJoinPoint point, Exception exception, Object[] reqValue, Object respValue) {
+        if(null == userDetails){
+            userDetails = new UserDetailBO();
+            userDetails.setUsername("none");
+            userDetails.setName("none");
+        }
+        MethodSignature signature = (MethodSignature) point.getSignature();
+        Method method = signature.getMethod();
+        Audit audit = method.getAnnotation(Audit.class);
+        if (audit == null) {
+            return;
+        }
+        //求值上下文
+        StandardEvaluationContext context = new StandardEvaluationContext();
+        //设置参数进去
+        context.setVariables(resolveArgs(point));
+        //给定特定的参数
+        context.setVariable("loginUser", userDetails);
+        context.setVariable("returnObj", respValue);
+        context.setBeanResolver(BEAN_FACTORY_RESOLVER);
+        //构建log对象
+        AuditLog logParam = new AuditLog();
+        try {
+            logParam.setBizNo(resolveValue(audit.bizNo(), context));
+        } catch (Exception e) {
+            logParam.setBizNo(String.format("SPEL-ERROR bizNo[%s]失败,原因:%s", audit.bizNo(), e.getMessage()));
+        }
+        try {
+            logParam.setContent(resolveValue(audit.content(), context));
+        } catch (Exception e) {
+            logParam.setContent(String.format("SPEL-ERROR content[%s]失败,原因:%s", audit.content(), e.getMessage()));
+        }
+        logParam.setType(audit.type().getCode());
+        logParam.setCreateTime(new Date());
+        logParam.setMethod(method.getDeclaringClass().getName() + "#" + method.getName());
+        logParam.setSuccess(success);
+        logParam.setException(exception != null && !success ? ExceptionUtils.getStackTrace(exception) : null);
+        logParam.setReqValue(getReqValue(reqValue));
+        logParam.setRespValue(getRespValue(respValue));
+        logParam.setOperatorUserName(userDetails.getUsername());
+        logParam.setOperatorUserRealName(userDetails.getName());
+        logParam.setOperatorIp(userDetails.getCurrentIp());
+        logParam.setOwnerSys(appName);
+        log.info("--生成的审计日志:{}", JSON.toJSONString(logParam));
+        auditLogService.save(logParam);
+    }
+
+    /**
+     * 解析参数
+     *
+     * @param point
+     * @return
+     */
+    private LinkedHashMap<String, Object> resolveArgs(ProceedingJoinPoint point) {
+        MethodSignature signature = (MethodSignature) point.getSignature();
+        Method method = signature.getMethod();
+        Object[] args = point.getArgs();
+        String[] argNames = getArgNames(method);
+        LinkedHashMap<String, Object> params = new LinkedHashMap<>();
+        for (int i = 0; i < args.length; i++) {
+            params.put(argNames[i], args[i]);
+        }
+        return params;
+    }
+
+    /**
+     * 获取参数属性名
+     *
+     * @param method
+     * @return
+     */
+    private String[] getArgNames(Method method) {
+        ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
+        return parameterNameDiscoverer.getParameterNames(method);
+    }
+
+    private String resolveValue(String content, EvaluationContext context) {
+        String value;
+        if (content.contains(spElFlag)) {
+            //解析SPEL
+            value = resolveValueByExpression(content, context);
+        } else {
+            //不处理
+            value = content;
+        }
+        return value;
+    }
+
+    private String resolveValueByExpression(String spElString, EvaluationContext context) {
+        //构建表达式
+        Expression expression = parser.parseExpression(spElString);
+        //解析
+        return expression.getValue(context, String.class);
+    }
+
+    class AuditThread implements Runnable {
+        private UserDetailBO userDetails;
+        private boolean success;
+        private ProceedingJoinPoint point;
+        private Exception exception;
+        private Object[] reqValue;
+        private Object respValue;
+
+        public AuditThread(UserDetailBO userDetails, boolean success, ProceedingJoinPoint point, Exception exception, Object[] reqValue, Object respValue) {
+            this.userDetails = userDetails;
+            this.success = success;
+            this.point = point;
+            this.exception = exception;
+            this.reqValue = reqValue;
+            this.respValue = respValue;
+        }
+
+        @Override
+        public void run() {
+            try {
+                buildAuditLog(userDetails, success, point, exception, reqValue, respValue);
+            } catch (Exception e) {
+                log.info("--审计日志记录失败:", e);
+            }
+        }
+    }
+}

+ 49 - 0
src/main/java/com/szwl/constant/AuditEnum.java

@@ -0,0 +1,49 @@
+package com.szwl.constant;
+
+import com.baomidou.mybatisplus.annotation.IEnum;
+
+/**
+ * 审计日志类别
+ *
+ * @author PT-ZHOUYUHENG
+ * @date 2022-05-17 11:29
+ */
+public enum AuditEnum implements IEnum<String> {
+
+    INSERT("INSERT","新增"),
+    QUERY("QUERY","查询"),
+    UPDATE("UPDATE","修改"),
+    DELETE("DELETE","删除"),
+    LOGIN("LOGIN","登录"),
+    PAY_ORDER("PAY_ORDER", "支付订单");
+
+    private String code;
+
+    private String desc;
+
+    AuditEnum(String code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    @Override
+    public String getValue() {
+        return this.code;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+}

+ 23 - 0
src/main/java/com/szwl/controller/DemoController.java

@@ -6,20 +6,26 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.szwl.annotation.Audit;
+import com.szwl.constant.AuditEnum;
 import com.szwl.constant.ResponseCodesEnum;
 import com.szwl.feign.bean.PayFeign;
+import com.szwl.manager.TokenManager;
 import com.szwl.model.bo.R;
 import com.szwl.model.bo.ResponseModel;
+import com.szwl.model.bo.UserDetailBO;
 import com.szwl.model.entity.TAdmin;
 import com.szwl.model.query.TAdminParam;
 import com.szwl.service.TAdminService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import org.apache.commons.collections.map.HashedMap;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
+import java.util.Map;
 
 
 @Api(value = "/test", tags = {"测试接口"})
@@ -32,7 +38,24 @@ public class DemoController {
     TAdminService tAdminService;
     @Autowired
     PayFeign payFeign;
+    @Autowired
+    TokenManager tokenManager;
 
+    @ApiOperation(value = "测试根据token获取用户")
+    @GetMapping("/testGetLogin")
+    public ResponseModel<?> testGetLogin() {
+        UserDetailBO userDetailBO = tokenManager.getLoginUserDetails();
+        return R.ok(userDetailBO);
+    }
+    @ApiOperation(value = "测试审计日志功能")
+    @GetMapping("/testAuditLog")
+    @Audit(type = AuditEnum.QUERY,bizNo = "#returnObj.data.num",
+            content = "#loginUser.userRealName + '调用 测试审计日志功能'")
+    public ResponseModel<?> testAuditLog() {
+        Map reslut = new HashedMap();
+        reslut.put("num","111");
+        return R.ok(reslut);
+    }
     @ApiOperation(value = "调用feign")
     @GetMapping("/testFeign")
     public ResponseModel<?> testPage(String id) {

+ 3 - 0
src/main/java/com/szwl/controller/TAdminController.java

@@ -6,6 +6,8 @@ import cn.hutool.core.lang.UUID;
 import cn.hutool.core.util.IdUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.szwl.annotation.Audit;
+import com.szwl.constant.AuditEnum;
 import com.szwl.constant.ResponseCodesEnum;
 import com.szwl.exception.BizException;
 import com.szwl.manager.TokenManager;
@@ -117,6 +119,7 @@ public class TAdminController {
     }
     @ApiOperation(value = "登录")
     @PostMapping("/login")
+    @Audit(type = AuditEnum.LOGIN,content = "#username + '请求登录'")
     public ResponseModel<UserDetailBO> login(String username, String password) {
         if(StringUtils.isEmpty(username)||StringUtils.isEmpty(password)){
             return R.fail(ResponseCodesEnum.A0001,"参数有空");

+ 44 - 0
src/main/java/com/szwl/filter/HeadTokenInterceptor.java

@@ -0,0 +1,44 @@
+package com.szwl.filter;
+
+import com.szwl.constant.ResponseCodesEnum;
+import com.szwl.exception.BizException;
+import com.szwl.manager.TokenManager;
+import com.szwl.util.UrlUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Component
+@Slf4j
+public class HeadTokenInterceptor implements HandlerInterceptor {
+    @Autowired
+    private TokenManager tokenManager;
+    @Value("${permitAll.url:[]}")
+    private String[] permitAll;
+
+    //在Controller执行之前调用,如果返回false,controller不执行
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        String uri = request.getRequestURI();
+        log.info("preHandle uri:{}",uri);
+        if(tokenManager.checkToken(request)){
+            return true;
+        }
+        throw new BizException(ResponseCodesEnum.L0005);
+    }
+    //controller执行之后,且页面渲染之前调用
+    @Override
+    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
+    }
+    //页面渲染之后调用,一般用于资源清理操作
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+        tokenManager.removeThreadLocalUser();
+    }
+}

+ 44 - 0
src/main/java/com/szwl/filter/MyWebMvcConfigurer.java

@@ -0,0 +1,44 @@
+package com.szwl.filter;
+
+import cn.hutool.core.collection.CollUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
+
+import java.util.ArrayList;
+
+@Configuration
+public class MyWebMvcConfigurer extends WebMvcConfigurationSupport {
+    @Value("${swagger.url:[]}")
+    private String[] swaggerExcludes;
+    // 白名单
+    @Value("${permitAll.url:[]}")
+    private String[] permitAll;
+    @Autowired
+    private HeadTokenInterceptor headTokenInterceptor;
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        // 自定义去除的路径
+        String[] myExcludes= {"/tAdmin/login"};
+        registry.addInterceptor(headTokenInterceptor).addPathPatterns("/**")
+                // swagger
+                .excludePathPatterns(swaggerExcludes)
+                // 白名单
+                .excludePathPatterns(permitAll)
+                .excludePathPatterns(myExcludes);
+        super.addInterceptors(registry);
+    }
+    /**
+     * 添加静态资源
+     * @param registry
+     */
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
+        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
+        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
+    }
+}

+ 64 - 64
src/main/java/com/szwl/filter/TokenFilter.java

@@ -1,64 +1,64 @@
-package com.szwl.filter;
-
-import com.szwl.constant.ResponseCodesEnum;
-import com.szwl.manager.TokenManager;
-import com.szwl.model.bo.R;
-import com.szwl.util.ServletUtil;
-import com.szwl.util.UrlUtil;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.stereotype.Component;
-import org.springframework.web.filter.OncePerRequestFilter;
-
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.util.List;
-
-/**
- * @author PT-ZHOUYUHENG
- * @date 2021-11-01 15:59
- */
-@Component
-@Slf4j
-public class TokenFilter extends OncePerRequestFilter {
-
-    @Autowired
-    private TokenManager tokenManager;
-
-    @Value("${permitAll.url:[]}")
-    private String[] permitAll;
-
-    /**
-     * token不能为空
-     *
-     * @param httpServletRequest
-     * @param httpServletResponse
-     * @param filterChain
-     * @throws ServletException
-     * @throws IOException
-     */
-    @Override
-    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
-        String uri = httpServletRequest.getRequestURI();
-        log.info("--request uri: {}", uri);
-        //如果是忽略token的放行
-        if ( UrlUtil.matches(uri, permitAll)) {
-            log.info("--the request uri is ignore token");
-            filterChain.doFilter(httpServletRequest, httpServletResponse);
-            return;
-        }
-        //校验 token 是否合法
-        if(tokenManager.checkToken(httpServletRequest)){
-            filterChain.doFilter(httpServletRequest, httpServletResponse);
-            return;
-        }
-        //响应错误
-        log.info("--the request valid fail");
-        ServletUtil.renderJSON(httpServletResponse, R.fail(ResponseCodesEnum.L0005));
-    }
-}
+//package com.szwl.filter;
+//
+//import com.szwl.constant.ResponseCodesEnum;
+//import com.szwl.manager.TokenManager;
+//import com.szwl.model.bo.R;
+//import com.szwl.util.ServletUtil;
+//import com.szwl.util.UrlUtil;
+//import lombok.extern.slf4j.Slf4j;
+//import org.springframework.beans.factory.annotation.Autowired;
+//import org.springframework.beans.factory.annotation.Value;
+//import org.springframework.data.redis.core.RedisTemplate;
+//import org.springframework.stereotype.Component;
+//import org.springframework.web.filter.OncePerRequestFilter;
+//
+//import javax.servlet.FilterChain;
+//import javax.servlet.ServletException;
+//import javax.servlet.http.HttpServletRequest;
+//import javax.servlet.http.HttpServletResponse;
+//import java.io.IOException;
+//import java.util.List;
+//
+///**
+// * @author PT-ZHOUYUHENG
+// * @date 2021-11-01 15:59
+// */
+//@Component
+//@Slf4j
+//public class TokenFilter extends OncePerRequestFilter {
+//
+//    @Autowired
+//    private TokenManager tokenManager;
+//
+//    @Value("${permitAll.url:[]}")
+//    private String[] permitAll;
+//
+//    /**
+//     * token不能为空
+//     *
+//     * @param httpServletRequest
+//     * @param httpServletResponse
+//     * @param filterChain
+//     * @throws ServletException
+//     * @throws IOException
+//     */
+//    @Override
+//    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
+//        String uri = httpServletRequest.getRequestURI();
+//        log.info("--request uri: {}", uri);
+//        //如果是忽略token的放行
+//        if ( UrlUtil.matches(uri, permitAll)) {
+//            log.info("--the request uri is ignore token");
+//            filterChain.doFilter(httpServletRequest, httpServletResponse);
+//            return;
+//        }
+//        //校验 token 是否合法
+//        if(tokenManager.checkToken(httpServletRequest)){
+//            filterChain.doFilter(httpServletRequest, httpServletResponse);
+//            return;
+//        }
+//        //响应错误
+//        log.info("--the request valid fail");
+//        ServletUtil.renderJSON(httpServletResponse, R.fail(ResponseCodesEnum.L0005));
+//    }
+//}

+ 18 - 0
src/main/java/com/szwl/manager/TokenManager.java

@@ -1,5 +1,6 @@
 package com.szwl.manager;
 
+import cn.hutool.core.thread.threadlocal.NamedThreadLocal;
 import cn.hutool.core.util.StrUtil;
 import com.alibaba.fastjson.JSON;
 import com.szwl.constant.ConfigConsts;
@@ -15,6 +16,7 @@ import java.util.concurrent.TimeUnit;
 @Slf4j
 @Component
 public class TokenManager {
+    private static ThreadLocal<UserDetailBO> threadLocal = new NamedThreadLocal<>("user");
     @Autowired
     private RedisTemplate<String, String> redisTemplate;
 
@@ -30,12 +32,28 @@ public class TokenManager {
         }
         UserDetailBO details = getUserDetails(token);
         if (null!= details) {
+            threadLocal.set(details);
             return true;
         }
         //登陆凭证已过期或不可用
         log.info("--token {} is expired", token);
         return false;
     }
+
+    /**
+     * 获取登录用户
+     * @return
+     */
+    public UserDetailBO getLoginUserDetails() {
+        return threadLocal.get();
+    }
+
+    /**
+     * 清除 threadLocal
+     */
+    public void removeThreadLocalUser(){
+        threadLocal.remove();
+    }
     /**
      * 根据token查找认证信息
      *

+ 16 - 0
src/main/java/com/szwl/mapper/AuditLogMapper.java

@@ -0,0 +1,16 @@
+package com.szwl.mapper;
+
+import com.szwl.model.entity.AuditLog;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ *  Mapper 接口
+ * </p>
+ *
+ * @author wuhs
+ * @since 2022-06-16
+ */
+public interface AuditLogMapper extends BaseMapper<AuditLog> {
+
+}

+ 54 - 0
src/main/java/com/szwl/model/entity/AuditLog.java

@@ -0,0 +1,54 @@
+package com.szwl.model.entity;
+
+import java.util.Date;
+import java.io.Serializable;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 
+ * </p>
+ *
+ * @author wuhs
+ * @since 2022-06-16
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@ApiModel(value="AuditLog对象", description="审计日志")
+public class AuditLog implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private String id;
+
+    private String content;
+
+    private String bizNo;
+
+    private String type;
+
+    private String operatorUserName;
+
+    private String operatorUserRealName;
+
+    private Date createTime;
+
+    private String operatorIp;
+
+    private String method;
+
+    private String reqValue;
+
+    private String respValue;
+
+    private Boolean success;
+
+    private String exception;
+
+    private String ownerSys;
+
+
+}

+ 16 - 0
src/main/java/com/szwl/service/AuditLogService.java

@@ -0,0 +1,16 @@
+package com.szwl.service;
+
+import com.szwl.model.entity.AuditLog;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ *  服务类
+ * </p>
+ *
+ * @author wuhs
+ * @since 2022-06-16
+ */
+public interface AuditLogService extends IService<AuditLog> {
+
+}

+ 20 - 0
src/main/java/com/szwl/service/impl/AuditLogServiceImpl.java

@@ -0,0 +1,20 @@
+package com.szwl.service.impl;
+
+import com.szwl.model.entity.AuditLog;
+import com.szwl.mapper.AuditLogMapper;
+import com.szwl.service.AuditLogService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ *  服务实现类
+ * </p>
+ *
+ * @author wuhs
+ * @since 2022-06-16
+ */
+@Service
+public class AuditLogServiceImpl extends ServiceImpl<AuditLogMapper, AuditLog> implements AuditLogService {
+
+}

+ 134 - 0
src/main/java/com/szwl/util/SpringUtils.java

@@ -0,0 +1,134 @@
+package com.szwl.util;
+
+import org.springframework.aop.framework.AopContext;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * spring工具类 方便在非spring管理环境中获取bean
+ *
+ * @author ruoyi
+ */
+@Component
+public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware {
+    /**
+     * Spring应用上下文环境
+     */
+    private static ConfigurableListableBeanFactory beanFactory;
+
+    private static ApplicationContext applicationContext;
+
+    @Override
+    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+        SpringUtils.beanFactory = beanFactory;
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        SpringUtils.applicationContext = applicationContext;
+    }
+
+    public static ApplicationContext getApplicationContext() {
+        return SpringUtils.applicationContext;
+    }
+
+    /**
+     * 获取对象
+     *
+     * @param name
+     * @return Object 一个以所给名字注册的bean的实例
+     * @throws BeansException
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getBean(String name) throws BeansException {
+        return (T) beanFactory.getBean(name);
+    }
+
+    /**
+     * 获取类型为requiredType的对象
+     *
+     * @param clz
+     * @return
+     * @throws BeansException
+     */
+    public static <T> T getBean(Class<T> clz) throws BeansException {
+        T result = (T) beanFactory.getBean(clz);
+        return result;
+    }
+
+    /**
+     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
+     *
+     * @param name
+     * @return boolean
+     */
+    public static boolean containsBean(String name) {
+        return beanFactory.containsBean(name);
+    }
+
+    /**
+     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
+     *
+     * @param name
+     * @return boolean
+     * @throws NoSuchBeanDefinitionException
+     */
+    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
+        return beanFactory.isSingleton(name);
+    }
+
+    /**
+     * @param name
+     * @return Class 注册对象的类型
+     * @throws NoSuchBeanDefinitionException
+     */
+    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
+        return beanFactory.getType(name);
+    }
+
+    /**
+     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
+     *
+     * @param name
+     * @return
+     * @throws NoSuchBeanDefinitionException
+     */
+    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
+        return beanFactory.getAliases(name);
+    }
+
+    /**
+     * 获取aop代理对象
+     *
+     * @param invoker
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getAopProxy(T invoker) {
+        return (T) AopContext.currentProxy();
+    }
+
+    /**
+     * 获取当前的环境配置,无配置返回null
+     *
+     * @return 当前的环境配置
+     */
+    public static String[] getActiveProfiles() {
+        return applicationContext.getEnvironment().getActiveProfiles();
+    }
+
+    /**
+     * 获取当前的环境配置,当有多个环境配置时,只获取第一个
+     *
+     * @return 当前的环境配置
+     */
+    public static String getActiveProfile() {
+        final String[] activeProfiles = getActiveProfiles();
+        return activeProfiles.length > 0 ? activeProfiles[0] : null;
+    }
+}

+ 2 - 0
src/main/resources/application.yml

@@ -0,0 +1,2 @@
+swagger:
+  url: /v2/api-docs,/swagger-resources,/swagger-resources/**,/configuration/ui,/configuration/security,/swagger-ui.html/**,/webjars/**,/doc.html,/doc.html/**,/*.html,/**.html,/**/*.html,/**/*.css,/**/*.js,/sys/api/list,/sys/user/batch,/sys/org/batch

+ 12 - 6
src/main/resources/bootstrap.yml

@@ -54,12 +54,18 @@ spring:
     listener.simple.max-concurrency: 2000
 
   redis:
-    timeout: 5000
-    cluster:
-      nodes: 120.78.140.173:7000,120.78.140.173:7001,47.112.127.131:7001,47.112.127.131:7000,112.74.63.148:7001,112.74.63.148:7000
-      max-attempts: 5
-      max-redirects: 7
-      timeout: 12000
+    database: 0
+    host: localhost         # Redis服务器地址
+    port: 6379              # Redis服务器连接端口
+    password:               # Redis服务器连接密码(默认为空)
+
+#  redis:
+#    timeout: 5000
+#    cluster:
+#      nodes: 120.78.140.173:7000,120.78.140.173:7001,47.112.127.131:7001,47.112.127.131:7000,112.74.63.148:7001,112.74.63.148:7000
+#      max-attempts: 5
+#      max-redirects: 7
+#      timeout: 12000
 
 elasticSearch:
   host: 120.78.140.173

+ 1 - 1
src/test/java/com/szwl/AutoGeneratorTests.java

@@ -47,7 +47,7 @@ class AutoGeneratorTests {
 		strategyConfig
 //				.setCapitalMode(true)//设置全局大写命名
 				.setInclude(new String[]{
-						""
+						"audit_log"
 				})//只会生成该表
 				.setEntityLombokModel(true)//实体类生成之后自动添加lombok注解
 				.setNaming(NamingStrategy.underline_to_camel)//数据库表映射到实体的命名策略