【问题标题】:Best way to authenticate access token for every Rest API request为每个 Rest API 请求验证访问令牌的最佳方式
【发布时间】:2016-09-30 16:33:33
【问题描述】:

一般来说,在拦截器中验证访问令牌的最佳方法是什么,如果每个用户的访问令牌信息都存储在 HashMap 中,则哈希图会随着用户数量的增加而增长。 如果我们为每个 api 请求查询数据库,它会增加数据库的负载。 请提及您是否知道其他任何技术。 在为每个请求验证访问令牌时,我还需要考虑哪些其他事项。验证访问令牌时的预处理和后处理步骤是什么。

提前致谢。

【问题讨论】:

  • 你在用spring吗?
  • @slambeth 是的,我正在使用 spring 和 cxf jax-rs,如果你知道的话,请提供一个关于 cxf 拦截器的适当示例..

标签: java rest authentication


【解决方案1】:

查看 Json Web 令牌。使用它们可以让您的服务器成为无状态的(不会在内存中保存任何会话)。 它的概念是为每个请求传递一个数字签名的令牌,并检查签名的完整性是否正确。 Json Web Encryption 还可以沿令牌加密敏感数据(如用户 id)。

这使得在分布式环境中工作变得非常容易

查看此网站:https://jwt.io/。 Java中有一些实现和大量的代码示例。

【讨论】:

  • 嗨@aviad,你有JWT的工作示例吗?
  • @Mohasin Ali,我添加了一个指向一个很好的站点的链接,其中包含 java 中的示例和实现
【解决方案2】:

关于使用令牌进行身份验证的一些话

使用 JAX-RS 后,请查看我不久前写的 answer

您的令牌可以是任何随机字符串,保留在某个存储中。另一方面,JWT 令牌允许您进行无状态身份验证(无持久性)。如果您需要跟踪 JWT 令牌(例如撤销它们),您必须至少保留它们的标识符(jti 声明)。 HashMap 不应该用于“持久化”令牌(这不是真正的持久性,它不会扩展)。相反,请考虑像 Redis 这样的数据库。

要生成和解析 JWT 令牌,请查看由 Stormpath 创建并由贡献者社区维护的 library。我目前在一些应用程序中使用它,我可以自信地说它运行良好且易于使用。

继续阅读以了解有关身份验证过程的更多详细信息。

使用令牌进行身份验证一目了然

简而言之,基于令牌的身份验证遵循以下步骤:

  1. 客户端将其凭据(用户名和密码)发送到服务器。
  2. 服务器对凭据进行身份验证并生成令牌。
  3. 服务器将先前生成的令牌连同用户标识符和到期日期一起存储在某个存储中。
  4. 服务器将生成的令牌发送给客户端。
  5. 在每个请求中,客户端都会将令牌发送到服务器。
  6. 服务器在每个请求中从传入请求中提取令牌。使用令牌,服务器查找用户详细信息以执行身份验证和授权。
    1. 如果令牌有效,则服务器接受请求。
    2. 如果令牌无效,服务器拒绝请求。
  7. 服务器可以提供一个端点来刷新令牌。

使用JAX-RS实现基于token的认证

当服务器收到用户的硬凭证(用户名和密码)并与客户端必须在每个请求中发送的令牌交换它们时,身份验证开始:

@Path("/authentication")
public class AuthenticationResource {

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Response authenticateUser(Credentials credentials) {
    
        try {

            // Authenticate the user using the credentials provided
            String username = credentials.getUsername();
            String password = credentials.getPassword();
            authenticate(username, password);

            // Issue a token for the user
            String token = issueToken(username);

            // Return the token on the response
            return Response.ok(token).build();

        } catch (Exception e) {
            return Response.status(Response.Status.UNAUTHORIZED).build();
        }      
    }

    private void authenticate(String username, String password) throws Exception {
        // Authenticate against a database, LDAP, file or whatever
        // Throw an Exception if the credentials are invalid
    }

    private String issueToken(String username) {
        // Issue a token (can be a random String persisted to a database or a JWT token)
        // The issued token must be associated to a user
        // Return the issued token
    }
}

将使用过滤器从 HTTP 请求中提取令牌并对其进行验证:

@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        // Get the HTTP Authorization header from the request
        String authorizationHeader = 
            requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);

        // Check if the HTTP Authorization header is present and formatted correctly 
        if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
            throw new NotAuthorizedException("Authorization header must be provided");
        }

        // Extract the token from the HTTP Authorization header
        String token = authorizationHeader.substring("Bearer".length()).trim();

        try {

            // Validate the token
            validateToken(token);

        } catch (Exception e) {
            requestContext.abortWith(
                Response.status(Response.Status.UNAUTHORIZED).build());
        }
    }

    private void validateToken(String token) throws Exception {
        // Check if it was issued by the server and if it's not expired
        // Throw an Exception if the token is invalid
    }
}

更多详情,请查看answer

JSON 网络令牌

JSON Web Token (JWT) 由 RFC 7519 定义,我认为它非常适合您的需求。

这是在两方(在这种情况下为客户端和服务器)之间安全地表示声明的标准方法。 JWT 是一个自包含的令牌,使您能够在有效负载中存储用户标识符、到期日期和任何您想要的(但不存储密码),这是一个编码为JSON Base64.

客户端可以读取负载,并且可以通过在服务器上验证其签名来轻松检查令牌的完整性。

要找到一些与 JWT 合作的重要资源,请查看 http://jwt.io

请记住:通过网络发送敏感数据时,您最好的朋友是 HTTPS。它可以保护您的应用程序免受man-in-the-middle attack 的侵害。

跟踪令牌

如果您不需要跟踪 JWT 令牌,则无需保留它们。

尽管如此,通过持久化令牌,您将有可能使它们失效并撤销它们的访问权限。要跟踪 JWT 令牌,而不是保留整个令牌,您可以根据需要保留令牌标识符(jti 声明)和一些元数据(您为其颁发令牌的用户、到期日期等)。

您可以在many databases 中保留您的令牌。根据您的要求,您可以探索不同的解决方案,例如relational databaseskey-value storesdocument stores

您的应用程序可以提供一些功能来撤销令牌,但始终考虑在用户更改密码时撤销令牌。持久化令牌时,请始终考虑删除旧令牌,以防止数据库无限增长。

【讨论】:

    【解决方案3】:

    您可以结合 HashMap 和数据库。在您的情况下, HashMap 可以用作缓存。第一次认证用户时,该用户的访问令牌信息应该存储到HashMap中。接下来的 API,我们将检查访问令牌是否存在于 HashMap 中。如果存在,我们使用它来进行身份验证,如果不存在,我们将从数据库中查询并将其存储到 HashMap。 您需要使 HashMap 中一段时间​​未使用的访问令牌过期。

    【讨论】:

      猜你喜欢
      • 2018-05-02
      • 2021-09-17
      • 1970-01-01
      • 2018-08-05
      • 1970-01-01
      • 2019-01-03
      • 2013-02-17
      • 2016-03-29
      • 2014-02-21
      相关资源
      最近更新 更多