Spring Security ?
Spring 기반의 애플리케이션 보안(인증과 권한, 인가 등) 을 담당해 관련 설정을 쉽게 할 수 있게 도와주는 스프링 하위 프레임워크이다.
Spring Security 는 '인증' 과 '권한' 에 대한 부분을 Filter 흐름에 따라 처리하고 있다.
- 인증(Authentication): 해당 사용자가 본인이 맞는지를 확인하는 절차
- 인가(Authorization): 인증된 사용자가 요청한 자원에 접근 가능한지를 결정하는 절차
전체적인 동작 흐름

- 사용자가 로그인 정보와 함께 인증을 요청한다.
- AuthenticationFilter 가 요청을 가로챈다.
이 때 가로챈 정보를 통해 UsernamePasswordAuthenticationToken 객체를 생성한다. (사용자가 입력한 데이터를 기반으로 생성하고, 현 상태는 미검증 상태이다.) - ProviderManager 에게 토큰 객체를 전달한다.
- AuthenticationProvider 에게 토큰 객체를 전달한다.
- UserDetailService 에게 사용자 정보를 넘겨준다.
- UserDetailService 는 실제 넘겨받은 정보를 통해 DB 로 부터 사용자 인증 정보를 가져오고 UserDetails 객체를 생성한다.
- AuthenticationProvider 는 UserDetails 를 넘겨받고 사용자 정보를 비교한다.
- 인증이 성공적으로 완료되면, 사용자 정보를 담은 Authentication 객체를 반환한다.
- AuthenticationFilter 에 Authentication 객체가 반환된다.
- Authentication 객체를 SecurityContext 에 저장한다.
이 과정 중 주의깊게 살펴봐야 할 부분은 UserDetailsService 와 UserDetails 이다.
Security 의 실질적인 인증 과정은 UserDetailsService 의 loadUserByUsername() 이라는 메서드가 반환하는 UserDetails 객체와 사용자 입력 정보를 비교함으로써 동작한다.
따라서 UserDetailsService 와 UserDetails 를 어떻게 구현하느냐에 따라서 인증의 세부 과정이 달라진다.
또한 만약 OAuth2.0 로그인을 사용한다면 UsernamePasswordAuthenticationFilter 대신 OAuth2LoginAuthenticationFilter 가 호출된다.
두 필터의 상위 클래스는 AbstractAuthenticationProcessingFilter 로, 앞선 두 필터는 이 클래스를 확장한 것이다.
Spring Securiy 모듈 상세히 알아보기
SecurityContextHolder

보안 주체의 세부 정보를 포함하여 응용프로그램의 현재 보안 컨텍스트에 대한 세부 정보가 저장된다.
이것을 좀 더 상세히 말하면, 인증된 사용자 정보인 Principal 이라는 정보를 Authentication 에서 관리하고 이것을 SecurityContext 가 관리하고 또 이것을 SecurityContextHolder 가 관리한다.
즉, 정리하자면 SecurityContextHolder 란 Authentication 을 담고 있는 Holder 라고 정의할 수 있다.
Authentication 자체는 인증된 정보이기 때문에 SecurityContextHolder 가 가지고 있는 값을 통해 인증이 되었는지 확인할 수 있다.
(예를 들면 다음과 같이)
Authentication.isAuthenticated()
SecurityContextHolder 는 기본적으로 쓰레드와 SecurityContext 를 연결해주어 Authentication 을 저장할 수 있다. 멀티쓰레드 사용 시 제대로 된 인증정보를 가져올 수 없으니 이 때는 SecurityContextHolderStategy 라는 인터페이스 구현체를 이용하여 전략을 결정하면 된다.
Authentication
현재 접근하는 주체의 정보와 권한을 담는 인터페이스이다. Spring Security 는 사용자 정보 및 인증 성공여부를 가지고 Authentication 객체를 생성한 후 보관한다.
Authentication 객체는 앞선 설명과 같이 SecurityContext 에 저장된다. 접근 방법은 SecurityContextHolder 를 통해 SecurityContext 에 접근하고, SecurityContext 를 통해 Authentication 에 접근할 수 있다.
Authentication 객체를 뜯어보면 다음과 같이 생겼다.
public interface Authentication extends Principal, Serializable {
// 현재 사용자의 권한 목록을 가져옴
Collection<? extends GrantedAuthority> getAuthorities();
// credentials 를 가져옴 (주로 비밀번호)
Object getCredentials();
Object getDetails();
Object getPrincipal();
// 인증 여부를 가져옴
boolean isAuthenticated();
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
UsernamePasswordAuthenticationToken
이 토큰은 Authentication 을 구현한 AbstractAuthenticationToken 의 하위 클래스로, UserId 가 Principal 역할을 하고, Password 가 Credential 역할을 한다.
이 토큰의 첫 번째 생성자는 인증 전의 객체를 생성하고, 두 번째 생성자는 인증이 완료된 객체를 생성한다.
public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
// 주로 사용자의 ID에 해당함
private final Object principal;
// 주로 사용자의 PW에 해당함
private Object credentials;
// 인증 완료 전의 객체 생성
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
super(null);
this.principal = principal;
this.credentials = credentials;
setAuthenticated(false);
}
// 인증 완료 후의 객체 생성
public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true); // must use super, as we override
}
}
public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
}
AuthenticationProvider
AuthenticationProvider 는 실제 인증에 대한 부분을 처리하는데, 인증 전의 Authentication 객체를 받아서 인증이 완료된 객체를 반환하는 역할을 한다.
AuthenticationManaer
인증이 성공했을 시 사용자 정보를 담은 Authentication 객체를 반환해 SecurityContext 에 저장한다. 그리고 인증 상태를 유지하기 위해 세션에 보관하며, 인증이 실패한 경우 AuthenticationException 을 발생시킨다.
UserDetails
UserDetailService 에 의해 UserDetails 객체가 생성되며, 이 객체는 AuthenticationProvider 에 의해 인증 과정이 실행된다. 인터페이스를 살펴 보면 아래와 같이 정보를 반환하는 메서드를 가지고 있다.
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
UserDetailsService
UserDetailsService 인터페이스는 UserDetails 객체를 반환하는 단 하나의 메서드를 가지고 있는데, 일반적으로 이를 구현한 클래스 내부에 UserRepository 를 주입받아 DB 와 연결해 처리한다.
public interface UserDetailsService {
UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}
Password Encoding
AuthenticationManagerBuilder.userDetailsService().passwordEncoder() 를 통해 패스워드 암호화에 사용될 PasswordEncoder 구현체를 지정할 수 있다.
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// TODO Auto-generated method stub
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
GrantedAuthoridty
GrantAuthority는 현재 사용자(Principal)가 가지고 있는 권한을 의미한다.
ROLE_ADMIN나 ROLE_USER와 같이 ROLE_*의 형태로 사용하며, 보통 "roles" 이라고 한다. GrantedAuthority 객체는 UserDetailsService에 의해 불러올 수 있고, 특정 자원에 대한 권한이 있는지를 검사하여 접근 허용 여부를 결정한다.
'스프링' 카테고리의 다른 글
[Spring Security] Spring Security + JWT 구현 (0) | 2023.07.25 |
---|
Spring Security ?
Spring 기반의 애플리케이션 보안(인증과 권한, 인가 등) 을 담당해 관련 설정을 쉽게 할 수 있게 도와주는 스프링 하위 프레임워크이다.
Spring Security 는 '인증' 과 '권한' 에 대한 부분을 Filter 흐름에 따라 처리하고 있다.
- 인증(Authentication): 해당 사용자가 본인이 맞는지를 확인하는 절차
- 인가(Authorization): 인증된 사용자가 요청한 자원에 접근 가능한지를 결정하는 절차
전체적인 동작 흐름

- 사용자가 로그인 정보와 함께 인증을 요청한다.
- AuthenticationFilter 가 요청을 가로챈다.
이 때 가로챈 정보를 통해 UsernamePasswordAuthenticationToken 객체를 생성한다. (사용자가 입력한 데이터를 기반으로 생성하고, 현 상태는 미검증 상태이다.) - ProviderManager 에게 토큰 객체를 전달한다.
- AuthenticationProvider 에게 토큰 객체를 전달한다.
- UserDetailService 에게 사용자 정보를 넘겨준다.
- UserDetailService 는 실제 넘겨받은 정보를 통해 DB 로 부터 사용자 인증 정보를 가져오고 UserDetails 객체를 생성한다.
- AuthenticationProvider 는 UserDetails 를 넘겨받고 사용자 정보를 비교한다.
- 인증이 성공적으로 완료되면, 사용자 정보를 담은 Authentication 객체를 반환한다.
- AuthenticationFilter 에 Authentication 객체가 반환된다.
- Authentication 객체를 SecurityContext 에 저장한다.
이 과정 중 주의깊게 살펴봐야 할 부분은 UserDetailsService 와 UserDetails 이다.
Security 의 실질적인 인증 과정은 UserDetailsService 의 loadUserByUsername() 이라는 메서드가 반환하는 UserDetails 객체와 사용자 입력 정보를 비교함으로써 동작한다.
따라서 UserDetailsService 와 UserDetails 를 어떻게 구현하느냐에 따라서 인증의 세부 과정이 달라진다.
또한 만약 OAuth2.0 로그인을 사용한다면 UsernamePasswordAuthenticationFilter 대신 OAuth2LoginAuthenticationFilter 가 호출된다.
두 필터의 상위 클래스는 AbstractAuthenticationProcessingFilter 로, 앞선 두 필터는 이 클래스를 확장한 것이다.
Spring Securiy 모듈 상세히 알아보기
SecurityContextHolder

보안 주체의 세부 정보를 포함하여 응용프로그램의 현재 보안 컨텍스트에 대한 세부 정보가 저장된다.
이것을 좀 더 상세히 말하면, 인증된 사용자 정보인 Principal 이라는 정보를 Authentication 에서 관리하고 이것을 SecurityContext 가 관리하고 또 이것을 SecurityContextHolder 가 관리한다.
즉, 정리하자면 SecurityContextHolder 란 Authentication 을 담고 있는 Holder 라고 정의할 수 있다.
Authentication 자체는 인증된 정보이기 때문에 SecurityContextHolder 가 가지고 있는 값을 통해 인증이 되었는지 확인할 수 있다.
(예를 들면 다음과 같이)
Authentication.isAuthenticated()
SecurityContextHolder 는 기본적으로 쓰레드와 SecurityContext 를 연결해주어 Authentication 을 저장할 수 있다. 멀티쓰레드 사용 시 제대로 된 인증정보를 가져올 수 없으니 이 때는 SecurityContextHolderStategy 라는 인터페이스 구현체를 이용하여 전략을 결정하면 된다.
Authentication
현재 접근하는 주체의 정보와 권한을 담는 인터페이스이다. Spring Security 는 사용자 정보 및 인증 성공여부를 가지고 Authentication 객체를 생성한 후 보관한다.
Authentication 객체는 앞선 설명과 같이 SecurityContext 에 저장된다. 접근 방법은 SecurityContextHolder 를 통해 SecurityContext 에 접근하고, SecurityContext 를 통해 Authentication 에 접근할 수 있다.
Authentication 객체를 뜯어보면 다음과 같이 생겼다.
public interface Authentication extends Principal, Serializable {
// 현재 사용자의 권한 목록을 가져옴
Collection<? extends GrantedAuthority> getAuthorities();
// credentials 를 가져옴 (주로 비밀번호)
Object getCredentials();
Object getDetails();
Object getPrincipal();
// 인증 여부를 가져옴
boolean isAuthenticated();
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
UsernamePasswordAuthenticationToken
이 토큰은 Authentication 을 구현한 AbstractAuthenticationToken 의 하위 클래스로, UserId 가 Principal 역할을 하고, Password 가 Credential 역할을 한다.
이 토큰의 첫 번째 생성자는 인증 전의 객체를 생성하고, 두 번째 생성자는 인증이 완료된 객체를 생성한다.
public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
// 주로 사용자의 ID에 해당함
private final Object principal;
// 주로 사용자의 PW에 해당함
private Object credentials;
// 인증 완료 전의 객체 생성
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
super(null);
this.principal = principal;
this.credentials = credentials;
setAuthenticated(false);
}
// 인증 완료 후의 객체 생성
public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true); // must use super, as we override
}
}
public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
}
AuthenticationProvider
AuthenticationProvider 는 실제 인증에 대한 부분을 처리하는데, 인증 전의 Authentication 객체를 받아서 인증이 완료된 객체를 반환하는 역할을 한다.
AuthenticationManaer
인증이 성공했을 시 사용자 정보를 담은 Authentication 객체를 반환해 SecurityContext 에 저장한다. 그리고 인증 상태를 유지하기 위해 세션에 보관하며, 인증이 실패한 경우 AuthenticationException 을 발생시킨다.
UserDetails
UserDetailService 에 의해 UserDetails 객체가 생성되며, 이 객체는 AuthenticationProvider 에 의해 인증 과정이 실행된다. 인터페이스를 살펴 보면 아래와 같이 정보를 반환하는 메서드를 가지고 있다.
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
UserDetailsService
UserDetailsService 인터페이스는 UserDetails 객체를 반환하는 단 하나의 메서드를 가지고 있는데, 일반적으로 이를 구현한 클래스 내부에 UserRepository 를 주입받아 DB 와 연결해 처리한다.
public interface UserDetailsService {
UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}
Password Encoding
AuthenticationManagerBuilder.userDetailsService().passwordEncoder() 를 통해 패스워드 암호화에 사용될 PasswordEncoder 구현체를 지정할 수 있다.
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// TODO Auto-generated method stub
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
GrantedAuthoridty
GrantAuthority는 현재 사용자(Principal)가 가지고 있는 권한을 의미한다.
ROLE_ADMIN나 ROLE_USER와 같이 ROLE_*의 형태로 사용하며, 보통 "roles" 이라고 한다. GrantedAuthority 객체는 UserDetailsService에 의해 불러올 수 있고, 특정 자원에 대한 권한이 있는지를 검사하여 접근 허용 여부를 결정한다.
'스프링' 카테고리의 다른 글
[Spring Security] Spring Security + JWT 구현 (0) | 2023.07.25 |
---|