0

I am trying to build an Oauth2 authorization server using spring. The thing is I am not able to make it work together the login and authorization form and a resource server to retrieve user data using an oauth2 token.

This is my main config apart from user services and repositories...

WebSecurityConfig

@EnableWebSecurity
@Configuration
@Order(2)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

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

    @Autowired
    private ClientDetailsService clientDetailsService;

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

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/login", "/oauth/authorize").permitAll()
                .and()
                .formLogin().permitAll();
    }

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

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

    @Bean
    public BCryptPasswordEncoder encoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(0);
        return bean;
    }
}

AuthorizationServerConfig

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private Environment env;

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private UserApprovalHandler userApprovalHandler;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    @Qualifier("dataSource")
    private DataSource dataSource;

    @Value("classpath:schema.sql")
    private Resource schemaScript;

    @Value("classpath:data.sql")
    private Resource dataScript;

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

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

    @Override
    public void configure(final AuthorizationServerSecurityConfigurer oauthServer) {
        oauthServer
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()");
    }

    @Bean
    public DataSourceInitializer dataSourceInitializer() {
        final DataSourceInitializer initializer = new DataSourceInitializer();
        initializer.setDataSource(dataSource);
        initializer.setDatabasePopulator(databasePopulator());
        return initializer;
    }

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

    @Bean
    public ClientCredentialsTokenEndpointFilter checkTokenEndpointFilter() {
        ClientCredentialsTokenEndpointFilter filter = new ClientCredentialsTokenEndpointFilter("/oauth/check_token");
        filter.setAuthenticationManager(authenticationManager);
        filter.setAllowOnlyPost(true);
        return filter;
    }


    private DatabasePopulator databasePopulator() {
        final ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
        populator.addScript(schemaScript);
        populator.addScript(dataScript);
        return populator;
    }

    private DataSource dataSource() {
        final DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("spring.datasource.driverClassName"));
        dataSource.setUrl(env.getProperty("spring.datasource.url"));
        dataSource.setUsername(env.getProperty("spring.datasource.username"));
        dataSource.setPassword(env.getProperty("spring.datasource.password"));
        return dataSource;
    }
}

ResourceServerConfig

@Configuration
@EnableResourceServer
@Order(3)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    private static final String RESOURCE_ID = "resource_id";

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId(RESOURCE_ID).stateless(false);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/user").authenticated()
                .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
    }

}

UserController

@Controller
public class UserController {

    @Autowired
    UserService userService;

    @PreAuthorize("#oauth2.hasScope('read_user_profile')")
    @GetMapping("/user")
    @ResponseBody
    public Optional<User> getUser(@RequestParam String email) {
        return userService.findAll().stream().filter(x -> x.getEmail().equals(email)).findAny();
    }

    @PostMapping(value = "/user", consumes = MediaType.APPLICATION_JSON_VALUE)
    @ResponseStatus(HttpStatus.CREATED)
    public void postMessage(@RequestBody User user) {
        userService.save(user);
    }

}

As you can see I assigned them an order. The thing is, if WebSecurityConfig is the first one, I am able to go to /login and /oauth/authorize screen but the user controller has not any security layer and it is open (no token needed) :S

If the order is the reverse, I can't see the login page but I see a 404. It redirects from /oauth/authorize by the way. But I am able to access the user controller using the generated token.

What I am doing wrong? Is it not possible to have both in the same module?

1 Answer 1

1

I think this is not 'order' problem.

@PreAuthorize annotation is used by Method security. If you want to let it work, you need to @EnableGlobalMethodSecurity(prePostEnabled=true) annotation.

But if you just want to protect your resource by OAuth, why don't you configure using url base security constraint at ResourceServerConfig?

For example, my ResourceServerConfig works fine like:

    @Override
    public void configure(HttpSecurity http) throws Exception {
         http.antMatcher("/api/**")
            .authorizeRequests()
            .antMatchers(HttpMethod.GET, "/user").access("#oauth2.hasScope('read')");
    }
Sign up to request clarification or add additional context in comments.

5 Comments

Because that does not work. It seems like the security config of the resource server is not taking into account.
Did you try my advice? I showed you 2 solutions. My code has no problem and works perfect. If your code didnt work there is something missing or wrong part.
Yeah it works using your adivce but not using annotations. Appart from this, my main issue is to have both configurations working together. If I change the order of one of them I can get the resource using the token but not log in in the form following the implicit flow. And the reverse. Do you have any suggestion to that? @Jes
I menthoned @EnableGlobalMethodSecurity(prePostEnabled=true) annotation. It doesnt work?
It works but by defining MethodSecurityConfig. But that is relating to use annotations in the method to set Oauth2 scopes not relating to my question which is if you have defined a global security config the resource security config is ignored. If you change the order the other one is ignored. github.com/spring-projects/spring-security-oauth/issues/1024

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.