当前位置:   article > 正文

Springboot 开发 -- 集成 JWT 构建安全的API接口服务_jwt api

jwt api

一、JWT简介

JSON Web Token(JWT)是一种基于JSON的开放标准(RFC 7519),用于在各方之间以JSON对象的形式安全地传输信息。JWT可以被签名,确保信息在传输过程中的完整性和可信度。JWT通常用于身份验证和信息交换,特别适用于分布式系统和跨域认证场景。

官网地址:https://jwt.io/introduction/
中文网站:http://jwt.uihtm.com/

二、为什么使用JWT

相较于传统的Session认证,JWT具有以下优势:

  • 无状态和可扩展性:服务端不需要存储Session信息,易于扩展。
  • 跨域认证:JWT可以在不同的域之间传递,便于单点登录(SSO)的实现。
  • 安全性:JWT可以通过数字签名保证信息传输的安全性。
  • 自包含:JWT包含了所有用户验证所需的信息,减少了数据库查询。

三、JWT的结构

JWT由三部分组成,用.连接:

Header(头部信息):包含令牌的类型和所使用的签名算法。
Payload(有效载荷):包含所谓的Claims(声明),它们是关于实体(通常是用户)和其他数据的声明。
Signature(签名):用于验证消息在传输过程中未被篡改,并且,对于使用私钥签名的令牌,还可以验证发送者的身份。

因此,JWT 格式通常如下所示。

xxxxx.yyyyy.zzzzz

1. 头部信息

通常由两部分组成:令牌的类型,即 JWT,以及正在使用的签名算法,例如 HMAC SHA256 或 RSA。

{
  "alg": "HS256",
  "typ": "JWT"
}
  • 1
  • 2
  • 3
  • 4

然后,这个 JSON 被Base64Url编码以形成 JWT 的第一部分。

2. 有效载荷

包含声明。声明是关于实体(通常是用户)和附加数据的陈述。索赔分为三种类型:注册索赔、公开索赔和私人索赔。
一个示例有效载荷可能是::

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}
  • 1
  • 2
  • 3
  • 4
  • 5

然后对有效负载进行Base64Url编码以形成 JSON Web 令牌的第二部分。

3. 签名

要创建签名部分,必须获取编码的标头、编码的有效负载、秘密、标头中指定的算法,并对其进行签名。

例如,如果想使用 HMAC SHA256 算法,签名将通过以下方式创建:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)
  • 1
  • 2
  • 3
  • 4

签名用于验证消息在此过程中没有被更改,并且在使用私钥签名的令牌的情况下,它还可以验证 JWT 的发送者就是它所说的那个人。

四、 验证 JWT

如果想玩 JWT 并将这些概念付诸实践,可以使用 jwt.io Debugger 来解码、验证和生成 JWT。
在这里插入图片描述

五、JWT 交互流程

  • 用户使用用户名密码来请求服务器
  • 服务器进行验证用户的信息
  • 服务器通过验证发送给用户一个token
  • 客户端存储token,并在每次请求时附送上这个token值
  • 服务端验证token值,并返回数据
    在这里插入图片描述

六、SpringBoot集成JWT

1. 添加依赖

在SpringBoot项目的pom.xml中添加JWT的依赖,例如使用java-jwt库:

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.18.2</version> <!-- 请使用最新版本 -->
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

2. 创建JWT工具类

创建一个工具类来生成和验证JWT:

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JwtTokenUtil {
    private String secret = "secretKey"; // 密钥

    public String generateToken(String username) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + 1000 * 60 * 60); // 1小时后过期

        Map<String, Object> claims = new HashMap<>();
        claims.put("username", username);

        return JWT.create()
                .withIssuer("yourIssuer") // 发行人
                .withIssuedAt(now)
                .withExpiresAt(expiryDate)
                .withClaim("username", username)
                .sign(Algorithm.HMAC256(secret));
    }

    public String getUsername(String token) {
        DecodedJWT jwt = JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
        return jwt.getClaim("username").asString();
    }

    // 验证Token
    public boolean validateToken(String token, String username) {
        try {
            DecodedJWT jwt = JWT.require(Algorithm.HMAC256(secret))
                    .withClaim("username", username)
                    .build()
                    .verify(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

3. 创建认证过滤器

创建一个过滤器来拦截请求,并验证JWT的有效性:

import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtTokenUtil jwtTokenUtil;

    public JwtAuthenticationFilter(JwtTokenUtil jwtTokenUtil) {
        this.jwtTokenUtil = jwtTokenUtil;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        final String authorizationHeader = request.getHeader("Authorization");
        String token = null;
        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            token = authorizationHeader.substring(7);
        }
        String username = null;
        boolean isTokenValid = false;
        if (token != null) {
            username = jwtTokenUtil.getUsername(token);
            isTokenValid = jwtTokenUtil.validateToken(token, username);
        }
        if (!isTokenValid) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }
        filterChain.doFilter(request, response);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

4. 创建用户登录接口

创建一个用户登录接口,用于生成JWT:

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
public class AuthenticationController {

    private final JwtTokenUtil jwtTokenUtil;

    public AuthenticationController(JwtTokenUtil jwtTokenUtil) {
        this.jwtTokenUtil = jwtTokenUtil;
    }

    @PostMapping("/login")
    public Map<String, String> login(@RequestBody LoginRequest loginRequest) {
        String username = loginRequest.getUsername();
        // 这里应该添加验证用户名和密码的逻辑
        // 如果验证成功
        String token = jwtTokenUtil.generateToken(username);
        Map<String, String> responseMap = new HashMap<>();
        responseMap.put("token", token);
        return responseMap;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

五、客户端存储和使用JWT

客户端(通常是浏览器)在收到JWT后,应将其存储在本地,如LocalStorage或SessionStorage中。在随后的请求中,客户端需要在HTTP请求头中添加JWT,以验证用户身份。

六、总结

通过上述步骤,我们成功地在SpringBoot项目中集成了JWT,实现了API接口服务的身份验证。JWT提供了一种安全、高效的方式来处理身份验证和信息交换,特别适合于微服务和分布式系统。然而,需要注意的是,JWT不适用于需要高度安全的场景,如支付系统,因为JWT一旦发出,就无法撤销,除非等到它自然过期。此外,由于JWT通常存储在客户端,因此它们容易受到跨站脚本攻击(XSS)的影响。开发者应采取适当的安全措施来保护JWT,例如使用HttpOnly的Cookie标志来存储令牌,或者使用内容安全策略(CSP)来减轻XSS攻击的风险。

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号