Spring Security 란
스프링 기반 애플리케이션의 보안(인증과 권한, 인가 등)을 담당하는 스프링 프레임워크
Spring Security 주된 목표는 rest api endpoint, mvc url, 정적 리소스와 같은 리소스들에 접근하려는 요청의 인증을 책임지는 것
Spring Security는 인증과 권한에 대한 부분을 Filter의 흐름에 따라 처리
보안과 관련해서 체계적으로 많은 옵션을 제공해주기 때문에 개발자가 일일이 보안 관련 로직을 작성하지 않아도 된다는 장점이 있음
Authentication(인증)
Who you are
•
로그인 뿐만 아니라 게시글 요청하는 등 여러 요청에 인증이 필요
1.
나는 유저 A입니다. ID:userA, Password:1234 ← 유저 A
2.
당신의 인증 key는 sdkflsdjfhasdkfjaosd입니다 ← 서버
3.
유저 A의 게시물을 주세요 + sdkflsdjfhasdkfjaosd ← 유저 A
4.
오늘의 일기, 아이폰 첫 사용 후기 ...← 서버
5.
유저 A의 게시물을 주세요 ← 유저 B
6.
누구세요? ← 서버
나의 ID와 패스워드를 서버에 주고 그 응답으로 아무나 해독이 불가능한 key를 받음
그 key를 모든 요청에 포함해서 보냄
Authorization(인가)
What you can do
•
인증 이후에 리소스에 대한 권한 통제를 의미
1.
저 userA인데 공지사항에 글을 올리고 싶어요 ← 유저 A
2.
인가에 실패하셨습니다. 오직 운영자만 공지사항에 글을 올릴 수 있습니다 ← 서버
Spring Security 내부 구조
SecurityContextHolder → SecurityContext → Authentication → Principal & GrantAuthority
•
SecurityContextHolder : SecurityContext를 제공하는 static 메소드를 지원
•
SecurityContext: 접근 주체와 인증에 대한 정보를 담고 있는 Context
•
Authentication: Principal과 GrantAuthority르 제공, 인증이 이루어 지면 해당 Authentication이 저장
•
Principal: 유저에 해당하는 정보, 대부분의 경우 Principal로 UserDetails를 반환
•
GrantAuthority: ROLE_ADMIN, ROLE_USER등 Principal이 가지고 있는 권한을 나타냄, prefixx로 ‘ROLE_’이 붙음, 권한은 여러개 일 수 있기 때문에 Collection 형태로 제공
ThreadLocal
Spring Security 내부 구조에서 유저에 대한 어떤 인자값도 주지 않았는데
SecurityContextHolder.getCotext();를 통해 principal의 username을 어떻게 구하는가
•
WebMVC 기반으로 프로젝트를 만든다는 가정하에 대부분의 경우에는 요청 1개에 Thread 1개가 생성됨
•
이때 ThreadLocal을 사용하면 Thread마다 고유한 공간을 만들수가 있고 그곳에 SecurityContext를 저장할 수 있음
그러나 ThreadLocal만 강제로 사용해야하는 것은 아니며, SecurityContext 공유 전략을 바꿀 수 있음
1.
MODE_THREADLACAL: 같은 Thread안에서 SecurityContext를 공유함
2.
MODE_INHERITABLETHREADLOCAL: 자식 Thread까지도 SecurityContext를 공유함
3.
MODE_GLOBAL: 애플리케이션 전체에서 SecurityContext를 공유함
보통은 전략 1을 사용.
ThreadLocal 사용시 주의사항
WAS와 같이 Thread Pool 환경에서는 Thread를 재활용하기 때문에 ThreadLocal에 데이터가 계속 남아있게 됨, 그러므로 remove() 함수를 호출하여 데이터를 지워줘야 함.
Spring Security 구현
1.
build.gradle에 sping security 추가
//Spring Security
implementation ('org.springframework.boot:spring-boot-starter-security')
JavaScript
복사
2.
UserDetails 확장
public class User implements UserDetails
JavaScript
복사
3.
기본 로그인 화면 제거
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.httpBasic().disable(); // 이 부분 추가
}
}
JavaScript
복사
Spring Security 내부 구조
Spring Security 동작과정
1.
HTTP 요청 수신
일반적으로 ID, PASSWORD 기반의 인증일 경우 가장 먼저 Application Filters라는 필터 뭉치에 도달합니다. 그리고 그 필터들 중 Authentication Filters라는 필터 뭉치에 다시 도달합니다. 다음으로 username, password를 사용하는 form 기반 인증을 처리하는 필터인 UsernamePasswordAuthenticationFilter에 도착하게 됩니다.
OAuth2.0 인증이나 JWT를 이용한 인증을 하려고 할 때는 해당 필터가 아닌 OAuth2ClientAuthenticationProcessingFilter를 거치가 됩니다.
UsernamePasswordAuthenticationFilter에 요청이 도착하면 해당 클래스의 attempAuthentication(request, response) 메서드가 동작합니다. 이 메서드는 request부터 username, password를 가지고 와서 사용자 자격 증명을 기반으로 한 UsernamePasswordAuthenticationToken을 생성합니다.
UsernamePasswordAuthenticationToken은 Authentication의 구현체
모든 접근 주체는 Authentication을 생성, 이것은 최종적으로 SecurityContext에 보관되고 사용됨