1

我正在尝试使用 Spring 创建一个 web 应用程序。该网络应用程序是一个测试版/演示网站,仅供邀请。因此,我需要生成一个指向 webapplication url 的链接,并附加一个唯一的 id,该 id 有效期为 24 小时。用户可以使用该链接 24 小时并使用(我也有计划通过 ip 限制用户)

为了实现这样的令牌生成和到期,我应该只依靠会话超时的容器来完成吗?或者创建带有令牌创建和到期服务的弹簧支持服务层是否有意义?

编辑

既然我有足够的钱来吸引更多的注意力,我想我会改写这个问题,这样它就更有意义了——在一段时间后迫使用户退出 Web 应用程序的好策略是什么?

4

7 回答 7

4

依靠会话超时是不够的。

我不熟悉 Spring 。对于任何有您要求的 Web 应用程序,我会将我的通用解决方案如下所示:

  1. 假定邀请包含指向 Web 应用程序的链接。

  2. 假定链接包含唯一 id 。

  3. 假设 Beta/Demo 用户表有一个beta-expiry(datetime) 列来保存到期日期和时间。

  4. 当您使用邀​​请中的链接访问您的 Web 应用程序时,请在beta-expiry24 小时后更新该 unique_id 的列。

  5. 当 Beta/Demo 用户尝试登录时,请检查该beta-expiry特定用户的列并在未到期时允许访问。在到期时显示适当的消息。

  6. 每当登录的 Beta/Demo 用户对您的 Web 应用程序执行后续访问时,请检查该beta-expiry特定用户的列并在未达到到期时允许访问。在到期时显示适当的消息。

  7. 如果看起来有用,请在标题区域的某处显示一个倒数计时器,显示剩余时间。

  8. 如果为所有或部分用户扩展 Beta 使用,您可以beta_expiry适当地更新该列。

  9. 此外,invitation-expiry考虑到特定的持续时间,您还可以有一个列来保存邀请到期,例如。发出邀请后的 48 小时。

我们使用一些类似的解决方案在特定的无访问持续时间后从我们的 SaaS 应用程序中注销(登录)用户。

于 2013-09-28T15:25:27.910 回答
3

我认为您应该更多地依赖后端而不是cookie上的会话,想象一下您将cookie设置为24小时到期但客户端从浏览器中删除cookie的情况,(取决于您的逻辑)您的链接将生成一个新会话,否则请求将被阻止。

恕我直言,您可以在客户端存储会话 cookie,但您需要在服务器中有第二个比较源,可能是数据库、no-sql 文档、缓存中的集合,您可以比较并检查对客户。

只是把所有的想象一下下面的用例:

  • 用户获得邀请链接并点击它
  • 系统检查他们是否第一次登录系统,并将该日期保存为“开始日期”,另一个保存为“最后访问”
  • 系统设置一个 24 小时过期的身份验证 cookie
  • 系统跟踪对服务器的每个回发/ajax 调用并更新“最后访问”日期
  • 如果用户删除 cookie,系统会检查“上次访问”并与当前服务器日期进行比较
  • 如果有效,系统将使用剩余时间创建一个新的身份验证 cookie
  • 如果无效,系统会向用户发送消息。
  • 对于用户,cookie 将根据剩余时间过期(开始日期和上次访问之间的计算)

我希望这有帮助。问候

于 2013-09-24T19:26:26.943 回答
3

一种策略是通过将相关数据存储在数据库中并使用缓存库来跟踪expiry date-time所有用户(在检查到期日期时减少数据库命中)。这是一个小例子:

创建一个数据库表,其中包含将用户 ID 映射到到期日期的列:id, unique_user_id, expiry_date_time。您需要在代码中创建唯一的用户 ID 并将其保存在数据库中,然后再将 URL 发送给具有此 ID 的用户。您可以保留null为 的初始值expiry_date_time。在 Java 中创建一个类来表示此映射:

class UserIdWithExpiryDate{
        private String userId;
        private Date expiryDateTime;
        ...
}

定义一个Service方法,该cacheable方法将为给定的返回 this 的实例userId

public interface CacheableService {
    @Cacheable("expiryDates")
    public UserIdWithExpiryDate getUserIdWithExpiryDate(String id);

    public void updateUserIdWithExpiryDate(String userId);
}

import org.joda.time.DateTime;
@Service
public class CacheableServiceImpl implements CacheableService {

    @Autowired
    private YourDao dao;

    @Override
    public UserIdWithExpiryDate getUserIdWithExpiryDate(String id) {
        return dao.getUserIdWithExpiryDate(id);
    }

    public void updateUserIdWithExpiryDate(String userId){
        Date expiryDate = new Date(new DateTime().plusHours(24).getMillis());
        dao.update(userId, expiryDate);
    }
}

方法的结果getUserIdWithExpiryDate存储在缓存中,因此在后续调用(使用相同的参数)时,无需实际执行方法即可返回缓存中的值。

下一步是在访问网站时检查用户的到期日期。这可以使用OncePerRequestFilter来完成:

@Component("timeoutFilter")
public class TimeoutFilter extends OncePerRequestFilter {

    @Autowired
    CacheableService cacheableService;
    // Here you need to decide whether to proceed with the request or not
    @Override
    protected void doFilterInternal(HttpServletRequest request,
            HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        filterChain.doFilter(request, response);
    }

}

在方法内部可以采取的步骤doFilterInternal来检查用户的有效性:

  1. 从请求中获取用户 ID
  2. 执行:cacheableService.getUserIdWithExpiryDate(userId)
  3. 如果第 2 步返回 null,则不存在具有此 ID 的用户。您不应继续执行该请求。
  4. 如果第 2 步返回 UserIdWithExpiryDate 的实例,请检查“expiryDateTime”的值
  5. 如果“expiryDateTime”的值为空,则表示用户是第一次访问该站点。更新“userExpiryDate”:cacheableService.updateUserIdWithExpiryDate(userId)并继续请求。
  6. 如果“expiryDateTime”不为空,请将其与当前 date_time 进行比较。如果expiryDateTime.isAfter(currentDateTime),请继续请求。

对于缓存,您可以将Spring Cache AbstractionEHCACHE一起使用。

于 2013-09-26T13:27:19.943 回答
2

在这里,我可以想到我见过的两个用例。

1.)这种管理用于在线测试或电子邮件验证等应用程序,其中为用户提供了一个带有一些令牌的链接。此链接和令牌在某个固定时间段内有效,并且只能使用一次。(例如在线测试或密码重置电子邮件)

2.)另一个用例是提供带有令牌的链接,该令牌在固定时间段内也有效,但在这种情况下,链接和令牌可以在允许的时间段内使用任意次数(例如在线注册考试或一些大学入学表格)。

因此,绝对只使用会话来管理它不是一个好的选择,因为可以清除浏览器。因此,您需要在服务器(可能在数据库中)维护链接和令牌对以及其他一些信息,如上次访问时间创建日期和时间有效期至

为了更加灵活,链接的有效时间和令牌可以设置不同(例如,链接可以用于一组用户,但令牌对用户来说是唯一的)。

因此,您可以在每次用户访问时使用此信息来检查链接或令牌是否仍然有效以及他们上次访问的时间。如果它已经过期,那么您可以显示相关的错误消息。

于 2013-09-26T07:26:50.847 回答
2

代币

出于营销目的,我们预先生成令牌,将它们与所有信息一起存储在数据库中,例如对某些用户帐户的限制、IP 范围(对于某些移动运营商)、使用日期和时间范围等。

如果是一次性代码,我们只需将其标记为在数据库中使用。

为了生成这些代码,我们使用随机数或编码一些信息并使用(SHA-1 或更好)。然后我们使用 bigInteger.toString(36) 或不同的方案并将前 16 个字母分组,让它看起来像一个许可证密钥。

如果我们用它来表达信息,例如用户帐户/名称或运营商(移动设备)或任何我们甚至可以在没有数据库的情况下验证令牌。

强制用户退出

只需在用户第一次启动会话或登录时检查令牌的有效性。在整个会话期间,您只需检查令牌是否已过期(如果您有一个值,则在会话中存储一个值,将其附加(加密)在url 或其他任何内容或检查数据库。

因此,在每次请求时,您只需检查用户是否有权访问您的网站,否则您会使用错误/信息页面阻止用户。

您还可以将 java 脚本添加到每个页面。该脚本可以通过以下方式确定用户使用您网站的权限是否已过期:

  • 加密令牌在隐藏 div 中有效的相对时间,并使用用户本地时间计算绝对时间。然后将绝对过期时间与用户时间进行比较(假设每 3 秒一次),您可以自己进行所有轮询和推送(comet、ajax、websockets)行为。

一旦 java 脚本程序注意到令牌已过期,请删除内容部分并将其替换为信息/错误页面内容(或将隐藏的 div 标记为可见)。这样,每次用户重新访问仍存储在浏览器历史记录中的页面时,也会被销毁。(如果这对您来说是必需品)。

概括

  • 使用随机令牌和数据库或加密信息(例如可以使用令牌的日期)和 SHA-1。
  • 每次用户请求页面时检查令牌的有效性(将时间范围存储在会话/url中以保存到数据库的往返)
  • 提供一个 java 脚本,在重新访问时破坏历史记录中的本地页面。
于 2013-09-27T19:05:58.860 回答
1

24 小时后“删除用户帐户”(取决于您构建日志系统的方式)怎么样。将此与每次加载页面时检查用户帐户相结合,将在 24 小时阈值过后第一次刷新时强制他/她退出。

于 2013-09-22T11:28:25.047 回答
1

您可以存储带有过期时间的 id,当一个带有该 id 的请求进来时,检查它是否仍然有效,如果您尝试完成类似 id 之类的事情,比如在第一次请求后 5 小时有效,然后存储第一次请求发出的时间和对于每个后续请求,检查它是否仍然有效

于 2013-09-22T16:24:21.707 回答