인프런 백기선님의 스프링 시큐리티 기초 강의 정리 내용입니다. 제가 보려고 해놨어요 😀
어렵다..
Principal : 어떤 어플리케이션에서 인증을 거치고 나면, 인증된 사용자 정보를 스프링에서 의미.
SecurityContextHolder
SecurityContext를 제공. 기본적으로 ThreadLocal을 사용
Thread Local 이란 한 쓰레드 내에서 쉐어하는 저장소. 메서드 파라미터를 쓰지 않더라고, 한 쓰레드에서 공용으로 저장하는 객체이기 때문에, 파라미터를 넘기지 않더라도 데이터에 접근이 가능. 즉, authentication을 한 쓰레드 내에서 공유할 수 있음.
결론적으로 SecurityContextHolder가 authentication을 담고 있는데, 얘는 ThreadLocal을 사용하기 때문에 이 정보는 어플리케이션 어디에서나 접근 할 수 있지. 만약 쓰레드가 달라진다면, 접이 불가능하겠지?
그렇다면, 시큐리티컨텍스트홀더가 제공하는 다른 전략을 써야해
보통은 웹 어플리케이션을 만들고, 서블릿 기반이라면, 요청이 들어왔을 때 요청이 처리되는 쓰레드는 명시적으로 async한 기능을 쓰지 않는 이상 동일한 쓰레드에서 처리하게 되니까. Thread per Request. 서블릿 컨텐이너 관련. 톰캣이 가지고 있는 앞단의 커넥터가 처리하는 거고. 최종적으로 어플리케이션에 요청이 들어와서 처리할 때는 대부분의 경우 하나의 쓰레드가 처리한다.
무튼, authenticartion을 넘겨주지 않아도 시큐리티컨텍스트홀더를 통해서 접근이 가능하다.
authentication의 경우도 여러 구현체가 있는데 폼인증의 경우, 유저네임패스워트어썬티케이션토큰이라는 객체가 최종적으로 시큐리티컨텍스트 홀더 안에 authentication 으로 담김
어썬티케이션에서 principal을 꺼낼 수 있는데 지금 보면 유저 타입임. 우리가 유저 디테일즈라는 패키지에서 제공하는 유저라는 객체 유저 디테일즈 서비스의 구현체에서 리턴한 타입이 곧 Principal.
이 롤 정보 역시 유저 디테일즈 서비스 구현할 때 넘겨준 거. 심플그랜티트어써리티에서 principal이 가지고 있는 권한을 나타냄.
User.build().roles(u.getRole()) 여기서 _를 붙여준거임.
principal
GrantAuthority
UserDeytailsService
public interface AuthenticationManager {
//폼인증의 경우, 유저 네임, 유저 비밀번호를 받는걸로 이해.
Authentication authenticate(Authentication authentication) throws AuthenticationException
//프린서펄을 담고 있는 Authentication 객체를 리턴.
}
이거의 구현체로는 ProviderManager를 가장 많이 씀. 여기서 구현이 이루어짐.
인증의 과정!
for(AuthenticationProvider provider : getproviders() ) {
if(!provider.supports(toTest)) {
continue;
}
….
}
-현재 우리가 넘겨주는 authentication 객체는 우저네임패스워드어썬티케이션토큰 타입임…
그래서, Authentication에서 만든 authentication이 언제 SecurityContextHolder에 ThreadLocal로 들어가는데? 에 관한
총 두개의 친구, UsernamePasswordAuthenticationFileter, SecurityContextPersistenceFileter가 관리함.
UsernamePasswordAuthenticationFileter, SecurityContextPersistenceFileter
이전에 로그인한 걸 알고 있을 수 있음! 정확하게 일치하고 있는. authentication이 여러 요청에 걸쳐서 유지가 됨.
먼저 SecurityContextPersistenceFilter에 먼저 걸림
-> 어딘가에서 걸려 있는, 캐싱하고 있는 시큐리티 컨텍스트 홀더를 복구하려고 요청함.
-> 요청이 끝나면 시큐리티 컨텍스트 홀더를 지워줌
->근데 없어서 다 지나가면, UsernamePasswordAuthenticationFilter에 걸림
여기서, authenticationmanager를 호출해서, 프로바이더 하고 인증을 함.
결론) 유저네임패스워드어썬티케이션필터에서 인증을 하고 이걸 넣는다.
시큐리티컨텍스트펄시스턴스필터에서 http그 어쩌구에 담아놓은것을 해서 세션으로 읽어옴. http session이 바뀐다? 그러면 인증이 날라간다는 거.
사실 이 두 필터만 쓰이는게 아니라 다른 15여가지의 필터가 있는데 이게 이제 어떻게 관리되고 언제 사용되는 걸까? 필터 개많네
우리가 만드는 SecurityConifg에서 오버라이드 하는 configure 여기서 프록시 체인을 설정하는 거임. *
그럼 이제 내가 요청을 보냈을 때, 그게 어떻게 필터 체인 프록시까지 가지?
지금까지 본 모든 필터가 다 서블릿 필터임.
컨텍스트 홀더가 어썬티케이션 가지고 있고, 매니저가 어썬티케이션을 인증을 해주고, 필터들이 인증된걸 다시 홀더에다 넣어주고, 이러한 필터들은 필터체인 프록시가 호출을 해주고 있고, 어케 필터체인으로 들어온거는 DelegatingFIlterProxy가 넣어준다.
http.antMatcher()를 사용해서 구분해 주는 것이 좋음.
인증은 했는데 권한 확인은 어디서해?
지금까지 살펴본 모든 요청은 스프링 시큐리티가 필터들을 적용해서 처리를 해 왔지..
지금 보면 총 3개의 요청이 가고 있음
@Override
public void configure(WebSecurity web) throw Exception {
//스태틱 리로스를 이렇게 쓰는게 귀찮으면
//web.ignoring().mvcMatchers("/favicon.io");
web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations());
}
http.authorizeRequests()
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitall 로 그냥 configure에서 설정을 한다면?
@GetMapping("/async-handler")
@ResponseBody
public Callable<String> asyncHandler() {
SecurityLogger.log("mvc");
//톰켓이 할당한 nio 쓰레
return new Callable<String>() {
@Override
public String call() throws Exception {
SecurityLogger.log("Callable");
//다른 쓰레드
/*
서로 다른 쓰레드지만 서로 같은 시큐리티 컨텍스트를 공유할 수 있음. 동일한 principal이 찍히는 것 확인 가능.
*/
return "Async Handler";
}
};
}
여러 요청 간의 시큐리티 컨텍스트를 공유할 수 있는.
응답 헤더에 시큐리티 관련 헤더를 추가해주는 필터. 딱히 알 필요는 없지만 고마운 친구라는 걸 알 수 있게 해줌.
async -> persistence -> headwriter의 순서로 필터가 적용됨.
어렵네여