首页 > 编程语言 >Java JWT:原理、机制及案例示范

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

时间:2024-10-13 19:50:49浏览次数:10  
标签:Java String 用户 JWT token 示范 Token import

一、什么是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连接的性能。

标签:Java,String,用户,JWT,token,示范,Token,import
From: https://blog.csdn.net/weixin_39996520/article/details/142886631

相关文章

  • 高校宿舍管理系统的设计与实现(Java+ssm+万字文档+系统源码+数据库 +调试)
    摘 要本文是对高校宿舍管理系统的概括总结,主要从开题背景,课题意义,研究内容,开发环境与技术,系统分析,系统设计,系统实现这几个角度来进行本高校宿舍管理系统的阐述。高校宿舍管理系统运用Eclipse工具进行开发,使用Java语言进行编码,后台框架采用SSM框架(Spring框架、SpringMVC框......
  • 基于VUE+ssm框架的在线交友系统(Java+ssm+万字文档+系统源码+数据库 +调试)
    摘要本文介绍了在线交友系统的开发全过程。通过分析企业对于在线交友系统的需求,创建了一个计算机管理在线交友系统的方案。文章介绍了在线交友系统的系统分析部分,包括可行性分析等,系统设计部分主要介绍了系统功能设计和数据库设计。本在线交友系统管理员功能有个人中心,用户......
  • Java日总结 --- JDBC
    JDBCAPI---StatementDDL为对数据库和表的增删改查操作,DML是对数据的增删改操作,DQL是对数据的查询操作Statement对象中的executeUpdate(sql)方法为执行sql语句的操作,同时该方法会返回一个值,为受影响的行数,也就是执行语句的条数当执行的是DML语句时,可以通过返回值是否大于......
  • JAVA毕业设计189—基于Java+Springboot+vue的自动售水机管理系统(源代码+数据库+13000
    毕设所有选题:https://blog.csdn.net/2303_76227485/article/details/131104075基于Java+Springboot+vue的自动售水机管理系统(源代码+数据库+13000字论文+任务书)189一、系统介绍本项目前后端分离(可以改为ssm版本),分为用户、员工、管理员三种角色1、用户:注册、登录、......
  • Java中的Iterator接口,以及HashSet和TreeSet
    在Java编程中,`Iterator`接口是一个非常重要的概念,它为我们提供了一种统一且方便的方式来遍历集合(如`List`、`Set`、`Map`等数据结构中的元素,不过遍历`Map`时稍显特殊,通常是遍历其键值对的集合视图)。##一、Iterator接口的定义与方法`Iterator`接口位于`java.util`包中,它定义......
  • StringUtils Java字符串工具类
    在我们的代码中经常需要对字符串判空,截取字符串、转换大小写、分隔字符串、比较字符串、拼接字符串、使用正则表达式等等。如果只用String类提供的那些方法,我们需要手写大量的额外代码,不然容易出现各种异常。现在有个好消息是:org.apache.commons.lang3包下的StringUtils工......
  • [vue3 JavaScript CSS]实现电商网站商品预览,图片放大镜功能
    da效果预览:当鼠标浮在图片上时,灰色小框跟随鼠标运动。右侧大图显示。灰色框不会跑出图片,鼠标移动,右侧大图相应跟随移动。实现思路在实现前,我们想梳理一下我们要实现什么功能灰色框跟随鼠标移动,注意处理边界情况当鼠标进入时右侧大图出现,鼠标移出时右侧大图消失鼠标向左......
  • Java——数组的定义与使用
    各位看官:如果您觉得这篇文章对您有帮助的话欢迎您分享给更多人哦感谢大家的点赞收藏评论,感谢您的支持!!!一:数组的概念以及定义,初始化1.1:数组概念以及定义数组概念:可以看成是相同类型元素的一个集合。数组定义:三种方法T[]数组名=newT[N];例如:int[]a......
  • java计算机毕业设计基于springboot和vue的耐思招聘网站的设计与实现(开题+程序+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着互联网的快速发展和信息技术的不断进步,招聘行业正经历着从传统模式向数字化、智能化转型的关键时期。传统的招聘方式存在信息不对称、效率低下等......
  • java+vue计算机毕设高信誉大学生就业网【源码+程序+论文+开题】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在当今社会,随着高等教育的普及和就业市场的日益竞争激烈,大学生面临的就业压力愈发沉重。传统的就业渠道和方式已难以满足当前大学生的多样化需求,尤其......