spring security oauth2 java config – 处理程序调度失败;嵌套异常是java.lang.StackOverflowError

java配置中使用OAuth2配置Spring Security,而client_credentails流工作正常,但是密码流正在抛出Handler dispatch失败;嵌套异常是java.lang.StackOverflowError,下面是日志信息

2016-10-10 23:19:08 DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver – 解析处理程序中的异常[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:处理程序调度失败;嵌套异常是java.lang.StackOverflowError

这是我的配置文件:

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();
    }

}

UserDetailsS​​erviceImpl.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表

oauth_client_details table

全栈跟踪

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

我是通过使用form-urlencoded和Authorization Basic Auth(客户端详细信息)在body中传递grant_type =“password”,username =“user”,password =“pass”来测试邮递员.

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();
    }
}
最佳答案
请尝试添加下一个配置,可能会帮助您:

endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);

这将允许两种授权方法.

并添加:

oauthServer.allowFormAuthenticationForClients() // it should allow authorize from form submitting.

还要检查是否为db中的客户端授予了authorizedGrantTypes“password”.

在您的User.class检查方法中:

@Override
public String getUsername() {
    return getUsername();
}

它是对自己的引用,当spring尝试获取用户名时引发StackOverflowException.请更改此方法的实现,它将修复StackOverflowException
.

转载注明原文:spring security oauth2 java config – 处理程序调度失败;嵌套异常是java.lang.StackOverflowError - 代码日志