【问题标题】:validate oauth2 access token using spring security使用 Spring Security 验证 oauth2 访问令牌
【发布时间】:2017-08-03 10:32:30
【问题描述】:

我有以下设置,所有进程都在不同的机器上运行,它们之间没有通用数据库。

1.Resource server(my protected resources)
2.authorization server(WS02--> for Issuing OAuth tokens)
3.client Application(trying to access the resource server)

ClientApp 联系授权服务器并获取访问令牌并使用此令牌联系资源服务器。如何验证访问令牌@resource 服务器?

我的授权服务器提供 REST API,我可以在其中提供访问令牌作为输入并检查令牌的有效性。

我是 Spring Security 的新手,所以在提供资源内容之前是否有任何内置 API/回调来验证令牌。有关如何验证令牌@resource 服务器的任何指示都会有所帮助。

【问题讨论】:

    标签: spring-boot spring-security


    【解决方案1】:

    client App 尝试访问resource server 时,资源服务器会咨询Auth server 并验证访问令牌。

    资源服务器的配置:

    <bean id="tokenServices"  class="org.springframework.security.oauth2.provider.token.RemoteTokenServices">
        <property name="checkTokenEndpointUrl" value="http://localhost:8181/OUTPOST/oauth/check_token"/>
        <property name="clientId" value="atlas"/>
        <property name="clientSecret" value="atlas"/>
    </bean>
    

    在资源服务器上使用RemoteTokenService 而不是DefaultTokenServiceoauth/check_token是连接resource-server和auth-server的端点。

    身份验证服务器上的配置

    @EnableAuthorizationServer
    @Configuration
    public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    
    private static String REALM = "OUTPOST_API";
    
    private ClientDetailsService clientService;
    
    @Autowired
    public AuthorizationServerConfig(AuthenticationManager authenticationManager,
            RedisConnectionFactory redisConnectionFactory, ClientDetailsService clientService) {
        this.authenticationManager = authenticationManager;
        this.redisTokenStore = new RedisTokenStore(redisConnectionFactory);
        this.clientService = clientService;
    }
    
    private AuthenticationManager authenticationManager;
    
    private TokenStore redisTokenStore;
    
    @Autowired
    private UserApprovalHandler userApprovalHandler;
    
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;
    
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    
        security.tokenKeyAccess("isAuthenticated()").checkTokenAccess("isAuthenticated()")
                .allowFormAuthenticationForClients().realm(REALM + "/client");
    }
    
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {        
        clients.withClientDetails(clientService);
    }
    
    @Bean
    @Autowired
    public TokenStore tokenStore(RedisConnectionFactory redisConnectionFactory) {
        this.redisTokenStore = new RedisTokenStore(redisConnectionFactory);
        return this.redisTokenStore;
    }
    
    @Bean
    public WebResponseExceptionTranslator loggingExceptionTranslator() {
        return new DefaultWebResponseExceptionTranslator() {
            @Override
            public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
                // This is the line that prints the stack trace to the log. You can customise
                // this to format the trace etc if you like
                e.printStackTrace();
    
                // Carry on handling the exception
                ResponseEntity<OAuth2Exception> responseEntity = super.translate(e);
                HttpHeaders headers = new HttpHeaders();
                headers.setAll(responseEntity.getHeaders().toSingleValueMap());
                OAuth2Exception excBody = responseEntity.getBody();
                return new ResponseEntity<>(excBody, headers, responseEntity.getStatusCode());
            }
        };
    }
    
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(redisTokenStore).userApprovalHandler(userApprovalHandler)
                .authenticationManager(authenticationManager).exceptionTranslator(loggingExceptionTranslator());
    }
    
    public void setRedisConnectionFactory(RedisConnectionFactory redisConnectionFactory) {
        this.redisConnectionFactory = redisConnectionFactory;
    }
    
    @Bean
    public TokenStoreUserApprovalHandler userApprovalHandler() {
        TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
        handler.setTokenStore(redisTokenStore);
        handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientService));
        handler.setClientDetailsService(clientService);
        return handler;
    }
    
    @Bean
    @Autowired
    public ApprovalStore approvalStore() throws Exception {
        TokenApprovalStore store = new TokenApprovalStore();
        store.setTokenStore(redisTokenStore);
        return store;
    }
    
    @Bean
    @Primary
    @Autowired
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setTokenStore(redisTokenStore);
        return tokenServices;
    }
    
    }
    
    @Component
    class MyOAuth2AuthenticationEntryPoint extends OAuth2AuthenticationEntryPoint {
    }
    

    @Configuration
    @EnableWebSecurity
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter { 
    
    private MyOAuth2AuthenticationEntryPoint authenticationEntryPoint;
    
    private AccessDecisionManager accessDecisionManager;
    
    private MongoDBAuthenticationProvider authenticationProvider;
    
    @Autowired
    public SecurityConfiguration( MyOAuth2AuthenticationEntryPoint authenticationEntryPoint,AccessDecisionManager accessDecisionManager, MongoDBAuthenticationProvider authenticationProvider) {
        this.authenticationEntryPoint = authenticationEntryPoint; 
        this.accessDecisionManager = accessDecisionManager;
        this.authenticationProvider = authenticationProvider;
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        /*.addFilterAfter(
                  new CustomFilter(), BasicAuthenticationFilter.class)*/
        .csrf().disable()
        .anonymous().disable()
        .authorizeRequests()
        .antMatchers("/oauth/token").fullyAuthenticated()
        .and()
        .httpBasic()
        .authenticationEntryPoint(authenticationEntryPoint)
        .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
    
        http
        /*.addFilterAfter(
                  new CustomFilter(), BasicAuthenticationFilter.class)*/
        .csrf().disable()
        .anonymous().disable()
        .authorizeRequests()
        .antMatchers("/oauth/check_token").fullyAuthenticated()
        .and()
        .httpBasic()
        .authenticationEntryPoint(authenticationEntryPoint)
        .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler())
        .and()
        .authorizeRequests()
        .anyRequest()
        .authenticated()
        .accessDecisionManager(accessDecisionManager);
    }
    
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider)
        /*.inMemoryAuthentication()
        .withUser("javabycode").password("123456").roles("USER")
        .and()
        .withUser("admin").password("admin123").roles("ADMIN");*/;
    }     
    }
    

    【讨论】: