Sto utilizzando Spring per IOC e gestione delle transazioni e sto pensando di utilizzare Apache Shiro come libreria di sicurezza.Spring ignora le annotazioni @Transactional nella classe Apache Shiro Realm
Ogni volta che si desidera verificare le autorizzazioni di un utente, chiamo subject.isPermitted("right")
, dopo di che Shiro verifica l'autorizzazione utilizzando un datastore. All'interno di queste chiamate, viene stabilita una connessione al database e ho annotato il metodo con @Transactional
. Tuttavia, viene sempre visualizzato un messaggio di errore che indica che la sessione di Hibernate non è legata al thread ogni volta che eseguo il controllo del permesso.
Il metodo è nella classe Realm. Ho definito una consuetudine Shiro classe Realm:
@Component
public class MainRealm extends AuthorizingRealm {
@Autowired
protected SysUserDao userDao;
@Transactional
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
...
final SysUser user = this.userDao.findByUsername(un);
...
return authInfo;
}
@Transactional
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
...
permissions = this.userDao.getAccessRights(un);
...
return authInfo;
}
}
Apache Shiro utilizza un filtro Servlet, così Ho il seguente definito in web.xml:
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
Sto usando la configurazione programmatica per la primavera. Qui è la mia classe App Config:
@Configuration //Replaces Spring XML configuration
@ComponentScan(basePackages = "com.mycompany")
@EnableTransactionManagement //Enables declarative Transaction annotations
public class SpringAppConfig {
@Bean
public DataSource sqlServerDataSource() throws Exception {...}
@Bean
@Autowired
public PlatformTransactionManager transactionManager(SessionFactory sessionFactory) {...}
@Bean
public AnnotationSessionFactoryBean getSessionFactory() throws Exception {...}
@Bean
public static PersistenceExceptionTranslationPostProcessor exceptionTranslation() {...}
@Bean
@Autowired
public DefaultWebSecurityManager securityManager(MainRealm mainRealm) {
final HashedCredentialsMatcher hcm = new HashedCredentialsMatcher(shiroHash);
hcm.setHashIterations(shiroIter);
hcm.setStoredCredentialsHexEncoded(shiroHexEncoded);
mainRealm.setCredentialsMatcher(hcm);
final DefaultWebSecurityManager sm = new DefaultWebSecurityManager();
sm.setRealm(mainRealm);
return sm;
}
@Bean
@Autowired
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
final ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
filter.setSecurityManager(securityManager);
return filter;
}
/**
* This method needs to be static due to issues defined here:<br>
* https://issues.apache.org/jira/browse/SHIRO-222
*/
@Bean
public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
LifecycleBeanPostProcessor lbpp = new LifecycleBeanPostProcessor();
return lbpp;
}
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public static DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
return new DefaultAdvisorAutoProxyCreator();
}
@Bean
@Autowired
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager secMan) {
AuthorizationAttributeSourceAdvisor advBean = new AuthorizationAttributeSourceAdvisor();
advBean.setSecurityManager(secMan);
return advBean;
}
}
In sintesi il problema, credo che la mia classe MainRealm viene collegata correttamente (ha una dipendenza @Autowired a un oggetto DAO e ho verificato che non è nullo) con la eccezione dell'annotazione @Transactional. A causa di ciò, non posso chiamare direttamente user.isPermitted("")
quando viene visualizzato un messaggio di errore: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
.
Vorrei chiedere aiuto per verificare se ho perso qualcosa nella mia configurazione di Primavera.
Nel frattempo ho risolto il problema chiamando la funzione user.isPermitted("")
all'interno di un metodo nella mia classe Servizio che è correttamente associato da un @Transactional.
EDIT Quando ho controllato i registri per l'inizializzazione primavera posso vedere questo:
Bean 'mainRealm' of type [class com.x.security.MainRealm] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
Secondo this SO answer significa MainRealm
non è essere postelaborati dal gestore di transazioni pertanto eventuali annotazioni @Transactional vengono ignorati. In tal caso, come posso correggerlo?
EDIT 2 Secondo this SO question: "In altre parole, se scrivo la mia BeanPostProcessor, e quella classe fa riferimento direttamente altri fagioli nel contesto, quindi quei fagioli riferimento non potranno beneficiare di auto-proxy, e un messaggio è registrato in tal senso. " Ho appena controllato ShiroFilterFactoryBean
ed è in effetti un BeanPostProcessor. E il problema è che richiede un'istanza SecurityManager
che a sua volta richiede un'istanza MainRealm
. Quindi, entrambi i bean sono autowired e quindi non sono eleggibili per il proxy. Mi sento come se fossi più vicino alla soluzione, ma non riesco ancora a risolverlo.
Si può provare a spostare ogni logica transazionale ad alcuni '' UserService' con @ Transactional' o '@Transactional (readOnly = true)' annotazioni e utilizzare questo servizio all'interno del vostro 'MainRealm'. – sody
Ho provato a convertire una classe di servizio nel mio reame, tuttavia, dopo averlo fatto, anche la classe Service ha riscontrato l'errore 'No hibernate session bound to thread' e le sue annotazioni @Transactional non sembravano funzionare più. Sembra che sto facendo qualcosa di sbagliato nell'inizializzare gli oggetti Shiro. –