在Spring Boot Web应用程序中,只要用户没有必要的权限,我都会在业务代码中抛出org.springframework.security.access.AccessDeniedException
。 AccessDeniedException
的优点是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建议这样做)?
答案 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
和您指定的消息。