我正在构建一个Spring Boot授权服务器,它需要使用两种不同的auth方法生成Oauth2令牌。我希望每个方法都有一个不同的端点,但默认情况下Spring只创建/oauth/token
,虽然它可以更改,但我认为它不可能有两个不同的路径。
作为替代方案,我正在尝试在控制器中创建两个方法,这些方法执行内部转发到/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);
}
这正确地执行了转发,但是auth失败了:
There is no client authentication. Try adding an appropriate authentication filter.
直接发送到/oauth/token
时,相同的请求正常工作,所以我猜测问题是BasicAuthenticationFilter在转发后没有运行。
我怎样才能让它发挥作用?
答案 0 :(得分:1)
我有完全一样的问题。经过研究后,我发现问题是由 Spring Boot 2 引起的,而不是由Spring Security配置引起的。根据{{3}}:
Spring Security和Spring Session过滤器针对ASYNC,ERROR和REQUEST调度程序类型进行配置。
和Spring Boot的Spring Boot 2.0 migration guide源代码:
@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));
}
默认情况下,Spring Boot配置Spring Security,以使其过滤器不会应用于FORWARD请求(仅ASYNC,ERROR和REQUEST),因此在将请求转发到/oauth/token
之前,不会应用过滤器对请求进行身份验证
解决方案很简单。您可以将以下行添加到application.properties
中,以便将默认过滤器应用于所有转发的请求
spring.security.filter.dispatcher-types=async,error,request,forward
或使用路径匹配器和dispatcherType = FORWARD创建您自己的自定义过滤器链,以仅过滤要发送到/oauth/token
的请求。
答案 1 :(得分:-1)
仔细查看为Oauth端点和转发控制器创建的过滤器链,很容易看到后者缺少BasicAuthenticationFilter,因为它们未经过身份验证,并且在转发后不再执行auth
为了解决这个问题,我创建了一个像这样的新配置:
@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
端点最终运行时,它会找到它所期望的auth结果,一切正常。
最后,如果要在两个转发端点上运行不同的ClientDetailsService,则只需创建两个这样的配置类,并在每个配置类中替换setSharedObject
调用中的ClientDetailsService。请注意,为此,您必须在每个类中设置不同的@Order
值。