Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to access resources with access_token : spring boot Oauth2

I am trying to implement Oauth2 in my existing application.Initially I have added spring security and then tried to add oauth2, After adding configuration I am able to generate access_token but by using access_token i am not able to access resources.

Here is my code:

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());
    }

}

I have used this tutorial for reference.

I am able to get access token using basic auth credentials.

enter image description here

But when i used the same access token to get resources, it is failing. enter image description here

I have added all required tables for oauth. Is there anything am i missing?

Update:

I removed .and().httpBasic(); and added @Order(3) in WebsecurityConfigurerAdapter and updated properties file with security.oauth2.resource.filter-order = 3

now getting error as { "timestamp": 1543500350487, "status": 403, "error": "Forbidden", "message": "Access Denied", "path": "/patient/1/" }

Update 2

here is my user and authorities schema:

user +----------+-----------------+------+-----+---------+----------------+ | 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 | | +----------+-----------------+------+-----+---------+----------------+

authorities +-----------+-----------------+------+-----+---------+----------------+ | 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 | | +-----------+-----------------+------+-----+---------+----------------+

like image 310
vjnan369 Avatar asked Nov 29 '18 10:11

vjnan369


2 Answers

You should use hasRole directly on your antmatcher instead of a string inside the access() function. This will evaluate the hasRole correctly and correctly determine that the user has access to the requested resource.

This will result in the following code for 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());
    }

}
like image 197
Sven Hakvoort Avatar answered Nov 15 '22 23:11

Sven Hakvoort


First off, you have two similar methods that modify the AuthenticationManagerBuilder

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

and

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

Is there a reason they both are there? I do not have this in my config set up.

Furthermore, your query might not be working properly. You should follow some guidelines into how to set up a user service to handle the loaduserbyusername call and the auth object with one. As a note: I do not have the same AuthenticationManagerBuilder set up as you, I have mine configured to use a userdetails service, along with a password Encoder like so.

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

If that doesn't help, here's an alternative way of configuring:

Change the class that extends WebSecurityConfigurerAdapter to only concern itself with the token endpoint.

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

Now in your ResourceServerConfigurerAdapter, have the configuration worry about the stuff in the resource server. Please note, this will work only if your AuthenticationManagerBuilder configuration is properly loading the Role correctly. As other's have noted, Spring has the prefix ROLE_. Which for some reason you are retrieving using a query, and they're authorities.

@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());

}

In my AuthServerConfig file I Do not have the following annotations:

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

I configure the AuthorizationServerSecurityConfigurer differently than the tutorial you followed, mine is the following:

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

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

My ClientDetailsServiceConfigurer is still in memory so that's different as well. My AuthorizationServerEndpointsConfigurer is slightly different too, I only add a tokenstore, an enhancer chain (Don't worry about this, it's extra), and an authenticationManager

    endpoints
        .tokenStore(tokenStore())
        .tokenEnhancer(tokenEnhancerChain)
        .authenticationManager(authenticationManager);
like image 20
Edwin Diaz Avatar answered Nov 16 '22 01:11

Edwin Diaz