一般来说,在拦截器中验证访问令牌的最佳方法是什么?如果访问令牌信息存储在每个用户的HashMap中,则散列映射会随着用户数量的增加而增长。 如果我们为每个api请求查询数据库,它会增加数据库的负载。 请提及您是否知道其他任何技术。 以及在为每个请求验证访问令牌时我还需要考虑的其他事项。在验证访问令牌时有哪些预处理和后处理步骤。
先谢谢。
答案 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。我目前在某些应用程序中使用它,我可以放心地说它工作正常并且易于使用。
继续阅读有关身份验证过程的更多详细信息。
简而言之,基于令牌的身份验证遵循以下步骤:
当服务器收到用户的硬凭证(用户名和密码)并使用客户端必须在每个请求中发送的令牌进行交换时,身份验证就会启动:
@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令牌(JWT)由RFC 7519定义,我认为它非常适合您的需求。
这是在双方之间安全地表示索赔的标准方法(在这种情况下,客户端和服务器)。 JWT是一个自包含令牌,使您能够在有效负载中存储用户标识符,到期日期和任何您想要的内容(但不存储密码),这是JSON编码为Base64。
客户端可以读取有效负载,并且可以通过验证服务器上的签名来轻松检查令牌的完整性。
要找到一些与JWT合作的优秀资源,请查看http://jwt.io。
请记住:通过网络发送敏感数据时,您最好的朋友就是HTTPS。它可以保护您的应用程序免受man-in-the-middle attack。
的攻击如果您不需要跟踪JWT令牌,则无需持久保存JWT令牌。
尽管如此,通过持久存在令牌,您将有可能使其无效并撤销其访问权限。要保持跟踪JWT令牌,而不是保留整个令牌,您可以保留令牌标识符(jti
声明)和一些元数据(您发出令牌的用户,到期日期等),如果您需要。
有many databases您可以持久保存令牌。根据您的要求,您可以探索不同的解决方案,例如relational databases,key-value stores或document stores。
您的应用程序可以提供一些撤消令牌的功能,但在用户更改密码时始终考虑撤消令牌。持久化令牌时,请始终考虑删除旧令牌,以防止数据库无限期增长。
答案 2 :(得分:1)
您可以将HashMap和数据库结合使用。在您的情况下,HashMap可以用作缓存。用户首次通过身份验证时,此用户的访问令牌信息应存储到HashMap。接下来的API,我们将检查HashMap中是否存在访问令牌。如果存在,我们使用它进行身份验证,如果不存在,我们将从数据库中查询并将其存储到HashMap。 您需要使HashMap中的访问令牌失效一段时间内未使用。