programing

Spring Security anonymous 403 대신 401

goodjava 2023. 3. 16. 21:29

Spring Security anonymous 403 대신 401

Java Config에서 제공되는 권한 요청과 함께 스프링 보안의 기본 동작에 문제가 있습니다.

http
       ....
       .authorizeRequests()
          .antMatchers("/api/test/secured/*").authenticated()

예를 들어 에 콜을 할 때/api/test/secured/user로그인하지 않은 경우(익명의 사용자가 있는 경우), 403 Forbidden을 반환합니다.익명의 사용자가 보안 보호를 원할 때 상태를 401 Unauthorized로 쉽게 변경할 수 있는 방법이 있습니까?authenticated()또는@PreAuthorize자원?

Spring Boot 2 클래스 Http401 현재AuthenticationEntryPoint가 삭제되었습니다(스프링 부트 문제 10725 참조).

Http401 대신AuthenticationEntryPoint는 HttpStatus와 함께 HttpStatusEntryPoint를 사용합니다.승인되지 않음:

http.exceptionHandling()
    .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));

spring security 4.x에서는 이미 그에 대한 클래스가 있습니다.

org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint 

스프링 부트에는 1개도 포함되어 있습니다.

org.springframework.boot.autoconfigure.security.Http401AuthenticationEntryPoint

또한 개발자가 401 응답으로서 스펙 준거를 사용하도록 요구하는 두 가지 이점 모두 헤더의 WWW-Authenticate를 설정해야 합니다.예를 들어 401 응답은 다음과 같습니다.

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="example",
                   error="invalid_token",
                   error_description="The access token expired"

따라서 보안 설정에서는 클래스 전체를 정의하고 자동 접속할 수 있습니다.

예를 들어 스프링 부트 앱의 경우:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

    @Bean
    public Http401AuthenticationEntryPoint securityException401EntryPoint(){

        return new Http401AuthenticationEntryPoint("Bearer realm=\"webrealm\"");
    }

...
@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
                .antMatchers("/login").anonymous()
                .antMatchers("/").anonymous()
                .antMatchers("/api/**").authenticated()
            .and()
            .csrf()
                .disable()
                .headers()
                .frameOptions().disable()
            .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .logout()
                .permitAll()
         .exceptionHandling().authenticationEntryPoint(securityException401EntryPoint());
}

관련 행은 다음과 같습니다.

 .exceptionHandling().authenticationEntryPoint(securityException401EntryPoint());

여기 해결책이 있습니다.

http
   .authenticationEntryPoint(authenticationEntryPoint)

AuthenticationEntryPoint 소스 코드:

@Component
public class Http401UnauthorizedEntryPoint implements AuthenticationEntryPoint {

    private final Logger log = LoggerFactory.getLogger(Http401UnauthorizedEntryPoint.class);

    /**
     * Always returns a 401 error code to the client.
     */
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException arg2) throws IOException,
            ServletException {

        log.debug("Pre-authenticated entry point called. Rejecting access");
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied");
    }
}

Spring Boot 2에서는 람다 표현을 사용한 간단한 접근법:

@Override
public void configure(HttpSecurity http) throws Exception {
    http.
        ...
        .exceptionHandling()
            .authenticationEntryPoint((request, response, e) -> {
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
                response.setContentType("application/json");
                response.getWriter().write("{ \"error\": \"You are not authenticated.\" }");
            })
        ...
}

연장해야 합니다.AuthenticationEntryPoint예외에 따라 커스터마이즈를 수행합니다.

@ControllerAdvice
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
  @Override
  public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
      throws IOException, ServletException {
    // 401
    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed");
  }

  @ExceptionHandler (value = {AccessDeniedException.class})
  public void commence(HttpServletRequest request, HttpServletResponse response,
      AccessDeniedException accessDeniedException) throws IOException {
    // 401
    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authorization Failed : " + accessDeniedException.getMessage());
  }
}

SecurityConfig에서 다음과 같이 위의 커스텀 AuthenticationEntryPoint를 지정합니다.

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

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.exceptionHandling()
        .authenticationEntryPoint(new MyAuthenticationEntryPoint());
  }
}

일의 메커니즘에 관심이 있는 사람.
설정하지 않으면http.exceptionHandling().authenticationEntryPoint()봄을 이용하다defaultAuthenticationEntryPoint()및 방법ExceptionHandlingConfigurer.createDefaultEntryPoint()돌아온다new Http403ForbiddenEntryPoint()
그래서 그냥 만들어요.Http401UnauthorizedEntryPoint()위의 답변은 중복되지 않았습니다.

추신: Spring Security 5.2.5에 대한 실제 정보입니다.풀어주다

언급URL : https://stackoverflow.com/questions/30643029/spring-security-anonymous-401-instead-of-403