如何使用 Java 创建身份验证令牌

问题描述 投票:0回答:7

在我的 Java EE6 REST 服务上,我想使用身份验证令牌从移动设备登录。用户将发送他们的用户名和密码,服务器将发回一个令牌,该令牌将用于授权用户在给定时间内执行进一步的请求。

我可以像这样简单地自己创建一个代币吗? (我想我不需要加密它,因为我将使用 HTTPS。)

String token = UUID.randomUUID().toString().toUpperCase() 
            + "|" + "userid" + "|"
            + cal.getTimeInMillis();

或者有更标准的方法来创建这些代币?也许它存在于某个 API 中?

java security rest authentication token
7个回答
53
投票

对于 Java 8 及更高版本,最快、最简单的解决方案是:

private static final SecureRandom secureRandom = new SecureRandom(); //threadsafe
private static final Base64.Encoder base64Encoder = Base64.getUrlEncoder(); //threadsafe

public static String generateNewToken() {
    byte[] randomBytes = new byte[24];
    secureRandom.nextBytes(randomBytes);
    return base64Encoder.encodeToString(randomBytes);
}

输出示例:

wrYl_zl_8dLXaZul7GcfpqmDqr7jEnli
7or_zct_ETxJnOa4ddaEzftNXbuvNSB-
CkZss7TdsTVHRHfqBMq_HqQUxBGCTgWj
8loHzi27gJTO1xTqTd9SkJGYP8rYlNQn

上面的代码将生成 32 个字符的 Base64 编码的随机字符串。在 Base64 编码中,每个字符对数据的 6 位进行编码。因此,对于上面示例中的 24 个字节,您将获得 32 个字符。您可以通过更改随机字节数来更改输出字符串的长度。此解决方案比

UUID
(仅使用 16 个随机字节)更安全,并且生成可以安全地在 HTTP url 中使用的字符串。


18
投票

要在 Java 中创建难以猜测的标记,请使用

java.security.SecureRandom

例如

SecureRandom random = new SecureRandom();
byte bytes[] = new byte[20];
random.nextBytes(bytes);
String token = bytes.toString();

与其将用户名包含在令牌中,不如将用户:令牌映射缓存在内存或数据库中。  


9
投票

您提出的方案有效地允许客户无限制地访问您的服务。初始登录后,UID 和“userid”将可供客户端使用,可以简单地与始终有效的时间戳相结合。

如果您需要具有“登录”和会话令牌的服务,那么为什么不直接使用 HttpSession 呢?


2
投票

创建一个独特的令牌,它完全基于逻辑和您使用了多少参数。

Supplier
Java 8的功能接口可以帮助您:

Supplier<String> tokenSupplier = () -> {

        StringBuilder token = new StringBuilder();
        long currentTimeInMilisecond = Instant.now().toEpochMilli();
        return token.append(currentTimeInMilisecond).append("-")
                .append(UUID.randomUUID().toString()).toString();
};

System.out.println(tokenSupplier.get());

输出:

1591457374665-d5eff25f-b083-41c3-a90d-a89adcc45043

1
投票

有一种创建令牌的方法,该令牌不会被泄露,但也可以用于身份验证。

创建一个组合的代币:

base64(用户名 + 过期时间 + 客户端的其他值 + 3des 编码(用户名、过期时间、源 ip、浏览器标识符、客户端的其他值))

客户端可以使用令牌来验证请求,例如使用 JSON Web Token (RFC 7515)。

在服务器端,用于 3des 编码的密钥可以随着时间轮换,作为令牌。每个请求都包含用于身份验证的令牌,每个响应都包含相同的令牌或过期前的新令牌。

在这种情况下,令牌包含用户名,因此请求身份验证只需检查 3des 编码部分是否有效(与 相同,请求 ip 的来源相同。在这种情况下,如果有人窃取了令牌,则令牌的可用性是作为会话 ID,您可以将其他标识符组成为令牌,例如浏览器等。更难伪造请求,因为攻击者必须伪造更多东西 - 这对他来说是未知的,因为他不知道编码部分是什么。 (事实上没有完美的安全性,只能让破解变得更困难)

该解决方案的优点是:

  • 每个部分都是标准的,但不是整体,攻击者必须知道实现细节才能进行攻击。
  • 客户端可以使用令牌的一部分来显示令牌中的信息,同时令牌本身是安全的,因为每个未加密的部分都包含在加密的部分中 - 因此在服务器端令牌失效的情况下无法修改 - 因此很容易检测到攻击。
  • 集群不需要会话复制/粘性会话。 3des 密钥足以在节点之间复制 - 因此它适合无状态后端策略。

缺点是

  • 在服务器端实现起来比较困难,因为对于此解决方案必须在服务器端实现令牌生成/验证算法,建议使用服务器过滤器。

  • 客户端必须实现令牌存储 - 建议使用浏览器会话存储代替 cookie - 更容易窃取 cookie。

  • 必须确保 3des 密钥足够安全 - 建议使用 Java 安全性以避免妥协。

1
投票
public class SecureTokenGenerator {
public static final String CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

// 2048 bit keys should be secure until 2030 - https://web.archive.org/web/20170417095741/https://www.emc.com/emc-plus/rsa-labs/historical/twirl-and-rsa-key-size.htm
public static final int SECURE_TOKEN_LENGTH = 256;

private static final SecureRandom random = new SecureRandom();

private static final char[] symbols = CHARACTERS.toCharArray();

private static final char[] buf = new char[SECURE_TOKEN_LENGTH];

/**
 * Generate the next secure random token in the series.
 */
public static String nextToken() {
    for (int idx = 0; idx < buf.length; ++idx)
        buf[idx] = symbols[random.nextInt(symbols.length)];
    return new String(buf);
}

}

取自并显着压缩 https://stackoverflow.com/a/41156/584947


-3
投票

REST 基于 HTTP,鼓励使用底层协议而不是重新发明轮子。 HTTP 使用 cookie 来支持有状态交互,例如记住身份验证,并且还支持用户名和密码身份验证。

此外,Java EE 开箱即用地支持所有这些。查看教程

http://docs.oracle.com/javaee/6/tutorial/doc/bncas.html

© www.soinside.com 2019 - 2024. All rights reserved.