1、简述
在现代Web开发中,安全性尤为重要。为了确保用户的身份,JSON Web Token(JWT)作为一种轻量级且无状态的身份验证方案,广泛应用于微服务和分布式系统中。本篇博客将讲解如何在Spring Boot 中集成JWT实现身份验证,并列出具体代码示例和常见应用场景。
2、什么是JWT?
JWT是一种开放标准(RFC 7519),定义了一种紧凑且自包含的方式,用于在各方之间安全地传输信息作为JSON对象。它包含三个部分:头部(Header)、载荷(Payload)和签名(Signature)。
- 头部:通常包含令牌类型(JWT)和签名算法(如HMAC SHA256)。
- 载荷:包含声明(claims),如用户ID和到期时间。
- 签名:使用头部中的算法对头部和载荷进行签名,确保令牌未被篡改。
为什么使用JWT?
- 无状态性(Stateless):JWT自包含信息,不需要服务器存储会话数据,适合分布式系统。
- 跨平台:JWT是基于JSON格式的,因此在不同语言和平台之间传递信息非常方便。
- 安全性:JWT使用签名验证信息的完整性,确保数据传输的安全性。
3、集成
3.1 项目设置
首先,创建一个Spring Boot项目,并添加以下依赖:
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
3.2 创建JWT工具类
创建一个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;
}
}
}
3.3 创建身份验证过滤器
创建一个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;
}
}
3.4 配置Spring Security
配置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();
}
}
3.5 实现认证控制器
创建一个认证控制器,用于处理用户登录并生成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
}
4、应用场景
- 用户登录
当用户登录时,服务器验证其凭据并生成一个JWT。客户端将这个JWT存储在本地(例如,LocalStorage或Cookie中),并在后续请求中通过HTTP头部传递。 - API访问控制
在后续的API请求中,服务器通过验证JWT来确保请求者的身份和权限。这可以用于保护敏感数据和功能。 - 分布式系统
在微服务架构中,各个服务之间可以使用JWT进行身份验证和信息传递,无需集中存储和管理会话状态。 - 无状态应用
对于需要无状态会话的应用,JWT是理想的选择,因为它不需要服务器保存会话数据,减轻了服务器的负担。
5、总结
通过本文的介绍,您应该已经了解了如何在Spring Boot应用中集成JWT,以及它的实际应用场景。JWT提供了一种轻量级且安全的身份验证方式,适合现代分布式系统和无状态应用。希望本文能帮助您在项目中实现JWT身份验证,提升应用的安全性和扩展性。
评论区