히콩쓰 개발 일지
[Spring Security] 권한을 올바르게 설정했는데 403 에러가 날 경우 고려해야 할 점 본문
카카오 OAuth로 로그인, 로그아웃 등의 기능을 구현하면서 인증받은 User 객체를 Userdetails에 담아 SecurityConfig에서 권한을 확인한 뒤 API 접근을 허가하고자 아래와 같이 WebSecurityConfig
코드를 작성하였다.
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurityConfig {
private final UserDetailsServiceImpl userDetailsService;
private final AuthenticationConfiguration authenticationConfiguration;
private final RestTemplate restTemplate;
...(중략)
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// CSRF 설정
http.csrf((csrf) -> csrf.disable());
// 기본 설정인 Session 방식은 사용하지 않고 JWT 방식을 사용하기 위한 설정
http.sessionManagement((sessionManagement) ->
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
);
http.authorizeHttpRequests((authorizeHttpRequests) ->
authorizeHttpRequests
.requestMatchers(PathRequest.toStaticResources().atCommonLocations())
.permitAll() // resources 접근 허용 설정
.requestMatchers("/").permitAll() // 로그인 페이지 요청 허가
.requestMatchers("/v1/user/kakao/**").permitAll() // 카카오 로그인
.requestMatchers(HttpMethod.PATCH, "/v1/users").hasRole("USER") // 프로필 수정
...(이하 생략)
USER
권한을 검사하여 /v1/users
에 정상적으로 접근할 수 있어야 하는데, 403 에러가 발생했다
이 때, 원인을 고민해보니 아래와 같이 정리할 수 있었다.
- UserDeatilsImpl(UserDetails)와 UserDetailsService를 구현을 잘못한 경우
- Autentication Context에 저장하는 로직 구현을 잘못한 경우
- UserDetails 구현체를 받아오지 못하는 경우 (첫번째 고민과 일맥상통함)
하지만 디버깅을 해보니, 코드에는 문제가 없었고 UserDetails도 잘 받아오는 것을 확인할 수 있었다.
이때, UserDetails는 인증받은 사용자 객체를 의미한다. UserEntity와 동일하다고 이해했다.
이 때부터 정신이 혼미해지기 시작했는데, User
객체를 잘 받아왔고, 권한도 USER로 동일한데 왜 자꾸 엑세스가 거부되는지 알 수 없었다.
심지어, 에러 로그도 자세히 나오지 않았고 Cache miss 만 반복할 뿐이었다.
Spring Security 내부적으로 놓친 부분이 있을 것이라고 생각하여, hasRole
메서드를 타고 들어가보았더니, 아래와 같이 구현되어 있었다.
rolePrefix를 확인하고, 저게 뭘까 하고 한 번 더 들어가 보았다.
Spring Security는 내부적으로 권한을 검사할 때, rolePrefix를 붙여 검사하고 있었는데, 실제로 아래와 같이 구현해서 계속 일치하지 않아 403 에러를 출력한 것이다.
public enum Role implements CommonEnum {
ADMIN("ROLE_ADMIN", "관리자"),
USER("ROLE_USER", "유저"),
GUEST("ROLE_GUEST", "게스트");
해결 방법은 두 가지였다.
- 인증 객체를 생성할 때 권한에 "ROLE_" 을 붙일 것
- 애초에 Role을 작성한 Enum 클래스의 권한을 ROLE_ADMIN, ROLE_USER 와 같이 바꿀 것
시간이 촉박하고 혹시나 구현해놓은 Security 쪽을 잘못 건드렸다가 겉잡을 수 없는 오류가 발생할까 하는 생각에, 아래의 방법을 선택하여 코드를 수정하였다.
public enum Role implements CommonEnum {
ROLE_ADMIN("ROLE_ADMIN", "관리자"),
ROLE_USER("ROLE_USER", "유저"),
ROLE_GUEST("ROLE_GUEST", "게스트");
위와 같이 코드를 수정했더니, 문제가 해결되었다.
하지만, 이는 그렇게 좋은 방법이 아닌 것 같다고 느꼈다. 아래와 같이 DB에 저장되는 것이 사실상 썩 예뻐보이지 않았기 때문이다.
따라서, 위 방식을 채택해서 나중에 리팩토링 할 계획이다.
Spring Security 내부적으로 어떤 동작이 이뤄지고 있는지 파악할 수 있는 계기가 되었다.
무작정 코드를 작성하기만 하는 사람이 될 것이 아니라, 내부 동작 원리를 파악하고 고민하며 구현하는 "개발자" 가 되어야겠다고 생각했다.
이상 끝 ! 👶🏻💕
'Spring' 카테고리의 다른 글
Controller Test 403 forbidden 해결 (1) | 2023.12.05 |
---|---|
Primitive Type(원시타입)과 Wrapper Class(래퍼 클래스) 의 Getter (1) | 2023.11.22 |
Spring Boot 프로젝트 Dto 사용 시 HTTP 406 Error 해결 방법! (0) | 2023.11.21 |
@ReqestBody String 사용 시 Json이 그대로 입력되는 이유 (1) | 2023.11.14 |
[Consider declaring it as object wrapper for the corresponding primitive type] 발생 원인과 해결 방법 (0) | 2023.11.10 |