Oauth2 资源服务器重叠 Spring Security 配置

Posted

技术标签:

【中文标题】Oauth2 资源服务器重叠 Spring Security 配置【英文标题】:Oauth2 Resource server overlap Spring Security configuration 【发布时间】:2017-03-25 12:29:59 【问题描述】:

我正在尝试在 java config 上配置 Spring Security 和 OAuth2。我正在使用 Spring Security 版本 4.0.4.RELEASE 和 OAuth2 版本 2.0.11.RELEASE。

Spring Security 配置运行良好。我也可以使用 OAuth2 AuthorizationServer 获取访问令牌,但我的 ResourceServer 无法正常工作。当我设置注释 @EnableResourceServer 时,我只能检查我的访问令牌和其他我无法打开的 URL(安全配置和 AuthorizationServer 配置不起作用)。我看到以下错误:

<oauth>
  <error_description>
     An Authentication object was not found in the SecurityContext
  </error_description>
  <error>unauthorized</error>
</oauth>

如果我删除注释 @EnableResourceServer,我的 ResourceServer 不会检查访问令牌。它只是重定向到身份验证页面。

这是我的代码:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class GlobalSecurityConfig extends GlobalMethodSecurityConfiguration 

    @Bean(name = "passwordEncoder")
    public PasswordEncoder passwordEncoder() 
        return new BCryptPasswordEncoder();
    


    @Autowired
    @Qualifier("authUserDetailsService")
    private UserDetailsService userDetailsService;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception 
        auth
                .userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    

    @Autowired
    @Qualifier("permissionEvaluator")
    private PermissionEvaluator permissionEvaluator;


    @Bean
    public DefaultMethodSecurityExpressionHandler expressionHandler() 
        DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
        handler.setDefaultRolePrefix("");
        handler.setPermissionEvaluator(permissionEvaluator);
        return handler;
    

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() 
        return expressionHandler();
    


安全配置:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter 

    @Bean(name = "clientAuthenticationEntryPoint")
    public OAuth2AuthenticationEntryPoint oauthAuthenticationEntryPoint() 
        OAuth2AuthenticationEntryPoint entry = new OAuth2AuthenticationEntryPoint();
        entry.setRealmName("myapp/client");
        entry.setTypeName("Basic");
        return entry;
    

    @Autowired
    @Qualifier("webExpressionHandler")
    private DefaultWebSecurityExpressionHandler expressionHandler;


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

    @Bean
    public SessionRegistry sessionRegistry() 
        return new SessionRegistryImpl();
    

    @Override
    public void configure(WebSecurity web) throws Exception 
        web.ignoring()
                .antMatchers("/html/**", "/webapi/**");
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http
                .requestMatchers().antMatchers("/admin/**", "/**")
                .and()

                .authorizeRequests()

                .expressionHandler(expressionHandler)

                .antMatchers("/admin/**").access("hasRole('ADMINISTRATOR')")
                .antMatchers("/1/admin/**").access("hasRole('ADMINISTRATOR')")
                .antMatchers("/profile**").authenticated()
                .antMatchers("/oauth/authorize").authenticated()
                .and()
                .formLogin().loginPage("/login")
                .failureUrl("/login?error=1")
                .loginProcessingUrl("/login-attempt")
                .defaultSuccessUrl("/", false)
                .and()
                .sessionManagement()
                .sessionFixation().migrateSession()
                .and()

                .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/")

                .and()
                .exceptionHandling()
                .accessDeniedPage("/access-denied")
                .and()
                .csrf();
    

Oauth 配置:

@Configuration
public class Oauth 

    @Configuration
    @EnableResourceServer
    public static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter 

        private static final String RESOURCE_ID = "my_oauth_server";

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception 
            resources.resourceId(RESOURCE_ID);
        

        @Override
        public void configure(HttpSecurity http) throws Exception 
            http
                    .anonymous().disable()
                    .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .authorizeRequests()

                    .regexMatchers("/api/v0/.*").authenticated()
                    .antMatchers("/**").denyAll()
            ;

        
    

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

        @Autowired
        private AuthorizationCodeServices verificationCodeService;

        @Autowired
        @Qualifier("clientDetails")
        private ClientDetailsService clientDetailsService;

        @Autowired
        @Qualifier("tokenStore")
        private TokenStore tokenStore;

        @Bean(name = "tokenServices")
        public DefaultTokenServices tokenServices() 
            DefaultTokenServices tokenServices = new DefaultTokenServices();
            tokenServices.setTokenStore(tokenStore);
            tokenServices.setSupportRefreshToken(true);
            tokenServices.setClientDetailsService(clientDetailsService);
            return tokenServices;
        

        @Bean
        public ClientCredentialsTokenEndpointFilter clientCredentialsTokenEndpointFilter() throws Exception 
            ClientCredentialsTokenEndpointFilter filter = new ClientCredentialsTokenEndpointFilter();
            filter.setAuthenticationManager(authenticationManager);
            return filter;
        

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

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
            endpoints.authenticationManager(authenticationManager);
            endpoints.authorizationCodeServices(verificationCodeService);
            endpoints.tokenServices(tokenServices());
            endpoints.reuseRefreshTokens(true);
        


        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception 
            oauthServer.tokenKeyAccess("permitAll()");
            oauthServer.checkTokenAccess("permitAll()");
            oauthServer.realm("myapp/client");
            oauthServer.addTokenEndpointAuthenticationFilter(clientCredentialsTokenEndpointFilter());
            oauthServer.allowFormAuthenticationForClients();
        
    

因此,ResourceServer 配置与其他配置重叠。我该如何解决?如有任何帮助,我将不胜感激。

【问题讨论】:

【参考方案1】:

我看到您想用访问令牌保护一些端点,而用普通表单登录保护其他端点。

您能否尝试通过http.requestMatcher(new AntPathRequestMatcher("/api/v0/**"))... 之类的方式将ResourceServerConfiguration 的适用性限制为仅适用于某些端点。对SecurityConfig 执行相同的操作,但对于您希望它处理的端点。

【讨论】:

嗨@sofiaguyang,感谢您提出一个想法。我已经限制了ResourceServerConfigurationSecurityConfig 的适用性。现在我看不到错误An Authentication object was not found in the SecurityContext。但是我丢失了一个登录按钮。我不明白我的代码有什么问题。 一般流程是:如果您访问受 OAuth2 保护的端点,您必须提供访问令牌,否则您将收到未经授权/禁止的响应。您可以通过从 /oauth/authorize 检索身份验证代码并将身份验证代码与来自 /oauth/token 的访问令牌交换来获得访问令牌。最初,当用户访问/oauth/authorize 时,用户尚未经过身份验证。但是因为你必须通过身份验证才能授权,所以你会被重定向到登录页面。 谢谢!您对登录按钮有什么想法吗?或者我需要尝试另一种方式来配置 ResourceServer?这是我的配置:http.requestMatcher(new AntPathRequestMatcher("/api/v0/**")).authorizeRequests().antMatchers("/api/v0/**").authenticated().antMatchers("/**").denyAll();

以上是关于Oauth2 资源服务器重叠 Spring Security 配置的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security OAuth2 纯资源服务器

Spring OAuth2 资源和授权服务器

Spring Security OAuth2 资源服务器重试/弹性

使用Spring boot的OAuth2认证服务器和资源服务器

如何为 spring webflux 应用程序配置 oauth2 资源服务器?

Spring oAuth2 授权和资源服务器