管理身份验证令牌的最佳实践
Posted
技术标签:
【中文标题】管理身份验证令牌的最佳实践【英文标题】:Best practices for managing auth token 【发布时间】:2017-07-05 20:14:51 【问题描述】:我正在使用 HttpCLient 用 Java 编写一个 REST 客户端,我访问的 REST API 需要一个用于每个 REST 操作的身份验证令牌。此令牌的有效期为 24 小时。
我现在处理这个问题的方式是每次我需要进行 REST 调用时调用“getAuth()
”方法,这似乎是身份验证服务器上的开销。
如何方便地存储此身份验证令牌并管理其生命周期? 是否有任何记录在案的最佳做法?
我想到了以下解决方案
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
然后将会话对象传递给任何需要令牌的类。如果token过期了,再次调用这个方法即可
【问题讨论】:
你的客户是什么样的?它是 Java 应用程序吗?您是否关心将令牌存储在客户端还是服务器端? 它是一个 java dropwizard 应用程序,我担心将令牌存储在数据库中并进行过多的数据库调用,而不是我应该继续使用令牌直到它过期然后请求一个新的(当它抛出一个“令牌过期”异常或其他东西.. 您能出示您的客户代码吗?在客户端,如果将令牌存储在数据库中可能会导致性能问题,则可以在内存中使用缓存。如果您知道令牌将过期并且您可以刷新它,那就去做吧:) 我删除了我的答案,因为我误解了你的问题。我认为这取决于您使用的 REST API 的安全问题。您对所有请求使用相同的凭据?如果令牌有 24 小时,我会重复使用它直到它过期。 @gabrielgiussi 是的,我对所有请求都使用相同的凭据,所以这个令牌在发出后 24 小时内有效,我没有 API 来刷新它,而是我必须请求一个新令牌到期后。如何跟踪到期时间?我是否只是依靠一个异常说令牌已过期? 【参考方案1】:如果您担心对数据库的点击次数过多,那么我假设有很多网络活动。
我不建议在您的情况下使用 Session,而是将令牌存储在客户端的 cookie 中。
在高流量环境中(我假设您的环境是这样),使用 Session 会消耗大量服务器内存,并且可伸缩性也可能是一个问题,必须在集群中保持会话同步。
正如@Cássio Mazzochi Molin 还提到的,您可以使用内存缓存来存储任何用户特定的数据和令牌。这将减少对数据库的访问,并允许您在需要时更轻松地扩展应用程序。
【讨论】:
【参考方案2】:事实上的标准不是实现你自己的解决方案(安全的基本规则:不要实现你自己的东西!),而是使用事实上的标准解决方案,即JSON Web Tokens .
网站上的文档,但基本思想是,您只需要存储一个值(服务器的私钥),然后您就可以验证服务器最初发出的每个声明(在您的情况下,它将包含一个到期时间)。
【讨论】:
【参考方案3】:使用 json web tokens ,在两个客户端之间交换信息。令牌只会在 24 小时内有效,在此之后,标头中的所有后续调用都将被拒绝。
【讨论】:
【参考方案4】:为简洁起见,我假设您正在调用无法更改的端点。您应该如何实施将在很大程度上取决于令牌是基于应用程序还是基于用户(共享应用程序实例上的所有用户一个令牌或每个用户一个令牌)。
如果它是整个应用程序的一个身份验证令牌:
将其与生存时间时间戳一起存储在内存中(或者捕获令牌过期错误,请求新令牌并重试原始请求),如果它不存在/已过期则刷新它 如果您担心在应用程序重启后重新请求 API 令牌,请将其存储在数据库中并在启动时加载(如果存在)如果是每个用户一个令牌:
将其存储在您的用户会话中,这正是会话的用途,如果您正在对用户进行身份验证,那么他们将拥有一个会话并且开销已经存在 如果您不想在他们每次登录时重新请求令牌,请将其当前令牌存储在数据库中,并在他们登录时将其加载到会话中【讨论】:
嗨,戴夫,这是一个非常恰当的答案。谢谢 这是整个应用程序的一个身份验证令牌。我最初的方法是继续使用令牌,然后捕获异常以请求新令牌。有了这个,我将监视每个 REST 调用的异常。这是一个好习惯吗?另外,如果我正在观看每个 REST 调用,那么生存时间属性有何不同?我还要在应用程序启动时请求令牌(第一个)吗? 抱歉延迟响应 - 如果您在获取新令牌后捕获异常并重播请求,您将不需要时间。无论如何,您应该关注每个异常调用,基本上没有开销处理明智,并且您希望确保没有其他问题(并且当您进行网络调用时,在某个阶段一切都会出错)。当我提出第一个请求时,我会请求令牌 - 没有必要提前关闭它。【参考方案5】:我建议你使用以下场景:
1) 首先调用auth(username, password)
rest api 获取auth token。
如果给定的凭据没问题,那么只需使用 HTTP 200 响应代码将身份验证 cookie 发送回客户端。
2) 然后,您可以调用受保护的 rest api。您每次都需要随请求一起发送 auth cookie。
3) Servlet 过滤器(或类似的东西)检查每个传入的请求并验证令牌。如果令牌有效,则请求将转发到 rest 方法,否则您需要生成 http 401/403 响应。
我建议您不要编写自己的身份验证层。而不是安装和使用现有的。我建议你OpenAM。它是一个极好的开源访问管理系统。
我还建议您不要在服务器端打开会话以进行身份验证。如果您有 10 个客户端,则需要由服务器管理 10 个会话。这不是一个大问题。但是,如果您有 100 或 1000 或数百万个不同的客户端,那么您需要更多的内存来在服务器上存储会话。
【讨论】:
【参考方案6】:您可以创建一个管理器并在登录期间将 auth-cookie 存储在线程本地,如下面的代码。只要线程存在,您就可以从getAuth()
获取cookie。
public class Manager
private static final ThreadLocal<String> 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();
【讨论】:
【参考方案7】:我假设您使用 OAuth 进行授权。无论您使用的是 JWT 还是其他令牌,都与这种情况无关。
在执行授权时,您将收到一个过期的access_token
,并且根据您请求的授权类型(客户端凭据、授权代码、隐式、资源所有者),一个refresh_token
。
客户端应保留access_token
和过期时间。如果发布了 refresh_token,则必须保密(注意为您的用例使用正确的授权)。
在随后的调用中,您的客户端不应在每次调用时请求新令牌,而应使用存储的 access_token
。
一旦 API 开始返回 401 Unauthorized
,access_token
可能已经过期。如果有的话,您的客户应该尝试使用refresh_token
刷新access_token
。
如果您没有refresh_token
或者刷新请求也失败了,因为refresh_token
不再有效,您可以执行新的授权流程。
您可以使用过期时间作为线索,以了解何时通过刷新或通过新的完整授权流程获取新的access_token
。这将避免401 Unauthorized
。在任何情况下,当您的客户在使用有效的access_token
进行某些调用后收到此响应时,应该有一个后备策略。
【讨论】:
我没有刷新令牌,在这种情况下我应该注意这个异常并再次设置身份验证令牌吗? 当您再次开始获取401 Unauthorized
时,使用凭据执行新的授权。如果你想稍微优化一点,也可以使用过期时间来提前一个新的令牌,甚至在它过期之前。
谢谢,所以我将在应用程序开始时用令牌和 TTL 实例化一个“会话”对象,并将其传递给任何进行 REST 调用的对象。当我开始收到 401 时,我在这个对象上设置了新令牌。对吗?
是的,会成功的。【参考方案8】:
你应该使用 JsonWebToken(简称 JWT)来处理这类东西。 JWT 内置支持设置到期日期。有很多库可以使用这种方法,您可以阅读更多here
目前有 4 个 java 实现,它们都可以检查令牌是否仍然有效(exp 检查)
【讨论】:
【参考方案9】:因此,如果我理解正确,您对所有请求都使用相同的令牌(这意味着只要您的应用程序启动并运行并且您刷新令牌,您应该没问题。我确实遇到了同样的问题这就是我解决它的方法。我有一个单例类,它在应用程序启动时初始化一次,并在其失效时刷新令牌。我使用 C#、Asp.NET MVC5 和 AutoFac 进行 DI,但我'确定你可以用 Java 和 Spring 做同样的事情。
Updating property of a singleton with Thread Safety
【讨论】:
【参考方案10】:-
每个请求的 Auth Token 是正确的方法,考虑 auth 服务器扩展以解决性能问题。
在首次成功验证(用户名和密码)时,生成私有公钥对。将私钥存储为 Session Security Token (SST) 并将公钥作为 Public Security Client Key (PSCK) 发送给客户端
在除登录(或身份验证)之外的所有请求中,客户端将发送 PSCK 以防止用户名和密码被盗,并且服务器可以定期在内部验证 PSCK 是否过期,从而节省处理时间。
如果系统在身份验证方面存在性能问题,请设置具有可扩展性的单独身份验证服务器。
没有要缓存、交换未加密和发送到安全区域之外的令牌或密码。不要使用 URL 参数发帖。
【讨论】:
以上是关于管理身份验证令牌的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章
通过安全令牌进行 API 身份验证的最佳实践(Node.js 服务器和移动应用程序)