在Spring Security中使用JWT通常需要进行以下步骤:
-
创建JWT工具类:首先,你需要创建一个用于生成和解析JWT的工具类。这个工具类通常会使用现有的JWT库(如jjwt)来实现JWT的生成和解析功能。
-
配置Spring Security:在Spring Security的配置中,你需要添加一个JWT过滤器来验证请求中的JWT,并根据其中的信息进行用户认证和授权。通常情况下,这个JWT过滤器会在UsernamePasswordAuthenticationFilter之前执行。
-
定义登录接口:客户端在登录时需要提供用户名和密码,服务器验证通过后会生成一个JWT,并返回给客户端。
-
处理登录请求:后端需要编写处理登录请求的控制器方法,验证用户的身份信息,并生成JWT返回给客户端。
-
验证JWT:客户端在每次请求时需要将JWT放在请求的Header中发送给服务器,服务器会验证JWT的签名和有效性,并根据其中的信息进行用户认证和授权。
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JwtTokenProvider {
@Value("${jwt.secret}")
private String jwtSecret;
@Value("${jwt.expiration}")
private int jwtExpirationMs;
public String generateToken(String username) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtExpirationMs);
return Jwts.builder()
.setSubject(username)
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public String getUsernameFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
return true;
} catch (SignatureException ex) {
System.out.println("Invalid JWT signature");
} catch (MalformedJwtException ex) {
System.out.println("Invalid JWT token");
} catch (ExpiredJwtException ex) {
System.out.println("Expired JWT token");
} catch (UnsupportedJwtException ex) {
System.out.println("Unsupported JWT token");
} catch (IllegalArgumentException ex) {
System.out.println("JWT claims string is empty.");
}
return false;
}
}
接着,我们需要编写一个JWT过滤器 JwtTokenFilter
,用于在Spring Security过滤器链中验证请求中的JWT。
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class JwtTokenFilter extends OncePerRequestFilter {
private JwtTokenProvider jwtTokenProvider;
public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && jwtTokenProvider.validateToken(jwt)) {
String username = jwtTokenProvider.getUsernameFromToken(jwt);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, null, null);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
最后,我们需要一个处理登录请求的控制器方法,并在认证成功后生成JWT返回给客户端。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenProvider jwtTokenProvider;
@Autowired
private CustomUserDetailsService userDetailsService;
@PostMapping("/api/auth/login")
public ResponseEntity<?> authenticateUser(@RequestBody LoginForm loginRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtTokenProvider.generateToken(loginRequest.getUsername());
return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
}
}
在这个示例中,AuthController
是一个处理登录请求的控制器类,JwtTokenProvider
是用于生成和验证JWT的工具类,JwtTokenFilter
是一个JWT过滤器,用于在Spring Security过滤器链中验证请求中的JWT。
最后,LoginForm
是一个简单的表单类,用于接收登录请求中的用户名和密码,JwtAuthenticationResponse
是一个简单的类,用于返回包含JWT的响应。
通过以上配置,我们完成了Spring Security中配合JWT使用的完整示例。