Spring Boot Security 권한 정리 (authorizeRequests, secured, preAuthorize)
Spring Security에서는 특정 권한을 가진 user만 해당 페이지로 이동할 수 있게끔 권한을 부여하는 방법이 있다.
가장 보편적인 방법으로는 authorizeRequests로 글로벌적으로 걸어주는 방법이 있고, 특정 컨트롤러에만 걸어주고 싶다면 secured를 사용하거나 preAuthorize를 사용할 수 있다.
authorizeRequests
먼저 글로벌적으로 권한을 걸 수 있는 authorizeRequests이다.
이는 SecurityFilterChain에서 filter로 글로벌 적으로 권한을 설정하는 방법으로 앞서 Bean에 등록했었지만 다시한번 보도록하자
@Configuration
@EnableWebSecurity // 스프링 시큐리티 필터가 스프링 필터 체인에 등록됨
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) //secured 어노테이션 활성화, preAuthorize 어노테이션 활성화
public class SecurityConfig {
@Bean
public SecurityFilterChain filter(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/user/**").authenticated()
.antMatchers("/manager/**").access("hasRole('ROLE_MANAGER') or hasRole('ROLE_ADMIN')") //hasRole 은 풀네임을 써줘야 하지만, hasAuthority는 앞에 ROLE_ 이 붙기 때문에
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")// hasRole("ROLE_ADMIN") == hasAuthority("ADMIN") 이다.
.anyRequest().permitAll()// 권한 설정, 해당페이지(antMatchers) 설정한 곳은 권한(hasAuthority)이 있어야 들어갈 수 있다.
위에서부터 user는 로그인한 누구나 들어갈 수 있고, /manager는 ROLE_MANAGER, ROLE_ADMIN을 가지고 있을 경우 접근 가능하며, /admin은 ROLE_ADMIN만 접근할 수 있다고 설정했다. permitAll은 기타 다른 페이지는 누구나 접근할 수 있도록 설정해두었다. 그러면 실제로 잘 작동하는지 테스트 해보자
먼저 user, manager admin권한이 각각 부여된 세가지 아이디를 준비하자
그리고 GetMapping 으로 /user, /manager, /admin 세 가지를 준비하면 된다.
@GetMapping("/user") //@ResponseBody 는 페이지를 만들지 않고 해당 문자만 return해서 테스팅하기위해 사용
public @ResponseBody String user() {
return "user";
}
@GetMapping("/admin")
public @ResponseBody String admin() {
return "admin";
}
@GetMapping("/manager")
public @ResponseBody String manager() {
return "manager";
}
@GetMapping("/test")
public @ResponseBody String page() {
return "누구나 접근 가능한 페이지";
}
먼저 테스트 페이지로 들어가보면 로그인 없이 그냥 들어갈 수 있는것을 볼 수 있다.
이유는 아까 SecurityFilter에서 /test라는 페이지에 권한을 부여하지 않았기 때문에 로그인 없이 누구나 접근할 수 있는 페이지이기 때문이다.
권한이 필요한 페이지는 /manager 하나만 테스트 해보도록 하자
/manager의 경우 권한(hasRole('ROLE_MANAGER')) 이 있기 때문에 접근할 수 있지만, /admin의 경우 권한이 없기 때문에 접근이 불가능한 403이 뜨게 된다.
Secured와 preAuthorize
secured와 preAuthorize는 글로벌로 권한을 부여하는 것이 아닌 특정 컨트롤러에만 특별히 권한을 부여하고 싶은 경우에 사용된다.
먼저 이전에 만들었던 SecurityConfig 클래스에서 @EnableGlobalMethodSecurity을 걸어서 활성화시켜줘야 한다.
@Configuration
@EnableWebSecurity // 스프링 시큐리티 필터가 스프링 필터 체인에 등록됨
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) //secured 어노테이션 활성화, preAuthorize 어노테이션 활성화
public class SecurityConfig {
@Bean
public SecurityFilterChain filter(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/user/**").authenticated()
.antMatchers("/manager/**").access("hasRole('ROLE_MANAGER') or hasRole('ROLE_ADMIN')") //hasRole 은 풀네임을 써줘야 하지만, hasAuthority는 앞에 ROLE_ 이 붙기 때문에
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")// hasRole("ROLE_ADMIN") == hasAuthority("ADMIN") 이다.
.anyRequest().permitAll();// 권한 설정, 해당페이지(antMatchers) 설정한 곳은 권한(hasAuthority)이 있어야 들어갈 수 있다.
}
securedEnabled = true 의 경우 secured를 활성화 시켜주며, prePostEnabled = true는 preAuthorize를 활성화 시켜주는 역할이다.
이제 컨트롤러 부분에서 두 가지 컨트롤러를 만들어 테스팅 해볼 예정이다.
Controller
@Secured("ROLE_ADMIN")
@GetMapping("/info")
public @ResponseBody String info() {
return "개인정보";
}
@PreAuthorize("hasRole('ROLE_MANAGER') or hasRole('ROLE_ADMIN')") //함수가 실행되기 이전에 걸림
@GetMapping("/data")
public @ResponseBody String data() {
return "데이터 정보";
}
보면 info 에는 @secured가 걸려있고, data 에는 @PreAuthorize가 걸려있는 것을 볼 수 있다.
secured는 하나의 권한만을 부여할 수 있지만, preAuthorize는 여러개의 권한을 걸 수 있다.