AdSense

網頁

2020/4/10

Spring AOP對Spring Security logout做切點

使用Spring AOP對Spring Security logout做切點的方式如下。

通常Spring專案會利用Spring AOP Aspect對Controller的請求做log(日誌)紀錄,不過Spring Security登出時預設是呼叫/logout API並透過SecurityContextLogoutHandler來處理登出邏輯,或透過自訂的登出處理器(logout handler)處理一些額外邏輯。若要用AOP對登出動作做log紀錄可對自訂登出處理器的方法設定Aspect切入點來達成。


範例環境如下:

  • Java 1.8
  • IntelliJ IDEA 2019.2.1(Community Edition)
  • Spring Boot 2.2.1.RELEASE
  • Maven

本範例的pom.xml依賴設定。

pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

設定的自訂登出處理器DemoLogoutHandler如下。

DemoLogoutHandler

package com.abc.demo.config.security;

import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.stereotype.Component;

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

/** 自訂Spring Security 登出處理器 */
@Component
public class DemoLogoutHandler implements LogoutHandler {

    @Override
    public void logout(
            HttpServletRequest httpServletRequest,
            HttpServletResponse httpServletResponse,
            Authentication authentication) {

        System.out.println("DemoLogoutHandler.logout()...");

    }
}

用來切向自訂登出處理器的的AOP Aspect類別DemoLogAspect設定如下。@Pointcut的切點為com.abc.demo.config.security.DemoLogoutHandler.logout(..)。因此當發出請求給/logout進行登出時,會先經過此切點並執行beforeLogout(JoinPoint joinPoint)

DemoLogAspect

package com.abc.demo.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class DemoLogAspect {

    @Pointcut("execution(* com.abc.demo.config.security.DemoLogoutHandler.logout(..))")
    public void logoutPointcut() {
    }

    @Before("logoutPointcut()")
    public void beforeLogout(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            System.out.println(arg.getClass().getName());
        }

        final String username = SecurityContextHolder.getContext().getAuthentication().getName(); // 取得Spring Security登入者名稱
        System.out.println("username:" + username + " logout start...");

    }

}

執行登出後會在console印出以下結果。

org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest
org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterResponse
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
username:user logout start...
DemoLogoutHandler.logout()...

參考:

沒有留言:

AdSense