【问题标题】:Unable to access resources with access_token : spring boot Oauth2无法使用 access_token 访问资源:spring boot Oauth2
【发布时间】:2019-05-01 09:43:06
【问题描述】:

我正在尝试在现有应用程序中实现 Oauth2。最初我添加了 Spring Security,然后尝试添加 oauth2,添加配置后我能够生成 access_token,但使用 access_token 我无法访问资源。

这是我的代码:

SecurityConfiguration.java

    @Configuration
    @EnableWebSecurity
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**");
    }

    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication().dataSource(dataSource);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/", "/patients").permitAll()
                .antMatchers("/oauth/token").permitAll()
                .anyRequest().authenticated()
                .and().httpBasic();
        http.csrf().disable();
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication().dataSource(dataSource)
                .usersByUsernameQuery("select username, password, 1 as enabled from user where username=?")
                .authoritiesByUsernameQuery("select username, authority from authorities where username=?");
    }

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

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

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

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

SecurityOAuth2Configuration.java

@Configuration
@EnableAuthorizationServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Import(SecurityConfiguration.class)
public class SecurityOAuth2Configuration extends AuthorizationServerConfigurerAdapter {
    private static String REALM = "CRM_REALM";
    private static final int ONE_DAY = 60 * 60 * 24;
    private static final int THIRTY_DAYS = 60 * 60 * 24 * 30;

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private UserApprovalHandler userApprovalHandler;

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

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.realm(REALM);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.jdbc(dataSource);
    }

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

ResourceServer.java

@Configuration
@EnableResourceServer
public class ResourceServer extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.anonymous().disable()
                .requestMatchers().antMatchers("/patients/**").and().authorizeRequests()
                .antMatchers("/patient/**").access("hasRole('USER')")
                .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
    }

}

我用过this的教程供参考。

我能够使用基本身份验证凭据获取访问令牌。

但是当我使用相同的访问令牌来获取资源时,它失败了。

我已为 oauth 添加了所有必需的表。 我有什么遗漏吗?

更新:

我删除了.and().httpBasic(); 和 在 WebsecurityConfigurerAdapter 中添加了 @Order(3) 并使用 security.oauth2.resource.filter-order = 3 更新了属性文件

现在出现错误 { "timestamp": 1543500350487, "status": 403, "error": "Forbidden", "message": "Access Denied", "path": "/patient/1/" }

更新 2

这是我的用户和权限架构:

用户 +----------+-----------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------+-----------------+------+-----+---------+----------------+ | id | int(6) unsigned | NO | PRI | NULL | auto_increment | | username | varchar(50) | NO | UNI | NULL | | | password | varchar(100) | NO | | NULL | | +----------+-----------------+------+-----+---------+----------------+

当局 +-----------+-----------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------+-----------------+------+-----+---------+----------------+ | id | int(6) unsigned | NO | PRI | NULL | auto_increment | | username | varchar(50) | NO | MUL | NULL | | | authority | varchar(50) | NO | | NULL | | +-----------+-----------------+------+-----+---------+----------------+

【问题讨论】:

  • 我认为.anyRequest().authenticated() 不是必需的。
  • @secretsuperstar,如果我删除它,那么即使我限制了资源服务器中的资源,我也可以访问这些资源。看起来资源服务器根本没有验证请求。
  • 您能否向我们展示您的用户架构/用户角色架构表?
  • @EdwinDiaz-Mendez 用架构更新了问题
  • 请在 ResourceServer 的 antMatchers 中尝试 */patient/** 而不是 /patient

标签: java spring spring-boot oauth-2.0


【解决方案1】:

您应该直接在 antmatcher 上使用 hasRole,而不是 access() 函数中的字符串。这将正确评估hasRole,并正确确定用户有权访问所请求的资源。

这将导致ResourceServer.java 的以下代码:

@Configuration
@EnableResourceServer
public class ResourceServer extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.anonymous().disable()
                .requestMatchers().antMatchers("/patients/**").and().authorizeRequests()
                .antMatchers("/patient/**").hasRole('USER')
                .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
    }

}

【讨论】:

  • 我也试过了,但仍然无法正常工作,每个请求都在 WebSecurityConfigurerAdapter 上得到验证。如果我在 websecurity 中删除.anyRequest().authenticated();,那么即使我限制了资源服务器中的资源,我也可以访问该资源。看起来 ResourceServer 根本没有验证请求。
  • 您是否也尝试过使用hasAuthority('USER') insetead 或hasRole()? (尝试在数据库列中同时使用ROLE_USERUSER
  • 是的,我尝试使用hasAuthority('USER') 仍然无法正常工作,但正如我告诉你的那样,请求没有在资源服务器中得到验证。看起来它被安全配置覆盖了
  • @vjnan369,我在我自己的、工作的、基于 JWT 访问的 spring API 中验证了这一点,它是一个扩展,而不是覆盖。当您使用hasAuthority('USER') 时,数据库中USER 角色的值是多少?
  • 它是ROLE_USER
【解决方案2】:

首先,您有两种类似的方法可以修改AuthenticationManagerBuilder

@Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {

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

他们俩都在那里有什么原因吗?我的配置设置中没有这个。

此外,您的查询可能无法正常工作。您应该遵循一些指南来设置用户服务来处理 loaduserbyusername 调用和 auth 对象。注意:我没有设置与您相同的AuthenticationManagerBuilder我已配置为使用 userdetails 服务以及密码编码器

    auth.userDetailsService(securityUserService)
        .passwordEncoder(passwordEncoders.userPasswordEncoder());

如果这没有帮助,这里有另一种配置方式:

将扩展WebSecurityConfigurerAdapter 的类更改为只关注令牌端点。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
    .authorizeRequests()
        .antMatchers("/api/oauth/**").permitAll()
        .and()
    .csrf()
        .disable();
}

现在在您的ResourceServerConfigurerAdapter 中,让配置担心资源服务器中的内容。请注意,这仅在您的AuthenticationManagerBuilder 配置正确加载角色时才有效。正如其他人所指出的,Spring 具有前缀ROLE_。出于某种原因,您正在使用查询进行检索,并且它们是权威。

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

    http.csrf().disable()
    .requestMatchers()
        .antMatchers("/api/**")
        .and()
    .authorizeRequests()
        .antMatchers("/api/**").access("hasRole('USER')")
        .and()
    .exceptionHandling()
    .accessDeniedHandler(new OAuth2AccessDeniedHandler());

}

在我的AuthServerConfig 文件中,我没有以下注释:

@EnableGlobalMethodSecurity(prePostEnabled = true)
@Import(SecurityConfiguration.class)

我对@9​​87654335@ 的配置与您遵循的教程不同,我的配置如下:

@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {

    oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}

我的ClientDetailsServiceConfigurer 仍在记忆中,所以这也是不同的。我的AuthorizationServerEndpointsConfigurer也略有不同,我只添加了一个tokenstore、一个enhancer chain(不用担心,它是额外的)和一个authenticationManager

    endpoints
        .tokenStore(tokenStore())
        .tokenEnhancer(tokenEnhancerChain)
        .authenticationManager(authenticationManager);

【讨论】:

    【解决方案3】:

    我怀疑问题可能出在您保存/加载角色的方式上。在 Spring Security 中,角色有一个默认前缀:ROLE_。所以在你的数据库(存储)中,你需要将它们保存为ROLE_FOO,然后你可以使用hasRole('FOO')

    我在这里发现了同样的问题,我的回答似乎解决了问题:https://stackoverflow.com/a/43568599/4473822

    遇到问题的人也有403 - Forbidden,并且在数据库中正确保存角色解决了问题。

    您也可以更改默认前缀,但我不建议您这样做,除非您想稍微弄乱一下 spring。

    【讨论】:

    • 感谢@Tom 的回复,我已将角色添加为 db 中的 ROLE_USER。看起来 websecurityconfiguration 首先验证了用户,因为当我在 websecurityconfiguration 中为/content/** 添加 permitAll() 时,它会覆盖 ResourceServerConfigurerAdapter,这是问题吗?
    • 看起来我的 ResourceServer 根本没有验证请求
    • 您的配置看起来与我使用的非常相似,我能找到的主要区别是我在WebSecurityConfiguration 上有@Order(Ordered.HIGHEST_PRECEDENCE)。可能是通过将web-securityresource-server 放在同一级别,它首先出现并且永远不会到达您的resource-server 声明
    • 也试过@Order(Ordered.HIGHEST_PRECEDENCE),还是不行,我没有加密数据库中的任何密码或令牌,根据我的代码,这有什么问题吗?
    • 如果是这种情况,您应该得到类似password is incorrect 而不是forbidden
    【解决方案4】:

    请在ResourceServer中更改如下代码:

    看看这一行:

    http.anonymous().disable()
                    .requestMatchers().antMatchers("/patients/**","/patient/**")
    

    由于 "/patient/"**, 没有作为请求匹配器的一部分添加,该请求实际上是由其他 configuration

    处理的
    package project.configuration;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
    import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
    import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
    
    
    @Configuration
    @EnableResourceServer
    public class ResourceServer extends ResourceServerConfigurerAdapter {
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.anonymous().disable()
                    .requestMatchers().antMatchers("/patients/**","/patient/**").and().
                    authorizeRequests().antMatchers("*/patient/**").hasRole("USER")
                    .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
        }
    
    }
    

    【讨论】:

      【解决方案5】:

      example,我也遇到了同样的问题,经过这么多的搜索和尝试,我通过添加以下解决了它。

      OAuth2AuthorizationServer上添加@Order(10)后,OAuth2ResourceServer上的@Order(20)上,@Order(30)SecurityConfig上,

      我终于可以通过访问令牌获取资源了。在@Order中,值越低优先级越高,所以我们无法通过access token获取资源的原因一定是SecurityConfig的优先级高于OAuth2ResourceServer。

      所以尝试在 SecurityConfiguration 上添加 @Order(30),在 SecurityOAuth2Configuration 上添加 @Order(10),在 ResourceServer 上添加 @Order(20)。

      【讨论】:

      • 我错了@Order(因为我合并了很多教程)。我想知道为什么答案会被否决。
      猜你喜欢
      • 2017-07-08
      • 2015-09-21
      • 2017-09-01
      • 2020-02-16
      • 2017-11-29
      • 2018-09-11
      • 2020-11-21
      • 2020-11-30
      • 2015-09-02
      相关资源
      最近更新 更多