Spring Security의 인가
인가는 인증된 사용자가 특정 리소스에 접근이 가능한지를 결정하는 과정이다. Spring Security에서의 인가는 인증 정보를 담은 Authentication의 authorities(역할)을 통해 접근이 가능한지를 결정한다.
Spring Security는 인증을 위한 필터로 AuthorizationFilter를 제공한다. AuthorizationFilter를 사용하려면, 다음과 같이 authorizeHttpRequests() 메소드를 통해 등록하면 된다.
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authHttp ->
authHttp.requestMatchers(POST, "/join", "/login").permitAll())
...
return http.build();
}
실행 흐름과 주요 모듈
- 먼저 AuthorizationFilter는 SecurityContextHolder에서 Authentication 객체를 얻어서 Authentication과 HttpServletRequest를 AuthorizationManager의 check() 메소드로 넘긴다.
- AuthorizationManager의 check() 메서드는 AuthorizationDecision을 반환한다.
- AuthorizationFilter는 반환받은 AuthorizationDecision을 검증하며 적절한 권한인지 확인한다.
- 만약 검증에 실패하면, AccessDeniedException 예외를 발생시킨다.
- 만약 검증에 성공하면, 적절한 권한인 것이므로 다음 FilterChain을 이어간다.
AuthorizationFilter
public class AuthorizationFilter extends GenericFilterBean {
...
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
if (this.observeOncePerRequest && isApplied(request)) {
chain.doFilter(request, response);
return;
}
if (skipDispatch(request)) {
chain.doFilter(request, response);
return;
}
String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
try {
// AuthorizationManager의 check() 메소드에 인증 정보인 Authentication과 HttpServletRequest 객체를 전달
// 이를 통해 AuthorizationDecision 객체를 얻어옴
AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, request);
this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, request, decision);
// !decision.isGranted()이면 AccessDeniedException 예외 발생
if (decision != null && !decision.isGranted()) {
throw new AccessDeniedException("Access Denied");
}
chain.doFilter(request, response);
}
finally {
request.removeAttribute(alreadyFilteredAttributeName);
}
}
...
}
AuthorizationManager
AuthorizationManager 인터페이스에는 두 가지 메서드(verify(), check())가 있다. verify 메서드는 내부적으로 check 메서드를 호출하고, check 메서드는 AuthorizationDecision을 반환한다.
@FunctionalInterface
public interface AuthorizationManager<T> {
default void verify(Supplier<Authentication> authentication, T object) {
AuthorizationDecision decision = check(authentication, object);
if (decision != null && !decision.isGranted()) {
throw new AccessDeniedException("Access Denied");
}
}
@Nullable
AuthorizationDecision check(Supplier<Authentication> authentication, T object);
}
AuthorizationDecision
AuthorizationDecision은 생성자로 boolean granted를 넣어서 생성하고, isGranted() 메서드를 통해 인증 여부를 확인할 수 있다. 즉, AuthorizationManager의 check() 메서드를 통해 반환된 AuthorizationDecision을 verify() 메서드에서 검증하여 검증에 실패하면 AccessDeniedException 예외를 발생시킨다.
public class AuthorizationDecision {
private final boolean granted;
public AuthorizationDecision(boolean granted) {
this.granted = granted;
}
public boolean isGranted() {
return this.granted;
}
@Override
public String toString() {
return getClass().getSimpleName() + " [granted=" + this.granted + "]";
}
}
Reference
'Backend > Spring Security' 카테고리의 다른 글
SecurityFilterChain에 필터를 등록할 때 주의할 점(WAS의 FilterChain과 Spring Security의 SecurityFilterChain의 차이) (0) | 2024.01.22 |
---|---|
permitAll 설정의 의미 (0) | 2024.01.22 |
Spring Security의 예외 처리 방식 (0) | 2024.01.22 |
Spring Security의 인증(Authentication) (0) | 2024.01.21 |
SecurityFilterChain (0) | 2024.01.21 |