关于使用令牌进行身份验证的一些话
使用 JAX-RS 后,请查看我不久前写的 answer。
您的令牌可以是任何随机字符串,保留在某个存储中。另一方面,JWT 令牌允许您进行无状态身份验证(无持久性)。如果您需要跟踪 JWT 令牌(例如撤销它们),您必须至少保留它们的标识符(jti 声明)。 HashMap 不应该用于“持久化”令牌(这不是真正的持久性,它不会扩展)。相反,请考虑像 Redis 这样的数据库。
要生成和解析 JWT 令牌,请查看由 Stormpath 创建并由贡献者社区维护的 library。我目前在一些应用程序中使用它,我可以自信地说它运行良好且易于使用。
继续阅读以了解有关身份验证过程的更多详细信息。
使用令牌进行身份验证一目了然
简而言之,基于令牌的身份验证遵循以下步骤:
- 客户端将其凭据(用户名和密码)发送到服务器。
- 服务器对凭据进行身份验证并生成令牌。
- 服务器将先前生成的令牌连同用户标识符和到期日期一起存储在某个存储中。
- 服务器将生成的令牌发送给客户端。
- 在每个请求中,客户端都会将令牌发送到服务器。
- 服务器在每个请求中从传入请求中提取令牌。使用令牌,服务器查找用户详细信息以执行身份验证和授权。
- 如果令牌有效,则服务器接受请求。
- 如果令牌无效,服务器拒绝请求。
- 服务器可以提供一个端点来刷新令牌。
使用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 databases、key-value stores 或document stores。
您的应用程序可以提供一些功能来撤销令牌,但始终考虑在用户更改密码时撤销令牌。持久化令牌时,请始终考虑删除旧令牌,以防止数据库无限增长。