Reputation: 3001
I would like to use Spring security with OAuth and JWT tokens. My current configurations are:
@Configuration
@EnableResourceServer
public class OAuth2ServerConfig {
@Configuration
@EnableWebSecurity
protected static class ResourceServer extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.anonymous().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler()) // handle access denied in general (for example comming from @PreAuthorization
.authenticationEntryPoint(entryPointBean()) // handle authentication exceptions for unauthorized calls.
.and()
.authorizeRequests()
// only allow this three endpoint to NOT be authenticated.
.antMatchers(HttpMethod.POST, "/users").permitAll()
.antMatchers(HttpMethod.POST, "/users/authenticate").permitAll()
.antMatchers(HttpMethod.GET, "/users/inviationCode/{code}").permitAll()
.antMatchers(HttpMethod.POST, "/**").fullyAuthenticated()
.antMatchers(HttpMethod.GET, "/**").fullyAuthenticated()
.antMatchers(HttpMethod.PUT, "/**").fullyAuthenticated()
.antMatchers(HttpMethod.DELETE, "/**").fullyAuthenticated()
.antMatchers(HttpMethod.OPTIONS, "/**").fullyAuthenticated()
.and()
.addFilterBefore(filterBean(), AbstractPreAuthenticatedProcessingFilter.class)
.requestMatcher(new NegatedRequestMatcher(new AntPathRequestMatcher("/oauth/**")))
.authorizeRequests().anyRequest().authenticated().expressionHandler(new OAuth2WebSecurityExpressionHandler())
.and()
.csrf().disable(); // for chrome/FF plugins to work. for now we shouldn't face any problem since there is no point that JS can be injected into our page...
// @formatter:on
}
@Bean(name="authenticationManager")
@Override
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManagerBean();
}
@Bean
@Autowired
AccessDeniedHandler accessDeniedHandler() {
return new AccessDeniedExceptionHandler();
}
@Bean
@Autowired
AuthenticationEntryPoint entryPointBean() {
return new UnauthorizedEntryPoint();
}
@Bean
@Autowired
GenericFilterBean filterBean() {
return new XAuthTokenFilter();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean(name="userAuthenticationManager")
public UserAuthenticationService userAuthenticationManager() throws Exception {
return new UserAuthenticationService();
}
}
@Configuration
@EnableAuthorizationServer
public static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
@Qualifier("authenticationManager")
private AuthenticationManager authenticationManager;
@Autowired
@Qualifier("restDataSource")
private BasicDataSource restDataSource;
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
return new JwtAccessTokenConverter();
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')").checkTokenAccess(
"hasAuthority('ROLE_TRUSTED_CLIENT')");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).accessTokenConverter(accessTokenConverter());
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("test")
.authorizedGrantTypes("client_credentials", "password")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write")
.secret("secret");
}
}
}
Those configurations are based on the official spring github repo The problem I am facing now, is that whenever I try to obtain a token using this url:
http://myapplication.com/test/oauth/token?grant_type=password
I am getting the following error:
java.lang.StackOverflowError
at org.apache.commons.logging.impl.Jdk14Logger.isDebugEnabled(Jdk14Logger.java:214)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:144)
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:427)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:177)
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:427)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:177)
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:427)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:177)
This seems like a loop in the authentication process but to be honest I can find the root of it. The flow I would like to use is the following:
Can someone advice on the appropriate configurations?
Best
Upvotes: 1
Views: 5460
Reputation: 3001
Found the problem, it was specific to the authentication manager. This is the working configuration for me:
@Configuration
@ComponentScan
@EnableResourceServer
@Import({SecurityConfig.class})
public class OAuth2ServerConfig {
@Configuration
@EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
@Qualifier("restDataSource")
private DataSource datasource;
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
return new JwtAccessTokenConverter();
}
@Bean
public CustomJwtTokenStore tokenStore() {
return new CustomJwtTokenStore();
}
// @Bean
// public JdbcTokenStore tokenStore() {
// return new JdbcTokenStore(datasource);
// }
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')").checkTokenAccess(
"hasAuthority('ROLE_TRUSTED_CLIENT')");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore()).accessTokenConverter(accessTokenConverter());
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("my-trusted-client")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.accessTokenValiditySeconds(60)
.and()
.withClient("my-client-with-registered-redirect")
.authorizedGrantTypes("authorization_code")
.authorities("ROLE_CLIENT")
.scopes("read", "trust")
.redirectUris("http://anywhere?key=value")
.and()
.withClient("my-client-with-secret")
.authorizedGrantTypes("client_credentials", "password")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write")
.secret("secret");
}
}
}
with the security config:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
@Autowired
private AccessDeniedHandler accessDeniedHandler;
@Autowired
private GenericFilterBean filter;
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/webjars/**", "/images/**", "/oauth/uncache_approvals", "/oauth/cache_approvals");
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userAuthenticationManager()).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler) // handle access denied in general (for example comming from @PreAuthorization
.authenticationEntryPoint(authenticationEntryPoint) // handle authentication exceptions for unauthorized calls.
.and()
.authorizeRequests()
.antMatchers("/xxx/**").fullyAuthenticated()
.antMatchers("/xxx/**").fullyAuthenticated()
.antMatchers("/xxx/**").fullyAuthenticated()
.and()
.csrf().disable();;
}
@Bean
@Autowired
ApplicationListener<AbstractAuthenticationEvent> loggerBean() {
return new org.springframework.security.authentication.event.LoggerListener();
}
@Bean
@Autowired
AccessDeniedHandler accessDeniedHandler() {
return new AccessDeniedExceptionHandler();
}
@Bean
@Autowired
AuthenticationEntryPoint entryPointBean() {
return new UnauthorizedEntryPoint();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean(name="userAuthenticationManager")
public UserDetailsService userAuthenticationManager() throws Exception {
return new UserServiceImpl();
}
@Bean
public UserService userService() {
return new UserServiceImpl();
}
}
Upvotes: 3