Spring AccessDeniedException中的自定义消息

时间:2019-12-12 10:34:23

标签: java spring-boot spring-mvc spring-security error-handling

在Spring Boot Web应用程序中,只要用户没有必要的权限,我都会在业务代码中抛出org.springframework.security.access.AccessDeniedExceptionAccessDeniedException的优点是Spring会自动调用AuthenticationEntryPoint等。

但是,我使用的是自定义异常消息来指示异常的确切原因(例如throw new AccessDeniedException("You do not have permissions to do XYZ"),但是在生成的JSON响应中,消息始终是“访问被拒绝”,例如:

{
  "timestamp": "2019-12-12T10:01:10.342+0000",
  "status": 403,
  "error": "Forbidden",
  "message": "Access Denied",
  "path": "/myurl"
}

深入研究Spring的代码(DefaultErrorAttributes方法addErrorDetails),可以发现该消息是基于1)异常消息2)请求属性javax.servlet.error.message构建的,第二个覆盖了第一。

有人知道为什么请求属性优先于异常消息吗?

有没有一种简单的方法可以在Spring Boot错误响应中显示自定义异常消息?我想保留默认的Spring Boot错误响应,但是在“消息”字段中输入了我的自定义消息,理想情况下,我希望Spring Boot构造其余的JSON对象(即时间戳,状态等)。最终结果应如下所示:

{
  "timestamp": "2019-12-12T10:01:10.342+0000",
  "status": 403,
  "error": "Forbidden",
  "message": "You do not have permissions to do XYZ",
  "path": "/myurl"
}

这确实适用于其他类型的异常。只是AccessDeniedException似乎是特例。对于这种要求,AccessDeniedException可能不是正确的选择(尽管javadoc建议这样做)?

2 个答案:

答案 0 :(得分:2)

  

将方法放在实现类中   具有AccessDeniedHandler接口的AccessDeniedHandlerImpl

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

    ObjectMapper mapper = new ObjectMapper();
    JsonObject jsonObject=new JsonObject();
    jsonObject.add("timestamp", Instant.now().toString());
    jsonObject.add("status", HttpStatus.FORBIDDEN.value());
    jsonObject.add("error", HttpStatus.FORBIDDEN.getReasonPhrase());
    jsonObject.add("message","********* custom message what you want ********");
    jsonObject.add("path", request.getRequestURI());

    response.setStatus(HttpStatus.FORBIDDEN.value());
    response.getWriter().write( mapper.writeValueAsString(jsonObject));
}

如果您具有以下和

这样的安全配置,请尝试此操作
  

添加 AccessDeniedHandlerImpl作为默认的accessDeniedHandler

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .antMatchers("/css/**", "/index").permitAll()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .antMatchers("/user/**").hasRole("USER")
            .and()
        .formLogin()
            .and()
        .exceptionHandling()
            .accessDeniedHandler((request, response, accessDeniedException) -> {
                AccessDeniedHandler defaultAccessDeniedHandler = new AccessDeniedHandlerImpl();
                defaultAccessDeniedHandler.handle(request, response, accessDeniedException); // handle the custom acessDenied class here
            });
}

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth
        .inMemoryAuthentication()
            .withUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER"))
            .withUser(User.withDefaultPasswordEncoder().username("admin").password("password").roles("ADMIN"));
}
}

答案 1 :(得分:1)

您可以使用Springs ControllerAdvice并使用public void Eat(ILiving food) { food.LifeStatus = LifeStatus.Dead; } 发送自定义错误:

HttpServletResponse

这将返回:

@ControllerAdvice
class AccessDeniedExceptionHandler {

    @ExceptionHandler(value = AccessDeniedException.class)
    public void handleConflict(HttpServletResponse response) throws IOException {
        response.sendError(403, "Your Message");
    }
}

这样,您可以全局捕获特定类型的所有Exception,并使控制器返回指定的自定义值。您也可以这样做,例如对于任何{ "timestamp": 1576248996366, "status": 403, "error": "Forbidden", "message": "Your Message", "path": "/foo/bar" } ,并返回404 Not Found IOException和您指定的消息。