5) 인증 저장소 필터 - SecurityContextPersistenceFilter 강의에서 clearContext() 로 remove 하는 데 어떻게 컨트롤러에서 조회가 가능한가요?
2565
작성한 질문수 1
SecurityContextPersistenceFilter 클래스 finally 구문에서 SecurityContext 를 remove 하는 데,
그러면 인증된 사용자 또한 SecurityContext 가 null 인데
어떻게 컨트롤러 단에서 SecurityContextHolder.getContext().getAuthentication(); 로 했을 때
null 이 아닌 이유가 궁금해서 여쭤봅니다
어디에서든 참조가능한 ThreadLocal 영역에 저장되기 때문에 SecurityContext 는 null 되도 상관없고
SecurityContextHolder.getContext() 는 ThreadLocal 에서 가져오기 때문인가요?
답변 1
3
해당 강의를 보시면 아시겠지만 SecurityContext 는 ThreadLocall 에 저장됩니다.
ThreadLocal 은 스레드마다 고유하게 할당되어 있는 저장소개념의 클래스입니다.
그리고 ThreadLocal 은 전략에 따라 SecurityContextHolder 클래스의 멤버 변수로 할당되어 있습니다.
Default 는 아래의 첫번째 전략으로 실행이 됩니다
public class SecurityContextHolder {
-- 중략
private static void initialize() {
if (!StringUtils.hasText(strategyName)) {
// Set default
strategyName = MODE_THREADLOCAL;
}
if (strategyName.equals(MODE_THREADLOCAL)) {
strategy = new ThreadLocalSecurityContextHolderStrategy();
}
else if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
}
else if (strategyName.equals(MODE_GLOBAL)) {
strategy = new GlobalSecurityContextHolderStrategy();
}
--중략
}
그래서 SecurityContextHolder.clearContext() 는 ThreadLocal 에 있는 SecurityContext 를 제거하는 역할입니다.
/**
* Explicitly clears the context value from the current thread.
*/
public static void clearContext() {
strategy.clearContext();
}
final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();
// ThreadLocal 에서 SecurityContext 제거
public void clearContext() {
contextHolder.remove();
}
이 상태에서 SecurityContextPersistenceFilter 가 하는 주된 역할은 사용자가 인증을 시도하게 되면 처음에 Authentication 이 null 상태인 SecurityContext 객체를 SecurityContextHolder 에 담아서 다음 필터로 전달하는 것입니다.
그리고 인증을 처리하는 필터인 UsernamePasswordAuthenticationFilter 에서 인증에 성공한 이후의 과정 중에 SecurityContextHolder 에 있는 SecurityContext 객체를 꺼내어 Authentication 객체를 저장하게 됩니다.
이후 클라이언트로 응답하는 시점에 SecurityContextPersistenceFilter 의 finally 구문을 거치게 되는데 이때 SecurityContextHolder.clearContext() 를 하게 됩니다.
여기서 중요한 포인트는 SecurityContextHolder.clearContext() 가 실행되기 전에 어떤 시점에서 SecurityContext 객체를 HttpSession 에 저장하는 처리를 먼저 하게 됩니다.
@Override
protected void saveContext(SecurityContext context) {
final Authentication authentication = context.getAuthentication();
HttpSession httpSession = request.getSession(false);
--중략
if (httpSession == null) {
httpSession = createNewSessionIfAllowed(context);
}
if (httpSession != null) {
// 세션에 SecurityContext 객체를 저장함
if (contextChanged(context)
|| httpSession.getAttribute(springSecurityContextKey) == null) {
httpSession.setAttribute(springSecurityContextKey, context);
if (logger.isDebugEnabled()) {
logger.debug("SecurityContext '" + context
+ "' stored to HttpSession: '" + httpSession);
}
}
}
}
그 처리는 HttpSessionSecurityContextRepository 클래스가 하고 있습니다.
그렇게 되면 SecurityContextPersistenceFilter 는 사용자가 인증 이후의 어떤 자원에 접근을 할 때 마다 SecurityContext 객체를 SecurityContextHolder 에 담는 역할을 하게 되는데 이 때 HttpSessioin 에 SecurityContext 가 저장되어 있는지를 조회하게 됩니다
private SecurityContext readSecurityContextFromSession(HttpSession httpSession) {
final boolean debug = logger.isDebugEnabled();
if (httpSession == null) {
if (debug) {
logger.debug("No HttpSession currently exists");
}
return null;
}
// 세션에 존재한다면 세션으로 부터 SecurityContext 객체를얻어옴
Object contextFromSession = httpSession.getAttribute(springSecurityContextKey);
if (contextFromSession == null) {
if (debug) {
logger.debug("HttpSession returned null object for SPRING_SECURITY_CONTEXT");
}
return null;
}
--중략
return (SecurityContext) contextFromSession;
}
그래서 세션안에 SecurityContext 가 존재하면 이 객체를 SecurityContextHolder 에 담고 세션에 존재하지 않으면 새롭게 생성해서 다음 필터로 전달하게 되는데 만약 이전에 인증을 성공했을 경우에는 세션에 SecurityContext 를 이미 저장했으므로 향후 모든 요청 시에는 세션에서 SecurityContext 를 꺼내어 SecurityContextHolder 에 담게 됩니다.
또한 이 SecurityContext 객체 안에는 인증 당시 저장했던 Authentication 객체도 저장되어 있습니다.
그래서 매 요청마다 세션에 저장된 SecurityContext 객체를 꺼내어서 새롭게 주어지는 요청 스레드의 ThreadLocal 에 다시 담기 때문에 응답하기 전에 SecurityContextHolder.clearContext() 를 실행시키는 것은 정상적인 과정이며 문제가 되지 않습니다.
그리고 기억하실 점은 사용자가 요청을 할 때 마다 요청 스레드 객체는 매번 달라질 것이므로 메모리 누수가 생기지 않도록 현재 요청 스레드 객체의 ThreadLocal 에 저장된 SecurityContext 객체는 최종 응답전에 remove 해 주어야 합니다.
즉 SecurityContextHolder.clearContext() 처리를 해 주는 것이 맞습니다.
제가 강의에 이 부분에 대해서 설명하고 있으니 다시 한번 해당 강좌를 참고해 주시기 바랍니다
감사합니다.
0
제가 컨트롤러에 임의로 SecurityContextHolder에 새로운 authentication을 넣었더니 로그인 된 세션이 없어졌습니다. (로그아웃된 상태가 되었다.) 이건 어떻게 된 일인지 모르겠습니다.
응답이 다 끝나면 SecurityContextHolder를 clear 해줄태고 Session은 건든 것이 없으니 변화가 없어야 한다고 생각했는데 로그아웃이 된 이유를 모르겠습니다. (어떤 부분에서 이런 작용이 된 것인지 궁금합니다.)
시큐리티 공부 버전 질문
0
175
1
[해결 방법] MethodSecurityConfig.customMethodSecurityMetadataSource() 호출하지 않는 이슈
0
186
1
AbstractSecurityInterceptor.class.beforeInvocation()를 2번 실행하는 경우
0
174
1
강의 코드가 왜이렇게 뒤죽박죽인가요...
0
249
1
메인 페이지로 접속해도 login url로 리다이렉트가 되지 않습니다..
0
235
1
파라미터값이 넘어가지 않습니다 ....
0
374
1
security filterChain 설정 질문이 있습니다.
0
331
1
소스 부분 질문 드립니다.
0
208
2
섹션4 7번 강의 문제가 있는거 같네요.
0
344
2
파일이 수시로 이름이 바껴있네요 ㄷㄷ
0
304
1
HttpSessionSecurityContextRepository를 사용안하는 문제
0
555
2
error , exception 이 잘 안됩니다.
0
280
2
thymeleaf tag 질문합니다.
0
196
2
버전업하면서 deprecated된 것들이 너무많아요
0
478
1
spring security 패치 관련
0
437
1
모바일을 사용할때 토큰말고 세션
0
845
2
DB 연동한 인가 부분에 대한 질문입니다!
0
264
1
Ajax방식도 똑같이 Session방식을 사용하는건가요?
0
307
1
Config 파일 생성 시 질문이 있습니다.
0
225
1
강사님 몇일동안 구글 검색만 100개 했는데도 이유를 모르겠습니다..
1
429
2
403 에러 뜹니다.
0
813
2
login_proc의 존재에 대한 간략한 설명입니다
0
276
1
top.html에 로그인 링크를 만들어서 로그인을 해봤습니다
0
282
2
안녕하세요. DB에 저장될 때 이해 안 가는 값이 있어서 질문드립니다!
0
189
1





