Spring Boot 3.x + Spring Security 搭建前后端分离JWT登录认证服务
·
Spring Boot 3.x + Spring Security 搭建前后端分离JWT登录认证服务
1. 引言
JSON Web Token(JWT)作为一种轻量级的认证机制,是目前非常流行的一种认证方式,它具有无状态、可扩展、跨语言等优点,非常适合前后端分离的应用架构。
本文将详细介绍如何使用Spring Boot 3.x和Spring Security搭建一个基于JWT的安全认证服务,包括项目初始化、核心功能实现、配置等内容。
2. 技术栈
- Spring Boot 3.5.x
- Spring Security 6.5.x
- Nimbus JOSE JWT 10.x
3. 项目初始化
3.1 创建Spring Boot项目
使用Spring Boot Initializr创建一个新的Spring Boot项目,取名如xxx-security-authentication-server,添加以下依赖:
- Spring Security
- Spring Boot Starter Web
- Lombok
3.2 添加依赖
在pom.xml文件中添加JSON,Nimbus JOSE JWT等依赖:
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Nimbus JOSE + JWT -->
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>${nimbus-jose-jwt.version}</version>
</dependency>
4. 核心功能实现
4.1 创建会话用户类
创建会话用户类User.java:
@Data
public class User implements UserDetails {
private String userId;
private String username;
private String password;
private Collection<GrantedAuthority> authorities = new HashSet<>();
private boolean enabled;
private boolean accountNonExpired;
private boolean accountNonLocked;
private boolean credentialsNonExpired;
}
创建认证响应类AuthResponse.java:
@Data
public class AuthResponse {
private String accessToken;
private String refreshToken;
private String tokenType;
private long expiresIn;
}
4.2 JWT工具类
创建JWT生成工具类JwtTokenGenerator.java:
public class JwtTokenGenerator {
private final JWSSigner signer;
private final JwtProperties jwtProperties;
public JwtTokenGenerator(JwtProperties jwtProperties, PrivateKey privateKey) {
this.jwtProperties = jwtProperties;
this.signer = createSigner(privateKey);
}
private JWSSigner createSigner(PrivateKey privateKey) {
return new RSASSASigner(privateKey);
}
public String generateRefreshToken(String subject) {
return generateToken(subject, Map.of("type", "refresh"),jwtProperties.getRefreshExpirationTimeInMinutes() * 60);
}
/**
* 生成jwt token
* @param subject 主题
* @param claims 声明
* @return jwt token
*/
public String generateToken(String subject, Map<String, Object> claims) {
return generateToken(subject, claims,jwtProperties.getExpirationTimeInMinutes() * 60);
}
/**
* 生成jwt token
* @param subject 主题
* @param claims 声明
* @param expireTimeInSeconds 超时时间
* @return jwt token
*/
public String generateToken(String subject, Map<String, Object> claims, long expireTimeInSeconds) {
try {
Instant now = Instant.now();
Instant expiration = now.plus(expireTimeInSeconds, ChronoUnit.SECONDS);
JWTClaimsSet.Builder claimsSetBuilder = new JWTClaimsSet.Builder()
.subject(subject)
.issuer(jwtProperties.getIssuer())
.issueTime(Date.from(now))
.expirationTime(Date.from(expiration));
claims.forEach(claimsSetBuilder::claim);
JWTClaimsSet claimsSet = claimsSetBuilder.build();
SignedJWT signedJWT = new SignedJWT(
new JWSHeader.Builder(JWSAlgorithm.RS256).build(),
claimsSet
);
signedJWT.sign(signer);
return signedJWT.serialize();
} catch (JOSEException e) {
throw new JwtGenerationException("Failed to generate JWT token", e);
}
}
}
创建JWT验证工具类JwtTokenValidator.java:
public class JwtTokenValidator {
private final JWSVerifier verifier;
private final String issuer;
public JwtTokenValidator(JwtProperties jwtProperties, PublicKey publicKey) {
this.issuer = jwtProperties.getIssuer();
this.verifier = createVerifier(publicKey);
}
private JWSVerifier createVerifier(PublicKey publicKey) {
return new RSASSAVerifier((RSAPublicKey) publicKey);
}
/**
* 验证jwt token
* @param token token
* @return true 通过 false 失败
*/
public boolean validateToken(String token) {
try {
SignedJWT signedJWT = SignedJWT.parse(token);
// Verify the signature
boolean signatureValid = signedJWT.verify(verifier);
if (!signatureValid) {
return false;
}
// Check if token is expired
if (signedJWT.getJWTClaimsSet().getExpirationTime().before(new Date())) {
return false;
}
// Check issuer
if (!issuer.equals(signedJWT.getJWTClaimsSet().getIssuer())) {
return false;
}
return true;
} catch (ParseException | JOSEException e) {
throw new JwtValidationException("Failed to validate JWT token", e);
}
}
public String getSubject(String token) {
try {
SignedJWT signedJWT = SignedJWT.parse(token);
return signedJWT.getJWTClaimsSet().getSubject();
} catch (ParseException e) {
throw new JwtValidationException("Failed to parse JWT token", e);
}
}
public <T> T getClaim(String token, String claimName, Class<T> claimType) {
try {
SignedJWT signedJWT = SignedJWT.parse(token);
return claimType.cast(signedJWT.getJWTClaimsSet().getClaim(claimName));
} catch (ParseException e) {
throw new JwtValidationException("Failed to parse JWT token", e);
}
}
public JWTClaimsSet getClaims(String token) throws ParseException {
SignedJWT signedJWT = SignedJWT.parse(token);
return signedJWT.getJWTClaimsSet();
}
}
4.3 认证服务类
创建认证服务AuthenticationService接口:
public interface AuthenticationService {
/**
* 根据用户名创建 Jwt Token
* @param username 用户名
* @param type token 类型
* @return JwtToken
*/
String generateToken(String username, String type);
/**
* 根据用户创建 JwtToken 响应
* @param user 用户
* @return JwtToken 响应
*/
AuthResponse generateAuthResponse(User user);
/**
* 根据refreshToken生成JwtToken 响应
* @param refreshToken 刷新 Token
* @return JwtToken 响应
*/
AuthResponse refreshToken(String refreshToken);
}
认证服务实现类AuthenticationServiceImpl:
public class AuthenticationServiceImpl implements AuthenticationService {
private final JwtTokenGenerator tokenGenerator;
private final JwtTokenValidator tokenValidator;
private final TokenBlacklist tokenBlacklist;
private final JwtProperties jwtProperties;
private final UserDetailsService userDetailsService;
@Override
public String generateToken(String username, String type) {
return tokenGenerator.generateToken(username, Map.of("type", type));
}
@Override
public AuthResponse generateAuthResponse(User user) {
String accessToken = tokenGenerator.generateToken(user.getUsername(), getClaims(user));
String refreshToken = tokenGenerator.generateRefreshToken(user.getUsername());
return new AuthResponse(accessToken, refreshToken, jwtProperties.getExpirationTimeInMinutes() * 60);
}
@Override
public AuthResponse refreshToken(String refreshToken) {
if (tokenBlacklist.isTokenBlacklisted(refreshToken)) {
throw new RuntimeException("Refresh token has been revoked");
}
if(!tokenValidator.validateToken(refreshToken)) {
throw new BadCredentialsException("Token Invalid");
}
String tokenType = tokenValidator.getClaim(refreshToken, "type", String.class);
if (!"refresh".equals(tokenType)) {
throw new RuntimeException("Invalid refresh token");
}
String username = tokenValidator.getSubject(refreshToken);
User user = (User) userDetailsService.loadUserByUsername(username);
String newAccessToken = tokenGenerator.generateToken(user.getUsername(), getClaims(user));
String newRefreshToken = tokenGenerator.generateRefreshToken(user.getUsername());
// Add old refresh token to blacklist
tokenBlacklist.addToken(refreshToken);
return new AuthResponse(newAccessToken, newRefreshToken, jwtProperties.getExpirationTimeInMinutes() * 60);
}
private Map<String, Object> getClaims(User user) {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", user.getUserId());
claims.put("username", user.getUsername());
List<String> roles = new ArrayList<>();
for(GrantedAuthority authority : user.getAuthorities()) {
roles.add(authority.getAuthority());
}
claims.put("authorities", roles);
return claims;
}
}
4.5 登录认证成功处理,返回JWT Token响应
创建LoginAuthenticationSuccessHandler.java类:
@RequiredArgsConstructor
public class LoginAuthenticationSuccessHandler extends AbstractAuthenticationSuccessHandler {
protected final AuthenticationService authenticationService;
private final JwtTokenValidator jwtTokenValidator;
private final ApplicationEventPublisher applicationEventPublisher;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
User user = (User) authentication.getPrincipal();
String userId = user.getUserId();
String username = user.getUsername();
LoginSuccessEvent loginSuccessEvent = new LoginSuccessEvent(this, username
, userId, null);
applicationEventPublisher.publishEvent(loginSuccessEvent);
AuthResponse authResponse = authenticationService.generateAuthResponse(user);
bindThirdPartyUser(request, response, username, userId);
writeResponse(response, authResponse);
}
@SneakyThrows
public void writeResponse(HttpServletResponse response, Object result) {
response.setContentType("application/json");
response.getWriter().write(JSONUtil.toJsonStr(result));
}
}
完整代码请参考项目仓库:mwc-security-authentication-server
演示地址:mwcs
更多推荐




所有评论(0)