如何将 403 错误映射到用户友好的错误?

How to map 403 error to user friendly error?

我是使用 spring 引导构建 rest APi 的新手。

这是我的 controller 片段

@PreAuthorize("hasRole('ADMIN')")
    @PostMapping(value = "/api/post/posts", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<PostDto> createPost(@Valid @RequestBody PostDto postDto) {

        System.out.println("postDto : "  + postDto.getId());
        return new ResponseEntity<>(postService.createPost(postDto), HttpStatus.CREATED);

    }

这是我的Security配置

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)    //give method level security
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable().authorizeRequests().antMatchers(HttpMethod.GET, "/api/**").permitAll().anyRequest()
                .authenticated().and().httpBasic();
    }

    @Override
    @Bean
    protected UserDetailsService userDetailsService() {
        // In Memory Users
        UserDetails ashish = User.builder().username("oxana").password(getPasswordEncoder().encode("password")).roles("USER").build();
        UserDetails admin = User.builder().username("admin").password(getPasswordEncoder().encode("admin")).roles("ADMIN").build();

        return new InMemoryUserDetailsManager(ashish, admin);
    }
    
    @Bean
    PasswordEncoder getPasswordEncoder() {
        
        return new BCryptPasswordEncoder();
    }
}

我在这里尝试着陆异常

@ExceptionHandler(Exception.class)
    public ResponseEntity<Errors> handleGlobalException(Exception exception,
                                                               WebRequest webRequest){
        Error errorDetails = new Error();
        errorDetails.setErrorDesc(exception.getMessage());
        errorDetails.setErrorCode(Error.ErrorCodeEnum.BAD_REQUEST);
        Errors errors = new Errors();
        errors.addErrorsItem(errorDetails);
        
        return new ResponseEntity<>(errors, HttpStatus.INTERNAL_SERVER_ERROR);
    }

但它没有出现,并给出了一大堆错误,就像这样

"timestamp": "2022-02-21T11:39:28.797+00:00",
    "status": 403,
    "error": "Forbidden",
    "trace": "org.springframework.security.access.AccessDeniedException: Access is denied\r\n\tat org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:73)\r\n\tat org.springframework.security.access.intercept.AbstractSecurityInterceptor.attemptAuthorization(AbstractSecurityInterceptor.java:238)\r\n\tat org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:208)\r\n\tat org.springframework.security.access.intercept.aopalliance.

任何人都可以建议我,我如何处理或捕获此异常以自定义错误,用户无权执行某些操作?

谢谢

更新

按以下方式实施AccessDeniedHandler

@ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "Dont have sufficient priviliges to perform this action")
public class AccessDeniedError implements AccessDeniedHandler {
    
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exec)
            throws IOException, ServletException {

        response.sendRedirect("Dont have sufficient priviliges to perform this action");

    }

}

现在可以收到这样的消息了

{
    "timestamp": "2022-02-21T13:29:08.377+00:00",
    "status": 404,
    "error": "Not Found",
    "message": "No message available",
    "path": "/api/post/Dont%20have%20sufficient%20priviliges%20to%20perform%20this%20action"
}

它稍微好一些,但是我怎样才能从上面的响应中控制这些变量 ("error", "message", "status") 值,以便我可以在其中添加我的自定义值?

AccessDeniedExceptionExceptionTranslationFilter 处理,然后委托 AccessDeniedHandler 将相应的响应写入客户端。

如果您想自定义此行为,那么您可以实现一个 AccessDeniedHandler,然后将您的实现设置为 HttpSecurity 对象。

MyAccessDeniedHandler.java

public class MyAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        writeCustomResponse(response);
    }

    private void writeCustomResponse(HttpServletResponse response) {
        if (!response.isCommitted()) {
            try {
                response.setStatus(HttpStatus.FORBIDDEN.value());
                response.getWriter().write("{ \"error\": \"User is not authorized.\"}");
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

SecurityConfiguration.java

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // set the customized AccessDeniedHandler to the HttpSecurity object
        http.exceptionHandling().accessDeniedHandler(new MyAccessDeniedHandler());
    }
}