example add jwt

This commit is contained in:
many2many 2024-11-19 14:36:09 +08:00
parent f99948603b
commit a1c65ac65f
5 changed files with 199 additions and 2 deletions

View File

@ -25,6 +25,11 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
runtimeOnly 'com.h2database:h2:2.1.214'
implementation 'io.jsonwebtoken:jjwt-api:0.12.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.5'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

View File

@ -26,9 +26,9 @@ public class CorsConfig {
CorsConfiguration config = new CorsConfiguration();
// 配置允许的源
config.setAllowedOrigins(List.of("http://localhost"));
config.setAllowedOrigins(List.of("http://localhost","http://localhost:8880"));
// 配置允许的方法
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
// 配置允许的头部
config.setAllowedHeaders(List.of("*"));
// 允许凭据

View File

@ -0,0 +1,36 @@
package com.lk.paopao.jwt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 配置类用于在Spring Boot应用中注册JWT过滤器
*/
@Configuration
public class JwtConfig {
/**
* 自动注入JwtFilter用于处理JWT相关的请求
*/
@Autowired
private JwtFilter jwtFilter;
/**
* 创建并配置JwtFilter的FilterRegistrationBean
*
* @return FilterRegistrationBean<JwtFilter> 配置了JwtFilter的FilterRegistrationBean实例
*/
@Bean
public FilterRegistrationBean<JwtFilter> loggingFilter() {
// 创建FilterRegistrationBean对象用于注册JwtFilter
FilterRegistrationBean<JwtFilter> registrationBean = new FilterRegistrationBean<>();
// 设置过滤器实例
registrationBean.setFilter(jwtFilter);
// 添加需要过滤的URL模式
registrationBean.addUrlPatterns("/v1/user/*","/v1/post/*");
// 返回配置好的FilterRegistrationBean
return registrationBean;
}
}

View File

@ -0,0 +1,69 @@
package com.lk.paopao.jwt;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
/**
* JWT过滤器用于处理每次请求验证JWT令牌并设置用户认证信息
*/
@Component
public class JwtFilter extends OncePerRequestFilter {
@Autowired
private JwtService jwtService;
/**
* 核心过滤方法处理每次请求
*
* @param request 请求对象用于获取请求头中的JWT令牌
* @param response 响应对象用于处理响应
* @param filterChain 过滤链用于继续处理请求链
* @throws ServletException 如果过滤过程中发生Servlet异常
* @throws IOException 如果过滤过程中发生IO异常
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
// 解析JWT令牌
String jwt = parseJwt(request);
if (jwt != null && jwtService.validateToken(jwt)) {
// 从JWT令牌中获取用户名
String username = jwtService.getUserNameFromJwtToken(jwt);
// 将用户名设置为请求属性以便后续处理使用
request.setAttribute("username", username);
}
} catch (Exception e) {
// 记录无法设置用户认证的错误
logger.error("Cannot set user authentication:", e);
}
// 继续处理请求链
filterChain.doFilter(request, response);
}
/**
* 解析请求中的JWT令牌
*
* @param request 请求对象用于获取请求头中的JWT令牌
* @return 如果解析成功则返回JWT令牌字符串否则返回null
*/
private String parseJwt(HttpServletRequest request) {
// 从请求头中获取"Authorization"字段
String headerAuth = request.getHeader("Authorization");
// 如果存在且以"Bearer "开头则提取并返回令牌字符串
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
return headerAuth.substring(7);
}
// 如果不符合条件则返回null
return null;
}
}

View File

@ -0,0 +1,87 @@
package com.lk.paopao.jwt;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Jwts;
import javax.crypto.SecretKey;
import java.util.Date;
/**
* JWT服务类用于生成和验证JWT Token
*/
@Component
public class JwtService {
// JWT密钥用于签名和验证
private static final String SECRET = "uYuVw0mke38MfLhO19wUQyRgwrmYo89ibpQTXPHi4vg=";
// JWT过期时间单位毫秒
private static final long JWT_EXPIRATION_MS = 1000 * 3600 * 24;
/**
* 生成JWT Token
*
* @param username 用户名作为JWT的主题
* @return 生成的JWT Token字符串
*/
public String generateJwtToken(String username) {
// 创建JWT设置主题签发时间过期时间和签名密钥
return Jwts.builder().subject(username)
.issuedAt(new Date())
.expiration(new Date(new Date().getTime() + JWT_EXPIRATION_MS))
.signWith(getSigningKey())
.compact();
}
/**
* 获取签名密钥
*
* @return 签名所用的SecretKey
*/
private SecretKey getSigningKey() {
// 解码JWT密钥
byte[] keyBytes = Decoders.BASE64.decode(SECRET);
return Keys.hmacShaKeyFor(keyBytes);
}
/**
* 从JWT Token中提取用户名
*
* @param token JWT Token字符串
* @return 提取的用户名
*/
public String getUserNameFromJwtToken(String token) {
// 解析JWT Token并提取主题用户名
return Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token)
.getPayload()
.getSubject();
}
/**
* 验证JWT Token的有效性
*
* @param authToken JWT Token字符串
* @return 如果Token有效返回true否则返回false
*/
public boolean validateToken(String authToken) {
try {
// 尝试解析JWT Token如无异常则表示Token有效
Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(authToken);
return true;
} catch (Exception e) {
// 记录JWT签名异常
System.out.println("Invalid JWT signature: " + e.getMessage());
}
return false;
}
}