管理身份validation令牌的最佳做法

我正在使用HttpCLient在Java中编写REST客户端,我访问的REST API需要每个REST操作的身份validation令牌。 此令牌有效期为24小时。

我现在处理这个问题的方法是每当我需要进行REST调用时调用“ getAuth() ”方法,这似乎是auth服务器上的开销。

如何方便地存储此身份validation令牌并管理其生命周期? 是否有任何记录的最佳实践?

我想到了以下解决方案

 public class MySession { String user; String pass; public MySession(String user, String pass) { this.user = user; this.pass = pass; } public getAuth() { //user user, pass to get auth token } } 

然后将sessions对象传递给任何需要该令牌的类。 如果令牌已过期,请再次调用此方法

我假设您正在使用OAuth进行授权。 无论您使用的是JWT还是其他令牌都与此情况无关。

执行授权时,将向您发出一个带有过期的access_token ,并根据您请求的授权类型(客户端凭据,授权代码,隐式,资源所有者), refresh_token

客户端应保持access_token和到期日期。 如果发出refresh_token,则必须保密(谨防为您的用例使用正确的授权)。

在后续调用中,您的客户端不应在每次调用时请求新令牌,它应使用存储的access_token

一旦API开始返回401 Unauthorizedaccess_token可能已过期。 您的客户端应该尝试使用refresh_token刷新access_token如果有的话)。

如果您没有refresh_token或刷新请求也失败,因为refresh_token不再有效,您可以执行新的授权流程。

您可以使用过期时间作为线索,通过刷新或通过新的完整授权流程来了解何时获取新的access_token 。 这将避免401 Unauthorized 。 在任何情况下,在对某些调用使用有效的access_token之后收到此响应时,您的客户端应该具有回退策略。

我建议你使用以下场景:

1)首先,调用auth(username, password) rest api获取auth令牌。 如果给定的凭证没问题,那么只需使用HTTP 200响应代码将auth cookie发送回客户端。

2)然后,您可以调用受保护的restapi。 您需要每次都发送带有您请求的身份validationcookie。

3)Servletfilter(或类似的东西)检查每个传入的请求并validation令牌。 如果令牌有效,则请求前进到rest方法,如果不是,则需要生成http 401/403响应。

我建议你不要编写自己的身份validation层。 而不是安装和使用现有的。 我建议你使用OpenAM 。 它是一个极好的开源访问管理系统。

我还建议您不要在服务器端打开会话以进行身份​​validation。 如果您有10个客户端,则需要由服务器管理10个会话。 这不是一个大问题。 但是,如果您拥有100或1000或数百万个不同的客户端,则需要更多内存来存储服务器上的会话。

如果你担心数据库的点击次数太多,那么我假设有很多网络活动。

我不建议在您的情况下使用Session,而是将令牌存储在客户端的cookie中。

在高流量环境(我假设你的环境)中,使用Session会占用大量服务器内存,并且可扩展性也是一个问题,必须在群集中保持会话同步。

正如@CássioMazzochiMolin所提到的,您可以使用内存缓存来存储任何特定于用户的数据和令牌。 这将减少对数据库的命中,并且还允许您在需要时更轻松地扩展应用程序。

事实上的标准并没有实现你自己的解决方案(安全性的基本规则: 不要实现你自己的东西! ),而是使用事实上的标准解决方案,即JSON Web Tokens 。

网站上的文档,但基本的想法是,您只需要存储一个值(服务器的私钥),然后您可以validation服务器最初发出的每个声明(在您的情况下将包含到期时间) 。

为简洁起见,我假设您正在调用一个无法更改的端点。 您应该如何实现将在很大程度上取决于令牌是基于应用还是基于用户(共享应用实例上的所有用户一个令牌或每个用户一个令牌)。

如果它是整个应用程序的一个身份validation令牌:

  • 将其与生存时间戳一起存储在内存中(或者捕获令牌过期错误,请求新令牌并重试原始请求),如果它不存在则刷新/已过期
  • 如果您担心在应用程序重新启动后重新请求API令牌,还会将其存储在数据库中并在启动时加载它(如果存在)

如果每个用户只有一个令牌:

  • 将它存储在您的用户会话中,这正是会话所用的内容,如果您正在为用户进行授权,那么他们将会有会话并且已经存在开销
  • 如果您不希望每次登录时都重新请求令牌,请将当前令牌存储在数据库中,并在登录时将其加载到会话中

你应该使用JsonWebToken(简称JWT)来做这种事情。 JWT已建立支持以设置到期日期。 有很多库可以使用这种方法,你可以在这里阅读更多

目前有4个java实现,所有这些实现都可以检查令牌是否仍然有效(exp check) 在此处输入图像描述

所以,如果我正确理解你正在为你的所有请求使用相同的令牌(这意味着只要你的应用程序启动并运行并刷新令牌,你应该没问题。我确实遇到了同样的问题,这是我是如何解决它的。我有一个单例类,它在应用程序启动时初始化一次并在令牌失效时刷新令牌。我使用C#,Asp.NET MVC5和AutoFac进行DI,但我确定你可以用Java和Spring做同样的事情。

使用Thread Safety更新单例的属性

您可以创建一个管理器并在登录时在本地线程中存储auth-cookie,如下面的代码所示。 只要线程存在,您就可以从getAuth()获取cookie。

 public class Manager { private static final ThreadLocal SECURITY_CONTEXT = new ThreadLocal<>(); public static void setAuth(String auth) { SECURITY_CONTEXT.set(auth); } public static String getAuth() { return SECURITY_CONTEXT.get(); } public static void clear(){ SECURITY_CONTEXT.remove(); } } 

使用json web令牌,在两个客户端之间交换信息。 令牌只会在24小时内保持活动状态,之后标题中的所有后续呼叫都将被拒绝。

  1. 每个请求的Auth Token都是正确的方法,请考虑auth服务器扩展以解决性能问题。
  2. 在首次成功validation(用户名和密码)时,生成私有公钥对。 将私钥存储为会话安全令牌(SST) ,并将公钥作为公共安全客户端密钥(PSCK)发送给客户端
  3. 在登录(或身份validation)以外的所有请求中,客户端将发送PSCK以保护用户名和密码被盗,服务器可以定期在内部validationPSCK到期,从而节省处理时间。
  4. 如果系统在身份validation方面存在性能问题,请设置具有可伸缩性的auth服务器。
  5. 没有令牌或密码被缓存,未加密交换并发送到安全区域外。 不要使用URL参数发布。