spring-security - 根据是否通过身份验证委托给不同的控制器?

Geo*_*ins 3 rest spring-mvc spring-security

而不是有一个 REST 控制器,在每个方法中我根据当前用户是否通过身份验证采取不同的操作,我想根据用户的身份验证状态委托给完全不同的控制器实现。

即,我将提供一个包含一组方法签名的接口,每个签名都有一个@RequestMapping注释,然后提供此接口的一个实现以用于经过身份验证的用户和另一个用于未经过身份验证的用户的实现。然后一些逻辑会为当前用户选择合适的实现并分派给它。

rhi*_*nds 5

我知道您有一个适合您的答案,但可能感兴趣的一个可能的解决方案(我猜只是在这一点上的信息)是使用 Spring 自定义映射条件。

我们可以定义一个注解,我们可以用它来装饰我们的控制器——也许像 @AuthenticatedMapping

@Target( ElementType.TYPE )
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthenticatedMapping {} 
Run Code Online (Sandbox Code Playgroud)

(您可以以不同的方式实现这一点,并有一个枚举值来指示特定的角色等,如果您更喜欢这种粒度,也可以将其设置为 METHOD 级别的注释)

然后你可以定义一个自定义RequestCondition- 这是 Spring 将用作为给定请求制定正确处理程序的一部分的类(就像@RequestMapping注释一样)

public class AuthenticatedMappingRequestCondition implements RequestCondition<AuthenticatedMappingRequestCondition> {


    @Override public AuthenticatedMappingRequestCondition getMatchingCondition( HttpServletRequest request ) {
        AuthenticatedMappingRequestCondition condition = null;
        //Check the user is authenticated, if so return this condition:
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if( (authentication != null && !(authentication instanceof AnonymousAuthenticationToken)){
            condition = this;
        }
        return condition;
    }

    //
    //TODO other methods need to be implemented here - all pretty simple
    //

}
Run Code Online (Sandbox Code Playgroud)

所以现在我们有了映射条件,我们只需要扩展标准 Spring,RequestMappingHandlerMapping以便在考虑映射决策时包括我们的自定义条件:

public class AuthenticatedMappingRequestMappingHandlerMapping extends RequestMappingHandlerMapping {

    @Override protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
        AuthenticatedMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, AuthenticatedMapping.class);
        return (typeAnnotation != null) ? new AuthenticatedMappingRequestCondition() : null;
    }
}
Run Code Online (Sandbox Code Playgroud)

一旦你用 Spring 连接了自定义条件,那么你就可以用你的注解装饰任何控制器,Spring 将使用条件来路由请求:

@RestController
@AuthenticatedMapping
@RequestMapping("/account")
public class AuthenticatedAccountRestController {
    @RequestMapping("/someCommonRequestA")
    public String someCommonRequestA() {
        return "Got authenticated someCommonRequestA";
    }
}

@RestController
@RequestMapping("/account")
public class AnonymousAccountRestController {
    @RequestMapping("/someCommonRequestA")
    public String someCommonRequestA() {
        return "Got anonymous someCommonRequestA";
    }
}
Run Code Online (Sandbox Code Playgroud)

一些注意事项:

  • 您需要测试是否缺少注释足以路由到匿名控制器 - 如果没有,可以使用允许的角色枚举来增强注释 - 并显式添加@AuthenticatedMapping( Roles.ANONYMOUS )到该控制器
  • 由于它是一个单一端点,它不处理特定的安全性内容,因此仍然需要放置安全性内容,而不是 100% 不会对 spring-security 身份验证内容有其他考虑

不建议它比你拥有的方法更好,但我不得不使用这种方法基于子域进行自定义路由一段时间(我在这里写了它- 这是上面代码的基础),我发现它很一个很好的、惯用的 Spring-y 解决方案,当我不得不将它推广到许多控制器/端点时,因为样板被抽象为 Spring 机器并且控制器被很好地装饰。

无论如何,如果没有别的,看看可能会很有趣:)