OJ项目中引入JWT
准备工作
添加pom依赖
▼xml复制代码<!-- JWT依赖 --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency>
添加配置接口
▼java复制代码/** * @version v1.0.0 * @belongsProject: fengoj-backend-microservice * @belongsPackage: com.feng.fengojbackenduserservice.config * @author: fgh * @description: JWT配置类 * @createTime: 2025-03-14 23:53 */ public interface JwtConfig { /** * JWT密钥 */ String JWT_SECRET = "fengoj-backend-user-service-jwt-secret-key"; /** * JWT过期时间(1天,单位:毫秒) */ long JWT_EXPIRATION = 1 * 24 * 60 * 60 * 1000L; /** * JWT令牌前缀 */ String JWT_TOKEN_PREFIX = "Bearer "; /** * JWT请求头 */ String JWT_HEADER = "Authorization"; /** * 创建JWT密钥 */ static SecretKey createSecretKey() { return Keys.hmacShaKeyFor(JWT_SECRET.getBytes(StandardCharsets.UTF_8)); } }
实现这个接口(那个模块中要使用就在那个模块中实现)
▼plain复制代码import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.crypto.SecretKey; /** *@belongsProject: fengoj-backend-microservice *@belongsPackage: com.feng.fengojbackendgateway.config *@author: fgh *@description: *@version v1.0.0 *@createTime: 2025-03-15 12:46 */ @Configuration public class JwtConfig implements com.feng.fengojbackendcommon.config.JwtConfig { /** * 创建JWT密钥 */ @Bean public SecretKey secretKey() { return com.feng.fengojbackendcommon.config.JwtConfig.createSecretKey(); } }
添加JWT工具类
▼java复制代码import com.feng.fengojbackendcommon.config.JwtConfig; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import javax.crypto.SecretKey; import java.util.Date; import java.util.Map; /** * @version v1.0.0 * @belongsProject: fengoj-backend-microservice * @belongsPackage: com.feng.fengojbackendcommon.utils * @author: fgh * @description: JWT工具类,用于生成和解析JWT令牌 * @createTime: 2025-03-15 12:53 */ public class JwtUtils { /** * 生成JWT令牌 * * @param claims 用户信息 * @param secretKey JWT密钥 * @return JWT令牌 */ public static String generateToken(Map<String, Object> claims, String subject, SecretKey secretKey) { return Jwts.builder() .setClaims(claims) .setSubject(subject) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + JwtConfig.JWT_EXPIRATION)) .signWith(secretKey, SignatureAlgorithm.HS256) .compact(); } /** * 从JWT令牌中获取用户ID * * @param token JWT令牌 * @param secretKey JWT密钥 * @return 用户ID */ public static Long getUserIdFromToken(String token, SecretKey secretKey) { Claims claims = getClaimsFromToken(token, secretKey); return Long.valueOf(claims.get("id").toString()); } /** * 从JWT令牌中获取用户账号 * * @param token JWT令牌 * @param secretKey JWT密钥 * @return 用户账号 */ public static String getUserAccountFromToken(String token, SecretKey secretKey) { Claims claims = getClaimsFromToken(token, secretKey); return claims.get("userAccount").toString(); } /** * 从JWT令牌中获取用户角色 * * @param token JWT令牌 * @param secretKey JWT密钥 * @return 用户角色 */ public static String getUserRoleFromToken(String token, SecretKey secretKey) { Claims claims = getClaimsFromToken(token, secretKey); return claims.get("userRole").toString(); } /** * 从JWT令牌中获取指定键的值 * * @param token JWT令牌 * @param secretKey JWT密钥 * @param key 键 * @return 值 */ public static Object getClaimFromToken(String token, SecretKey secretKey, String key) { Claims claims = getClaimsFromToken(token, secretKey); return claims.get(key); } /** * 验证JWT令牌是否有效 * * @param token JWT令牌 * @param secretKey JWT密钥 * @return 是否有效 */ public static boolean validateToken(String token, SecretKey secretKey) { try { Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token); return true; } catch (Exception e) { return false; } } /** * 从JWT令牌中获取Claims * * @param token JWT令牌 * @param secretKey JWT密钥 * @return Claims */ private static Claims getClaimsFromToken(String token, SecretKey secretKey) { return Jwts.parserBuilder() .setSigningKey(secretKey) .build() .parseClaimsJws(token) .getBody(); } }
实现方法一
在网管拦截器中添加JWT逻辑
▼java复制代码import com.feng.fengojbackendcommon.common.ErrorCode; import com.feng.fengojbackendcommon.config.JwtConfig; import com.feng.fengojbackendcommon.utils.JwtUtils; import com.feng.fengojbackendmodel.model.entity.User; import org.apache.commons.lang3.StringUtils; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.util.AntPathMatcher; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import javax.annotation.Resource; import javax.crypto.SecretKey; import java.nio.charset.StandardCharsets; @Component public class GlobalAuthFilter implements GlobalFilter, Ordered { private AntPathMatcher antPathMatcher = new AntPathMatcher(); @Resource private SecretKey secretKey; // 不需要验证token的路径 private static final String[] WHITE_LIST = { "/api/user/login", "/api/user/get/login", "/api/user/register", "/api/user/logout", "/api/doc.html", "/api/v3/api-docs", "/api/v2/api-docs", "/api/swagger-resources", "/api/swagger-ui.html", "/api/webjars/**" }; /** * 判断是否为白名单路径 * @param path 请求路径 * @return 是否在白名单中 */ private boolean isWhiteListPath(String path) { for (String whitePath : WHITE_LIST) { if (antPathMatcher.match(whitePath, path)) { return true; } } return false; } /** * 处理无权限的情况 * 当用户请求需要权限但未提供有效凭证时调用此方法 * @param response 响应对象,用于向客户端发送响应 * @param code 错误码,表示具体的错误类型 * @param message 错误信息,描述错误的详细信息 * @return Mono<Void> 表示异步操作的完成 */ private Mono<Void> handleNoAuth(ServerHttpResponse response, int code, String message) { // 设置响应状态码为未授权 response.setStatusCode(HttpStatus.UNAUTHORIZED); // 设置响应头,指定内容类型和字符集 response.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); // 创建DataBufferFactory,用于生成数据缓冲区 DataBufferFactory dataBufferFactory = response.bufferFactory(); // 构建JSON响应体,包含错误码和错误信息 String json = String.format("{\"code\":%d,\"data\":null,\"message\":\"%s\"}", code, message); // 将JSON响应体包装到数据缓冲区中 DataBuffer dataBuffer = dataBufferFactory.wrap(json.getBytes(StandardCharsets.UTF_8)); // 使用Mono将数据缓冲区写入响应中 return response.writeWith(Mono.just(dataBuffer)); } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest serverHttpRequest = exchange.getRequest(); String path = serverHttpRequest.getURI().getPath(); // 判断路径中是否包含 inner,只允许内部调用 if (antPathMatcher.match("/**/inner/**", path)) { ServerHttpResponse response = exchange.getResponse(); response.setStatusCode(HttpStatus.FORBIDDEN); DataBufferFactory dataBufferFactory = response.bufferFactory(); DataBuffer dataBuffer = dataBufferFactory.wrap("无权限".getBytes(StandardCharsets.UTF_8)); return response.writeWith(Mono.just(dataBuffer)); } // todo 统一权限校验,通过 JWT 获取登录用户信息 // return chain.filter(exchange); // 白名单路径直接放行 if (isWhiteListPath(path)) { return chain.filter(exchange); } // 统一权限校验,通过 JWT 获取登录用户信息 String token = serverHttpRequest.getHeaders().getFirst(JwtConfig.JWT_HEADER); // 如果请求头中没有token if (StringUtils.isBlank(token)) { return handleNoAuth(exchange.getResponse(), ErrorCode.NOT_LOGIN_ERROR.getCode(), ErrorCode.NOT_LOGIN_ERROR.getMessage()); } // 如果token以Bearer 开头,则去掉前缀 if (token.startsWith(JwtConfig.JWT_TOKEN_PREFIX)) { token = token.substring(JwtConfig.JWT_TOKEN_PREFIX.length()); } // 验证token if (!JwtUtils.validateToken(token, secretKey)) { return handleNoAuth(exchange.getResponse(), ErrorCode.NOT_LOGIN_ERROR.getCode(), "Token无效或已过期"); } try { // 从token中获取用户信息 Long userId = JwtUtils.getUserIdFromToken(token, secretKey); String userAccount = JwtUtils.getUserAccountFromToken(token, secretKey); String userRole = JwtUtils.getUserRoleFromToken(token, secretKey); // 将用户信息添加到请求头中,传递给下游服务 ServerHttpRequest mutableRequest = serverHttpRequest.mutate() .header("userId", String.valueOf(userId)) .header("userAccount", userAccount) .header("userRole", userRole) .build(); // 使用修改后的请求继续过滤器链 return chain.filter(exchange.mutate().request(mutableRequest).build()); } catch (Exception e) { return handleNoAuth(exchange.getResponse(), ErrorCode.SYSTEM_ERROR.getCode(), "Token解析失败"); } } /** * 优先级提到最高 * @return */ @Override public int getOrder() { return 0; } }
使用
▼java复制代码default User getLoginUser(HttpServletRequest request) { // 先判断是否已登录 // Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE); // User currentUser = (User) userObj; // 从请求头中获取用户信息 String userIdStr = request.getHeader("userId"); String userAccount = request.getHeader("userAccount"); String userRole = request.getHeader("userRole"); if (userIdStr == null) { throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR); } try { // 构建用户对象 User currentUser = new User(); currentUser.setId(Long.parseLong(userIdStr)); currentUser.setUserAccount(userAccount); currentUser.setUserRole(userRole); return currentUser; } catch (NumberFormatException e) { throw new BusinessException(ErrorCode.SYSTEM_ERROR, "用户信息格式错误"); } }
实现方法二
添加拦截器配置
▼java复制代码import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.annotation.Resource; /** * @version v1.0.0 * @belongsProject: fengoj-backend-microservice * @belongsPackage: com.feng.fengojbackenduserservice.config * @author: fgh * @description: Web配置类 * @createTime: 2025-03-15 13:58 */ @Configuration public class WebConfig implements WebMvcConfigurer { @Resource private JwtAuthInterceptor jwtAuthInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { // 注册JWT认证拦截器 registry.addInterceptor(jwtAuthInterceptor) // 拦截所有请求 .addPathPatterns("/**") // 排除不需要拦截的路径 .excludePathPatterns( "/login", "/register", "/error", "/swagger-resources/**", "/swagger-ui/**", "/v2/api-docs", "/doc.html" ); } }
实现拦截器
▼java复制代码import com.feng.fengojbackendcommon.common.ErrorCode; import com.feng.fengojbackendcommon.exception.BusinessException; import com.feng.fengojbackendcommon.utils.JwtUtils; import com.feng.fengojbackendmodel.model.entity.User; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.annotation.Resource; import javax.crypto.SecretKey; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @version v1.0.0 * @belongsProject: fengoj-backend-microservice * @belongsPackage: com.feng.fengojbackenduserservice.filter * @author: fgh * @description: JWT认证拦截器 * @createTime: 2025-03-15 13:44 */ @Component @Slf4j public class JwtAuthInterceptor implements HandlerInterceptor { @Resource private SecretKey secretKey; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 检查是否是内部调用 String internalCall = request.getHeader("Internal-Call"); if ("true".equals(internalCall)) { // 内部调用,跳过JWT认证 log.info("内部服务调用,跳过JWT认证"); return true; } // 从请求头中获取token String token = request.getHeader(JwtConfig.JWT_HEADER); // 如果请求头中没有token,尝试从请求参数中获取 if (StringUtils.isBlank(token)) { token = request.getParameter("token"); } // 如果token为空,则未登录 if (StringUtils.isBlank(token)) { throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR); } // 如果token以Bearer 开头,则去掉前缀 if (token.startsWith(JwtConfig.JWT_TOKEN_PREFIX)) { token = token.substring(JwtConfig.JWT_TOKEN_PREFIX.length()); } // 验证token if (!JwtUtils.validateToken(token, secretKey)) { throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR, "token无效或已过期"); } // 从token中获取用户信息 Long userId = JwtUtils.getUserIdFromToken(token, secretKey); String userAccount = JwtUtils.getUserAccountFromToken(token, secretKey); String userRole = JwtUtils.getUserRoleFromToken(token, secretKey); // 构建用户对象并存入请求属性中,供后续使用 User user = new User(); user.setId(userId); user.setUserAccount(userAccount); user.setUserRole(userRole); request.setAttribute("loginUser", user); return true; } }
使用
▼java复制代码/** * 获取当前登录用户 * * @param request * @return */ @Override public User getLoginUser(HttpServletRequest request) { // 先判断是否已登录 // Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE); // User currentUser = (User) userObj; // 从请求属性中获取用户信息(由JWT拦截器设置) User currentUser = (User) request.getAttribute("loginUser"); if (currentUser == null || currentUser.getId() == null) { throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR); } // 从数据库查询(追求性能的话可以注释,直接走缓存) // long userId = currentUser.getId(); // currentUser = this.getById(userId); // 从数据库查询完整用户信息 currentUser = this.getById(currentUser.getId()); if (currentUser == null) { throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR); } return currentUser; }
两种方法优劣对比
| 方案 | 使用场景 | 优势 | 局限性 |
|---|---|---|---|
| Header | 跨服务传递 | 可传递到下游服务 | 安全性低,占用宽带 |
| Attribute | 服务内部处理 | 安全,无需暴露敏感信息 | 仅限当前服务 |
评论
问答助学
相关内容
0个评论
全部评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
作者分享
llm-wiki:把 AI 会话沉淀成可长期复用的本地知识库
7
我做了一个 AI Token Dashboard:把 Claude Code、Codex、Gemini 等 AI 工具的 Token 消耗统一看清楚
13
最近公司一直在推进AI的产品落地,我负责了一块有关于数据查询AI化的部分,大致能力就是,
实现用自然语言转换成sql进行查询,得到数据,返回给用户。我找了网上很多的资料,都是介绍
用对应的mcp和插件来实现,不能满足当前需求,因为这个能力是需要单独部署成mcp工具,给很
多部门做数据分析用。所以想请教大家有没有帖子或资源,是使用代码实现的。语言java和python都可。
3
error installing 14.21.3: open C:\Users\qcdfz\AppData\Local\Temp\nvm-npm-1073152608\npm-v6.14.18.zip
2
超级AI智能体完结篇
9

