假设我们已经有了登录注册接口,接下来我们在 REST API 中使用 JWT 来验证用户身份。我们将分几个步骤来实现
技术栈
Spring Boot 3.4.1
Spring Security
jjwt 0.12.6
步骤 1:添加 JWT 依赖
首先,我们需要在 build.gradle 中添加相关的 JWT 依赖。使用 jjwt 库来生成和验证 JWT。
dependencies {
implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'
}
步骤 2:创建 JWT 工具类
然后,创建一个工具类来生成和解析 JWT。
package com.example.demo.util;
import io.jsonwebtoken.*;
import java.util.Date;
public class JwtUtil {
private final String secretKey = "4D4A614E645267554B58703273357638756F423F4428472B4B6250653368566C"; // 替换为你的密钥
// 生成 JWT
public String generateToken(String username) {
return Jwts.builder()
.subject(username)
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60))
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
}
// 获取用户名
public String extractUsername(String token) {
return Jwts.parser()
.setSigningKey(secretKey)
.build()
.parseSignedClaims(token)
.getPayload()
.getSubject();
}
// 验证 token 是否有效
public boolean isTokenExpired(String token) {
return Jwts.parser()
.setSigningKey(secretKey)
.build()
.parseSignedClaims(token)
.getPayload()
.getExpiration()
.before(new Date());
}
// 解析 token
public boolean validateToken(String token, String username) {
return (username.equals(extractUsername(token)) && !isTokenExpired(token));
}
}
步骤 3:修改登录接口生成 JWT
在登录接口中,当用户登录成功后,生成 JWT 并返回给客户端。
package com.example.demo.controller;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import com.example.demo.util.JwtUtil;
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;
@RestController
@RequestMapping("/api")
public class AuthController {
private final UserService userService;
private final JwtUtil jwtUtil = new JwtUtil();
public AuthController(UserService userService) {
this.userService = userService;
}
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody User user) {
if (userService.loginUser(user.getUsername(), user.getPassword())) {
String token = jwtUtil.generateToken(user.getUsername());
return ResponseEntity.ok(new JwtResponse(token)); // 返回 token
}
return ResponseEntity.badRequest().body("Login fail");
}
}
class JwtResponse {
private String token;
public JwtResponse(String token) {
this.token = token;
}
getter and setter...
}
步骤 4:添加 JWT 过滤器来验证每个请求
为了保护 REST API,我们需要一个过滤器来验证每个请求中的 JWT 是否有效。在 Spring Security 中,你可以通过自定义一个 OncePerRequestFilter 来实现这一点。
package com.example.demo;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import java.io.IOException;
import java.util.ArrayList;
import com.example.demo.util.JwtUtil;
@WebFilter("/*")
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
private JwtUtil jwtUtil = new JwtUtil();
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 从请求头获取 token
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7); // 移除 "Bearer " 前缀
// 从 token 中提取用户名并验证
String username = jwtUtil.extractUsername(token);
if (username != null && jwtUtil.validateToken(token, username)) {
// 创建一个认证对象
Authentication authentication = new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(request, response); // 继续请求处理
}
}
步骤 5:配置 Spring Security
需要确保 Spring Security 配置正确,以便 JWT 过滤器能够生效并保护你的 API。
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.example.demo.JwtRequestFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final JwtRequestFilter jwtRequestFilter;
public SecurityConfig(JwtRequestFilter jwtRequestFilter) {
this.jwtRequestFilter = jwtRequestFilter;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
// .csrf(csrf -> csrf
// .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
// .ignoringRequestMatchers("/api/login", "/api/register")
// )
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/api/register", "/api/login").permitAll()
.anyRequest().authenticated()
)
// 添加 JWT 过滤器
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); // 添加自定义的 JWT 过滤器
return http.build();
}
测试 API
- 使用
/login
接口登录,获取到一个 JWT。 - 在随后的请求中(比如访问
/test
接口),在请求头中带上Authorization: Bearer <your-jwt-token>
,来验证身份。