在我的Spring项目中的自定义AuthenticationProvider中,我尝试读取已记录用户的权限列表,但遇到以下错误:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.horariolivre.entity.Usuario.autorizacoes, could not initialize proxy - no Session at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:566) at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:186) at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:545) at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:124) at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:266) at com.horariolivre.security.CustomAuthenticationProvider.authenticate(CustomAuthenticationProvider.java:45) at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156) at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:177) at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:94) at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:211) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:744)
从StackOverflow的此处阅读其他主题,我理解这种情况的发生是由于框架处理此类属性的方式引起的,但是我无法为我的案例找到任何解决方案。有人可以指出我做错了什么,我该怎么做才能解决?
我的Custom AuthenticationProvider的代码是:
@Component public class CustomAuthenticationProvider implements AuthenticationProvider { @Autowired private UsuarioHome usuario; public CustomAuthenticationProvider() { super(); } @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { System.out.println("CustomAuthenticationProvider.authenticate"); String username = authentication.getName(); String password = authentication.getCredentials().toString(); Usuario user = usuario.findByUsername(username); if (user != null) { if(user.getSenha().equals(password)) { List<AutorizacoesUsuario> list = user.getAutorizacoes(); List <String> rolesAsList = new ArrayList<String>(); for(AutorizacoesUsuario role : list){ rolesAsList.add(role.getAutorizacoes().getNome()); } List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); for (String role_name : rolesAsList) { authorities.add(new SimpleGrantedAuthority(role_name)); } Authentication auth = new UsernamePasswordAuthenticationToken(username, password, authorities); return auth; } else { return null; } } else { return null; } } @Override public boolean supports(Class<?> authentication) { return authentication.equals(UsernamePasswordAuthenticationToken.class); } }
我的实体类是:
UsuarioHome.java
@Entity @Table(name = "usuario") public class Usuario implements java.io.Serializable { private int id; private String login; private String senha; private String primeiroNome; private String ultimoNome; private List<TipoUsuario> tipoUsuarios = new ArrayList<TipoUsuario>(); private List<AutorizacoesUsuario> autorizacoes = new ArrayList<AutorizacoesUsuario>(); private List<DadosUsuario> dadosUsuarios = new ArrayList<DadosUsuario>(); private ConfigHorarioLivre config; public Usuario() { } public Usuario(String login, String senha) { this.login = login; this.senha = senha; } public Usuario(String login, String senha, String primeiroNome, String ultimoNome, List<TipoUsuario> tipoUsuarios, List<AutorizacoesUsuario> autorizacoesUsuarios, List<DadosUsuario> dadosUsuarios, ConfigHorarioLivre config) { this.login = login; this.senha = senha; this.primeiroNome = primeiroNome; this.ultimoNome = ultimoNome; this.tipoUsuarios = tipoUsuarios; this.autorizacoes = autorizacoesUsuarios; this.dadosUsuarios = dadosUsuarios; this.config = config; } public Usuario(String login, String senha, String primeiroNome, String ultimoNome, String tipoUsuario, String[] campos) { this.login = login; this.senha = senha; this.primeiroNome = primeiroNome; this.ultimoNome = ultimoNome; this.tipoUsuarios.add(new TipoUsuario(this, new Tipo(tipoUsuario))); for(int i=0; i<campos.length; i++) this.dadosUsuarios.add(new DadosUsuario(this, null, campos[i])); } @Id @Column(name = "id", unique = true, nullable = false) @GeneratedValue(strategy=GenerationType.AUTO) public int getId() { return this.id; } public void setId(int id) { this.id = id; } @Column(name = "login", nullable = false, length = 16) public String getLogin() { return this.login; } public void setLogin(String login) { this.login = login; } @Column(name = "senha", nullable = false) public String getSenha() { return this.senha; } public void setSenha(String senha) { this.senha = senha; } @Column(name = "primeiro_nome", length = 32) public String getPrimeiroNome() { return this.primeiroNome; } public void setPrimeiroNome(String primeiroNome) { this.primeiroNome = primeiroNome; } @Column(name = "ultimo_nome", length = 32) public String getUltimoNome() { return this.ultimoNome; } public void setUltimoNome(String ultimoNome) { this.ultimoNome = ultimoNome; } @ManyToMany(cascade=CascadeType.ALL) @JoinTable(name = "tipo_usuario", joinColumns = { @JoinColumn(name = "fk_usuario") }, inverseJoinColumns = { @JoinColumn(name = "fk_tipo") }) @LazyCollection(LazyCollectionOption.TRUE) public List<TipoUsuario> getTipoUsuarios() { return this.tipoUsuarios; } public void setTipoUsuarios(List<TipoUsuario> tipoUsuarios) { this.tipoUsuarios = tipoUsuarios; } @ManyToMany(cascade=CascadeType.ALL) @JoinTable(name = "autorizacoes_usuario", joinColumns = { @JoinColumn(name = "fk_usuario") }, inverseJoinColumns = { @JoinColumn(name = "fk_autorizacoes") }) @LazyCollection(LazyCollectionOption.TRUE) public List<AutorizacoesUsuario> getAutorizacoes() { return this.autorizacoes; } public void setAutorizacoes(List<AutorizacoesUsuario> autorizacoes) { this.autorizacoes = autorizacoes; } @ManyToMany(cascade=CascadeType.ALL) @JoinTable(name = "dados_usuario", joinColumns = { @JoinColumn(name = "fk_usuario") }, inverseJoinColumns = { @JoinColumn(name = "fk_dados") }) @LazyCollection(LazyCollectionOption.TRUE) public List<DadosUsuario> getDadosUsuarios() { return this.dadosUsuarios; } public void setDadosUsuarios(List<DadosUsuario> dadosUsuarios) { this.dadosUsuarios = dadosUsuarios; } @OneToOne @JoinColumn(name="fk_config") public ConfigHorarioLivre getConfig() { return config; } public void setConfig(ConfigHorarioLivre config) { this.config = config; } }
AutorizacoesUsuario.java
@Entity @Table(name = "autorizacoes_usuario", uniqueConstraints = @UniqueConstraint(columnNames = "id")) public class AutorizacoesUsuario implements java.io.Serializable { private int id; private Usuario usuario; private Autorizacoes autorizacoes; public AutorizacoesUsuario() { } public AutorizacoesUsuario(Usuario usuario, Autorizacoes autorizacoes) { this.usuario = usuario; this.autorizacoes = autorizacoes; } @Id @Column(name = "id", unique = true, nullable = false) @GeneratedValue(strategy=GenerationType.AUTO) public int getId() { return this.id; } public void setId(int id) { this.id = id; } @OneToOne @JoinColumn(name = "fk_usuario", nullable = false, insertable = false, updatable = false) public Usuario getUsuario() { return this.usuario; } public void setUsuario(Usuario usuario) { this.usuario = usuario; } @OneToOne @JoinColumn(name = "fk_autorizacoes", nullable = false, insertable = false, updatable = false) public Autorizacoes getAutorizacoes() { return this.autorizacoes; } public void setAutorizacoes(Autorizacoes autorizacoes) { this.autorizacoes = autorizacoes; } }
Autorizacoes.java
@Entity @Table(name = "autorizacoes") public class Autorizacoes implements java.io.Serializable { private int id; private String nome; private String descricao; public Autorizacoes() { } public Autorizacoes(String nome) { this.nome = nome; } public Autorizacoes(String nome, String descricao) { this.nome = nome; this.descricao = descricao; } @Id @Column(name = "id", unique = true, nullable = false) @GeneratedValue(strategy=GenerationType.AUTO) public int getId() { return this.id; } public void setId(int id) { this.id = id; } @Column(name = "nome", nullable = false, length = 16) public String getNome() { return this.nome; } public void setNome(String nome) { this.nome = nome; } @Column(name = "descricao", length = 140) public String getDescricao() { return this.descricao; } public void setDescricao(String descricao) { this.descricao = descricao; } }
你需要fetch=FetchType.EAGER在ManyToMany批注中添加以自动拉回子实体:
fetch=FetchType.EAGER
@ManyToMany(fetch = FetchType.EAGER)
更好的选择是通过将以下内容添加到你的spring配置文件中来实现spring transactionManager:
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <tx:annotation-driven />
然后可以将@Transactional批注添加到你的authenticate方法中,如下所示:
@Transactional public Authentication authenticate(Authentication authentication)
然后,这将在authenticate方法期间启动db事务,从而允许在尝试使用它们时从db中检索任何惰性集合。