7

I’m using Spring 4.1.5.RELEASE, Spring Security 3.2.5.RELEASE, and OAuth2 2.0.7.RELEASE. I’m using JdbcTokenStore and have this in my oauth_client_details table …

client_id = dave
resource_ids = /dashboard
scope = read,write,delete
authorized_grant_types = implicit,password,client_credentials
web_server_redirect_uri = /redirect
authorities = ROLE1,ROLE2,ROLE3
access_token_validity = 3600
refresh_token_validity = 3600

But I’m having trouble accessing my secured resource, /dashboard. Below I make the request to get the access token …

Daves-MacBook-Pro:biv2 davea$ curl -v -d grant_type=password \
        -d username=ROLE3 \
        -d password=dave \
        -u dave:davesecret \
        http://localhost:8080/mycontext-path/oauth/token
*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
* Server auth using Basic with user 'dave'
> POST /mycontext-path/oauth/token HTTP/1.1
> Authorization: Basic YmltOmJpbXNlY3JldA==
> User-Agent: curl/7.40.0
> Host: localhost:8080
> Accept: */*
> Content-Length: 50
> Content-Type: application/x-www-form-urlencoded
> 
* upload completely sent off: 50 out of 50 bytes
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Cache-Control: no-store
< Pragma: no-cache
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Fri, 03 Jul 2015 16:33:39 GMT
< 
* Connection #0 to host localhost left intact
{"access_token":"2648069d-607d-449d-af10-cb38d5fd7614","token_type":"bearer","expires_in":470,"scope":"delete read write"}

and then I try and use that access token to get my resource, /dashboard, but I’m getting a redirect back to my login page …

Daves-MacBook-Pro:biv2 daveacurl -v -H 'Authorization: Bearer 2648069d-607d-449d-af10-cb38d5fd7614' http://localhost:8080/mycontext-path/dashboard
*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> GET /mycontext-path/dashboard HTTP/1.1
> User-Agent: curl/7.40.0
> Host: localhost:8080
> Accept: */*
> Authorization: Bearer 2648069d-607d-449d-af10-cb38d5fd7614
> 
< HTTP/1.1 302 Found
< Server: Apache-Coyote/1.1
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Set-Cookie: JSESSIONID=1EF45E8864F5A03CA0D2895E8E851E87; Path=/mycontext-path/; HttpOnly
< Location: http://localhost:8080/mycontext-path/login
< Content-Length: 0
< Date: Fri, 03 Jul 2015 16:34:06 GMT
< 
* Connection #0 to host localhost left intact

What am I missing? Below is how I configure my OAuth2Config as well as my Spring security config.

@Configuration
@ComponentScan
@EnableAutoConfiguration
@RestController
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Configuration
    @EnableAuthorizationServer
    // [1]
    protected static class OAuth2Config extends
            AuthorizationServerConfigurerAdapter {

        @Autowired
        private AuthenticationManager authenticationManager;
        @Autowired
        private DataSource dataSource;
        @Autowired
        private PasswordEncoder passwordEncoder;
        @Autowired
        private ClientDetailsService clientDetailsService;
        @Autowired
        private TokenStore tokenStore;

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

        @Override
        // [3]
        public void configure(ClientDetailsServiceConfigurer clients)
                throws Exception {
            // @formatter:off
            clients.jdbc(dataSource);
        }

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

        @Bean
        public TokenApprovalStore tokenApprovalStore() {
            TokenApprovalStore tokenApprovalStore = new TokenApprovalStore();
            tokenApprovalStore.setTokenStore(tokenStore);
            return tokenApprovalStore;
        }

        @Bean
        public OAuth2RequestFactory getOauth2RequestFactory()
        {
            return new DefaultOAuth2RequestFactory(clientDetailsService);
        }   // getOauth2RequestFactory

        @Bean
        public UserApprovalHandler userApprovalHandler() {
            final TokenStoreUserApprovalHandler userApprovalHandler = new TokenStoreUserApprovalHandler();
            userApprovalHandler.setRequestFactory(getOauth2RequestFactory());
            userApprovalHandler.setTokenStore(tokenStore);
            return userApprovalHandler;
        }   // userApprovalHandler

        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
            oauthServer.realm("abcdefgh/client");
        }       
    }
}

Spring security config :

@Configuration
@EnableWebSecurity
@ComponentScan(basePackages="com.myproject", excludeFilters=@ComponentScan.Filter(Controller.class))
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private static final String ROLE1 = "ROLE1";
    private static final String ROLE2 = "ROLE2";
    private static final String ROLE3 = "ROLE3";

    @Resource(name="userDetailsService")
    private UserDetailsService userDetailsService;

    @Resource(name="jsonAuthenticationSuccessHandler")
    private daveAuthenticationSuccessHandler authSuccessHandler;

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

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(authenticationFilter(), JSONUsernamePasswordAuthenticationFilter.class);
        http
            .authorizeRequests()
                .antMatchers("/403").permitAll()
                .antMatchers("/login/**").permitAll()
                .antMatchers("/resources/**").permitAll()
                // TODO: Add secured patterns
                .antMatchers("/ROLE3/**").hasRole(ROLE3)
                .antMatchers("/common/**").hasAnyRole(ROLE3, ROLE2, ROLE1)
                .antMatchers("/ROLE1/**").hasAnyRole(ROLE3, ROLE2, ROLE1)
                .antMatchers("/ROLE2/**").hasAnyRole(ROLE3, ROLE2)
                // Allow access to all other resources only if authenticated
                .antMatchers("/*/**").fullyAuthenticated()
            .and().formLogin()
                .loginPage("/login")
                .failureUrl("/login?error")
                .usernameParameter("username")
                .passwordParameter("password")
                .successHandler(authSuccessHandler)
            .and().logout().logoutSuccessUrl("/login?logout")
            .and().exceptionHandling().accessDeniedPage("/403")
            .and().csrf().disable();
    }

    @Bean(name="passwordEncoder")
    public PasswordEncoder passwordEncoder() {
        return new StandardPasswordEncoder();
    }

    @Bean
    public JSONUsernamePasswordAuthenticationFilter authenticationFilter() throws Exception {
        final JSONUsernamePasswordAuthenticationFilter authFilter = new JSONUsernamePasswordAuthenticationFilter();
        //authFilter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login","POST"));
        authFilter.setAuthenticationManager(authenticationManagerBean());
        authFilter.setAuthenticationSuccessHandler(authSuccessHandler);
        authFilter.setUsernameParameter("username");
        authFilter.setPasswordParameter("password");
        return authFilter;
    }

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

}
Dave
  • 15,639
  • 133
  • 442
  • 830
  • Dashboard is accessed using .antMatchers("/*/**").fullyAuthenticated() and your token has authorities ROLE1/2/3. What happens if you add .antMatchers("/dashboard").hasAnyRole(ROLE3, ROLE2, ROLE1) (Just to see if it is really a token issue)? Also I am wondering what your 403 page does: It does not happen to redirect to you login page in another part of the configuration? Are there any exceptions logged? – Norbert Jul 11 '15 at 00:00

1 Answers1

0

In OAuth2 protected resource are part of a ResourceServer.

spring-security-oauth2 uses OAuth2AuthenticationProcessingFilter to handle OAuth2 authentication.

@EnableWebSecurity is not enough in your SecurityConfig class.

You need to annotate it with @EnableResourceServer and extending ResourceServerConfigurerAdapter will make it easier to configure the corresponding http security.

See Reference documentation

Community
  • 1
  • 1
paskos
  • 689
  • 8
  • 21