Java JWT:原理、机制及案例示范

一、什么是JWT?

1.1 JWT的基本概念

JWT(JSON Web Token)是一种用于在各方之间传递JSON格式信息的紧凑、URL安全的令牌(Token)。JWT的主要作用是验证用户身份或权限。它由三部分组成:

  1. Header(头部):标识令牌的类型和加密算法。
  2. Payload(载荷):包含了实际的身份信息及其他数据。
  3. Signature(签名):使用头部和载荷生成的签名,用于验证数据完整性和来源的可靠性。

1.2 JWT的结构

JWT的结构由三部分组成,它们通过点号(.)进行分隔,格式如下:

Header.Payload.Signature

具体每一部分的内容如下:

  • Header(头部): 通常包含两部分信息:令牌的类型(JWT)和签名的算法(如HMAC SHA256或RSA)。

    {
      "alg": "HS256",
      "typ": "JWT"
    }
    
  • Payload(载荷): 载荷是JWT的主体部分,包含了需要传输的数据。通常包含以下几种常见的声明(Claims):

    • iss:签发者

    • exp:过期时间

    • sub:主题

    • aud:接收者

    • iat:签发时间

    • nbf:在此之前不可用

      {
      "sub": "1234567890",
      "name": "John Doe",
      "admin": true
      }

  • Signature(签名): 签名部分是用来验证消息的完整性,并确保其未被篡改。签名的生成方式如下:

    HMACSHA256(
      base64UrlEncode(header) + "." + base64UrlEncode(payload),
      secret)
    

二、JWT的原理和机制

2.1 JWT的工作机制

JWT是无状态的令牌,每次客户端(如浏览器)与服务器进行交互时,客户端会在请求头中携带这个令牌,服务器根据JWT中的签名验证令牌的合法性,并通过载荷(Payload)中的信息进行身份确认。

JWT认证的基本流程

  1. 用户登录:用户提交用户名和密码。
  2. 服务器验证用户:服务器验证用户名和密码是否正确。
  3. 生成JWT:验证成功后,服务器根据用户信息生成一个JWT并返回给客户端。
  4. 客户端保存JWT:客户端通常会将JWT存储在本地存储(LocalStorage)或Cookies中。
  5. 请求携带JWT:每次客户端向服务器发送请求时,会在请求头中附加JWT。
  6. 服务器验证JWT:服务器通过解析JWT,确认用户身份及权限,进而决定是否响应请求。

2.2 为什么使用JWT?

在一个复杂的分布式系统中,例如电商交易系统,多个微服务之间需要频繁通信。传统的基于会话的认证方式存在以下缺点:

  • 状态依赖:服务器需要存储每个用户的会话信息,随着用户数量的增加,服务器的存储负担增加。
  • 扩展性差:在分布式环境中,多个服务器需要共享会话状态,这增加了系统复杂性。

相比之下,JWT是一种无状态的认证机制,不需要服务器存储会话信息。JWT本身携带了用户的认证信息,具有如下优点:

  • 扩展性强:无状态特性使得JWT在分布式架构下具有良好的扩展性。
  • 跨平台、跨语言支持:JWT是JSON格式的令牌,几乎可以被所有语言和平台解析使用。
  • 灵活性:JWT的Payload部分可以自定义数据,从而支持复杂的权限系统。

2.3 JwtUtils.extractClaims实现

JwtUtils.extractClaims是一个工具方法,用于解析JWT并提取其中的声明(claims),如用户ID、角色信息和过期时间。下面是一个典型的实现,它展示了如何验证JWT的有效性、检查签名,以及处理异常。

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.UnsupportedJwtException;

import java.security.Key;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class JwtUtils {
    // 用于签名验证的密钥(示例使用HMAC)
    private static final String SECRET_KEY = "your-256-bit-secret";

    // 解码并生成用于验证的密钥对象
    private static Key getSigningKey() {
        byte[] keyBytes = Base64.getDecoder().decode(SECRET_KEY);
        return new SecretKeySpec(keyBytes, "HmacSHA256");
    }

    // 解析JWT并提取Claims
    public static Claims extractClaims(String token) {
        try {
            return Jwts.parserBuilder()
                    .setSigningKey(getSigningKey()) // 设置密钥用于签名验证
                    .build()
                    .parseClaimsJws(token) // 解析并验证JWT
                    .getBody(); // 返回Payload部分的Claims
        } catch (ExpiredJwtException e) {
            System.err.println("JWT已过期: " + e.getMessage());
            throw e;
        } catch (UnsupportedJwtException | MalformedJwtException e) {
            System.err.println("无效的JWT: " + e.getMessage());
            throw new IllegalArgumentException("Invalid JWT");
        } catch (SignatureException e) {
            System.err.println("JWT签名不匹配: " + e.getMessage());
            throw new SecurityException("Invalid signature");
        } catch (IllegalArgumentException e) {
            System.err.println("JWT为空或格式不正确: " + e.getMessage());
            throw e;
        }
    }
}

解析说明:

  1. 密钥管理getSigningKey()方法用于将Base64编码的字符串转换为签名密钥。
  2. 异常处理 :解析JWT时可能出现多种异常,例如JWT过期签名无效格式错误等。通过捕获这些异常,我们可以进行相应的错误处理。
  3. 验证Token的有效性parseClaimsJws()会自动验证签名和Token格式,确保JWT未被篡改。

2.4 JwtUtils.extractClaims 所需的 Maven 依赖

为了实现 JwtUtils.extractClaims 方法,需要使用一个专门处理 JWT 的库。在之前的实现中,我们使用了 JJWT (Java JWT) 库。以下是 JJWT 所需的 Maven 依赖配置。

<!-- pom.xml -->
<dependencies>
    <!-- JJWT: JSON Web Token for Java and Android -->
    <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> <!-- or jjwt-gson if you prefer -->
        <version>0.11.5</version>
        <scope>runtime</scope>
    </dependency>

    <!-- 其他依赖项 -->
    <!-- ... -->
</dependencies>

依赖说明:

  1. jjwt-api:提供 JWT 的核心接口和类。
  2. jjwt-impl :包含 jjwt 的实现部分,必须与 jjwt-api 一起使用。
  3. jjwt-jackson :用于 JSON 处理,支持使用 Jackson 库进行序列化和反序列化。如果您更喜欢使用 Gson,可以替换为 jjwt-gson

注意事项:

  • 确保所有 jjwt 依赖的版本一致,以避免兼容性问题。
  • 如果您使用的是 Spring Boot,可以考虑使用 Spring Security 提供的 JWT 支持,以简化集成过程。

三、JWT在电商交易系统中的应用

3.1 电商系统中的角色与权限

在电商交易系统中,涉及多个角色,例如:

  • 普通用户:可以浏览商品、下订单、查看订单状态。
  • 商家用户:可以管理商品、处理订单。
  • 管理员:可以管理平台用户、审核商家、处理投诉等。

系统需要根据不同用户的角色赋予相应的权限,而JWT可以通过其载荷中的角色信息实现权限管理。

3.2 电商系统的JWT认证流程

我们以一个电商交易系统为例,说明JWT在用户身份验证、订单管理中的应用。系统架构主要分为以下几个模块:

  1. 用户服务:负责用户的注册、登录、生成JWT令牌。
  2. 商品服务:提供商品的查询、展示等功能。
  3. 订单服务:处理订单的生成、状态管理。
  4. 支付服务:处理用户支付。
3.2.1 用户登录与JWT生成

用户登录时,系统会验证用户的用户名和密码,验证通过后生成JWT令牌并返回给客户端。这个令牌包含了用户的身份信息和角色权限。

登录流程的时序图:

3.2.2 请求保护资源

用户登录后,可以浏览商品或下订单。在每次请求时,客户端会在请求头中携带JWT令牌,服务器通过验证JWT来确认用户身份。

请求订单详情时的时序图:

3.3 电商系统中的JWT权限控制

在电商系统中,不同角色的用户具备不同的权限,JWT的Payload中可以存储用户的角色信息(如role),系统在处理请求时,根据JWT中的角色信息进行权限验证。

3.3.1 权限检查

例如:

  • 普通用户只能查看自己的订单;
  • 商家用户可以查看商家店铺的订单;
  • 管理员可以查看所有订单。

订单服务中的权限检查流程:

@RestController
@RequestMapping("/orders")
public class OrderController {

    @GetMapping("/{orderId}")
    public ResponseEntity<?> getOrder(@RequestHeader("Authorization") String token, @PathVariable Long orderId) {
        Claims claims = JwtUtils.extractClaims(token.replace("Bearer ", ""));
        String role = claims.get("role", String.class);
        Long userId = claims.get("userId", Long.class);
        
        Order order = orderService.findOrderById(orderId);
        
        if (order == null) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Order not found");
        }

        // 权限检查
        if ("USER".equals(role) && !order.getUserId().equals(userId)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body("You don't have permission to access this order");
        }

        return ResponseEntity.ok(order);
    }
}

在这个示例中,OrderController首先从JWT中提取用户角色和用户ID,然后根据不同角色的权限规则决定是否返回订单详情。

四、前端与后端的JWT交互示例

在电商系统中,前端通常通过JavaScript与后端进行交互。以下我们提供一个简单的HTML和JavaScript示例,展示如何通过JWT进行用户登录、访问受保护的资源。

4.1 登录功能

当用户提交用户名和密码时,前端会将这些数据发送给后端,后端验证成功后返回JWT,前端将其存储在localStorage中,用于后续的API请求。

HTML和JavaScript代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JWT Login</title>
</head>
<body>
    <h1>JWT Login Example</h1>

    <form id="loginForm">
        <label for="username">Username:</label>
        <input type="text" id="username" required><br>
        <label for="password">Password:</label>
        <input type="password" id="password" required><br>
        <button type="submit">Login</button>
    </form>

    <div id="orderDetails" style="display:none;">
        <h2>Order Details</h2>
        <pre id="orderContent"></pre>
    </div>

    <script>
        document.getElementById('loginForm').addEventListener('submit', function (event) {
            event.preventDefault();

            const username = document.getElementById('username').value;
            const password = document.getElementById('password').value;

            // 发送登录请求
            fetch('http://localhost:8080/api/login', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ username, password })
            })
            .then(response => response.json())
            .then(data => {
                // 存储JWT到localStorage
                localStorage.setItem('jwt', data.token);
                alert('Login successful');
            })
            .catch(error => console.error('Error:', error));
        });

        // 请求受保护的资源
        function fetchOrderDetails(orderId) {
            const token = localStorage.getItem('jwt');
            if (!token) {
                alert('Please login first');
                return;
            }

            fetch(`http://localhost:8080/api/orders/${orderId}`, {
                method: 'GET',
                headers: {
                    'Authorization': `Bearer ${token}`
                }
            })
            .then(response => response.json())
            .then(order => {
                document.getElementById('orderContent').textContent = JSON.stringify(order, null, 2);
                document.getElementById('orderDetails').style.display = 'block';
            })
            .catch(error => console.error('Error:', error));
        }

        // 示例调用
        fetchOrderDetails(12345);
    </script>
</body>
</html>

4.2 前端代码说明

  • 用户登录时,前端通过fetch API向后端发送POST请求,后端验证成功后返回JWT,前端将其存储在localStorage中。
  • 在请求订单详情时,前端会将JWT添加到请求头中发送给后端,后端验证JWT的合法性后返回订单数据。

五、常见问题与解决方案

5.1 JWT过期问题的细化解决方案

问题 :JWT一旦达到exp设定的过期时间,就会被视为无效,用户需要重新登录。这在用户体验上可能造成困扰,特别是当会话中断时。

解决方案一:刷新Token机制(Refresh Token)

Refresh Token是一种辅助机制,用于延长会话时长。具体步骤如下:

  1. 初次登录:服务器返回一个短时效的JWT和一个长期有效的Refresh Token。

  2. Token过期检查:在客户端请求时,如果JWT已过期,则使用Refresh Token获取新的JWT。

  3. 刷新JWT:

    • 客户端向服务器发送Refresh Token。
    • 服务器验证Refresh Token的合法性,若有效则生成新的JWT并返回。
    • 将新的JWT存储到客户端,并继续操作。

示例代码:刷新Token的实现

@PostMapping("/api/refresh-token")
public ResponseEntity<?> refreshToken(@RequestBody String refreshToken) {
    try {
        Claims claims = JwtUtils.extractClaims(refreshToken);
        if (claims != null) {
            // 验证通过,生成新的JWT
            String newJwt = JwtUtils.generateToken(claims.getSubject(), 15); // 新的JWT有效期15分钟
            return ResponseEntity.ok(Map.of("token", newJwt));
        }
    } catch (ExpiredJwtException e) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Refresh Token expired");
    }
    return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Invalid Refresh Token");
}

注意:Refresh Token应该比JWT更长效,但同样需要妥善保护,避免泄露。


解决方案二:短时效与长会话相结合

思路:通过设置短时效JWT并动态刷新,确保在用户活动期间会话不会中断。

流程

  1. JWT过期时间设置为10~15分钟
  2. 每次用户操作时,通过客户端检测JWT的剩余有效期:
    • 如果剩余有效期不足5分钟,则向服务器请求新的Token。
  3. 用户不活动时,不自动刷新JWT。这样可以限制Token滥用的风险。

示例:检测并自动刷新Token的JavaScript代码

function isTokenExpiringSoon(token) {
    const payload = JSON.parse(atob(token.split('.')[1]));
    const expTime = payload.exp * 1000; // 转换为毫秒
    const currentTime = Date.now();
    return (expTime - currentTime) < 5 * 60 * 1000; // 剩余不足5分钟
}

function refreshTokenIfNeeded() {
    const token = localStorage.getItem('jwt');
    if (token && isTokenExpiringSoon(token)) {
        fetch('http://localhost:8080/api/refresh-token', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(token)
        })
        .then(response => response.json())
        .then(data => localStorage.setItem('jwt', data.token))
        .catch(err => console.error('Token刷新失败:', err));
    }
}

// 页面加载或用户操作时调用
document.addEventListener('click', refreshTokenIfNeeded);

5.2 JWT的大小问题

问题: JWT包含较多信息(如用户ID、角色、权限等),且使用Base64编码后体积较大,增加了网络带宽消耗。

解决方案:

  1. 精简 Payload:减少JWT中存储的数据,只存储必要的用户身份信息。
  2. 压缩 Token:可以通过GZIP等方式对JWT进行压缩后再进行传输。
5.2.1 精简 Payload

策略:

  • 仅存储必要信息:确保JWT的Payload中只包含验证用户身份所需的最少信息。例如,用户ID、用户名和角色可能是必要的,而不需要包含用户的详细资料。
  • 使用简短的声明名称 :采用简短的键名来减少总体大小。例如,使用uid代替userIdr代替role

示例:

{
  "uid": "1234567890",
  "n": "John Doe",
  "r": "ADMIN"
}

实现示例:

public class JwtUtils {
    // 生成精简的JWT
    public static String generateToken(String userId, String username, String role, long expirationMinutes) {
        Claims claims = Jwts.claims().setSubject(userId);
        claims.put("n", username); // 简短的键名
        claims.put("r", role);     // 简短的键名

        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expirationMinutes * 60 * 1000))
                .signWith(getSigningKey(), SignatureAlgorithm.HS256)
                .compact();
    }
}

优点:

  • 减少 Token 大小:通过减少Payload中的数据量,显著降低JWT的总体大小。
  • 提升性能:较小的Token减少了网络传输的带宽消耗,提升请求的响应速度。

注意事项:

  • 平衡信息量与安全性:确保仅删除不必要的信息,避免影响系统的功能和安全性。
  • 一致性:在前后端以及各个微服务中统一Payload的结构和字段名称,避免解析错误。
5.2.2 压缩 Token

策略:

  • 使用压缩算法:在生成JWT后,使用GZIP等压缩算法对其进行压缩,然后进行Base64编码传输。
  • 服务器端解压:接收端需要在解析JWT前先进行解压缩。

实现示例:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.zip.GZIPOutputStream;
import java.util.zip.GZIPInputStream;

public class JwtUtils {

    // 压缩JWT
    public static String compressToken(String token) throws IOException {
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream(token.length());
        try (GZIPOutputStream gzip = new GZIPOutputStream(byteStream)) {
            gzip.write(token.getBytes());
        }
        return Base64.getEncoder().encodeToString(byteStream.toByteArray());
    }

    // 解压JWT
    public static String decompressToken(String compressedToken) throws IOException {
        byte[] compressedBytes = Base64.getDecoder().decode(compressedToken);
        ByteArrayInputStream byteStream = new ByteArrayInputStream(compressedBytes);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try (GZIPInputStream gzip = new GZIPInputStream(byteStream)) {
            byte[] buffer = new byte[256];
            int n;
            while ((n = gzip.read(buffer)) >= 0) {
                out.write(buffer, 0, n);
            }
        }
        return out.toString();
    }
}

前端与后端的集成:

  • 生成压缩Token:

    String token = JwtUtils.generateToken(userId, username, role, 15);
    String compressedToken = JwtUtils.compressToken(token);
    // 将compressedToken发送给客户端
    
  • 客户端存储与传输:

    客户端接收并存储压缩后的Token。每次发送请求时,发送压缩Token。

  • 服务器端接收与解压:

    @GetMapping("/api/orders/{orderId}")
    public ResponseEntity<?> getOrder(@RequestHeader("Authorization") String compressedToken, @PathVariable Long orderId) {
        try {
            String token = JwtUtils.decompressToken(compressedToken.replace("Bearer ", ""));
            Claims claims = JwtUtils.extractClaims(token);
            // 继续处理请求
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Invalid Token");
        }
        // ...
    }
    

优点:

  • 显著减小Token体积:尤其是在Payload包含较多数据时,压缩效果显著。
  • 透明性:对前后端系统的改动较小,只需在传输环节增加压缩与解压步骤。

缺点:

  • 增加处理开销:压缩与解压缩会增加一定的CPU资源消耗,可能影响性能。
  • 复杂性:需要在前后端系统中增加相应的压缩与解压缩逻辑,增加系统复杂性。

注意事项:

  • 选择合适的压缩算法:GZIP是常用的选择,但也可以根据具体需求选择其他压缩算法。
  • 确保一致性:前后端必须严格按照相同的压缩与解压缩流程进行操作,避免Token解析失败。
5.2.3 其他优化策略

除了精简Payload和压缩Token外,还可以考虑以下优化策略:

  1. 使用短期Token与会话机制
    • 概述:结合使用短期有效的JWT和基于会话的状态管理。
    • 实现:JWT用于身份验证,短期内有效,结合服务器端的会话存储,用于管理用户的会话状态。
  2. 分片存储
    • 概述:将部分数据存储在客户端(如LocalStorage),将敏感数据存储在服务器端。
    • 实现:JWT只包含必要的身份信息,其他详细数据通过API查询获取。
  3. 自定义编码
    • 概述:使用自定义的编码方式替代标准的Base64,以减少字符数。
    • 实现:例如使用Base62编码替代Base64,减少Token长度。

选择适合的优化策略需要根据具体的系统需求和约束条件进行权衡。


5.3 JWT的安全性问题

问题: 虽然JWT签名可以保证数据不被篡改,但JWT的Payload部分是明文的,攻击者可以解码其中的敏感信息。

解决方案:

  1. 加密JWT:使用JWE(JSON Web Encryption)标准对JWT进行加密,确保敏感信息的安全性。
  2. 使用 HTTPS:始终通过HTTPS协议传输JWT,避免在网络传输中被拦截。
5.3.1 加密JWT

概述:

  • JWE (JSON Web Encryption) 是一种标准,用于对JWT的内容进行加密,确保数据的机密性。
  • 与JWS(JSON Web Signature)不同,JWE不仅可以验证数据的完整性和来源,还能保护数据不被未经授权的第三方读取。

实现示例:

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.util.Base64;
import java.util.Date;

public class JwtUtils {
    // 生成加密密钥(示例使用AES)
    private static final String SECRET_KEY = "your-256-bit-secret-your-256-bit-secret"; // 32字节密钥
    private static final SecretKey KEY = Keys.hmacShaKeyFor(SECRET_KEY.getBytes());

    // 生成加密JWT
    public static String generateEncryptedToken(String userId, String username, String role, long expirationMinutes) {
        return Jwts.builder()
                .setSubject(userId)
                .claim("n", username)
                .claim("r", role)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expirationMinutes * 60 * 1000))
                .signWith(KEY, SignatureAlgorithm.HS256)
                .compressWith(CompressionCodecs.GZIP) // 可选:压缩Payload
                .encryptWith(Keys.secretKeyFor(SignatureAlgorithm.HS256)) // 使用对称密钥加密
                .compact();
    }

    // 解密JWT
    public static Claims extractEncryptedClaims(String encryptedToken) {
        try {
            return Jwts.parserBuilder()
                    .setSigningKey(KEY)
                    .build()
                    .parseClaimsJws(encryptedToken)
                    .getBody();
        } catch (JwtException e) {
            // 处理异常
            throw new SecurityException("Invalid encrypted JWT");
        }
    }
}

注意事项:

  • 密钥管理:加密密钥必须妥善保管,避免泄露。建议使用环境变量或专用的密钥管理服务(如 AWS KMS、Azure Key Vault)。
  • 性能开销:加密与解密会带来额外的计算开销,需评估对系统性能的影响。
  • 兼容性:确保前后端系统支持JWE标准,或使用相同的加密机制。
5.3.2 使用 HTTPS

概述:

  • HTTPS (HTTP Secure) 是在HTTP基础上加入SSL/TLS协议,确保数据在传输过程中被加密,防止被窃听或篡改。
  • 在传输JWT时,使用HTTPS可以防止Token被中间人攻击者截获和利用。

实现步骤:

  1. 获取 SSL/TLS 证书
  • 可以通过证书颁发机构(如 Let's Encrypt)获取免费的SSL/TLS证书。
  1. 配置服务器
  • 在后端服务器(如 Nginx、Apache、Tomcat)上配置SSL/TLS,确保所有API请求通过HTTPS进行。

  • 示例(Nginx)

    server {
        listen 443 ssl;
        server_name yourdomain.com;
    
        ssl_certificate /path/to/fullchain.pem;
        ssl_certificate_key /path/to/privkey.pem;
    
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers HIGH:!aNULL:!MD5;
    
        location /api/ {
            proxy_pass http://localhost:8080/api/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
    
  1. 强制 HTTPS
  • 确保所有HTTP请求重定向到HTTPS,避免用户通过不安全的连接访问系统。

  • 示例(Nginx)

    server {
        listen 80;
        server_name yourdomain.com;
        return 301 https://$host$request_uri;
    }
    
  1. 前端配置
  • 确保前端应用通过HTTPS协议与后端进行通信,避免混合内容问题。

优点:

  • 数据加密:确保JWT在传输过程中不被窃听或篡改。
  • 数据完整性:防止数据在传输过程中被修改。

注意事项:

  • 证书管理:定期更新SSL/TLS证书,避免证书过期导致服务中断。
  • 性能优化:使用HTTP/2等协议优化HTTPS连接的性能。
相关推荐
GoodStudyAndDayDayUp8 分钟前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea
信号处理学渣16 分钟前
matlab画图,选择性显示legend标签
开发语言·matlab
红龙创客17 分钟前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
jasmine s26 分钟前
Pandas
开发语言·python
装不满的克莱因瓶39 分钟前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
n北斗1 小时前
常用类晨考day15
java
biomooc1 小时前
R 语言 | 绘图的文字格式(绘制上标、下标、斜体、文字标注等)
开发语言·r语言
骇客野人1 小时前
【JAVA】JAVA接口公共返回体ResponseData封装
java·开发语言
black^sugar1 小时前
纯前端实现更新检测
开发语言·前端·javascript
404NooFound1 小时前
Python轻量级NoSQL数据库TinyDB
开发语言·python·nosql