在现代Web开发中,安全性尤为重要。为了确保用户的身份,JSON Web Token(JWT)作为一种轻量级且无状态的身份验证方案,广泛应用于微服务和分布式系统中。本篇博客将讲解如何在Spring Boot 中集成JWT实现身份验证,并列出具体代码示例和常见应用场景。
JWT是一种开放标准(RFC 7519),定义了一种紧凑且自包含的方式,用于在各方之间安全地传输信息作为JSON对象。它包含三个部分:头部(Header)、载荷(Payload)和签名(Signature)。
为什么使用JWT?
首先,创建一个Spring Boot项目,并添加以下依赖:
org.springframework.boot
spring-boot-starter-security
io.jsonwebtoken
jjwt
0.9.1
org.springframework.boot
spring-boot-starter-web
创建一个JwtUtils类,用于生成和验证JWT:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JwtUtils {
private final String secret = "yourSecretKey";
private final long expiration = 86400000; // 1 day in milliseconds
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public String getUsernameFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
}
创建一个JwtAuthenticationFilter类,用于拦截和验证JWT:
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
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;
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtUtils jwtUtils;
public JwtAuthenticationFilter(JwtUtils jwtUtils) {
this.jwtUtils = jwtUtils;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String jwt = extractJwtFromRequest(request);
if (jwt != null && jwtUtils.validateToken(jwt)) {
String username = jwtUtils.getUsernameFromToken(jwt);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
username, null, null);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
private String extractJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
配置Spring Security以使用JWT进行身份验证:
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.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final JwtUtils jwtUtils;
public SecurityConfig(JwtUtils jwtUtils) {
this.jwtUtils = jwtUtils;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(new JwtAuthenticationFilter(jwtUtils), UsernamePasswordAuthenticationFilter.class);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 配置认证管理器
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
创建一个认证控制器,用于处理用户登录并生成JWT:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtUtils jwtUtils;
@Autowired
private UserDetailsService userDetailsService;
@PostMapping("/login")
public String authenticateUser(@RequestBody AuthRequest authRequest) throws AuthenticationException {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword()));
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
return jwtUtils.generateToken(userDetails.getUsername());
}
}
class AuthRequest {
private String username;
private String password;
// Getters and setters
}
用户登录
当用户登录时,服务器验证其凭据并生成一个JWT。客户端将这个JWT存储在本地(例如,LocalStorage或Cookie中),并在后续请求中通过HTTP头部传递。
API访问控制
在后续的API请求中,服务器通过验证JWT来确保请求者的身份和权限。这可以用于保护敏感数据和功能。
分布式系统
在微服务架构中,各个服务之间可以使用JWT进行身份验证和信息传递,无需集中存储和管理会话状态。
无状态应用
对于需要无状态会话的应用,JWT是理想的选择,因为它不需要服务器保存会话数据,减轻了服务器的负担。
通过本文的介绍,您应该已经了解了如何在Spring Boot应用中集成JWT,以及它的实际应用场景。JWT提供了一种轻量级且安全的身份验证方式,适合现代分布式系统和无状态应用。希望本文能帮助您在项目中实现JWT身份验证,提升应用的安全性和扩展性。