Setup - diging/oauth-tokens GitHub Wiki
Setting your project up with Spring OAuth2 and oauth-tokens
After adding the Spring OAuth2 libraries to your project, there are a couple of required configuration. To avoid issues with classes in different contexts, it's easiest to scan the root package of your project in your root context.
CSRF exemptions
To exempt your API endpoints from requiring CSRF tokens, add the following RequestMatcher:
public class CsrfSecurityRequestMatcher implements RequestMatcher {
private Pattern allowedMethods = Pattern.compile("^(GET|HEAD|TRACE|OPTIONS)$");
private RegexRequestMatcher unprotectedMatcher = new RegexRequestMatcher("/api/v1/.*", null);
@Override
public boolean matches(HttpServletRequest request) {
if (allowedMethods.matcher(request.getMethod()).matches()) {
return false;
}
return !unprotectedMatcher.matches(request);
}
}
Security Context
There are three security configurations necessary:
@EnableWebSecurity
public class SecurityContext extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web
// Spring Security ignores request to static resources such as CSS or JS
// files.
.ignoring().antMatchers("/static/**");
}
@Configuration
@EnableAuthorizationServer
@PropertySource("classpath:/config.properties")
public static class OAuth2Configuration extends AuthorizationServerConfigurerAdapter {
@Value("${_oauth_token_validity}")
private int oauthTokenValidity;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private OAuthClientRepository clientRepo;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Autowired
@Qualifier("OAuthClientDetailsService")
private ClientDetailsService clientDetailsService;
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/oauth/token").permitAll().anyRequest()
.authenticated();
}
@Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
configurer.withClientDetails(clientDetailsService);
}
@Bean
public IOAuthClientManager oauthClientManager() {
return new OAuthClientManager(clientRepo, bCryptPasswordEncoder, oauthTokenValidity);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.pathMapping("/oauth/authorize", "/api/v1/oauth/authorize")
.pathMapping("/oauth/check_token", "/api/v1/oauth/check_token")
.pathMapping("/oauth/confirm_access", "/api/v1/oauth/confirm_access")
.pathMapping("/oauth/error", "/api/v1/oauth/error")
.pathMapping("/oauth/token", "/api/v1/oauth/token").tokenStore(tokenStore())
.authenticationManager(authenticationManager);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')")
.checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')").allowFormAuthenticationForClients();
}
}
@Configuration
@Order(1)
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().requireCsrfProtectionMatcher(new CsrfSecurityRequestMatcher()).and().formLogin()
// Configures the logout function
.and().logout().deleteCookies("JSESSIONID").and()
.exceptionHandling().accessDeniedPage("/403")
.and().requestMatchers().requestMatchers(new NegatedRequestMatcher(
new OrRequestMatcher(
new AntPathRequestMatcher("/api/**")
)
))
// Configures url based authorization
.and()
.authorizeRequests()
// Anyone can access the urls
.antMatchers("/", "/resources/**",
"/register").permitAll()
// The rest of the our application is protected.
.antMatchers("/users/**", "/admin/**").hasRole("ADMIN")
.anyRequest().hasRole("USER");
}
public AuthenticationFailureHandler customAuthenticationFailureHandler(String defaultFailureUrl) {
return new CustomAuthenticationFailureHandler(defaultFailureUrl);
}
}
@Configuration
@EnableResourceServer
@Order(2)
public static class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {
// configure
private static final String RESOURCE_ID = "my_rest_api";
@Autowired
private TokenStore tokenStore;
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.tokenStore(tokenStore).resourceId(RESOURCE_ID).stateless(false);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/api/**").authenticated().and()
.exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(4);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}