2013-04-10 12 views
6

Używam Spring do zarządzania IOC i transakcji i planuję użyć Apache Shiro jako biblioteki bezpieczeństwa.Wiosna ignoruje @ Adransakcyjne adnotacje w klasie Apache Shiro Realm

Za każdym razem, gdy chcę sprawdzić uprawnienia użytkownika, dzwonię pod numer subject.isPermitted("right"), po czym Shiro sprawdza, czy jest to uprawnienie, korzystając z bazy danych. W ramach tych wywołań nawiązano połączenie z bazą danych, a ja opatrzyłem tę metodę annotacją za pomocą @Transactional. Zawsze jednak pojawia się błąd, że sesja Hibernate nie jest powiązana z wątkiem za każdym razem, gdy wykonuję kontrolę uprawnień.

Metoda znajduje się w klasie Realm. I zdefiniowano niestandardowy Shiro klasę 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 wykorzystuje filtr serwletu, więc mam następujący zdefiniowane w 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> 

Używam programową konfigurację na wiosnę. Oto moja klasa 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; 
} 
} 

Podsumowując kwestię uważam, moja klasa MainRealm jest prawidłowo podłączony (to ma @Autowired zależność do obiektu DAO i sprawdzeniu, że nie jest null) z wyjątkiem adnotacji @Transactional. Z tego powodu nie mogę bezpośrednio wywołać user.isPermitted(""), ponieważ monituje o błąd: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here.

Chciałbyś poprosić o pomoc w sprawdzeniu, czy czegoś nie przeoczyłem w konfiguracji wiosennej.

W międzyczasie zhakowałem ten problem, wywołując funkcję user.isPermitted("") w ramach metody w mojej klasie usług, która jest poprawnie związana z @Transactional.

EDIT Kiedy sprawdziłem dzienniki Spring inicjalizacji widzę tak:

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) 

Według this SO answer Oznacza to MainRealm nie jest postprocessed przez menedżera transakcji w związku z tym wszelkie @Transactional adnotacje ignorowany. Jeśli tak, jak mogę to poprawić?

EDIT 2 Według this SO question: „Innymi słowy, jeśli piszę własną BeanPostProcessor, a klasa bezpośrednio odwołuje innych ziaren w kontekście, to te przywoływane fasola nie będzie kwalifikował się do auto-proxy, a wiadomość jest rejestrowana w tym celu. " Właśnie sprawdziłem ShiroFilterFactoryBean i jest to w rzeczywistości proces BeanPostProcessor. Problem polega na tym, że wymaga instancji SecurityManager, która z kolei wymaga instancji MainRealm. Tak więc obydwie fasole są autowyredowane i dlatego nie kwalifikują się do proxy. Czuję, że jestem bliżej rozwiązania, ale nadal nie mogę go rozwiązać.

+0

Można spróbować przenieść wszystkie logiki transakcyjnej w pewnym 'UserService' z' @ Transactional' lub '@Transactional (tylko do odczytu = true)' adnotacji i skorzystać z tej usługi w swoim 'MainRealm'. – sody

+0

Próbowałem wyposażyć klasę usług w moją dziedzinę, jednak w tym przypadku klasa usługi napotkała również błąd "Brak sesji hibernacji związanej z wątkiem", a adnotacje @Transactional nie działały. Wygląda na to, że robię coś złego podczas inicjowania obiektów Shiro. –

Odpowiedz

4

Przyczyną problemu jest w rzeczywistości ze względu na następujące kwestie:

Wszystkie BeanPostProcessors i ich bezpośrednio odwołuje fasola będzie instancja na starcie ... Od AOP auto-proxy jest zaimplementowany jako samego BeanPostProcessor żadnych BeanPostProcessors lub bezpośrednio odwołuje fasola kwalifikują się do auto-proxy (i dlatego nie będą mieć w sobie aspektów "wplecionych".

Przywoływane pytanie typu SO to here.

Rozwiązałem ten problem, oddzielając tworzenie komponentu Realm od tworzenia komponentu bean programu SecurityManager.

Odpowiednia zmiana jest z następującego kodu:

@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; 
} 

do następującego kodu:

@Bean 
public DefaultWebSecurityManager securityManager() { 
    final DefaultWebSecurityManager sm = new DefaultWebSecurityManager(); 
    //sm.setRealm(mainRealm); -> set this AFTER Spring initialization so no dependencies 
    return sm; 
} 

Potem wykorzystać do ServletContextListener który słucha podczas inicjalizacji kontekstu Wiosna kończy i mam zarówno fasoli MainRealm i SecurityManager. Potem podłączam jedną fasolkę do drugiej.

@Override 
public void contextInitialized(ServletContextEvent sce) { 
    try { 

     //Initialize realms 
     final MainRealm mainRealm = (MainRealm)ctx.getBean("mainRealm"); 
     final DefaultWebSecurityManager sm = (DefaultWebSecurityManager)ctx.getBean("securityManager"); 
     sm.setRealm(mainRealm); 
    } catch (Exception e) { 
     System.out.println("Error loading: " + e.getMessage()); 
     throw new Error("Critical system error", e); 
    } 
} 
2

@Transactional Adnotacja może być stosowany przed:

  • interfejs def
  • Metoda interfejs
  • Klasa def
  • PUBLIC metodą klasy

Jako wyjaśnione w documentation

Fakt, że metoda jest protected musi być przyczyną problemu, a metoda usługa została może zadeklarowane jako public który wyjaśnił, dlaczego to działa w tym przypadku

+0

Dzięki za cynk. Jednak próbowałem tego i to nie działało. Myślę, że może to być problem związany z moimi błędnie konfigurującymi obiektami Shiro ze Spring. –

Powiązane problemy