Java Web Spring Boot 中 JWT 令牌相关内容
这里写目录标题
1. JWT 简介
JWT(JSON Web Token) 是一种开放标准(RFC 7519),用于在网络应用间安全地传输信息。JWT 通常用于身份验证和信息交换。JWT 由三部分组成:头部(Header)、载荷(Payload)和签名(Signature),这三部分通过.
分隔。
- Header:包含令牌的类型(JWT)和所使用的签名算法(如 HMAC SHA256 或 RSA)。
- Payload:包含声明(claims),声明是关于实体(通常是用户)和其他数据的声明。
- Signature:用于验证消息在传输过程中没有被更改,并且,如果使用私钥签名的话,还可以验证 JWT 的发送方是否是它所声称的发送方。
2. JWT 的优点
- 无状态:JWT 自身包含了所有必要的信息,服务器不需要存储会话状态。
- 跨域支持:JWT 可以在不同的域之间传递,适用于分布式系统。
- 安全性:通过签名和加密,确保信息不被篡改。
3. JWT 的使用场景
- 身份验证:用户登录后,服务器生成 JWT 返回给客户端,客户端在后续请求中携带 JWT 进行身份验证。
- 信息交换:确保信息在传输过程中不被篡改。
4. Spring Boot 中集成 JWT
4.1 添加依赖
在 pom.xml
文件中添加 JWT 相关的依赖:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
4.2 配置 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;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
@Component
public class JwtTokenUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
public String generateToken(String username) {
Map<String, Object> claims = new HashMap<>();
return createToken(claims, username);
}
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
public Boolean validateToken(String token, String username) {
final String extractedUsername = extractUsername(token);
return (extractedUsername.equals(username) && !isTokenExpired(token));
}
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
public Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}
private Boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
}
在 application.properties
文件中配置 JWT 的密钥和过期时间:
jwt.secret=your_secret_key
jwt.expiration=86400
这段代码是一个用于生成、验证和解析 JWT(JSON Web Token)的工具类。它使用了 io.jsonwebtoken
库来处理 JWT 的创建和解析。下面是对代码中各个部分的详细解释:
类和字段
@Component
public class JwtTokenUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
@Component
:将这个类标记为一个 Spring 组件,使其可以被 Spring 容器管理。@Value("${jwt.secret}")
:从配置文件中读取jwt.secret
属性的值,并将其注入到secret
字段中。@Value("${jwt.expiration}")
:从配置文件中读取jwt.expiration
属性的值,并将其注入到expiration
字段中。
生成 JWT
public String generateToken(String username) {
Map<String, Object> claims = new HashMap<>();
return createToken(claims, username);
}
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
generateToken(String username)
:生成一个 JWT,传入用户名作为参数。createToken(Map<String, Object> claims, String subject)
:创建 JWT 的实际方法。Jwts.builder()
:创建一个 JWT 构建器。setClaims(claims)
:设置自定义声明(claims),这里是一个空 map。setSubject(subject)
:设置 JWT 的主题(通常是用户名)。setIssuedAt(new Date(System.currentTimeMillis()))
:设置 JWT 的签发时间。setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
:设置 JWT 的过期时间。signWith(SignatureAlgorithm.HS256, secret)
:使用 HS256 算法和密钥对 JWT 进行签名。compact()
:生成最终的 JWT 字符串。
验证 JWT
public Boolean validateToken(String token, String username) {
final String extractedUsername = extractUsername(token);
return (extractedUsername.equals(username) && !isTokenExpired(token));
}
validateToken(String token, String username)
:验证 JWT 是否有效。extractUsername(token)
:从 JWT 中提取用户名。isTokenExpired(token)
:检查 JWT 是否过期。- 返回
true
如果用户名匹配且 JWT 未过期,否则返回false
。
提取信息
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
public Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}
private Boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
extractUsername(String token)
:从 JWT 中提取用户名。extractExpiration(String token)
:从 JWT 中提取过期时间。extractClaim(String token, Function<Claims, T> claimsResolver)
:提取 JWT 中的声明。extractAllClaims(String token)
:解析 JWT 并提取所有声明。isTokenExpired(String token)
:检查 JWT 是否过期。
总结
这个 JwtTokenUtil
类提供了以下功能:
- 生成 JWT:根据用户名生成一个 JWT。
- 验证 JWT:验证 JWT 是否有效,包括检查用户名和过期时间。
- 提取信息:从 JWT 中提取用户名和过期时间。
通过这些方法,可以方便地在 Spring Boot 应用中使用 JWT 进行身份验证和信息交换。
4.3 创建过滤器
创建一个过滤器,用于拦截请求并验证 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 JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtTokenUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwt, userDetails.getUsername())) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
4.4 配置 Spring Security
在 Spring Security 配置类中添加 JWT 过滤器:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@EnableWebSecurity
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService myUserDetailsService;
@Autowired
private JwtRequestFilter jwtRequestFilter;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("/authenticate").permitAll()
.anyRequest().authenticated()
.and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
4.5 创建认证控制器
创建一个控制器,用于生成 JWT:
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.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService;
@PostMapping("/authenticate")
public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthRequest authRequest) throws Exception {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
);
} catch (BadCredentialsException e) {
throw new Exception("Incorrect username or password", e);
}
final UserDetails userDetails = userDetailsService
.loadUserByUsername(authRequest.getUsername());
final String jwt = jwtTokenUtil.generateToken(userDetails.getUsername());
return ResponseEntity.ok(new AuthResponse(jwt));
}
}
4.6 创建请求和响应类
创建请求和响应类,用于接收认证请求和返回 JWT:
public class AuthRequest {
private String username;
private String password;
// Getters and Setters
}
public class AuthResponse {
private final String jwt;
public AuthResponse(String jwt) {
this.jwt = jwt;
}
public String getJwt() {
return jwt;
}
}
5. 总结
通过以上步骤,我们完成了在 Spring Boot 中集成 JWT 的基本流程。主要包括以下几个方面:
- 添加 JWT 依赖。
- 配置 JWT 生成和验证工具类。
- 创建过滤器拦截请求并验证 JWT。
- 配置 Spring Security 以支持 JWT。
- 创建认证控制器生成 JWT。
通过这些步骤,我们可以实现基于 JWT 的身份验证和信息交换,确保系统的安全性和无状态性。