为每个Rest API请求验证访问令牌的最佳方法

时间:2016-09-30 16:33:34

标签: java rest authentication

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

先谢谢。

3 个答案:

答案 0 :(得分:3)

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

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

查看此网站:https://jwt.io/。 java中有一些实现,还有很多代码示例。

答案 1 :(得分:2)

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

使用JAX-RS后,请查看我刚才写的answer

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

要生成并解析JWT令牌,请查看由Stormpath创建并由贡献者社区维护的library。我目前在某些应用程序中使用它,我可以放心地说它工作正常并且易于使用。

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

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

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

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

    当服务器收到用户的硬凭证(用户名和密码)并使用客户端必须在每个请求中发送的令牌进行交换时,身份验证就会启动:

    @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 Web令牌

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

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

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

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

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

    的攻击

    跟踪令牌

    如果您不需要跟踪JWT令牌,则无需持久保存JWT令牌。

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

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

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

答案 2 :(得分:1)

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