【问题标题】:JPA with Hibernate - object 'get' method returns different versions of one to one relationshipJPA with Hibernate - 对象'get'方法返回一对一关系的不同版本
【发布时间】:2015-09-08 23:58:06
【问题描述】:

我有一个与 AuthorizationToken 对象一对一关系的 User 对象。这个项目的基础可以找到here

当用户登录时,通过以一对一的方式保持与用户相关联的授权令牌创建一个新会话,并指定到期时间。出于开发目的,此到期时间设置为 1 个月,这在我的脑海中。现在我的令牌已经开始过期,系统出现错误。

用户登录时调用如下方法:

public AuthorizationToken createAuthorizationToken(User user) {
    if(user.getAuthorizationToken() == null || user.getAuthorizationToken().hasExpired()) {
        LOG.debug("UserToken has expired or is null, creating new token");
        user.setAuthorizationToken(new AuthorizationToken(user, applicationConfig.getAuthorizationExpiryTimeInSeconds()));
        userRepository.save(user);
    }
    return user.getAuthorizationToken();
}

这将返回当前令牌,如果它为空或过期,则返回一个新令牌。如果 null 或过期(setAuthorizationToken 方法),连接到用户的令牌也会被替换。这行得通,我的新令牌在数据库中创建并返回给客户端。

然后我们使用令牌为每个后续请求创建请求签名。服务器接收签名,使用存储的令牌进行复制并检查它们是否相同以识别发送者。

问题出在这里,下面是在服务器端创建签名的方法:

private boolean isAuthorized(User user, AuthorizationRequestContext authorizationRequest, String hashedToken) {
    String unEncodedString = composeUnEncodedRequest(authorizationRequest);
    AuthorizationToken authorizationToken = user.getAuthorizationToken();
    String userTokenHash = encodeAuthToken(authorizationToken.getToken(), unEncodedString);
        if (hashedToken.equals(userTokenHash)) {
            return true;
        }
    return false;
}

我使用与原始登录过程相同的 user.getAuthorizationToken() 方法,但它产生两个不同的值。 login 方法返回新创建的令牌,签名服务返回数据库中与该用户相关的第一个实例(现在已过期,不应再映射到用户对象)。这里出了什么问题?是否只是清理过期令牌以免混淆的问题?我确信这是一个更深层次的问题,因为用户一次应该只有一个身份验证令牌映射。

感谢任何有关代码的 cmets。

【问题讨论】:

  • 你是如何在用户中阅读并保持用于阅读用户的上下文的? EntityManager 是事务性上下文,并维护它们用于读取的托管实体的缓存。如果您保留它,则在其他上下文中所做的任何更改都不会在长期存在的 EntityManager 中可见,除非它们首先被清除或实体被刷新.
  • 只是想补充一点,是的,这也是一个问题。将@Transactional 注释添加到该方法可确保将更改提交到数据库。 (repository.save 没有执行数据库提交)

标签: java hibernate session jpa


【解决方案1】:

这就是我认为正在发生的事情。

查看源代码,在AuthorizationToken 中有一个到User 的映射(这是关系的拥有方)

@JoinColumn(name = "user_id")
@OneToOne(fetch = FetchType.LAZY)
private User user;

User 中,你有这种关系的另一面

@OneToOne(fetch = FetchType.LAZY,
        mappedBy = "user",
        cascade = CascadeType.ALL)
private AuthorizationToken authorizationToken;

当您在createAuthorizationToken() 中创建新令牌时,您只需将该新令牌设置为user 并保存用户的更改。您没有在旧令牌上设置 null 用户。在此之后,由于AuthorizationToken 拥有关系(外键在其表中),您最终在rest_authorization_token 表中得到两行,两者都有user_id 指向您的用户。这意味着用户返回的令牌是数据库作为第一个返回的令牌,调用之间可能会有所不同,从而导致您在不同的方法中看到不同的令牌实例。

解决方案是将null 设置为旧令牌或完全删除旧令牌。这是一个@OneToOne 关系,你应该对rest_authorization_token.user_id 有一个唯一的约束。像这样的

public AuthorizationToken createAuthorizationToken(User user) {
    if(user.getAuthorizationToken() == null) {
        LOG.debug("UserToken is null, creating new token");
        user.setAuthorizationToken(new AuthorizationToken(user, applicationConfig.getAuthorizationExpiryTimeInSeconds()));
        userRepository.save(user);
    } else if (user.getAuthorizationToken().hasExpired()) {
        LOG.debug("UserToken has expired, creating new token");
        AuthorizationToken oldToken = user.getAuthorizationToken();
        oldToken.setUser(null);
        tokenRepository.update(oldToken);
        user.setAuthorizationToken(new AuthorizationToken(user, applicationConfig.getAuthorizationExpiryTimeInSeconds()));
        userRepository.save(user);
    }
    return user.getAuthorizationToken();
}

【讨论】:

  • 太棒了!谢谢你。正在创建两个令牌,您的建议非常有效
猜你喜欢
  • 2010-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多