[spring security 6] SecurityContext

rejs·2024년 6월 6일

스프링시큐리티6

목록 보기
3/3

SecurityContext

ThreadLocal에 저장된다. ThreadLocal은 해당 Thread만 접근 가능한 공간이다. (Thread ID를 key로 하는 Map이라고 생각하자)

기본적으로 다른 Thread의 ThreadLocal에 접근할 수 없기 때문에, 동시성 문제로부터 안전하다. 그러나 ThreadPool을 사용하는 경우 이전의 ThreadLocal에 저장해둔 SecurityContext가 남아있을 수 있으니, Thread가 사용자의 요청을 처리한 뒤 SecurityContext를 삭제할 필요가 있다.

SecurityContext는 현재 사용자의 Authentication 객체를 저장한다.

SecurityContextHolder

SecurityContext를 저장한다

SecurityContextHolderStrategy

SecurityContext(SecurityContextHolder)의 저장전략은 3가지가 있다

모드소개
MODE_THREADLOCAL기본적인 스레드로컬이다
MODE_INHERITABLETHREADLOCAL부모스레드의 스레드 로컬을 자식스레드에서 접근할 수 있다.(ThreadLocal과 InheritableThreadLocal객체를 참조하다.)
MODE_GLOBAL전역변수로 저장

이 저장전략을 지원하기 위해 SecurityContextHolderStrategy인터페이스를 지원한다.

(MODE_PRE_INITIALIZED는 찾아봐야겠다)

/* AuthorizationFilter에서 */
    private Authentication getAuthentication() {
    	/* ## SecurityContext 가져오기 ## */
        Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
        if (authentication == null) {
            throw new AuthenticationCredentialsNotFoundException("An Authentication object was not found in the SecurityContext");
        } else {
            return authentication;
        }
    }
/* SecuirtyContextHolderFilter에서 */
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
    if (request.getAttribute(FILTER_APPLIED) != null) {
        chain.doFilter(request, response);
    } else {
        request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
        Supplier<SecurityContext> deferredContext = this.securityContextRepository.loadDeferredContext(request);
    try {
        this.securityContextHolderStrategy.setDeferredContext(deferredContext);
        chain.doFilter(request, response);
    } finally {
    	/* ## SecurityContext 삭제 ## */
        this.securityContextHolderStrategy.clearContext();
        request.removeAttribute(FILTER_APPLIED);
    }

}

}


구버전과 다르게 SecurityContext를 가져올때 SecurityContextHolderStrategy를 사용하는 모습을 볼 수 있다. 


# SecurityContextRepository
사용자의 인증 정보를 유지하기 위해 사용되는 클래스이다. 

사용자의 인증, 권한은 SecurityContext에 저장된다.

SecurityContext는 SecurityContextRepoistory에 의해서 저장된다. 

## SecurityContextRepository의 메서드
> [Spring docs - SecurityContext](https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/context/SecurityContextRepository.html)

|메서드 | 설명|
|---|---|
|containsContext(HttpServletRequest request)||
|**loadDeferredContext(HttpServletRequest request)**|`loadContext` 대신 이메서드를 사용해야한다|
|saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response)||

## SecurityContextRepository의 구현체
|구현객체|설명|
|:---:|:---|
|HttpSessionSecurityContextRepository|session에 저장한다|
|RequestAttributeSecurityContextRepository|ServletRequest에 저장한다. 즉 다음 요청때에는 인증정보가 남지 않는다. (Basic 인증 등)|
|NullSecurityContextRepository |아예 저장하지 않는다.|
|DelegatingSecurityContextRepository |  |

# SecurityContextHolderFilter
SecurityContextHolder에 SecurityContext 저장해서 넣어주는 필터이다. 

스프링 시큐리티의 필터들 중에서 먼저 실행되는 필터이다. 왜냐하면 사용자의 인증정보를 먼저 불러와야 그 이후의 보안처리가 가능하기 때문이다. 

> **인증정보가 없는 경우**
스프링 시큐리티에서는 인증받지 않는 사용자에게 `익명 사용자`라는 토큰을 부여하는데, SecurityContextHolderFilter에서 인증정보가 없으면 SecurityContext에서 `익명사용자` 정보를 넣어준다. 이후 필터들에서 SecurityContext가 null인 것보다 `익명사용자` 토큰이 들어있는 것이 보다 처리하기 유용하기 때문이다. 

SecurityContext를 저장하지 않는다. 구 버전의 SecurityContextPersistenceFilter는 알아서 저장까지 해주었지만, 이후에 나올 각 인증 시스템(UsernamePasswordAuthenticationFilter 등)에 의해서 저장할 수 있도록 변경되었다.

SecurityContextHolder는 ThreadLocal에 저장된다. 즉 ThreadPool을 사용한다면, 이전 요청을 보낸 클라이언트의 인증정보가 남아있게 된다. 그러므로 SecurityContextHolderFilter는 SecurityContextHolder에 SecurityContext를 삭제한다. (필터의 실행순서는 `요청 -> A필터 -> 서블릿 -> A필터 -> 응답`이다)

0개의 댓글