SpringBoot 项目中 JWT 令牌

一、介绍

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间安全地传输声明。它通常由三部分组成:头部(Header)、载荷(Payload)和签名(Signature),形式为 Header.Payload.Signature。下面为你详细介绍它的相关知识。

二、结构

  • 头部(Header):通常由两部分组成,令牌的类型(即 JWT)和使用的签名算法,如 HMAC SHA256 或 RSA。它会被 Base64Url 编码形成 JWT 的第一部分。
  • 载荷(Payload):包含声明(Claims),声明是关于实体(通常是用户)和其他数据的声明。声明有三种类型:注册声明、公开声明和私有声明。同样,它也会被 Base64Url 编码形成 JWT 的第二部分。
  • 签名(Signature):为了创建签名部分,你需要使用编码后的头部、编码后的载荷、一个秘钥(secret)和头部中指定的签名算法。签名用于验证消息在传输过程中没有被更改,并且在使用私钥签名的情况下,还可以验证 JWT 的发送者的身份。

三、工作流程

  1. 用户登录时,服务器验证用户的身份。
  2. 如果验证成功,服务器会创建一个 JWT 并将其返回给客户端。
  3. 客户端在后续的请求中都会携带这个 JWT,通常是在请求头的 Authorization 字段中。
  4. 服务器接收到请求后,会验证 JWT 的签名和有效性,如果验证通过,则处理请求并返回响应。

四、优点

  • 无状态:JWT 是无状态的,服务器不需要存储会话信息,这使得它非常适合于微服务架构和分布式系统。
  • 跨域支持:由于 JWT 可以在请求头中传输,因此它可以很方便地用于跨域请求。
  • 可扩展性:JWT 的载荷部分可以包含任意的声明,因此可以根据需要扩展其功能。

五、缺点

  • 安全性问题:如果 JWT 的秘钥泄露,攻击者可以伪造 JWT。此外,JWT 一旦发布,在过期之前无法撤销。
  • 体积较大:由于 JWT 包含了头部、载荷和签名,因此它的体积相对较大,会增加请求的大小。

六、使用场景

  • 身份验证:在用户登录后,服务器可以生成一个 JWT 并返回给客户端,客户端在后续的请求中携带这个 JWT,服务器通过验证 JWT 来确认用户的身份。
  • 信息交换:JWT 可以在不同的服务之间安全地传输信息,因为签名可以确保信息的完整性和真实性。

七、实例

下面为你提供一个使用 Spring Boot 3 结合 com.auth0:java-jwt 库实现 JWT 认证的 Web 实例。该实例包含用户注册、登录、生成 JWT 以及使用拦截器验证 JWT 等功能。

pom.xml

<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Java-JWT -->
    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>4.4.0</version>
    </dependency>
    <!-- Spring Boot Starter Data JPA -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <!-- H2 Database -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>    

JwtUtils.java

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Date;

public class JwtUtils {
    private static final String SECRET = "yourSecretKey";
    private static final long EXPIRATION_TIME = 86400000; // 24 hours

    public static String generateToken(String username) {
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        return JWT.create()
               .withSubject(username)
               .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
               .sign(algorithm);
    }

    public static String validateToken(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(token);
            return jwt.getSubject();
        } catch (JWTVerificationException e) {
            return null;
        }
    }
}    

WebMvcConfig.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private JwtInterceptor jwtInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor)
               .addPathPatterns("/api/**")
               .excludePathPatterns("/api/users/register", "/api/users/login");
    }
}    

JwtInterceptor.java

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@Component
public class JwtInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String tokenHeader = request.getHeader("Authorization");
        if (tokenHeader != null && tokenHeader.startsWith("Bearer ")) {
            String token = tokenHeader.substring(7);
            String username = JwtUtils.validateToken(token);
            if (username != null) {
                return true;
            }
        }
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        return false;
    }
} 

UserController.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
@RequestMapping("/api/users")
public class UserController {
    @Autowired
    private UserService userService;

    @PostMapping("/register")
    public ResponseEntity<?> register(@RequestBody User user) {
        if (userService.findByUsername(user.getUsername()) != null) {
            return new ResponseEntity<>("Username already exists", HttpStatus.BAD_REQUEST);
        }
        userService.save(user);
        return new ResponseEntity<>("User registered successfully", HttpStatus.CREATED);
    }

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody User user) {
        User existingUser = userService.findByUsername(user.getUsername());
        if (existingUser != null && existingUser.getPassword().equals(user.getPassword())) {
            String token = JwtUtils.generateToken(user.getUsername());
            Map<String, String> response = new HashMap<>();
            response.put("token", token);
            return new ResponseEntity<>(response, HttpStatus.OK);
        }
        return new ResponseEntity<>("Invalid credentials", HttpStatus.UNAUTHORIZED);
    }
}    

当然这之前也要有现实的 User 类,UserService 类,UserServiceImpl

代码说明

  1. pom.xml:添加了 Spring Boot Web、java-jwt、Spring Data JPA 和 H2 数据库依赖。
  2. JwtUtils.java:使用 com.auth0:java-jwt 库生成和验证 JWT。
  3. UserController.java:处理用户注册和登录请求,登录成功后返回 JWT。
  4. JwtInterceptor.java:实现 HandlerInterceptor 接口,在请求处理前验证 JWT。
  5. WebMvcConfig.java:将 JwtInterceptor 注册到 Spring MVC 的拦截器链中,对 /api/** 路径进行拦截,排除注册和登录接口。

运行步骤

  1. 创建一个 Spring Boot 3 项目,并将上述代码添加到相应的文件中。
  2. 启动 Spring Boot 应用程序。
  3. 发送注册请求到 /api/users/register 接口,注册新用户。
  4. 发送登录请求到 /api/users/login 接口,获取 JWT。
  5. 在后续请求的 Authorization 头中添加 Bearer <JWT> 进行身份验证。

希望对你有帮助!

对于Spring Boot应用程序记住密码和使用JWT令牌进行身份验证的场景,你可以按照以下步骤进行操作: 1. 添加依赖:在`pom.xml`文件中添加Spring Security和JWT的依赖项。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> ``` 2. 创建Spring Security配置类:创建一个类来扩展`WebSecurityConfigurerAdapter`,并覆盖`configure`方法。 ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/api/auth/**") .permitAll() .anyRequest() .authenticated(); } @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService()) .passwordEncoder(passwordEncoder()); } @Bean @Override public UserDetailsService userDetailsService() { // 实现自定义的UserDetailsService接口来获取用户信息并进行身份验证 return new CustomUserDetailsService(); } @Bean public PasswordEncoder passwordEncoder() { // 使用BCryptPasswordEncoder来对密码进行加密和验证 return new BCryptPasswordEncoder(); } } ``` 3. 创建用户实体类:创建一个用户实体类,用于保存用户信息。 ```java @Entity @Table(name = "users") public class User implements UserDetails { // 实现UserDetails接口的方法 // 用户名、密码等其他属性 // getter和setter方法 } ``` 4. 创建自定义UserDetailsService:实现Spring Security的`UserDetailsService`接口,用于获取用户信息。 ```java @Service public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username); if (user == null) { throw new UsernameNotFoundException("User not found with username: " + username); } return user; } } ``` 5. 创建JWT工具类:创建一个JWT工具类,用于生成和解析JWT令牌。 ```java @Component public class JwtTokenProvider { private static final String SECRET_KEY = "yourSecretKey"; private static final long EXPIRATION_TIME = 86400000; // 24小时 public String generateToken(Authentication authentication) { User user = (User) authentication.getPrincipal(); Date now = new Date(); Date expiryDate = new Date(now.getTime() + EXPIRATION_TIME); return Jwts.builder() .setSubject(user.getUsername()) .setIssuedAt(new Date()) .setExpiration(expiryDate) .signWith(SignatureAlgorithm.HS512, SECRET_KEY) .compact(); } public boolean validateToken(String token) { try { Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token); return true; } catch (SignatureException ex) { System.out.println("Invalid JWT signature"); } catch (MalformedJwtException ex) { System.out.println("Invalid JWT token"); } catch (ExpiredJwtException ex) { System.out.println("Expired JWT token"); } catch (UnsupportedJwtException ex) { System.out.println("Unsupported JWT token"); } catch (IllegalArgumentException ex) {
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值