Spring:转发到 /oauth/token 端点失去身份验证

Spring: forwarding to /oauth/token endpoint loses authentication

我正在构建一个 Spring 启动授权服务器,它需要使用两种不同的身份验证方法生成 Oauth2 令牌。我希望每个方法都有不同的端点,但默认情况下 Spring 只创建 /oauth/token,虽然它可以更改,但我认为不可能有两个不同的路径。

作为替代方案,我试图在控制器中创建两个方法,它们执行内部 forward/oauth/token,向请求添加一个参数,所以我可以知道它是从哪里来的。

我有这样的东西:

@RequestMapping(value = "/foo/oauth/token", method = RequestMethod.POST)
public ModelAndView fooOauth(ModelMap model) {
    model.addAttribute("method", "foo");
    return new ModelAndView("forward:/oauth/token", model);
}

这正确执行了转发,但身份验证失败:

There is no client authentication. Try adding an appropriate authentication filter.

同样的请求直接发送到/oauth/token时可以正常工作,所以我猜测问题是转发后的BasicAuthenticationFilter不是运行。

我怎样才能让它发挥作用?

仔细查看为 Oauth 端点和转发控制器创建的过滤器链,很容易看出后者缺少 BasicAuthenticationFilter,因为它们未经过身份验证,并且在之后不会再次执行身份验证前锋。

为了解决这个问题,我创建了一个这样的新配置:

@Configuration
public class ForwarderSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private List<AuthorizationServerConfigurer> configurers = Collections.emptyList();

    @Autowired
    private FooClientDetailsService fooClientDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer();
        for (AuthorizationServerConfigurer configurerBit : configurers) configurerBit.configure(configurer);
        http.apply(configurer);
        http
                .authorizeRequests()
                    .antMatchers("/foo/oauth/token").fullyAuthenticated()
                .and()
                    .requestMatchers()
                    .antMatchers("/foo/oauth/token");
        http.setSharedObject(ClientDetailsService.class, fooClientDetailsService);

    }

}

此代码模仿 Spring Oauth 在幕后所做的工作 (here),运行在两个端点上使用相同的身份验证选项设置相同的过滤器链。

/oauth/token 端点最终 运行 时,它找到了它期望的身份验证结果,并且一切正常。

最后,如果您想 运行 在两个转发端点上使用不同的 ClientDetailsS​​ervice,您只需创建两个像这样的配置 classes,并替换 [=12] 上的 ClientDetailsS​​ervice =] 召集他们每个人。请注意,为此,您必须在每个 class.

中设置不同的 @Order

我遇到了完全相同的问题。经过一些研究,我发现问题是由 Spring Boot 2 而不是 Spring 安全配置引起的。根据 Spring Boot 2.0 migration guide:

Spring Security and Spring Session filters are configured for ASYNC, ERROR, and REQUEST dispatcher types.

和 Spring Boot 的 SecurityFilterAutoConfiguration 源代码:

@Bean
@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
        SecurityProperties securityProperties) {
    DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
            DEFAULT_FILTER_NAME);
    registration.setOrder(securityProperties.getFilter().getOrder());
    registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
    return registration;
}

private EnumSet<DispatcherType> getDispatcherTypes(
        SecurityProperties securityProperties) {
    if (securityProperties.getFilter().getDispatcherTypes() == null) {
        return null;
    }
    return securityProperties.getFilter().getDispatcherTypes().stream()
            .map((type) -> DispatcherType.valueOf(type.name())).collect(Collectors
                    .collectingAndThen(Collectors.toSet(), EnumSet::copyOf));
}

其中 securityProperties.getFilter().getDispatcherTypes() 的默认值在 SecurityProperties 中定义为:

private Set<DispatcherType> dispatcherTypes = new HashSet<>(
    Arrays.asList(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST));

因此,默认情况下,Spring Boot 配置 Spring 安全性,以便其过滤器不会应用于 FORWARD 请求(但仅适用于 ASYNC、ERROR 和 REQUEST),因此没有安全过滤器会在将请求转发到 /oauth/token.

时应用于对请求进行身份验证

解决方法很简单。您可以将以下行添加到 application.properties 以便将默认过滤器应用于所有转发的请求

spring.security.filter.dispatcher-types=async,error,request,forward

或者使用路径匹配器和 dispatcherType=FORWARD 创建您自己的自定义过滤器链,以仅过滤转发到 /oauth/token.

的请求