[Spring Security] 우당탕탕 API 권한 설정

chiyongs·2022년 8월 26일
4
post-thumbnail

Spring Security는 API에 대해 접근 권한을 설정을 통해 지정할 수 있습니다.

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
            // 회원가입과 로그인 API request는 permitAll, 그 외 모든 요청은 인증 필요
            .authorizeHttpRequests()
            .antMatchers("허용하기 싶은 API").permitAll()
            
            // 위의 허용하는 API를 제외한 모든 Request에 Role을 가지고 있어야 함
            .anyRequest().hasAnyRole("ROLE_USER","ROLE_ADMIN")

    return http.build();
}

저는 회원가입, 로그인, Reissue 등 임시등급인 회원도 사용할 수 있는 API를 제외한 모든 API에 접근 권한을 설정했습니다.

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
            .csrf().disable()

            .cors().and()

            .exceptionHandling()
            .authenticationEntryPoint(jwtAuthenticationEntryPoint)
            .accessDeniedHandler(jwtAccessDeniedHandler)

            // JWT를 사용하기 때문에 Session을 Stateless하게 관리
            .and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)

            // 회원가입과 로그인 API 등 request는 permitAll, 그 외 모든 요청은 인증 필요
            .and()
            .authorizeHttpRequests()
            .antMatchers("/api/v1/auth/**").permitAll()
            
            // 권한이 필요
            .anyRequest().hasAnyRole("ROLE_USER","ROLE_ADMIN")

            .and()
            .apply(new JwtSecurityConfig(tokenProvider));

    return http.build();
}

위와 같이 ROLE_USERROLE_ADMIN 권한이 있는 사용자들에게만 회원가입, 로그인을 제외한 API에 접근 권한을 부여했습니다.
하지만, Postman으로 테스트를 해보니

모든 API에서 403 Forbidden, 권한이 없는 사용자라는 응답이 날아왔습니다... 😢

@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        // 필요한 권한이 없이 접근 시 403
        setResponse(response, ErrorCode.USER_NO_PERMISSION);
    }

    private void setResponse(HttpServletResponse response, ErrorCode errorCode) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        response.getWriter().println("{ \"message\" : \"" + errorCode.getMessage()
                + "\", \"code\" : \"" +  errorCode.getCode()
                + "\", \"success\" : " + false
                + "}");
    }

}

Custom AccessDeniedHandler를 만들어 권한이 없다면 body에 에러메세지가 나가게끔 의도했지만, 무엇이 잘못된 것인지 아무것도 나오지 않았어요..

지속되던 삽질 중 제가 접근 제한 설정을 할 때 메서드를 잘못 사용했다는 것을 깨달았습니다ㅠㅠㅠ

바로, hasAnyRole을 썼던 것이죠.
그럼, hasAnyRole을 사용하면 안되는 걸까요?? 🤔
그건 아니에요.. Spring Security에서 잘못 만들어진 메서드를 제공할리 없죠! (Spring 찬양)

저의 문제는 바로 UserDetailsService를 상속받은 CustomUserDetailsService에서
UserDetails를 만들 때 저는 Role이 아닌 Authority를 주입했기 때문이에요.
hasAnyRole 메서드를 사용하여 접근을 제한하다보니 모든 Request에 대한 사용자들은 Role을 가지고 있지 않기 때문에 권한이 없다는 403 에러가 발생했던 것이었죠...

private UserDetails createUserDetails(com.infomansion.server.domain.user.domain.User myUser) {
        GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(myUser.getAuthority().toString());

        return new User(
                String.valueOf(myUser.getId()),
                myUser.getPassword(),
                Collections.singleton(grantedAuthority)
        );
    }

문제를 파악하고 다음과 같이 hasAnyRole 에서 hasAnyAuthority로 변경하여 해결했습니다!!

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
            .csrf().disable()

            .cors().and()

            .exceptionHandling()
            .authenticationEntryPoint(jwtAuthenticationEntryPoint)
            .accessDeniedHandler(jwtAccessDeniedHandler)

            // JWT를 사용하기 때문에 Session을 Stateless하게 관리
            .and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)

            // 회원가입과 로그인 API request는 permitAll, 그 외 모든 요청은 인증 필요
            .and()
            .authorizeHttpRequests()
            .antMatchers("/api/v1/auth/**").permitAll()

            // 권한이 필요 -> Role에서 Authority로 변경!!!
            .anyRequest().hasAnyAuthority("ROLE_USER","ROLE_ADMIN")

            .and()
            .apply(new JwtSecurityConfig(tokenProvider));

    return http.build();
}


위와 같이 오류메세지도 잘 나오는 것을 확인했어요!!
간단한 문제였는데 많은 시간을 소비했던 지라 혹시 403에러가 발생하시는 분들은 UserDetails를 어떻게 만드셨는지 체크해보시면 좋을 것 같습니다.

0개의 댓글