美文网首页
API 安全机制 | 审计日志

API 安全机制 | 审计日志

作者: 乌鲁木齐001号程序员 | 来源:发表于2020-07-12 16:26 被阅读0次

审计日志

  • 审计在认证之后,授权之前;
  • 在认证之后,才知道是谁;
  • 在授权之前,才能记录下被授权机制拒绝的请求;
  • 审计日志一定要持久化,数据库或文件中,一般会直接发到统一的的日志服务上,由日志服务去记日志;
  • 审计日志要做两次,一次是在进入的时候,一次是在出去的时候;

Spring 的拦截机制

Spring 的拦截机制.png
  • Filter 不是 Spring 的机制,而是 Servlet 规范中定义的;
  • ControllerAdvice 一般做全局的异常处理;

Filter 和 Intercepor 的区别

  • Filter 对请求进入业务逻辑之前和之后的拦截是在一个方法 doFilter 中完成的,如果下游的业务方法在执行的时候出现异常,在 Filter 中是拦截不到的;
  • Interceptor 对请求进入业务逻辑之前和之后的拦截是在多个方法 preHandle、postHandle、afterCompletion 中完成的,如果在下游的业务方法在执行的时候出现异常,可以在 aflterCompletion 中拦截到;
  • 所有的 Filter 都在 Interceptor 之前执行;

审计日志 | 实现思路

  • 先确定审计对象,也就是表结构,因为这里的方案是将审计日志记录在数据库中;
  • 再写一个拦截器,在请求的前后,向审计日志保存在数据库中;
  • 完了把拦截器添加到 Spring 的上下文中;
  • 在配置类上启用 @EnableJpaAuditing 注解,在审计类上启用 @CreatedBy 注解,并创建一个为 @CreatedBy 服务的 Bean,用来获取当前修改数据的人;
  • 通过 @RestControllerAdvice 标注的类,解决在业务方法执行异常的时候,会重定向到 /error,从而导致 2 条审计记录的产生;

审计类

package com.lixinlei.security.api.entity;


import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import lombok.Data;

@Entity
@Data
@EntityListeners(AuditingEntityListener.class)
public class AuditLog {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Temporal(TemporalType.TIMESTAMP)
    @CreatedDate
    private Date createdTime;

    @Temporal(TemporalType.TIMESTAMP)
    @LastModifiedDate
    private Date modifyTime;

    @CreatedBy
    private String username;

    private String method;

    private String path;

    private Integer status;

}

审计日志拦截器

package com.lixinlei.security.api.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.lixinlei.security.api.entity.AuditLog;
import com.lixinlei.security.api.dao.AuditLogRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

@Component
public class AuditLogInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private AuditLogRepository auditLogRepository;

    /**
     * 在业务方法执行之前执行
     * postHandle 是在业务方法执行成功之后执行
     * @param request
     * @param response
     * @param handler
     * @return 如果返回 false,就可以拒绝请求的执行,直接返回
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        System.out.println(3);

        AuditLog log = new AuditLog();
        log.setMethod(request.getMethod());
        log.setPath(request.getRequestURI());

        // 保存日志
        auditLogRepository.save(log);

        request.setAttribute("auditLogId", log.getId());
        return true;
    }

    /**
     * 业务方法处理成功与否都会执行
     * @param request
     * @param response
     * @param handler
     * @param ex 如果业务方法处理成功,就不会有这个异常
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex)
            throws Exception {

        Long auditLogId = (Long) request.getAttribute("auditLogId");
        AuditLog log = auditLogRepository.findById(auditLogId).get();

        // 就算业务方法执行失败,这里的 status 还是 200,然后会跳到一个 /error 的路径,这个的 status 是 500
        log.setStatus(response.getStatus());

        // 更新日志
        auditLogRepository.save(log);
    }

} 

添加拦截器到 Spring 中

package com.lixinlei.security.api.config;

import java.util.Optional;

import com.lixinlei.security.api.interceptor.AuditLogInterceptor;
import com.lixinlei.security.api.vo.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
// 这个注解和 @EntityListeners(AuditingEntityListener.class) 没有的话,@CreatedBy 就不知道把谁注入到其标注的属性中
@EnableJpaAuditing
public class SecurityConfig implements WebMvcConfigurer {

    @Autowired
    private AuditLogInterceptor auditLogInterceptor;

    /**
     * 先添加的拦截器先生效
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(auditLogInterceptor);
    }

    /**
     * 这个 Bean 是用来让 @CreatedBy 知道是谁修改了数据
     * @CreatedBy 标注的属性的类型是 String,这里 AuditorAware 的泛型就用 String
     * @return
     */
    @Bean
    public AuditorAware<String> auditorAware() {
        return new AuditorAware<String>() {
            @Override
            public Optional<String> getCurrentAuditor() {
                ServletRequestAttributes servletRequestAttributes
                        = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
                UserInfo info = (UserInfo)servletRequestAttributes.getRequest().getSession().getAttribute("user");
                String username = null;
                if(info != null) {
                    username = info.getUsername();
                }
                return Optional.ofNullable(username);
            }
        };
    }

}

@RestControllerAdvice

package com.lixinlei.security.api.controller.advice;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import lombok.extern.slf4j.Slf4j;

@RestControllerAdvice
@Slf4j
public class ErrorHandler {

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public Map<String, Object> handle(Exception ex){
        log.error("system error", ex);
        Map<String, Object> info = new HashMap<>();
        info.put("message", ex.getMessage());
        info.put("time", new Date().getTime());
        return info;
    }

}

相关文章

  • 五、API安全机制-审计(日志)

    源码下载 一、API安全机制-审计(日志)API安全机制.png 审计日志应该在认证处理之后,这样我们就知道谁在发...

  • API 安全机制 | 审计日志

    审计日志 审计在认证之后,授权之前; 在认证之后,才知道是谁; 在授权之前,才能记录下被授权机制拒绝的请求; 审计...

  • 八、API安全机制-数据库层安全策略

    源码下载 一、API安全机制-数据库层安全策略API安全机制.png 在前面说的API安全机制包含业务逻辑外(流控...

  • 四、API安全机制-认证

    源码下载 一、API安全机制-认证API安全机制.png 认证过滤器

  • 日志审计系统的基本原理

    日志审计系统简介 什么是日志审计? 综合日志审计平台,通过集中采集信息系统中的系统安全事件、用户访问记录、系统运行...

  • 六、API安全机制-访问控制(授权)

    源码下载 一、API安全机制-访问控制 API安全机制.png 访问控制 访问控制算是API业务层的安全策略,为了...

  • 阿里大师带你详解API接口安全

    API安全机制 为什么要保证API安全 接口的安全性主要围绕Token、Timestamp和Sign三个机制展开设...

  • 三、API安全机制-流控

    源码下载 一、API安全机制-流控API安全机制.png controller 流控过滤器 流控效果请求次数太多.png

  • rsyslog日志管理

    一:日志的重要性 日志分类:系统日志,进程日志,应用程序日志记录日志的用处:排错,追溯事件,统计流量,审计安全行为...

  • API安全机制

    为什么要保证API安全 防止别人随便调用你的api 保证传输数据的安全 设计签名 防止别人调用你的API其实并不难...

网友评论

      本文标题:API 安全机制 | 审计日志

      本文链接:https://www.haomeiwen.com/subject/gubgcktx.html