2016-01-06 301 views
1

我有一个使用XML的Spring-Security OAuth2配置,我试图将其转换为基于Java的配置。这是一个简单的密码授权方案,授权和资源服务器都在同一个应用程序中。 XML配置工作正常。然而,在访问/的OAuth /令牌端点时请求令牌,基于Java的配置给人的StackOverflowError,循环上使用Java配置的Spring-Security OAuth2 StackOverflowError

org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:446)  
org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:192). 

是怎么回事?

Java的配置:

@Configuration 
@EnableWebSecurity 
public class MyAppSpringSecurityConfig extends WebSecurityConfigurerAdapter { 
    @Override 
    protected void configure(AuthenticationManagerBuilder auth) throws Exception { 
     auth.inMemoryAuthentication() 
       .withUser("admin") 
       .password("supersecret") 
       .roles("ROLE_USER", "ROLE_ADMIN"); 
    } 

    @Bean 
    public AuthenticationManager authenticationManager() throws Exception { 
     return super.authenticationManagerBean(); 
    } 

    @Configuration 
    @EnableAuthorizationServer 
    protected static class OAuth2AuthConfig extends AuthorizationServerConfigurerAdapter { 
     @Autowired private AuthenticationManager authenticationManager; 

     @Override 
     public void configure(ClientDetailsServiceConfigurer clients) throws Exception { 
      clients.inMemory() 
        .withClient("myclient") 
        .secret("password123") 
        .authorizedGrantTypes("password", "refresh_token") 
        .authorities("ROLE_APP") 
        .scopes("myapp") 
        .accessTokenValiditySeconds(60 * 60); // 1 hour 
     } 

     @Override 
     public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { 
      endpoints.authenticationManager(authenticationManager); 
     } 
    } 

    @Configuration 
    @EnableResourceServer 
    protected static class OAuth2ResourceConfig extends ResourceServerConfigurerAdapter { 
     @Override 
     public void configure(HttpSecurity http) throws Exception { 
      http.requestMatchers().antMatchers("/services/**").and() 
        .authorizeRequests().antMatchers("/services/**").authenticated(); 
     } 
    } 
} 

XML配置(工作):

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:oauth="http://www.springframework.org/schema/security/oauth2" 
     xmlns:sec="http://www.springframework.org/schema/security" 
     xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2.xsd 
          http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd 
          http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 

    <sec:http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="clientAuthenticationManager"> 
     <sec:intercept-url pattern="/oauth/token" access="isFullyAuthenticated()"/> 
     <sec:http-basic entry-point-ref="clientAuthenticationEntryPoint"/> 

     <sec:custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER"/> 

     <sec:access-denied-handler ref="oauthAccessDeniedHandler"/> 

     <sec:anonymous enabled="false"/> 
     <sec:csrf disabled="true"/> 
    </sec:http> 

    <sec:http pattern="/services/**" create-session="never" entry-point-ref="oauthAuthenticationEntryPoint"> 
     <sec:intercept-url pattern="/services/**" access="hasRole('ROLE_USER')"/> 

     <sec:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER"/> 

     <sec:access-denied-handler ref="oauthAccessDeniedHandler"/> 

     <sec:anonymous enabled="false"/> 
     <sec:csrf disabled="true"/> 
    </sec:http> 

    <bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"> 
     <property name="realmName" value="myapp"/> 
    </bean> 

    <bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"> 
     <property name="realmName" value="myapp/client"/> 
     <property name="typeName" value="Basic"/> 
    </bean> 

    <bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"/> 

    <bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter"> 
     <property name="authenticationManager" ref="clientAuthenticationManager"/> 
    </bean> 

    <bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased"> 
     <constructor-arg> 
      <list> 
       <bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter"/> 
       <bean class="org.springframework.security.access.vote.RoleVoter"/> 
       <bean class="org.springframework.security.access.vote.AuthenticatedVoter"/> 
      </list> 
     </constructor-arg> 
    </bean> 

    <sec:authentication-manager id="clientAuthenticationManager"> 
     <sec:authentication-provider user-service-ref="clientDetailsUserService"/> 
    </sec:authentication-manager> 

    <bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService"> 
     <constructor-arg ref="clientDetails"/> 
    </bean> 

    <bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/> 

    <sec:authentication-manager alias="authenticationManager"> 
     <sec:authentication-provider> 
      <sec:user-service> 
       <sec:user name="admin" password="supersecret" authorities="ROLE_USER,ROLE_ADMIN"/> 
      </sec:user-service> 
     </sec:authentication-provider> 
    </sec:authentication-manager> 

    <bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore"/> 

    <bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices"> 
     <property name="tokenStore" ref="tokenStore"/> 
     <property name="supportRefreshToken" value="true"/> 
     <property name="accessTokenValiditySeconds" value="3600"/> 
     <property name="clientDetailsService" ref="clientDetails"/> 
    </bean> 

    <oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices"> 
     <oauth:refresh-token/> 
     <oauth:password/> 
    </oauth:authorization-server> 

    <oauth:resource-server id="resourceServerFilter" resource-id="myapp" token-services-ref="tokenServices"/> 

    <oauth:client-details-service id="clientDetails"> 
     <oauth:client client-id="myapp" secret="password123" authorized-grant-types="password,refresh_token" scope="myapp" authorities="ROLE_APP"/> 
    </oauth:client-details-service> 
</beans> 

我知道很多在XML中指定的东西是通过@EnableAuthorizationServer和@EnableResourceServer违约,但显然我还是错过了一些东西。我已经回顾了示例应用程序和单元测试。但是在这些配置中似乎还会出现更多与Spring Boot相关的魔法。

这是春天4.2.3,Spring Security的4.0.3和Spring安全的OAuth 2.0.8

回答

1

如果你想暴露的AuthenticationManager实例作为一个bean,你需要重写public AuthenticationManager authenticationManagerBean()而非protected AuthenticationManager authenticationManager()

见的JavaDoc:http://docs.spring.io/spring-security/site/docs/current/apidocs/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.html#authenticationManagerBean--

+0

通过最简单的错误咬伤,并覆盖有正确的名称,而不是真实的埋在Javadoc中的方法。谢谢Christophe。 –