Spring Boot 如何将403错误Map为用户友好的错误?

qlvxas9a  于 2022-12-13  发布在  Spring
关注(0)|答案(1)|浏览(109)

我是使用 Spring Boot 构建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")值从上述响应,以便我可以添加我的自定义值在它?

bnl4lu3b

bnl4lu3b1#

AccessDeniedExceptionExceptionTranslationFilter处理,然后ExceptionTranslationFilter委托AccessDeniedHandler向客户端写入相应的响应。
如果要自定义此行为,则可以实现AccessDeniedHandler,然后将实现设置为HttpSecurity对象。

我的访问被拒绝处理程序.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);
            }
        }
    }
}

编辑

WebSecurityConfigurerAdapter was deprecated in Spring Security 5.7.0-M2开始,以下内容显示了如何通过注册SecurityFilterChain bean来对其进行配置。

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.exceptionHandling().accessDeniedHandler(new MyAccessDeniedHandler())

    return http.build();
}

老办法

安全配置.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());
    }
}

相关问题