Configured Spring Security with OAuth2 in java config, And client_credentails flow is working fine, but password flow is throwing Handler dispatch failed; nested exception is java.lang.StackOverflowError below is the log information
2016-10-10 23:19:08 DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolving exception from handler [public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException]: org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.StackOverflowError
here is my configuration files:
AuthorizationServerConfiguration.java
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
DataSource dataSource;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Override
public void configure(
AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
@Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.jdbc(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager);
}
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
}
ResourceServerConfiguration.java
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private String resourceId = "rest-api";
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
// @formatter:off
resources.resourceId(resourceId);
// @formatter:on
}
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest().authenticated();
// @formatter:on
}
}
WebApplicationInitializer.java
public class WebAppInitializer implements WebApplicationInitializer {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext rootContext=new AnnotationConfigWebApplicationContext();
rootContext.register(ApplicationRootConfig.class, HibernateConfig.class, AnnotationBasedSecurityConfig.class,
GlobalAuthenticationConfig.class, SecurityConfiguration.class, AuthorizationServerConfiguration.class, ResourceServerConfiguration.class);
servletContext.addListener(new ContextLoaderListener(rootContext));
servletContext.setInitParameter("defaultHtmlEscape", "true");
FilterRegistration.Dynamic encodingFilter=servletContext.addFilter("encoding-filter", new CharacterEncodingFilter());
encodingFilter.setInitParameter("encoding", "UTF-8");
encodingFilter.setInitParameter("forceEncoding", "true");
encodingFilter.addMappingForServletNames(null, true, "/*");
FilterRegistration.Dynamic securityFilter = servletContext.addFilter("securityFilter",new DelegatingFilterProxy("springSecurityFilterChain"));
securityFilter.addMappingForUrlPatterns(null, false,"/*");
servletContext.addListener(new HttpSessionEventPublisher());
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext));
dispatcher.setLoadOnStartup(1);
Set<String> mappingConflicts = dispatcher.addMapping("/");
if(!mappingConflicts.isEmpty()){
for (String map : mappingConflicts) {
logger.error("Mapping Conflict "+map);
}
throw new IllegalStateException("Dispatcher : cannot be mapped to '/' under Tomcat vesions <= 7.0.4");
}
rootContext.setServletContext(servletContext);
rootContext.refresh();
}
}
UserDetailsServiceImpl.java
@Service
@Transactional(readOnly=true)
public class UserDetailsServiceImpl implements UserDetailsService{
Logger logger = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
@Autowired
UserDAO userDAO;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
logger.info("loadUserByUsername "+username);
Collection<User> user = userDAO.findByEmail(username);
return user.iterator().next();
}
}
SecurityConfiguration.java
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userDetailsService);
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean()
throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
}
oauth_client_details table
Full Stack Trace
2016-10-14 18:05:43 DEBUG o.s.o.h.HibernateTransactionManager - Creating new transaction with name [org.springframework.security.oauth2.provider.token.DefaultTokenServices.createAccessToken]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2016-10-14 18:05:43 DEBUG o.s.o.h.HibernateTransactionManager - Opened new Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList@1e09a738 updates=org.hibernate.engine.spi.ExecutableList@67d0f9f8 deletions=org.hibernate.engine.spi.ExecutableList@c60ee2d orphanRemovals=org.hibernate.engine.spi.ExecutableList@3bdd7e0f collectionCreations=org.hibernate.engine.spi.ExecutableList@683e2e2b collectionRemovals=org.hibernate.engine.spi.ExecutableList@84115ed collectionUpdates=org.hibernate.engine.spi.ExecutableList@3db934e collectionQueuedOps=org.hibernate.engine.spi.ExecutableList@5326b83c unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
2016-10-14 18:05:43 DEBUG o.s.o.h.HibernateTransactionManager - Preparing JDBC Connection of Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList@1e09a738 updates=org.hibernate.engine.spi.ExecutableList@67d0f9f8 deletions=org.hibernate.engine.spi.ExecutableList@c60ee2d orphanRemovals=org.hibernate.engine.spi.ExecutableList@3bdd7e0f collectionCreations=org.hibernate.engine.spi.ExecutableList@683e2e2b collectionRemovals=org.hibernate.engine.spi.ExecutableList@84115ed collectionUpdates=org.hibernate.engine.spi.ExecutableList@3db934e collectionQueuedOps=org.hibernate.engine.spi.ExecutableList@5326b83c unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
2016-10-14 18:05:43 DEBUG o.s.o.h.HibernateTransactionManager - Exposing Hibernate transaction as JDBC transaction [com.mchange.v2.c3p0.impl.NewProxyConnection@4a1e11db]
2016-10-14 18:05:44 DEBUG o.s.o.h.HibernateTransactionManager - Initiating transaction rollback
2016-10-14 18:05:44 DEBUG o.s.o.h.HibernateTransactionManager - Rolling back Hibernate transaction on Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList@1e09a738 updates=org.hibernate.engine.spi.ExecutableList@67d0f9f8 deletions=org.hibernate.engine.spi.ExecutableList@c60ee2d orphanRemovals=org.hibernate.engine.spi.ExecutableList@3bdd7e0f collectionCreations=org.hibernate.engine.spi.ExecutableList@683e2e2b collectionRemovals=org.hibernate.engine.spi.ExecutableList@84115ed collectionUpdates=org.hibernate.engine.spi.ExecutableList@3db934e collectionQueuedOps=org.hibernate.engine.spi.ExecutableList@5326b83c unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
2016-10-14 18:05:44 DEBUG o.s.o.h.HibernateTransactionManager - Closing Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList@1e09a738 updates=org.hibernate.engine.spi.ExecutableList@67d0f9f8 deletions=org.hibernate.engine.spi.ExecutableList@c60ee2d orphanRemovals=org.hibernate.engine.spi.ExecutableList@3bdd7e0f collectionCreations=org.hibernate.engine.spi.ExecutableList@683e2e2b collectionRemovals=org.hibernate.engine.spi.ExecutableList@84115ed collectionUpdates=org.hibernate.engine.spi.ExecutableList@3db934e collectionQueuedOps=org.hibernate.engine.spi.ExecutableList@5326b83c unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] after transaction
2016-10-14 18:05:44 DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolving exception from handler [public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException]: org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.StackOverflowError
2016-10-14 18:05:44 DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Invoking @ExceptionHandler method: public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.exceptions.OAuth2Exception> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.handleException(java.lang.Exception) throws java.lang.Exception
2016-10-14 18:05:44 INFO o.s.s.o.p.endpoint.TokenEndpoint - Handling error: NestedServletException, Handler dispatch failed; nested exception is java.lang.StackOverflowError
2016-10-14 18:05:44 DEBUG o.s.w.s.m.m.a.HttpEntityMethodProcessor - Written [error="server_error", error_description="Handler dispatch failed; nested exception is java.lang.StackOverflowError"] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@120db352]
2016-10-14 18:05:44 DEBUG o.s.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'dispatcher': assuming HandlerAdapter completed request handling
2016-10-14 18:05:44 DEBUG o.s.web.servlet.DispatcherServlet - Successfully completed request
2016-10-14 18:05:44 DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'delegatingApplicationListener'
2016-10-14 18:05:44 DEBUG o.s.s.w.a.ExceptionTranslationFilter - Chain processed normally
2016-10-14 18:05:44 DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed
Am testing from postman by passing grant_type = "password", username="user", password="pass" in body with form-urlencoded and Authorization Basic Auth(client details).
User.java
@Entity
@Table(name="xt_user_profile")
public class User extends XtremandTimeStamp implements UserDetails{
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="user_id_seq")
@SequenceGenerator(
name="user_id_seq",
sequenceName="user_id_seq",
allocationSize=1
)
@Column(name="user_id")
private Integer userId;
@Column(name="user_name")
private String userName;
@Column(name="email_id")
private String emailId;
@Column(name="password")
private String password;
@Column(name="firstname")
private String firstName;
@Column(name="lastname")
private String lastName;
@Column(name="status")
@org.hibernate.annotations.Type(type="com.xtremand.user.bom.UserStatusType")
private UserStatus userStatus;
private String country;
private String city;
private String zip;
@Column(name="headline")
private String headLine;
private String description;
@Column(name="googlelogin")
private String googleLogin;
@Column(name="linkedinlogin")
private String linkedinLogin;
@Column(name="twitterlogin")
private String twitterLogin;
@Column(name="facebooklogin")
private String facebookLogin;
@Column(name="datereg", columnDefinition="DATETIME")
@Temporal(TemporalType.TIMESTAMP)
private Date dateReg;
@Column(name="datelastedit", columnDefinition="DATETIME")
@Temporal(TemporalType.TIMESTAMP)
private Date dateLastEdit;
@Column(name="datelastlogin", columnDefinition="DATETIME")
@Temporal(TemporalType.TIMESTAMP)
private Date dateLastLogin;
@Column(name="datelastnav", columnDefinition="DATETIME")
@Temporal(TemporalType.TIMESTAMP)
private Date dateLastNav;
@Column(name="alias")
private String alias;
public enum UserStatus{
// UNAPPROVED,APPROVE,DECLINE,BLOCK,SUSPEND,DELETE
UAPPROVED("UnApproved"),
APPROVED("APPROVED"),
DECLINE("DECLINE"),
BLOCK("BLOCK"), SUSPEND("SUSPEND"), DELETE("DELETE");
protected String status;
private UserStatus(String status) {
this.status = status;
}
public String getStatus() {
return status;
}
}
public void initialiseCommonFields(boolean isCreate, int updatedBy){
super.initialiseCommonFields(isCreate, updatedBy);
this.setDateLastEdit(new Date());
this.setDateLastLogin(new Date());
this.setDateLastNav(new Date());
this.setDateReg(new Date());
}
@ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@Fetch(FetchMode.SUBSELECT)
@JoinTable(name = "xt_user_role", joinColumns = {
@JoinColumn(name = "user_id", nullable = false, updatable = false) },
inverseJoinColumns = { @JoinColumn(name = "role_id",
nullable = false, updatable = false) })
private Set<Role> roles = new HashSet<Role>(0);
@Fetch(FetchMode.SUBSELECT)
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<UserSubscription> userSubscriptions = new HashSet<UserSubscription>(0);
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getEmailId() {
return emailId;
}
public void setEmailId(String emailId) {
this.emailId = emailId;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public UserStatus getUserStatus() {
return userStatus;
}
public void setUserStatus(UserStatus userStatus) {
this.userStatus = userStatus;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
public String getHeadLine() {
return headLine;
}
public void setHeadLine(String headLine) {
this.headLine = headLine;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getGoogleLogin() {
return googleLogin;
}
public void setGoogleLogin(String googleLogin) {
this.googleLogin = googleLogin;
}
public String getLinkedinLogin() {
return linkedinLogin;
}
public void setLinkedinLogin(String linkedinLogin) {
this.linkedinLogin = linkedinLogin;
}
public String getTwitterLogin() {
return twitterLogin;
}
public void setTwitterLogin(String twitterLogin) {
this.twitterLogin = twitterLogin;
}
public String getFacebookLogin() {
return facebookLogin;
}
public void setFacebookLogin(String facebookLogin) {
this.facebookLogin = facebookLogin;
}
public Date getDateReg() {
return dateReg;
}
public void setDateReg(Date dateReg) {
this.dateReg = dateReg;
}
public Date getDateLastEdit() {
return dateLastEdit;
}
public void setDateLastEdit(Date dateLastEdit) {
this.dateLastEdit = dateLastEdit;
}
public Date getDateLastLogin() {
return dateLastLogin;
}
public void setDateLastLogin(Date dateLastLogin) {
this.dateLastLogin = dateLastLogin;
}
public Date getDateLastNav() {
return dateLastNav;
}
public void setDateLastNav(Date dateLastNav) {
this.dateLastNav = dateLastNav;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
public Set<UserSubscription> getUserSubscriptions() {
return this.userSubscriptions;
}
public void setUserSubscriptions(Set<UserSubscription> userSubscriptions) {
this.userSubscriptions = userSubscriptions;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
return getRoles();
}
@Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return true;
}
@Override
public String getUsername() {
return getUsername();
}
}
Please try to add next configuration, might help you:
endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
This will allow both methods for authorization.
And add:
oauthServer.allowFormAuthenticationForClients() // it should allow authorize from form submitting.
Also check that you granted authorizedGrantTypes "password" for your clients in db.
In your User.class check method:
@Override
public String getUsername() {
return getUsername();
}
it's references on himself and when spring tries get username raise StackOverflowException. Please, change implementation of this method, it will fix your StackOverflowException .
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With