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?