Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/spring-mvc/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Spring mvc 带有@Preauthorize和@ControllerAdvice的自定义错误消息_Spring Mvc_Spring Security_Annotations - Fatal编程技术网

Spring mvc 带有@Preauthorize和@ControllerAdvice的自定义错误消息

Spring mvc 带有@Preauthorize和@ControllerAdvice的自定义错误消息,spring-mvc,spring-security,annotations,Spring Mvc,Spring Security,Annotations,我们正在使用spring和spring-security-3.2。最近,我们在RestAPIs中添加了注解@PreAuthorize(之前它是基于URL的) 我们已经有了用-@ControllerAdvice注释的全局异常处理程序和定制的PermissionEvaluator,除了错误消息之外,一切正常 假设某个用户在没有“ViewSalesOrder”权限的情况下访问API,那么spring在默认情况下抛出异常“Access is denied”,但没有指出缺少哪个权限(我们的要求是提及缺少哪

我们正在使用spring和spring-security-3.2。最近,我们在RestAPIs中添加了注解@PreAuthorize(之前它是基于URL的)

我们已经有了用-@ControllerAdvice注释的全局异常处理程序和定制的PermissionEvaluator,除了错误消息之外,一切正常

假设某个用户在没有“ViewSalesOrder”权限的情况下访问API,那么spring在默认情况下抛出异常“Access is denied”,但没有指出缺少哪个权限(我们的要求是提及缺少哪个权限)

是否可能引发也包含权限名称的异常,因此最终的错误消息应该是“访问被拒绝,您需要ViewSalesOrder权限”(此处权限名称应该来自@PreAuthorize annotation)


请注意,我们已经准备了100个这样的restAPI,因此我们非常感谢通用解决方案。

由于
PermissionEvaluator
界面不允许您在传递缺少的权限的同时传递评估结果,因此无法实现您的期望。
此外,
AccessDecisionManager
决定与
AccessDecisionVoter
实例投票相关的最终授权,其中一个实例是
PreInvocationAuthorizationAdviceVoter
,它就
@PreAuthorize
值的评估进行投票。
长话短说,
PreInvocationAuthorizationAdviceVoter
在您的自定义
PermissionEvaluator
返回
false
hasPermission
调用时对请求投反对票(给予请求-1分)。如您所见,无法传播此流中失败的原因

另一方面,您可以尝试一些变通方法来实现所需的功能。

一种方法是在权限检查失败时,在自定义的
PermissionEvaluator
中引发异常。您可以使用此异常将缺少的权限传播到全局异常处理程序。在这里,您可以将缺少的权限作为参数传递给消息描述符。请注意,这将停止执行
AccessDecisionManager
的过程,这意味着不会执行后续投票者(默认值为和)。如果你选择走这条路,你应该小心。


另一种更安全但更笨拙的方法是在响应403之前实现自定义并自定义错误消息
AccessDeniedHandler
提供当前的
HttpServletRequest
,可用于检索请求URI。但是,在这种情况下,坏消息是,您需要URI到权限的映射才能找到丢失的权限。

我已经实现了Mert Z提到的第二种可能的解决方案。我的解决方案仅适用于API层中使用的@PreAuthorize注释(例如使用@RequestMapping)。我已经注册了一个自定义AccessDeniedHandler bean,在该bean中我获得了禁止API方法的@PreAuthorize注释的值,并将其填入错误消息中

公共类CustomAccessDeniedHandler实现AccessDeniedHandler{
私人调度员小调度员小调度员;
公共无效句柄(HttpServletRequest请求、HttpServletResponse响应、,
AccessDeniedException(AccessDeniedException)引发IOException,
ServletException{
如果(!response.isCommitted()){
List handlerMappings=dispatcherServlet.getHandlerMappings();
if(handlerMappings!=null){
HandlerExecutionChain处理程序=null;
用于(HandlerMapping HandlerMapping:handlerMappings){
试一试{
handler=handlerMapping.getHandler(请求);
}捕获(例外e){}
if(处理程序!=null)
打破
}
if(handler!=null&&handler.getHandler()HandlerMethod实例){
HandlerMethod=(HandlerMethod)handler.getHandler();
预授权methodAnnotation=method.getMethodAnnotation(预授权.class);
if(methodAnnotation!=null){
response.sendError(HttpStatus.FORBIDDEN.value(),
未满足授权条件:“+methodAnnotation.value());
返回;
}
}
}
response.sendError(HttpStatus.FORBIDDEN.value(),
HttpStatus.FORBIDDEN.GetReasonPhase());
}
}
@注入
公共无效setDispatcherServlet(DispatcherServlet DispatcherServlet){
this.dispatcherServlet=dispatcherServlet;
}
}
处理程序已在WebSecurity配置适配器中注册:

@EnableGlobalMethodSecurity(jsr250Enabled=true,preprestenabled=true)
@启用Web安全性
公共抽象类BaseSecurityInitializer扩展了WebSecurity配置适配器{
@凌驾
受保护的无效配置(HttpSecurity http)引发异常{
...
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler());
...
}
@豆子
公共AccessDeniedHandler AccessDeniedHandler(){
返回新的CustomAccessDeniedHandler();
}
}
请注意,如果还有带有@ControllerAdvice的全局资源异常处理程序,则不会执行CustomAccessDeniedHandler。我通过在全局处理程序中重新调用异常(如此处所建议的)来解决此问题:

@ControllerAdvice
公共类ResourceExceptionHandler{
@ExceptionHandler(AccessDeniedException.class)
公共响应Entity accessDeniedException(accessDeniedException e)抛出accessDeniedException{
log.info(例如toString());
投掷e;
}
}

您采用了什么解决方案?我还是不能忍受
     @PreAuthorize("hasPermission('salesorder','ViewSalesOrder')")
  @RequestMapping(value = "/restapi/salesorders/", method = RequestMethod.GET)
  public ModelAndView getSalesOrders(){}