Spring MVC访问Spring Security ConfigAttributes?

JJ *_*kar 5 spring spring-mvc spring-security spring-boot

我想生成HTTP响应主体,除了403 ForbiddenHTTP状态代码之外,还引用了一个错误消息,引用了诸如_"missing ...'CUSTOM_AUTHORITY'"_之类的内容.

我的应用程序是@PreAuthorize在Spring-MVC-REST中使用Spring-Security-Secured 方法的Spring Boot @Controller:

myController的

@Controller
@RequestMapping("/foo")
public FooController{
  @PreAuthorize("hasAuthority('CUSTOM_AUTHORITY')")
  public Object getSomething(){ ... }
}
Run Code Online (Sandbox Code Playgroud)

GlobalExceptionHandlerResolver

@ControllerAdvice
public class GlobalExceptionHandler {
  @ExceptionHandler(AccessDeniedException.class)
  @ResponseStatus(HttpStatus.FORBIDDEN)
  public Object forbidden(AccessDeniedException exception){ ... }
}
Run Code Online (Sandbox Code Playgroud)

想要的是暴露/注入Collection<ConfigAttribute>.在Spring Security的文档中引用它.

Nat*_*Far 1

似乎没有一种简单的方法可以实现这一点。(AccessDecisionManagerAffirmativeBased) 抛出 ,AccessDeniedException但没有任何您想要的信息。因此,如果您想“公开/注入” Collection<ConfigAttribute>,您需要提供自己的AccessDecisionManager异常来抛出保存ConfigAttributes 的自定义异常。

最简单的方法是用AccessDecisionManager您自己的默认值包装默认值并对其进行委托方法调用:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled=true)
CustomMethodSecurityConfig extends GlobalMethodSecurityConfiguration

    @Override
    protected AccessDecisionManager accessDecisionManager() {
        AccessDecisionManager default = super.accessDecisionManager();
        MyCustomDecisionManager custom = new CustomDecisionManager(default);
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以AccessDecisionManager按如下方式定义您的自定义:

public class MyCustomDecisionManager implements AccessDecisionManager {

    private AccessDecisionManager default;

    public MyCustomDecisionManager(AccessDecisionManager acm) {
        this.default = acm;
    }

    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException{
        try {
            default.decide(authentication, object, configAttributes)
        } catch(AccessDeniedException ex) {
            throw new CustomAccessDeniedException(ex.getMessage(), configAttributes);
        }
    }

    // other methods delegate to default
}
Run Code Online (Sandbox Code Playgroud)

现在,每当访问被拒绝时,您都会收到一个包含Collection<ConfigAttribute>.

您的自定义异常可能如下所示:

public class CustomAccessDeniedException extends AccessDeniedException {
    private Collection<ConfigAttribute> attributes;

    public CustomAccessDeniedException(String message, Collection<ConfigAttribute> attr) {
        super(message);
        this.attributes = attr;
    }

    public Collection<ConfigAttribute> getAttributes() {
        return this.attributes;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您的 @ExceptionHandler 可以处理您的CustomAccessDeniedException并可以访问ConfigAttributes。

但是... 我不确定这是否会向您提供您想要的错误消息。该ConfigAttribute接口只有一个方法:

String getAttribute();
Run Code Online (Sandbox Code Playgroud)

javadoc 指出:

如果 ConfigAttribute 无法以足够的精度表示为字符串,则应返回 null。

由于我们不能依赖接口方法,因此如何处理每个方法ConfigAttribute将在很大程度上取决于您正在处理的特定对象的类型。

例如,ConfigAttribute对应于@PreAuthorize("hasAuthority('CUSTOM_AUTHORITY')")is PreInvocationExpressionAttribute,要打印类似于您想要的内容,您可以执行以下操作:

PreInvocationExpressionAttribute attr = (PreInvocationExpressionAttribute)configAttribute;

String expressionString = attr.getAuthorizeExpression().getExpressionString();

System.out.println(expressionString); // "hasAuthority('CUSTOM_AUTHORITY')"
Run Code Online (Sandbox Code Playgroud)

这是主要的缺点。此外,您将获得所有s ConfigAttribute,而不一定是失败的。