连接 OAuth2 资源服务器和认证服务器

Connecting OAuth2 resource server with authentication server

我正在尝试制作示例 OAuth2 Spring 授权和资源服务器。我的意图是实现两个独立的应用程序——一个代表授权服务器,另一个代表资源服务器。由于我是 Spring 安全方面的新手,我想我需要一些指导才能完成我的任务。

我已经设法使用内存中令牌存储(名为 "OAuth" 的应用程序)实现了一个简单的授权服务器。

AuthServerOAuth2Config.java

@Configuration
@EnableAuthorizationServer
public class AuthServerOAuth2Config extends AuthorizationServerConfigurerAdapter {
    private static final String RESOURCE_ID = "myResource";

    @Autowired
    private UserApprovalHandler handler;

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authManager;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
         // @formatter:off
         clients.inMemory()
                 .withClient("test")
                    .authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
                    .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
                    .scopes("read", "write", "trust")
                    .resourceIds(RESOURCE_ID)
                    .secret("test")
                    .accessTokenValiditySeconds(300).//invalid after 5 minutes.
                    refreshTokenValiditySeconds(600);//refresh after 10 minutes.
         // @formatter:on
    }

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

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

}

OAuth2SecurityConfig.java

@Configuration
@EnableWebSecurity
public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter {
    private static final Logger LOG = LoggerFactory.getLogger(OAuth2SecurityConfig.class);

    @Autowired
    private ClientDetailsService clientService;

    @Autowired
    private DataSource dataSource;

    @Autowired
    public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
        // @formatter:off
        auth.inMemoryAuthentication()
        .withUser("javabycode").password("123456").roles("USER")
        .and()
        .withUser("admin").password("admin123").roles("ADMIN");
        // @formatter:on
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
         http
         .csrf().disable()
         .anonymous().disable()
            .authorizeRequests()
            .antMatchers("/oauth/token").permitAll();
        // @formatter:on
    }

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

    @Bean
    public TokenStore tokenStore() {
        return new JdbcTokenStore(dataSource);
    }

    @Bean
    @Autowired
    public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore) {
        TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
        handler.setTokenStore(tokenStore);
        handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientService));
        handler.setClientDetailsService(clientService);
        return handler;
    }

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

}

正在按预期访问 http://localhost:9081/OAuth/oauth/token?grant_type=password&username=admin&password=admin123 returns 令牌,所以我猜授权服务器配置正常。

现在有一个资源服务器部分(名为 "RestTest" 的应用程序)。我设法找到了一些使用 RemoteTokenServices 访问驻留在另一个应用程序中的令牌服务的示例。到目前为止,这是我的资源服务器。

OAuth2ResourceConfig.java

@Configuration
@EnableResourceServer
@EnableWebSecurity
public class OAuth2ResourceConfig extends ResourceServerConfigurerAdapter {
    private static final String RESOURCE_ID = "myResource";

    private TokenExtractor tokenExtractor = new BearerTokenExtractor();

    @Override
    public void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http.
        anonymous().disable()
        .requestMatchers().antMatchers("/v1/**")
        .and().authorizeRequests()
        .antMatchers("/v1/**").access("hasRole('ADMIN')")
        .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
        // @formatter:on
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws 
        Exception {
         resources.tokenServices(tokenService()).resourceId(RESOURCE_ID).stateless(true);
    }

    @Primary
    @Bean
    public RemoteTokenServices tokenService() {
        RemoteTokenServices tokenService = new RemoteTokenServices();
        tokenService.setCheckTokenEndpointUrl("http://localhost:9081/OAuth/oauth/check_token/");
        tokenService.setClientId("test");
        tokenService.setClientSecret("test");
        return tokenService;
    }
}

我正在尝试保护我的 REST API (http://localhost:9081/RestTest/v1/foobar),所以我相信上面的配置是正确的,对吗?问题是,当我访问 v1/foobar 端点(通过 Postman)时,无需任何身份验证即可访问它。所以我认为我只是缺少配置的某些部分,但我无法弄清楚如何正确连接到授权服务器。还有一件事要提 - 我没有使用 Spring Boot!

我非常感谢您提供一些指导来使我的示例工作。谢谢!

EDIT1:我已将 resourceId 添加到身份验证和资源服务器 - 运气不好。 resourceId 甚至是强制性的吗?

您应该在 ResourceServerAuthorizationServer 中添加 RESOURCE_ID 的方式(您使用该片段更新了您的问题)

@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
    resources.tokenServices(tokenService()).resourceId(RESOURCE_ID).stateless(true);
}

并且在您的授权服务器中

.scopes("read", "write", "trust").resourceIds(RESOURCE_ID)

添加一个 springSecurityFilterChain 因为你在 web.xml 中遗漏了你已经在评论中说过的

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

来自spring docs

It creates a Servlet Filter known as the springSecurityFilterChain which is responsible for all the security (protecting the application URLs, validating submitted username and passwords, redirecting to the log in form, etc) within your application.