[Spring Security] Spring Security Authority & Exception Handling

Barded·2023년 2월 24일

Spring Security

목록 보기
4/4
post-thumbnail

Spring Security Authority

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class SecurityConfig {

    private final JwtProvider jwtProvider;
    private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
    private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
            .httpBasic().disable()
            .csrf().disable()
            .cors().and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .authorizeRequests()
            .antMatchers("/api/login", "/api/signup").permitAll()
            .anyRequest().authenticated().and()
            .addFilterBefore(new JwtFilter(jwtProvider), UsernamePasswordAuthenticationFilter.class)
            .build();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

}

Authority별로 권한을 다르게 주기 위해서 @EnableGlobalMethodSecurity(prePostEnabled = true) 애노테이션을 추가했다.

이후 권한이 필요한 곳에서 @PreAuthorize 애노테이션을 활용하여 권한에 따라 인가여부를 설정하면된다.

   @GetMapping("/user")
    @PreAuthorize("hasAnyRole('USER', 'ADMIN')")
    public ResponseEntity<User> getMyUserInfo() {
        return ResponseEntity.ok(userService.getMyUserWithAuthorities().get());
    }
    @GetMapping("/user/{username}")
    @PreAuthorize("hasAnyRole('ADMIN')")
    public ResponseEntity<User> getMyUserInfo(@PathVariable String username) {
        return ResponseEntity.ok(userService.getUserWithAuthorities(username).get());
    }

Spring Security Exception Handling

JwtAuthenticationEntryPoint

@Slf4j
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    }
}

JwtAuthenticationEntryPointAuthenticationEntryPoint을 상속받아 commence를 오버라이딩한다.
보통 AuthenticationEntryPointAuthenticationException을 처리한다. AuthenticationException의 하위 클래스로는 BadCredentialsException, DisabledException, LockedException, UsernameNotFoundException 등이 있다.

AuthenticationEntryPointAuthenticationException을 처리할 때, 보통은 401 Unauthorized 상태코드와 함께 인증에러 메시지를 응답으로 반환한다. 예를 들어, 사용자가 보호된 리소스에 접근하려고 하지만 인증되지 않은 경우 AuthenticationEntryPoint가 발생시키는 AuthenticationException을 처리하여 "인증이 필요합니다"와 같은 메시지를 응답으로 반환할 수 있다.

JwtAccessDeniedHandler

@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        response.sendError(HttpServletResponse.SC_FORBIDDEN);
    }
}

JwtAccessDeniedHandler AccessDeniedHandler 상속받아 handle을 오버라이딩한다. AccessDeniedHandler는 사용자가 인증은 되었지만 인가되지 않은 경우 처리하는 핸들러이다. 인가되지 않은 사용자가 보호된 리소스에 접근하면, AccessDeniedException이 발생하며, 이를 처리하기 위해 AccessDeniedHandler가 호출된다. 이때 처리되는 예외는 AccessDeniedException입니다.

AccessDeniedException은 인가에러를 나타내며, AccessDeniedHandler는 이를 처리하여 사용자가 접근 권한이 없음을 알리는 응답을 생성한다. 이때 응답은 일반적으로 403 Forbidden 상태 코드와 함께 보내지며, 사용자에게 인가되지 않은 접근에 대한 정보를 제공한다.

SecurityFilterChain

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
            .httpBasic().disable()
            .csrf().disable()
            .cors().and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .authorizeRequests()
            .antMatchers("/api/login", "/api/signup").permitAll()
            .anyRequest().authenticated().and()
            .addFilterBefore(new JwtFilter(jwtProvider), UsernamePasswordAuthenticationFilter.class)
            .exceptionHandling()
            .authenticationEntryPoint(jwtAuthenticationEntryPoint)
            .accessDeniedHandler(jwtAccessDeniedHandler).and()
            .build();
    }

위에서 작성한 JwtAuthenticationEntryPoint JwtAccessDeniedHandler

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

를 통해 등록하면 적용된다.

profile
Now or Never

0개의 댓글