Spring Cloud微服務安全實戰_3-6_API安全機制之審計


 

 

審計日志

定義:誰,在什么時間,干了什么事。

位置:認證之后,授權之前。

   這樣就知道是誰在訪問,拒絕掉的訪問也能被記錄。如果放在認證之前,那么就不知道是誰在訪問;如果放在授權之后,就沒辦法記錄被拒絕的訪問。

存儲:審計日志一定要持久化,記在數據庫里或者是文件,放在內存會丟失。(輸出到公司的日志服務)

怎么記:請求進來的時候記錄一次,請求出去的時候,更新日志。

    如果只在請求進來的時候記,那么請求的成功與否是不知道的。如果只在請求返回的時候記,那么如果一個請求把你的系統搞掛了,也沒有記,是不知道誰搞掛的。

技術選擇:過濾器 VS 攔截器  VS ControllerAdvice VS AOP

       過濾器,不好分辨是請求過來執行的還是請求出去執行的; ControllerAdvice-做全局異常處理 ;AOP -不說了

                 就用攔截器,攔截器在過濾器之后執行。

 限流過濾器用 @Order注解,控制在第一個執行

 

 認證過濾器,排在老二位置:

再寫個審計攔截器,就是過濾器之后執行了

實現

 

 

 

 

數據庫

 實體類:

/**
 * <p>
 * 審計日志
 * </p>
 *
 * @author 李浩洋
 * @since 2019-10-27
 */
@Data
public class AuditLog implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    /**
     * http方法
     */
    private String method;

    /**
     * 請求路徑
     */
    private String path;

    /**
     * http狀態碼
     */
    private Integer status;

    /**
     * 請求用戶名
     */
    private String username;

    /**
     * 創建時間
     */
    private Date createTime;

    /**
     * 修改時間
     */
    private Date updateTime;


}

審計攔截器:

package com.nb.security.interceptor;

import com.nb.security.entity.AuditLog;
import com.nb.security.entity.User;
import com.nb.security.service.IAuditLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

/**
 * 審計日志攔截器
 * 攔截流程
 * 流控 -- 認證 --審計 -- 授權 -- 業務
 * 審計要在進入接口之前,insert 數據庫(實際可能發送到專門的日志服務器),執行完后 update,過濾器不便於判斷攔截之前、之后,故用攔截器
 */
@Component
public class AuditLogInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private IAuditLogService auditLogService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

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

        User user = (User) request.getAttribute("user");
        if (user != null) {
            user.setUsername(user.getUsername());
        }
        auditLogService.save(log);

        //將審計日志的id傳給request,以便於請求處理完成后更新審計日志
        request.setAttribute("auditLogId", log.getId());

        return super.preHandle(request, response, handler);
    }


    /**
     * 請求處理成功失敗,都更新審計日志
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        //審計日志id
        Long auditLogId = (Long) request.getAttribute("auditLogId");
        AuditLog log = auditLogService.getById(auditLogId);
        log.setStatus(response.getStatus());
        log.setUpdateTime(new Date());
        auditLogService.updateById(log);

        super.afterCompletion(request, response, handler, ex);
    }
}

攔截器配置:

@Configuration
public class SecurityConfig implements WebMvcConfigurer {

    //審計日志
    @Autowired
    private AuditLogInterceptor auditLogInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(auditLogInterceptor);//.addPathPatterns();//先add的先執行,默認所有請求都攔截
    }
}

用Postman來一個正確的請求:

 

數據庫新增了一條數據

 

 

 控制台可以看到執行順序,就是想要的結果。

 

 錯誤的訪問:

 

 數據庫insert了兩條數據,一個是我的請求 /users/12 ,另一個 /error 是SpringBoot拋出異常后,會跳到一個/error 的路徑, 

 

 

 新建一個異常處理器:

 

 再發一個失敗的請求:

 

 數據庫就不會再有 /error 請求了。

代碼:https://github.com/lhy1234/springcloud-security/tree/master/nb-user-api

+++++++++++++++++++++分割線++++++++++++++++++++++++++++++

小結

本篇說了什么是審計,審計在代碼中的位置,以及用攔截器來實現審計

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM