zoukankan      html  css  js  c++  java
  • API安全(八)-审计

    1、审计所在安全链路的位置,为什么

      如图所示,审计应该做在认证之后,授权之前。因为只有在认证之后,我们在记录日志的时候,在知道请求是那个用户发过来的;做在授权之前,哪些请求被拒绝了,在响应的时候,也可以把它记录下来。如果放到授权之后 ,那么被拒绝的请求就不能记录了。

      审计日志一定要持久化,方便我们对问题的追溯,可以把它放到数据库中,也可以写到磁盘中。实际工作中,一般会发送到公司统一的日志服务上,由日志服务来存储。

    2、审计采用的组件,及安全链路顺序的保障

      首先,我们来明确一下各组件在请求中的执行顺序,如下图,依次是 Filter -> Interceptor -> ControllerAdvice -> AOP -> Controller

      对于Filter之间,我们可以使用@Order注解来确定执行顺序;对于Interceptor之间根据注册的先后顺序执行。这里我们的审计功能选择Filter和Interceptor都可以,根据自己的喜好即可。

    3、实现审计功能

      3.1、审计日志类及持久层接口

    /**
     * 审计日志
     *
     * @author caofanqi
     * @date 2020/1/28 22:55
     */
    @Data
    @Entity
    @Table(name = "audit_log")
    @EntityListeners(value = AuditingEntityListener.class)
    public class AuditLogDO {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        private String httpMethod;
    
        private String path;
    
        private Integer httpStatus;
    
        @CreatedBy
        private String username;
    
        @CreatedDate
        private LocalDateTime requestTime;
    
        @LastModifiedDate
        private LocalDateTime responseTime;
    
        private String errorMessage;
    
    }
    /**
     * 审计日志Repository
     * @author caofanqi
     * @date 2020/1/28 23:13
     */
    public interface AuditLogRepository extends JpaRepositoryImplementation<AuditLogDO,Long> {
    }

      3.2、开启JPA审计功能配置

    /**
     * JPA相关配置
     *
     * @author caofanqi
     * @date 2020/1/29 1:13
     */
    @Configuration
    @EnableJpaAuditing
    public class JpaConfig {
    
        /**
         * 获取当前登陆用户
         */
        @Bean
        public AuditorAware<String> auditorAware() {
            return () -> {
                HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
                UserDO user = (UserDO) request.getAttribute("user");
                if (user != null) {
                    return Optional.of(user.getUsername());
                } else {
                    return Optional.of("anonymous");
                }
            };
        }
    
    }

      此处不懂的,可以去看我写的JPA文章: https://www.cnblogs.com/caofanqi/p/11996718.html

      3.3、基于Filter实现审计功能 AuditLogFilter,流控过滤器设置@Order(1)、认证过滤器设置@Order(2)

    /**
     * 审计过滤器
     *
     * @author caofanqi
     * @date 2020/1/29 0:08
     */
    @Slf4j
    @Order(3)
    @Component
    public class AuditLogFilter extends OncePerRequestFilter {
    
    
        @Resource
        private AuditLogRepository auditLogRepository;
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    
            log.info("++++++3、审计++++++");
    
            AuditLogDO auditLogDO = new AuditLogDO();
            auditLogDO.setHttpMethod(request.getMethod());
            auditLogDO.setPath(request.getRequestURI());
            //放入持久化上下文中,供异常处理使用
            auditLogRepository.save(auditLogDO);
            request.setAttribute("auditLogId",auditLogDO.getId());
    
            // 执行请求
            filterChain.doFilter(request,response);
    
            // 执行完成,从持久化上下文中获取,并记录响应信息
            auditLogDO = auditLogRepository.findById(auditLogDO.getId()).get();
            auditLogDO.setHttpStatus(response.getStatus());
    
            auditLogRepository.save(auditLogDO);
    
        }
    
    }

      3.4、异常处理ControllerAdvice

        /**
         *
         * @param e 系统异常
         * @return 系统异常及时间
         */
        @ExceptionHandler
        @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
        public Map<String,Object> exceptionHandler(Exception e){
            /*
             *  如果有异常的化,将审计日志取出,记录异常信息
             */
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            Long auditLogId = (Long) request.getAttribute("auditLogId");
            AuditLogDO auditLogDO = auditLogRepository.findById(auditLogId).orElse(new AuditLogDO());
            auditLogDO.setErrorMessage(e.getMessage());
            auditLogRepository.save(auditLogDO);
    
            Map<String, Object> info = Maps.newHashMap();
            info.put("message", e.getMessage());
            info.put("time", LocalDateTime.now());
            return info;
        }

      3.5、启动项目,进行测试,访问http://127.0.0.1:9090/users/40,并填写正确的用户名密码

      执行顺序如下

      数据库审计日志表

       准备一个有错误的方法

        @DeleteMapping("/{id}")
        public void delete(@PathVariable Long id){
            int i = 1 / 0 ;
        }

      测试如下:

      数据库审计日志表

       3.6、如果想基于Interceptors来实现,做如下修改

        3.6.1、AuditLogInterceptor拦截器

    /**
     * 基于Interceptor的审计拦截器 ,与AuditLogFilter同时只能使用一个
     *
     * @author caofanqi
     * @date 2020/1/28 23:12
     */
    @Slf4j
    @Component
    public class AuditLogInterceptor extends HandlerInterceptorAdapter {
    
    
        @Resource
        private AuditLogRepository auditLogRepository;
    
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    
            log.info("++++++3、审计++++++");
    
            AuditLogDO auditLogDO = new AuditLogDO();
            auditLogDO.setHttpMethod(request.getMethod());
            auditLogDO.setPath(request.getRequestURI());
    
            auditLogRepository.save(auditLogDO);
    
            request.setAttribute("auditLogId",auditLogDO.getId());
    
            return true;
        }
    
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex){
            Long auditLogId = (Long) request.getAttribute("auditLogId");
    
            AuditLogDO auditLogDO = auditLogRepository.findById(auditLogId).orElse(new AuditLogDO());
            auditLogDO.setHttpStatus(response.getStatus());
    
            auditLogRepository.save(auditLogDO);
    
        }
    
    }

      3.6.2、注册拦截器

    /**
     * web配置类
     *
     * @author caofanqi
     * @date 2020/1/28 22:32
     */
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
    
        @Resource
        private AuditLogInterceptor auditLogInterceptor;
    
        /**
         * 注册拦截器
         */
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(auditLogInterceptor);
        }
    
    }

      3.6.3、进行3.5的测试效果相同

    项目源码:https://github.com/caofanqi/study-security/tree/dev-auditing

     

  • 相关阅读:
    weblogic 正常启动页面还是404
    oracle awr 生成
    jre 修改timezone 夏令时冬令时问题
    apache 2.4 配置loadbalance
    plsq 调试存储过程
    Windows怎么命令行修改文件权限
    Windows上面挂载NFS共享
    linux sar命令详解
    Tomcat Connector connectionTimeout含义和验证
    c++STL系列之Set
  • 原文地址:https://www.cnblogs.com/caofanqi/p/12239473.html
Copyright © 2011-2022 走看看