Spring OAuth2 2.0.9升级

Spring OAuth2 2.0.9 upgrade

我正在尝试将 Spring Security OAuth2 从 2.0.3 升级到 2.0.9。 下面是我的配置。

@Configuration
public class SecurityConfig {

@Autowired
private ClientDetailsService clientDetailsService;

@Autowired
private RedisConnectionFactory redisConnectionFactory;

@Bean
public TokenStore tokenStore() {
    return new RedisTokenStore(redisConnectionFactory);
}

@Primary
@Bean
public AuthorizationServerTokenServices tokenServices() throws Exception {
    final DefaultTokenServices tokenServices = new DefaultTokenServices();
    tokenServices.setAccessTokenValiditySeconds(6000);
    tokenServices.setClientDetailsService(clientDetailsService);
    tokenServices.setTokenEnhancer(new RedrumTokenEnhancer());
    tokenServices.setSupportRefreshToken(true);
    tokenServices.setTokenStore(tokenStore());
    return tokenServices;
}

@Bean
public UserApprovalHandler userApprovalHandler() throws Exception {
    RedrumUserApprovalHandler handler = new RedrumUserApprovalHandler();
    handler.setApprovalStore(approvalStore());
    handler.setClientDetailsService(clientDetailsService);
    handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
    handler.setUseApprovalStore(true);
    return handler;
}

@Bean
public ApprovalStore approvalStore() {
    TokenApprovalStore store = new TokenApprovalStore();
    store.setTokenStore(tokenStore());
    return store;
}

@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
@EnableWebSecurity
protected static class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${baseUrl}") 
    private String baseUrl;

    @Autowired
    private DataSource dataSource;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Bean
    public ClientDetailsService clientDetailsService() throws Exception {
        ClientDetailsServiceConfiguration serviceConfig = new ClientDetailsServiceConfiguration();

        serviceConfig.clientDetailsServiceConfigurer().inMemory()
            .withClient("xyz")
            .secret("...................")
            .authorizedGrantTypes("password", "authorization_code", "refresh_token", "client_credentials")
            .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
            .scopes("blah")

        return serviceConfig.clientDetailsService();
    }

    @Bean
    public UserDetailsService clientDetailsUserDetailsService() throws Exception {
        return new ClientDetailsUserDetailsService(clientDetailsService());
    }

    @Bean
    public ClientCredentialsTokenEndpointFilter clientCredentialsTokenEndpointFilter() throws Exception {
        ClientCredentialsTokenEndpointFilter filter = new ClientCredentialsTokenEndpointFilter();
        filter.setAuthenticationManager(authenticationManagerBean());
        filter.afterPropertiesSet();
        return filter;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcUserDetail = new JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder>();
        jdbcUserDetail.dataSource(dataSource);
        jdbcUserDetail.passwordEncoder(passwordEncoder);
        jdbcUserDetail.authoritiesByUsernameQuery("select a.username, r.role_name from account a, role r, account_role ar where a.id = ar.account_id and r.id = ar.role_id and a.username = ?");
        jdbcUserDetail.usersByUsernameQuery("select a.username, a.password, a.enabled, a.email from account a where a.username = ?");
        auth.apply(jdbcUserDetail);

        auth.userDetailsService(clientDetailsUserDetailsService());


    }

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

    @Bean
    protected AuthenticationEntryPoint authenticationEntryPoint() {
        OAuth2AuthenticationEntryPoint entryPoint = new OAuth2AuthenticationEntryPoint();
        entryPoint.setTypeName("Basic");
        entryPoint.setRealmName("zzz/client");
        return entryPoint;
    }

    @Override
    public void configure(WebSecurity webSecurity) throws Exception {
        webSecurity
            .ignoring()
            .antMatchers("/resources/**", "/swagger/**", "/copyright*", "/api-docs/**")
            .antMatchers(HttpMethod.POST, "/api/**/account")
        .and()
            .debug(false);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // @formatter:off
        http
            .anonymous().disable()
            .requiresChannel().anyRequest().requiresSecure();

        http
            .antMatcher("/oauth/token")
            .authorizeRequests().anyRequest().authenticated()
        .and()
            .httpBasic().authenticationEntryPoint(authenticationEntryPoint())
        .and()
            .csrf().requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/token")).disable()
            .exceptionHandling().accessDeniedHandler(oAuth2AccessDeniedHandler())
        .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http
            .addFilterBefore(clientCredentialsTokenEndpointFilter(), BasicAuthenticationFilter.class);
        // @formatter:on

    }

    @Bean
    public OAuth2AccessDeniedHandler oAuth2AccessDeniedHandler() {
        return new OAuth2AccessDeniedHandler();
    }


}

@Configuration
@EnableResourceServer
protected static class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    private ResourceServerTokenServices tokenServices;

    @Autowired
    private OAuth2AccessDeniedHandler oAuth2AccessDeniedHandler;

    @Autowired
    private ClientCredentialsTokenEndpointFilter clientCredentialsTokenEndpointFilter;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.tokenServices(tokenServices);
        resources.resourceId("My resource");
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {

        // @formatter:off
        http
            .requiresChannel().anyRequest().requiresSecure();

        // API calls
        http
            .anonymous().disable()
            .authorizeRequests()
            .antMatchers("/api/**", "/whatever")
            .access("#oauth2.hasScope('blah') and (hasRole('ROLE_USER'))")
        .and()
            .addFilterBefore(clientCredentialsTokenEndpointFilter, BasicAuthenticationFilter.class)
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.NEVER)
        .and()
            .exceptionHandling()
            .accessDeniedHandler(oAuth2AccessDeniedHandler);

        // @formatter:on
    }

}

@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthorizationServerTokenServices tokenServices;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    private UserApprovalHandler userApprovalHandler;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private AuthenticationEntryPoint authenticationEntryPoint;

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

    @Override
    public void configure(ClientDetailsServiceConfigurer clients)
            throws Exception {
        clients.withClientDetails(clientDetailsService);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer)
            throws Exception {
        oauthServer.authenticationEntryPoint(authenticationEntryPoint)
                .realm("zzz/clients");
    }

}
}

这在 2.0.3 上运行良好,但现在升级到 2.0.9 后,我开始获得 "Unsupported grant type: password"

这是测试;

curl -k -i -H "Accept: application/json" -X POST -d "grant_type=password&client_id=xyz&client_secret=zzzzzz&scope=blah&username=tester&password=121212" https://localhost:8443/myapp/oauth/token

结果是;

{"error":"unsupported_grant_type","error_description":"Unsupported grant type: password"}

我正在使用 springframework.version 4.1.8.RELEASE 和 spring-security.version 3.2.8.RELEASE 如果我能得到这方面的帮助,我将不胜感激。

问题是你配置太多了。 大多数东西,例如 SessionCreationPolicyOAuth2AccessDeniedHandlerAuthenticationEntryPointClientCredentialsTokenEndpointFilter 已经配置好了。

不要混淆 ClientDetailsServiceUserDetailsService。如果您不知道它是什么,请尽量避免使用 ClientDetailsUserDetailsService

@Bean 声明移动到正确的位置,这样它们就可以连接起来了。

关心 @Order

试试这个:

@Configuration
public class SecurityConfig {

    @Configuration
    @Order(4)
    @EnableWebSecurity
    protected static class WebSecurityConfig extends WebSecurityConfigurerAdapter {

        @Value("${baseUrl}")
        private String baseUrl;

        @Autowired
        private DataSource dataSource;

        @Resource
        private PasswordEncoder passwordEncoder;

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcUserDetail = new JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder>();
            jdbcUserDetail.dataSource(dataSource);
            jdbcUserDetail.passwordEncoder(passwordEncoder);
            jdbcUserDetail.authoritiesByUsernameQuery(
                    "select a.username, r.role_name from account a, role r, account_role ar where a.id = ar.account_id and r.id = ar.role_id and a.username = ?");
            jdbcUserDetail.usersByUsernameQuery(
                    "select a.username, a.password, a.enabled, a.email from account a where a.username = ?");
            auth.apply(jdbcUserDetail);
        }

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

        @Override
        public void configure(WebSecurity webSecurity) throws Exception {
            webSecurity
                .ignoring()
                    .antMatchers("/resources/**", "/swagger/**", "/copyright*", "/api-docs/**")
                    .antMatchers(HttpMethod.POST, "/api/**/account")
                .and()
                    .debug(false);
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {

            http.requiresChannel().anyRequest().requiresSecure();

            // @formatter:off
            http
                .authorizeRequests()
                    .anyRequest().hasRole("USER")
                    .and()
                // TODO put CSRF protection back into this endpoint
                .csrf()
                    .requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize")).disable()
                     ;
            // @formatter:on
        }

    }

    @Configuration
    @EnableResourceServer
    protected static class ResourceServerConfig extends ResourceServerConfigurerAdapter {

        @Autowired
        private ResourceServerTokenServices tokenServices;

        @Autowired
        private OAuth2AccessDeniedHandler oAuth2AccessDeniedHandler;

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.tokenServices(tokenServices);
            resources.resourceId("My resource");
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {

            // @formatter:off
            http.requiresChannel().anyRequest().requiresSecure();

            // API calls
            http
                .requestMatchers()
                    .antMatchers("/api/**", "/whatever")
                .and()
                    .authorizeRequests()
                    .anyRequest()
                    .access("#oauth2.hasScope('blah') and (hasRole('ROLE_USER'))");

            // @formatter:on
        }

    }

    @Configuration
    @EnableAuthorizationServer
    protected static class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

        @Autowired
        private AuthenticationManager authenticationManager;

        @Autowired
        private RedisConnectionFactory redisConnectionFactory;

        @Bean
        public TokenStore tokenStore() {
            return new RedisTokenStore(redisConnectionFactory);
        }

        @Bean
        public UserApprovalHandler userApprovalHandler() throws Exception {
            RedrumUserApprovalHandler handler = new RedrumUserApprovalHandler();
            handler.setApprovalStore(approvalStore());
            handler.setClientDetailsService(clientDetailsService());
            handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService()));
            handler.setUseApprovalStore(true);
            return handler;
        }

        @Bean
        public ApprovalStore approvalStore() {
            TokenApprovalStore store = new TokenApprovalStore();
            store.setTokenStore(tokenStore());
            return store;
        }

        @Primary
        @Bean
        public DefaultTokenServices tokenServices() throws Exception {
            final DefaultTokenServices tokenServices = new DefaultTokenServices();
            tokenServices.setAccessTokenValiditySeconds(6000);
            tokenServices.setClientDetailsService(clientDetailsService());
            tokenServices.setTokenEnhancer(new RedrumTokenEnhancer());
            tokenServices.setSupportRefreshToken(true);
            tokenServices.setTokenStore(tokenStore());
            return tokenServices;
        }

        @Bean
        public ClientDetailsService clientDetailsService() throws Exception {
            ClientDetailsServiceConfiguration serviceConfig = new ClientDetailsServiceConfiguration();

            serviceConfig.clientDetailsServiceConfigurer().inMemory()
                .withClient("xyz")
                .secret("...................")
                .authorizedGrantTypes("password", "authorization_code", "refresh_token", "client_credentials")
                .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
                .scopes("blah")
                ;

            return serviceConfig.clientDetailsService();
        }

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

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients
                .withClientDetails(clientDetailsService());
        }

        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
            oauthServer
                .realm("zzz/clients")
                .allowFormAuthenticationForClients();
        }

    }

}