使用 Spring Security 集成 JWT(JSON Web Token)身份验证是一种常见的方式来实现基于令牌的身份验证。在 Spring Boot 应用程序中使用 Spring Security 和 JWT,可以创建一个安全、可扩展的身份验证系统。下面是一个示例,展示如何在 Spring Boot 项目中集成 JWT 身份验证:
1. 添加依赖:
首先,在你的 pom.xml
中添加与 JWT 和 Spring Security 相关的依赖:
<dependencies>
<!-- Spring Boot Starter Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- JJWT (Java JSON Web Token) -->
<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>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
</dependencies>
2. 配置 JWT 密钥:
在 application.properties
或 application.yml
文件中配置用于签署和验证 JWT 的密钥:
properties code
# JWT 配置
jwt.secret=your-secret-key
jwt.expiration-time=86400000 # 令牌有效时间,单位为毫秒
jwt.header=Authorization # JWT 令牌的请求头
jwt.prefix=Bearer # 令牌前缀
3. JWT 工具类:
创建一个工具类来生成和解析 JWT:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JwtTokenUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration-time}")
private long expirationTime;
// 生成 JWT 令牌
public String generateToken(String username) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + expirationTime);
return Jwts.builder()
.setSubject(username)
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
// 解析 JWT 令牌
public String getUsernameFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}
// 验证 JWT 令牌是否有效
public boolean validateToken(String token, String username) {
String tokenUsername = getUsernameFromToken(token);
Date expiration = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody()
.getExpiration();
return (username.equals(tokenUsername) && expiration.after(new Date()));
}
}
4. JWT 过滤器:
创建一个过滤器,用于在请求中拦截 JWT 令牌,并根据令牌进行身份验证:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
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 {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String header = request.getHeader("${jwt.header}");
if (header != null && header.startsWith("${jwt.prefix} ")) {
String token = header.replace("${jwt.prefix} ", "");
String username = jwtTokenUtil.getUsernameFromToken(token);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(token, username)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
chain.doFilter(request, response);
}
}
5. 配置 Spring Security:
在 SecurityConfig
中配置 Spring Security 来使用 JWT 过滤器:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Autowired
private UserDetailsService userDetailsService;
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/auth/**").permitAll() // 允许未认证访问登录和注册等路径
.anyRequest().authenticated() // 其他请求需要身份验证
.and()
.exceptionHandling()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// 添加 JWT 过滤器
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
6. 登录和令牌颁发:
在控制器中实现登录和令牌颁发:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody AuthRequest authRequest) {
try {
// 进行身份验证
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
);
// 加载用户详细信息
UserDetails userDetails = userDetailsService.loadUserByUsername(authRequest.getUsername());
// 生成 JWT 令牌
String token = jwtTokenUtil.generateToken(userDetails.getUsername());
// 返回令牌
return ResponseEntity.ok(new AuthResponse(token));
} catch (BadCredentialsException e) {
return ResponseEntity.status(401).body("Invalid credentials");
}
}
}
数据模型:
-
AuthRequest
:表示登录请求,包括用户名和密码。 -
AuthResponse
:表示登录响应,包括生成的 JWT 令牌。
// 登录请求数据模型
public class AuthRequest {
private String username;
private String password;
// Getters and setters...
}
// 登录响应数据模型
public class AuthResponse {
private String token;
public AuthResponse(String token) {
this.token = token;
}
// Getters and setters...
}
通过以上配置和代码,你可以在 Spring Boot 项目中集成 JWT 身份验证。你可以根据自己的需求进一步定制和优化。