1

我正在使用 Shiro 来保护我的 Spring MVC webapp。我使用 Hibernate 进行持久性,所以我有一个 HibernateRealm 来获取和填充一个 AuthenticationInfo 对象。

@Override
@Transactional
protected AuthenticationInfo doGetAuthenticationInfo(
        AuthenticationToken token) throws AuthenticationException {
    Account account = accountDao.findByUsername((String)token.getPrincipal());

    SimplePrincipalCollection principals = new SimplePrincipalCollection(account, getName());

    SimpleAccount info = new SimpleAccount(principals, account.getPassword());

    return info;
}

Account是我的自定义用户类。我使用 DAOAccount按用户名检索。我想知道制作这种方法是否有任何意义@Transactional。毕竟这是一个只读操作。

我也遇到了以下问题:DAO 确实sessionFactory.getCurrentSession()会获得一个会话,但我得到了一个

HibernateException: No Session found for current thread 

当方法被调用时。我在我的应用程序上下文中有这些:

<tx:annotation-driven transaction-manager = "transactionManager" />
<bean id="transactionManager"
    class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

我不明白为什么 Spring 不为我打开会话。

编辑:要登录,我们@Controller使用 Shiro 的 Spring 方法执行此操作Subject

@RequestMapping(value = "/account/login", method = RequestMethod.POST)
public String login(@RequestParam("username") String username, @RequestParam("password") String password) {
    Subject currentUser = SecurityUtils.getSubject(); 
    if (!currentUser.isAuthenticated) {
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        currentUser.login(token);
        return "profile";
    } 
    return "home";
}

在内部,Shiro 使用我上面的领域方法来获取存储的用户名/密码信息。它使用@AutowiredDAO 检查我的数据库中是否有正确的帐户。然后它将密码与CredentialsMatcher实现进行匹配。

4

3 回答 3

2

所以你有两个问题。通常最好将这些问题一分为二,因为这些问题之间并没有真正的联系。

  1. No Session found for current thread 似乎@Transactional注释不起作用。为了确保您可以在调试模式下运行代码或测试并在堆栈中查找 JdkDynamicAopProxy 或类似的东西 - 如果它存在,那么您的领域将通过事务拦截代理调用,但我想目前没有代理. 为了让它工作,你需要从 SpringContext 中获取,而不是直接从 HibernateRealm 中获取,而是从这个领域正在实现的接口中获取。这是因为内置的标准 java 库代理只能处理接口。
  2. 至于使只读服务方法具有事务性。这样做有几个正当的理由:
    • 由于您使用的是 Hibernate,因此您实际上可能使用多个查询来获取您的 Account 对象。如果同时修改此帐户,可能会导致状态不一致:
      • 帐户检索的第一个查询
      • 帐号被修改或删除
      • 用于帐户检索的第二个查询 - 此查询将看到修改结果与第一个查询的结果一起可能导致不一致的行为,但如果第一个和第二个查询在同一事务中且具有适当的事务隔离级别,则第二个查询将看不到修改。
    • 对数据库的统一访问——当你的所有数据库连接层以一种相同的方式访问数据库时,这真的很有帮助——我大大简化了应用程序的维护和扩展。
    • 使用一些事务提示,如@Transactional(readOnly=true)通过适当的配置可能会提高您的性能(例如,对于真正高负载的应用程序,只读查询可能会使用数据库服务器的辅助副本)。java.sql.Connection.setReadOnly()将方法设置为 Spring 事务的一部分确实比其他方式更容易。
于 2013-01-21T08:57:26.040 回答
2

Spring 似乎没有为您的 Realm bean 创建事务代理。这是我可以看到为什么 Hibernate Session 不可用的唯一原因 - 因为支持基础设施不存在(在线程上)准备好使用。

至于您的问题,如果您确实想标记它@Transactional,您可以考虑指定@Transactional(readOnly=true)

于 2013-01-21T17:17:37.743 回答
0

Shiro 创建了它自己的 Realm 实例,因此 Spring 无权将其包装在代理中。这就是为什么它不能添加事务行为。

于 2013-01-25T15:57:52.200 回答