API 安全机制 | 审计日志

2020-07-12  本文已影响0人  乌鲁木齐001号程序员

审计日志

Spring 的拦截机制

Spring 的拦截机制.png

Filter 和 Intercepor 的区别

审计日志 | 实现思路

审计类

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;
    }

}
上一篇下一篇

猜你喜欢

热点阅读