假設(shè)我們已經(jīng)有了登錄注冊接口跨晴,接下來我們在 REST API 中使用 JWT 來驗證用戶身份呆细。我們將分幾個步驟來實現(xiàn)
技術(shù)棧
Spring Boot 3.4.1
Spring Security
jjwt 0.12.6
步驟 1:添加 JWT 依賴
首先,我們需要在 build.gradle 中添加相關(guān)的 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:創(chuàng)建 JWT 工具類
然后哀峻,創(chuàng)建一個工具類來生成和解析 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
在登錄接口中哲泊,當(dāng)用戶登錄成功后剩蟀,生成 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 來實現(xiàn)這一點先朦。
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)) {
// 創(chuàng)建一個認(rèn)證對象
Authentication authentication = new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(request, response); // 繼續(xù)請求處理
}
}
步驟 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>
刺彩,來驗證身份迷郑。