인가(Authorization)는 인증(Authentication)이 이루어진 다음에 사용자자에게 부여된 권한을 의미한다. 시큐리티에서 사용자인 UserDetails에 보면 List<GrantedAuthority>를 반환 값으로 가지는 메서드가 있는데 권한 목록을 의미한다.
따라서 사용자는 여러개의 권한을 가질 수 있다는 것을 알 수 있다.
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
...
}
public interface GrantedAuthority extends Serializable {
String getAuthority();
}
시큐리티에서 권한은 권한과 역할로 나뉠 수 있다. GrantedAuthority 인터페이스는 String 값을 반환하는 메서드를 갖는걸 볼 수 있는데 여기서 반환하는 String에 접두어로 ROLE_ 사용되었으면 역할로 구분 할 수 있다.
아래는 권한과 역할을 각각 설정한 부분이다.
// 권한을 설정
var user1 = User.builder()
.username("john")
.password("12345")
.authorities("READ")
.build();
// 역할을 설정
var user2 = User.builder()
.username("jane")
.password("12345")
.authorities("ROLE_MANAGER")
.build();
이러한 권할을 설정하는 방법을 배웠다면 어떻게 권한으로 접근 제어를 할 수 있을까?
이는 SecurityFilterChane을 빈으로 등록하고 설정해주면 되는데 아래와 같은 예제 코드를 볼 수 있다.
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
...
http.authorizeHttpRequests(auth -> {
auth.requestMatchers(HttpMethod.GET, "/hello").hasAnyAuthority("READ")
.anyRequest().hasAnyRole("ADMIN");
});
return http.build();
}
여기서 중요한점은 Authority로 검증하게 되면 위에서 설정한 문자열 그대로 검사를 하게 되고 Role이라는 키워드로 검증하게 되면 ROLE_ 접두어를 제거한 문자열로 검증하게 된다.
참고로 User Builder는 .roles("ADMIN")와 같이 사용은 할 수 있지만 내부적으로 접두어(ROLE_)를 붙여 authorities를 설정한다.
최신 스프링 버전에서는 SecurityFilterChane을 빈으로 등록하는 방식을 이용해 시큐리티 흐름에 대해 여러가지 설정을 할 수 있다. 여기서 엔드포인트에 대한 접근 제어를 할 수 있는데 위에 예제에서 잠깐 본거와 같이 HttpSecurity에 authorizeHttpRequessts 사용 할 수 있다.
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> {
auth.anyRequest().hasAnyRole("ADMIN");
});
return http.build();
}
스프링 시큐리티 5.8버전 이전까지는 authorizeRequests()를 시작으로 체이닝 메서드인 mvcMatchers(), antMatchers(), regexMatchers()를 이용했지만 최신 버전에서는 authorizeHttpRequests()를 통해 람다 형식으로 설정할 수 있다.
람다 부분에서 설정된 경로와 이에 따른 권한 제한 설정은 이미 숙지하고 있으므로 생략하도록 한다.