无法使用access_token访问资源:春季启动Oauth2

时间:2018-11-29 10:41:57

标签: java spring spring-boot oauth-2.0

我正在尝试在现有应用程序中实现Oauth2。最初我添加了spring安全性,然后尝试添加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教程作为参考。

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

enter image description here

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

我已经为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 | | +-----------+-----------------+------+-----+---------+----------------+

5 个答案:

答案 0 :(得分:4)

您应该直接在您的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());
    }

}

答案 1 :(得分:3)

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

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

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

他们俩都在那里是有原因的吗?我的配置中没有此设置。

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

    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)

我对AuthorizationServerSecurityConfigurer的配置与您遵循的教程不同,以下是我的:

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

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

我的ClientDetailsServiceConfigurer仍在内存中,因此也有所不同。我的AuthorizationServerEndpointsConfigurer也略有不同,我只添加了令牌存储,增强链(不用担心,这是多余的)和authenticationManager

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

答案 2 :(得分:0)

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

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

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

您也可以更改默认前缀,但是除非您想弄乱spring,否则我不建议您使用它。

答案 3 :(得分:0)

请像以下ResourceServer中那样更改代码:

看看这一行:

http.anonymous().disable()
                .requestMatchers().antMatchers("/patients/**","/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());
    }

}

答案 4 :(得分:-1)

example中,我遇到了同样的问题,经过大量搜索并尝试后,我通过添加以下内容解决了该问题。

  

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

我终于可以通过访问令牌获得资源。在@Order中,较低的值具有较高的优先级,因此我们无法通过访问令牌获取资源的原因必须是SecurityConfig的优先级高于OAuth2ResourceServer。

因此,请尝试在SecurityConfiguration上添加@Order(30),在SecurityOAuth2Configuration上添加@Order(10),在ResourceServer上添加@Order(20)。