SpringBoot 프로젝트에 Security를 적용하고 권한 관리할 때 사용할 수 있는 어노테이션인 @Secured, @PreAuthorize, @PostAuthorize를 알아보자.
Spring Security의 API 권한 관리 방식은 SecurityConfig에 설정하는 방법과 메소드 수준의 권한 관리 방법이 있다.
SecurityConfig 권한 관리
@Configuration
public class WebSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(
authorize -> authorize
.requestMatchers("/api/{version}/swagger/**", "/api/{version}/login/**").permitAll()
.requestMatchers("/api/{version}/member/**").hasAnyRole(Role.SUPER.getRole(), Role.ADMIN.getRole())
.anyRequest().authenticated()
)
...
.build();
}
...
}
보통은 SecurityConfig에서 SecurityFilterChain에 authorizeHttpRequests()로 API 엔드포인트에 대한 권한을 설정한다.
메소드 수준의 API 권한 관리
@Configuration
@EnableMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig {
...
}
메소드에 @Secured, @PreAuthorize, @PostAuthorize 어노테이션을 적용하기 위해서는 위와 같이 활성화시켜줘야 한다.
@PreAuthorize, @PostAuthorize를 활성화시키는 prePostEnabled는 default 값이 true이므로 굳이 true로 설정하지 않아도 된다.
Method Security를 활성화했으면 이제 메소드에 적용만 하면 된다. 매우 간편하다.
@Secured
@Secured 어노테이션은 Spring EL(표현식)이 사용 불가하고 , 로 접근 가능한 권한들을 OR로만 설정 가능하다.
// ROLE_ADMIN OR ROLE_SUPER
@Secured({"ROLE_ADMIN", "ROLE_SUPER"})
@GetMapping("/search")
public ResponseEntity<Object> searchMember() {
return ResponseEntity.ok(memberService.searchMember());
}
{
"result": false,
"code": "403",
"message": "접근 권한이 없습니다."
}
ROLE_USER 권한으로 해당 API 호출 시 정상적으로 403 Forbidden 에러를 응답받는다.
@PreAuthorize, @PostAuthorize
이 두 어노테이션은 Spring EL(표현식)을 사용할 수 있고, AND, OR 사용이 가능하다.
Spring EL
hasRole([role]) | 사용자의 권한과 동일한 경우 |
hasAnyRole([role1, role2, ...]) | 사용자의 권한과 일치하는 것이 있는 경우 |
principal | User 객체 |
authentication | Security Context의 Authentication 객체 |
permitAll | 모든 접근 허용 |
denyAll | 모든 접근 비허용 |
isAnonymous() | (비로그인) 사용자가 익명인 경우 |
isRememberMe() | 사용자가 RememberMe인 경우 |
isAuthenticated() | (로그인) 사용자가 익명이 아닌 경우 / 어떤 권한이든 인증된 사용자 |
isFullyAuthenticated() | 사용자가 익명이거나 RememberMe가 아닌 경우 |
@PreAuthorize
@PreAuthorize("#userId == principal.username") // #1
@PreAuthorize("isAuthenticated()) // #2
@PreAuthorize("hasAnyRole('ROLE_ADMIN')") // #3
@GetMapping("/search")
public ResponseEntity<Object> searchMember(@RequestParam String userId) {
return ResponseEntity.ok(memberService.searchMember());
}
이런 메소드가 있다고 했을 때 @PreAuthorize의 경우 #1, #2, #3처럼 사용할 수 있다.
#1 : '#'을 통해 값을 가져올 수 있다. userId가 인증객체의 username과 같은지 비교
#2 : 어떤 권한이든 상관없이 인증 유무 확인
#3 : 'ROLE_ADMIN' 권한 체크
@PostAuthorize
@PostAuthorize("returnObject.userId == principal.username")
@GetMapping("/{id}")
public MemberDto getMemberInfo(@PathVariable Long id) {
return memberService.getMemberInfo(id);
}
@PostAuthorize는 returnObject를 통해 반환 값을 가져올 수 있다.
예시 코드처럼 MemberDto의 userId와 인증객체의 username이 같은지 비교하여 권한 수준을 관리한다.
{
"result": false,
"code": "403",
"message": "접근 권한이 없습니다."
}
@PreAuthorize, @PostAuthorize 조건에 부합하지 않은 경우 403 Forbidden 에러를 반환한다.
끝.
'Spring Boot' 카테고리의 다른 글
[Spring Boot] @Valid 어노테이션 사용하기 (0) | 2023.12.24 |
---|---|
[Spring Boot] 공통 에러 처리 (0) | 2023.11.26 |
[Spring Boot] @RestControllerAdvice를 사용한 공통 응답 처리 (0) | 2023.11.26 |