避免REST服务被二次消费
Avoid REST service to be consumed twice
我有一个关于 Spring MVC 控制器范围和 REST 服务的问题。我有几个 REST 服务,其中 returns 响应中有一个令牌,因此我可以稍后重新创建应用程序的状态,但我不希望用户两次使用相同的令牌,所以我决定在令牌和 HttpServletRequest 中保存一个唯一标识符,这样我就可以在收到请求时检查它(每个请求都会生成一个新标识符)。
所以,我的问题是:
1) 是否有任何其他方法可以确保某些用户不会多次使用相同的令牌(也考虑将该标识符保存在数据库中,但我会有很多查询要插入、删除、验证等。
2) 接收请求的控制器可以是单例,还是应该是原型? (考虑到标识符是从会话中获取的,我不想在不同的会话之间混合它)。
由于 HttpSession#getId() 是唯一的,您可以使用它来创建一个唯一的标记:
// pseudo code
String token = httpSession.getId() + "-" + System.currentTimeMillis();
您也可以创建自己的计数器。
关于只有一次有效的令牌的几句话
不可能实现
没有在某处跟踪令牌。此安全模式需要一些权衡,处理它。
给用户一个令牌并在服务器端跟踪它,就像一个白名单:
- 发行令牌时,将其加入白名单。
- 当带有令牌的请求到达服务器时,检查白名单并:
- 如果令牌有效,则接受请求并将令牌从白名单中移除。
- 如果令牌无效,通过返回适当的状态代码拒绝请求,例如
403
。
此外,考虑为令牌指定一个到期日期,并拒绝任何使用过期令牌发送到服务器的请求。
关于您的性能问题:请记住 premature optimization is the root of all evil。在遇到性能问题并且您 已经证明 性能问题来自您存储令牌的方式之前,您不应该进行优化。例如,您可以开始将令牌存储在数据库中,然后考虑在内存中进行缓存。但是在解决你目前没有的问题时一定要小心。
使用 JWT
如果您选择 JWT,有几个 Java 库可以发布和验证 JWT 令牌,例如:
针对您在服务器端拥有的令牌标识符的 jti
claim should be used to store the token identifier on the token. When validating the token, ensure that it's valid by checking the value of the jti
声明。
对于令牌标识符,您可以使用 UUID。在Java中,很简单:
String uuid = UUID.randomUUID().toString();
这是我的两个预防技巧
禁用提交按钮:
我们可以在我们的函数调用 HTTP 请求之前禁用提交按钮,并在完成获得 HTTP 响应后再次启用它。此技术对于需要很长时间才能完成(超过 5 秒)的过程有效。由于急于得到结果,用户无法点击 n' 再次点击。此外,我们可能会显示一个加载框以获得良好的体验。
发布请求token/id:
这种技术实际上更复杂,更难实现,但由于有一个很好的框架(如Spring Boot)使这变得更容易。在我们开始代码实现之前,先说一下机制;
加载表单页面时,发出新的 requestId
在调用后端服务之前将发出的 requestId 放入 HTTP header
后端服务识别 requestId 是否已经注册
如果 requestId 已经注册那么我们可以标记为违规请求
我有一个关于 Spring MVC 控制器范围和 REST 服务的问题。我有几个 REST 服务,其中 returns 响应中有一个令牌,因此我可以稍后重新创建应用程序的状态,但我不希望用户两次使用相同的令牌,所以我决定在令牌和 HttpServletRequest 中保存一个唯一标识符,这样我就可以在收到请求时检查它(每个请求都会生成一个新标识符)。
所以,我的问题是:
1) 是否有任何其他方法可以确保某些用户不会多次使用相同的令牌(也考虑将该标识符保存在数据库中,但我会有很多查询要插入、删除、验证等。
2) 接收请求的控制器可以是单例,还是应该是原型? (考虑到标识符是从会话中获取的,我不想在不同的会话之间混合它)。
由于 HttpSession#getId() 是唯一的,您可以使用它来创建一个唯一的标记:
// pseudo code
String token = httpSession.getId() + "-" + System.currentTimeMillis();
您也可以创建自己的计数器。
关于只有一次有效的令牌的几句话
不可能实现 没有在某处跟踪令牌。此安全模式需要一些权衡,处理它。
给用户一个令牌并在服务器端跟踪它,就像一个白名单:
- 发行令牌时,将其加入白名单。
- 当带有令牌的请求到达服务器时,检查白名单并:
- 如果令牌有效,则接受请求并将令牌从白名单中移除。
- 如果令牌无效,通过返回适当的状态代码拒绝请求,例如
403
。
此外,考虑为令牌指定一个到期日期,并拒绝任何使用过期令牌发送到服务器的请求。
关于您的性能问题:请记住 premature optimization is the root of all evil。在遇到性能问题并且您 已经证明 性能问题来自您存储令牌的方式之前,您不应该进行优化。例如,您可以开始将令牌存储在数据库中,然后考虑在内存中进行缓存。但是在解决你目前没有的问题时一定要小心。
使用 JWT
如果您选择 JWT,有几个 Java 库可以发布和验证 JWT 令牌,例如:
针对您在服务器端拥有的令牌标识符的 jti
claim should be used to store the token identifier on the token. When validating the token, ensure that it's valid by checking the value of the jti
声明。
对于令牌标识符,您可以使用 UUID。在Java中,很简单:
String uuid = UUID.randomUUID().toString();
这是我的两个预防技巧
禁用提交按钮: 我们可以在我们的函数调用 HTTP 请求之前禁用提交按钮,并在完成获得 HTTP 响应后再次启用它。此技术对于需要很长时间才能完成(超过 5 秒)的过程有效。由于急于得到结果,用户无法点击 n' 再次点击。此外,我们可能会显示一个加载框以获得良好的体验。
发布请求token/id: 这种技术实际上更复杂,更难实现,但由于有一个很好的框架(如Spring Boot)使这变得更容易。在我们开始代码实现之前,先说一下机制; 加载表单页面时,发出新的 requestId 在调用后端服务之前将发出的 requestId 放入 HTTP header 后端服务识别 requestId 是否已经注册 如果 requestId 已经注册那么我们可以标记为违规请求