Skip to content

加密与鉴权

约 2578 字大约 9 分钟

安全HTTP

2025-09-11

在现代Web应用开发中,数据的安全性如同数字世界的护城河与城门守卫,至关重要。作为开发者,我们必须确保用户数据在传输和存储过程中不被窃取或篡改。本文将深入浅出地探讨Java后端与前端交互时涉及的信息传输加密身份鉴权两大核心安全机制。

一、 为何需要加密?—— 不设防的世界很危险

想象一下,如果你在网上购物时,你的账号、密码、支付信息全部以明文(就像这张纸条:密码:123456)的形式在网络上传输,会发生什么?任何一个能够截获网络数据包的人(比如在公共Wi-Fi上)都能轻易地看到这一切。这就是著名的 “中间人攻击”(Man-in-the-Middle Attack)

为了杜绝这种情况,我们需要对传输的信息进行加密,将其变成一段乱码(密文),即使被截获,攻击者也难以解读其原始含义。

二、 加密的两种核心方式:对称 vs 非对称

加密算法主要分为两大类:对称加密非对称加密。理解它们的区别是理解现代网络安全的基础。

1. 对称加密 (Symmetric Encryption)

核心思想: 使用同一把钥匙进行加密和解密。

  • 比喻: 就像用一个特定的钥匙锁上宝箱,对方必须用同一把钥匙才能打开。这把钥匙通常被称为密钥(Key)
  • 工作流程:
    1. 发送方(前端)和接收方(后端)预先安全地商量好一个共同的密钥 S
    2. 前端用密钥 S 加密数据,得到密文,发送给后端。
    3. 后端用同样的密钥 S 解密密文,得到原始数据。
  • 常见算法: AES (Advanced Encryption Standard, 最常用)、DES、3DES。
  • 优点: 算法简单,加解密速度快,效率高,适合加密大量数据。
  • 缺点: 密钥分发困难。如何安全地把密钥 S 告诉对方?如果在互联网上直接传输密钥,密钥本身也可能被截获。

2. 非对称加密 (Asymmetric Encryption)

核心思想: 使用一对钥匙:一个公钥(Public Key)和一个私钥(Private Key)

  • 比喻: 公钥就像一把打开的挂锁(任何人都可以拿到),私钥则是唯一能打开这把锁的钥匙(自己严格保管)。
    • 公钥是公开的,可以发给任何人。
    • 私钥是绝对私密的,只能由自己持有。
  • 工作流程(两种用途):
    • 加密信息: 如果A想发送加密信息给B。
      1. A获取B的公钥。
      2. A用B的公钥加密信息,发送给B。
      3. B收到密文后,用自己的私钥解密。因为只有B的私钥能解密用B的公钥加密的内容,所以信息是安全的。
    • 数字签名(用于身份验证): 如果A想向B证明信息是自己发的且未被篡改。
      1. A用自己的私钥对信息的摘要进行加密,这个加密后的摘要就是数字签名,附在信息后面一起发送给B。
      2. B收到信息后,用A的公钥对签名进行解密,得到摘要A。
      3. B再用同样的算法计算收到信息的摘要B。
      4. 如果摘要A和摘要B一致,则证明信息确实来自A且未被修改。
  • 常见算法: RSA (最常用)、ECC(椭圆曲线加密)。
  • 优点: 安全性更高,解决了密钥分发问题,公钥可以任意公开。
  • 缺点: 计算非常复杂,加解密速度慢,比对称加密慢几个数量级,不适合加密大量数据。

三、 强强联合:HTTPS与SSL/TLS

在实际应用中(比如HTTPS),我们并不单独使用某一种加密方式,而是将它们结合使用,取长补短。这个过程主要由SSL/TLS协议完成。

  1. 握手阶段(非对称加密立功):

    • 浏览器(客户端)访问服务器, say hello。
    • 服务器返回其SSL证书,其中包含服务器的公钥
    • 浏览器验证证书的合法性(是否由可信机构颁发,域名是否匹配等)。
    • 验证通过后,浏览器生成一个随机的对称密钥(称为Pre-Master Secret)。
    • 浏览器用服务器的公钥加密这个随机对称密钥,发送给服务器。
    • 服务器用自己的私钥解密,得到这个对称密钥。
    • 至此,双方安全地共享了同一把对称密钥,且从未在网络上明文传输过。
  2. 通信阶段(对称加密立功):

    • 后续所有的数据传输,都使用这把对称密钥进行加密和解密。
    • 因为对称加密速度快,保证了通信的高效。

结论:HTTPS = HTTP + SSL/TLS,它利用非对称加密的安全特性来交换对称密钥,再利用对称加密的高效来进行实际的数据传输。

四、 身份鉴权 (Authentication):你是谁?

加密保证了通信内容的安全,但还有一个关键问题:如何确认“你是你声称的那个人”? 这就是鉴权要解决的问题。

传统的鉴权方式是Session-Cookie机制:

  1. 用户登录,后端验证用户名密码正确。
  2. 后端在服务器内存或数据库中创建一个Session对象,存储用户信息,并生成一个唯一的SessionId
  3. 后端将SessionId通过Set-Cookie头部返回给前端浏览器。
  4. 浏览器后续每次请求都会自动带上这个Cookie(包含SessionId)。
  5. 后端根据SessionId找到对应的Session,从而知道用户身份。

缺点: 服务器需要存储所有用户的Session信息,在分布式或微服务架构中,需要做Session共享,增加了复杂度和服务器开销。

五、 现代解决方案:JWT (JSON Web Token)

JWT是一种无状态(Stateless) 的鉴权机制,完美解决了分布式系统的鉴权问题。服务器不再需要存储Session信息。

1. JWT是什么?

JWT是一个紧凑的、URL安全的令牌,它由三部分组成,用点.分隔: Header.Payload.Signature

  • Header (头部): 通常由两部分组成,令牌类型(即JWT)和所使用的签名算法(如HMAC SHA256或RSA)。
    {
      "alg": "HS256",
      "typ": "JWT"
    }
  • Payload (负载): 存放实际需要传递的信息,比如用户ID、用户名、过期时间等。这些信息被称为声明(Claims)
    {
      "sub": "1234567890",
      "name": "John Doe",
      "admin": true,
      "exp": 1516239022
    }
  • Signature (签名): 对前两部分的签名,用于防止令牌被篡改。
    • 签名的生成方式:HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

最终,一个JToken看起来是这样: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

2. JWT的工作流程

  1. 登录: 用户用用户名密码登录。
  2. 签发Token: 后端验证成功後,生成一个JWT(包含用户信息如userId),并用一个只有服务器知道的密钥(Secret) 为其签名,然后将JWT返回给前端。
  3. 存储与发送: 前端(通常存放在localStorage或Cookie中)在后续请求的HTTP Header的Authorization字段中携带此Token:Authorization: Bearer <token>
  4. 验证Token: 后端收到请求后:
    • 检查JWT格式是否正确。
    • 用同样的密钥(Secret) 验证签名是否有效。签名验证是核心,如果有人篡改了Payload,签名就会对不上,验证就会失败。
    • 检查Token是否过期(检查exp声明)。
    • 如果全部通过,即可信任Payload中的用户信息,完成鉴权。

3. JWT的优点与注意事项

  • 优点:

    • 无状态: 服务器不需要存储Token,减轻了存储压力。
    • 易于扩展: 非常适合分布式和微服务系统,任何服务只要持有密钥就能验证Token。
    • 多端支持: 天然支持移动端、API调用等场景。
  • 注意事项:

    • Token一旦签发,在过期前始终有效。无法在服务端直接废止某个Token,这是JWT的一个设计特点。解决方案有:使用黑名单、设置较短的过期时间、使用Refresh Token机制等。
    • Payload默认只是Base64编码,并非加密绝对不要在JWT的Payload中存放敏感信息(如密码)。如果需要加密,可以选择JWE(JSON Web Encryption)规范。
    • 密钥(Secret) 必须严格保管在服务端。

六、 Java后端实战:生成与验证JWT

在Java中,我们可以使用流行的库如jjwt来方便地操作JWT。

Maven依赖:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>

示例代码:

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
import java.util.Date;

public class JwtUtil {

    // 生成一个安全密钥(HS256算法要求密钥长度至少256位)
    private static final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
    // Token有效期
    private static final long EXPIRATION_TIME = 86400000; // 24小时

    // 生成JWT
    public static String generateToken(String userId) {
        return Jwts.builder()
                .setSubject(userId) // 设置主题(用户ID)
                .setIssuedAt(new Date()) // 签发时间
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) // 过期时间
                .signWith(key) // 使用密钥签名
                .compact();
    }

    // 解析验证JWT
    public static String validateToken(String token) {
        try {
            Claims claims = Jwts.parserBuilder()
                    .setSigningKey(key) // 设置签名密钥
                    .build()
                    .parseClaimsJws(token)
                    .getBody();

            return claims.getSubject(); // 返回用户ID
        } catch (ExpiredJwtException e) {
            throw new RuntimeException("Token已过期", e);
        } catch (JwtException e) {
            throw new RuntimeException("Token无效", e);
        }
    }
}

总结

主题核心技术解决的核心问题应用场景
传输加密HTTPS (TLS)
- 非对称加密交换密钥
- 对称加密加密数据
防止通信内容被窃听和篡改所有Web通信的基石
身份鉴权JWT (JSON Web Token)
- 数字签名保证Token可信
无状态地确认用户身份,解决分布式鉴权难题API接口、移动端、单点登录(SSO)

将加密与鉴权结合,就构建了一套完整的安全体系:

  1. 全程HTTPS:保证数据传输过程中的安全。
  2. JWT鉴权:保证API请求来源的合法性和身份真实性。