微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Spring在Apache Shiro Realm类中忽略了@Transactional注释

我正在使用Spring进行IOC和事务管理,并计划使用Apache Shiro作为安全库.

每当我想检查用户的权限时,我都会调用subject.isPermitted(“right”),然后Shiro使用数据存储区检查权限.在这调用中,建立了数据库连接,并且我使用@Transactional注释了该方法.但是,每当我执行权限检查时,总是会收到一个错误,即没有Hibernate会话绑定到该线程.

方法在Realm类中.我定义了一个自定义的Shiro 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使用Servlet过滤器,因此我在web.xml中定义了以下内容

aram>
        aram-name>targetFilterLifecyclearam-name>
        aram-value>truearam-value>
    aram>

我正在使用Spring的编程配置.这是我的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: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;
}
}

总结一下这个问题,我相信我的MainRealm类正在正确连接(它有一个@Autowired依赖于DAO对象,我验证它不是null),但@Transactional注释除外.因此,我无法直接调用user.isPermitted(“”),因为它会提示错误:没有Hibernate Session绑定到线程,并且配置不允许在此处创建非事务性的.

想请求帮助检查我的Spring配置中是否遗漏了任何内容.

与此同时,我通过调用我的Service类中由@Transactional正确绑定的方法中的user.isPermitted(“”)函数解决此问题.

编辑当我检查日志的Spring初始化时,我可以看到:

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)

根据this SO answer,这意味着MainRealm不会被事务管理器进行后处理,因此任何@Transactional注释都会被忽略.如果是这样,我该如何纠正?

编辑2根据this SO question:“换句话说,如果我编写自己的BeanPostProcessor,并且该类直接引用上下文中的其他bean,那么那些引用的bean将不符合自动代理的条件,并且会记录一条消息“.我刚检查了ShiroFilterfactorybean,它实际上是一个BeanPostProcessor.问题是它需要一个SecurityManager实例,而该实例又需要一个MainRealm实例.因此两个bean都是自动装配的,因此没有资格进行代理.我觉得我更接近解决方案,但我仍然无法解决它.

最佳答案
问题的根本原因实际上是由于以下原因:

所有BeanPostProcessors及其直接引用的bean都将在启动时实例化…由于AOP自动代理是作为BeanPostProcessor本身实现的,因此没有BeanPostProcessors或直接引用的bean有资格进行自动代理(因此不会将方面’编织’到他们.

参考SO问题是here.

我已经通过将Realm bean创建与SecurityManager bean创建分离来解决了这个问题.

相关更改来自以下代码

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

然后我使用一个servletcontextlistener,它监听Spring上下文初始化完成时我有MainRealm和SecurityManager bean.然后我只是将一个bean插入另一个bean.

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐