我正在构建一个Spring Boot授权服务器,该服务器需要使用两种不同的auth方法来生成Oauth2令牌。我希望每种方法都有一个不同的端点,但是默认情况下,Spring仅创建/oauth/token
,尽管可以更改,但我认为不可能有两个不同的路径。
作为替代,我试图在控制器中创建两个对/oauth/token
进行内部forward的方法,向请求中添加参数,以便可以知道它的来源。
我有这样的东西:
@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没有运行。
如何使它起作用?
我有完全相同的问题。经过研究,我发现问题是由Spring Boot 2引起的,而不是由Spring Security配置引起的。根据Spring Boot 2.0 migration guide:
Spring Security和Spring Session过滤器配置为ASYNC,ERROR和REQUEST调度程序类型。
和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 Security,以便其过滤器不会应用于FORWARD请求(而仅应用于ASYNC,ERROR和REQUEST),因此,在将请求转发至[时,不会应用安全过滤器对请求进行身份验证C0]。
解决方案很简单。您可以将以下行添加到/oauth/token
,以便将默认过滤器应用于所有转发的请求
application.properties
或使用路径匹配器和dispatcherType = FORWARD创建您自己的自定义过滤器链,以仅过滤要发送到spring.security.filter.dispatcher-types=async,error,request,forward
的请求。
仔细查看为Oauth端点和转发控制器创建的过滤器链,很容易看到后者缺少BasicAuthenticationFilter,因为它们没有经过身份验证,并且转发后不再执行auth。
为了解决这个问题,我创建了一个新的配置,如下所示:
/oauth/token
此代码模仿了Spring Oauth在后台(@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);
}
}
)所做的工作,在两个端点上使用相同的身份验证选项运行相同的筛选器链。
here端点最终运行时,它将找到期望的身份验证结果,并且一切正常。
最后,如果要在两个转发终结点上运行不同的ClientDetailsService,只需创建两个这样的配置类,然后在每个/oauth/token
调用上替换ClientDetailsService。请注意,为此,您必须在每个类中设置不同的setSharedObject
值。