[Day16] Bug 排查记录:若依框架二次开发中的经验与教训 contract-security-ruoyi

目录

  1. 前言
  2. @Anonymous注解不生效
  3. 游客登录后Token立即过期
  4. 内部服务调用鉴权失败
  5. 经验总结
  6. 后续改进计划
  7. 写在最后
  8. 阶段总结

前言

在软件开发的进程中,形形色色的问题层出不穷。在此,我将记录在若依框架二次开发期间遭遇的若干难题及其排查历程。这不仅便于自身日后回顾反思,也期望能为面临类似困境的开发者提供有益的参考。

@Anonymous注解不生效

问题描述

在特定接口添加@Anonymous注解后,该接口依旧被鉴权拦截器所拦截。

现象

java 复制代码
@Anonymous
@GetMapping("/public/data")
public AjaxResult getPublicData() {
    return AjaxResult.success("公开数据");
}

当尝试访问此接口时,系统返回401未授权错误。这就好比你拿着一张"免门票"(@Anonymous注解)进入一个场所(接口),却还是被保安(鉴权拦截器)拦住,告知你没有权限进入。

排查过程

  1. 检查注解定义 :经确认,@Anonymous注解定义无误,同时支持类和方法级别。这一步就像是检查"免门票"的制作是否合规,确保它本身是有效的。
  2. 检查拦截器配置 :拦截器的配置准确,其中包含对@Anonymous注解的检查逻辑。相当于查看保安的工作流程里,有没有对"免门票"的检查环节。
  3. 调试拦截器代码 :在调试过程中发现,拦截器获取到的Handler类型并非HandlerMethod。这就好比保安拿到的门票信息格式不对,导致无法正确识别。
  4. 查看URL映射 :察觉到该接口存在两个路径映射,分别为/public/data/api/public/data。这就如同一个场所设置了两个入口,但它们的通行规则可能存在差异。
  5. 进一步调试 :当访问/api/public/data时,请求被一个前端控制器拦截。这意味着在通行过程中,可能在其他地方出现了阻碍。

根本原因

项目内存在多个拦截器与过滤器,请求链路如下:

复制代码
请求 → Filter1 → Filter2 → 拦截器1 → 拦截器2 → Controller

@Anonymous注解的检查位于拦截器2之中,然而拦截器1已因缺少token而提前返回401。这就好比在进入场所的一系列检查环节中,第二个检查点(拦截器2)虽然会查看"免门票",但第一个检查点(拦截器1)因为其他原因已经拒绝放行。

解决方案

  1. 调整拦截器顺序 :将@Anonymous注解的检查逻辑提前至过滤器链的最前端,或者调整拦截器顺序。
java 复制代码
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 匿名访问拦截器优先级最高
        registry.addInterceptor(new AnonymousInterceptor())
              .addPathPatterns("/**")
              .order(-100); // 设置最高优先级

        // 权限拦截器
        registry.addInterceptor(new PermissionInterceptor())
              .addPathPatterns("/**")
              .order(0);
    }
}
  1. 在过滤器中增加检查 :同时在过滤器中增添@Anonymous注解的检查。
java 复制代码
@Override
protected void doFilterInternal(HttpServletRequest request,
                                HttpServletResponse response,
                                FilterChain chain) {
    // 提前检查@Anonymous注解
    if (isAnonymous(request)) {
        chain.doFilter(request, response);
        return;
    }
    // ... 其他逻辑
}

游客登录后Token立即过期

问题描述

游客成功登录后,首次使用Token请求接口便收到"Token已过期"的错误提示。这就像你刚拿到一张有效期为一天的车票,结果刚上车就被告知车票过期了。

排查过程

  1. 查看登录响应:登录接口返回的Token看似正常,就像车票外观没问题。
  2. 检查Token解析 :在解析Token时发现,exp(过期时间)为当前时间减去30分钟,而非当前时间加上30分钟。这相当于车票上印的有效期不是从现在往后算一天,而是从现在往前倒推一天。
  3. 查看JWT生成代码:进而发现时间计算存在问题。

根本原因

JwtUtils中生成Token时,时间计算逻辑有误:

java 复制代码
// 错误的写法
long exp = System.currentTimeMillis() - expireTime * 60 * 1000;

// 正确的写法应该是
long exp = System.currentTimeMillis() + expireTime * 60 * 1000;

这是一个较为粗心的错误,把加号写成了减号。就好比在计算有效期时,把加法运算写成了减法运算。

解决方案

修正时间计算逻辑:

java 复制代码
public static String generateToken(String subject, long expireMinutes) {
    Date now = new Date();
    Date expiryDate = new Date(now.getTime() + expireMinutes * 60 * 1000);

    return Jwts.builder()
          .setSubject(subject)
          .setIssuedAt(now)
          .setExpiration(expiryDate)
          .signWith(secretKey)
          .compact();
}

预防措施

  1. 增加单元测试:添加Token过期时间验证的测试用例。
java 复制代码
@Test
public void testTokenExpiryTime() {
    String token = JwtUtils.generateToken("test", 30);
    Claims claims = JwtUtils.parseToken(token);
    Date expiration = claims.getExpiration();
    Date now = new Date();

    assertTrue(expiration.after(now));
    assertTrue(expiration.getTime() - now.getTime() < 31 * 60 * 1000);
    assertTrue(expiration.getTime() - now.getTime() > 29 * 60 * 1000);
}

这就好比给车票的有效期设定了一个检测机制,每次生产车票时都检查一下有效期是否正确。

内部服务调用鉴权失败

问题描述

在配置内部服务免鉴权后,内部服务调用时仍返回403权限不足的错误。这就像在一个公司内部,员工拿着内部通行凭证(配置好的免鉴权设置)去不同部门(内部服务),却还是被拦住说权限不够。

排查过程

  1. 检查配置 :确认配置文件中remote.auth.internal - token已正确配置,就像确认员工的通行凭证已经制作好并且放在正确的地方。
  2. 检查请求头 :内部服务请求已携带X - Internal - Token请求头,说明员工已经拿着通行凭证去尝试通行了。
  3. 调试过滤器代码:发现过滤器获取请求头时使用了错误的header名称。这就好比员工拿着通行凭证,但保安认错了凭证上的标志,导致无法识别。
  4. 查看常量定义 :定义的是X - Internal - Token,但代码中获取的是Internal - Token

根本原因

常量定义和使用不一致:

java 复制代码
// 常量定义
public static final String INTERNAL_TOKEN_HEADER = "X - Internal - Token";

// 代码中使用(错误)
String token = request.getHeader("Internal - Token");

这就像公司规定的通行凭证标志是一个样子,但实际使用时却按照另一个样子去识别,导致混乱。

解决方案

统一常量定义和使用:

java 复制代码
public class AuthFeignConstants {
    public static final String INTERNAL_TOKEN_HEADER = "X - Internal - Token";
    public static final String BEARER_PREFIX = "Bearer ";
}

// 使用时
String token = request.getHeader(AuthFeignConstants.INTERNAL_TOKEN_HEADER);

这样就统一了标志,让保安能正确识别通行凭证。

经验总结

接口设计

  • 统一返回格式:统一采用JSON格式返回错误信息,就像所有的路标都使用相同的语言和格式,便于大家理解。
  • 明确异常定义:明确定义接口在异常情况下的返回内容,比如提前告诉大家遇到不同的路况(异常情况)该怎么走。
  • 规范错误信息:提供清晰的错误码和错误信息,如同每个路标都有明确的编号和解释,方便定位问题。

配置管理

  • 文档说明:对超时、重试等配置给出明确的文档说明,就像给每个工具的使用方法都写了一本说明书。
  • 环境区分:在生产环境和开发环境使用不同的配置,这就好比不同的路况(环境)需要不同的驾驶模式(配置)。
  • 启动校验:在启动时进行配置校验,确保一开始就使用正确的设置,如同出发前检查车辆是否能正常行驶。

监控和日志

  • 详细记录:对关键接口调用记录详细日志,就像记录重要行程的每一步,方便日后回顾。
  • 性能监控:添加性能监控,及时发现慢接口,如同监测车辆的行驶速度,及时发现行驶缓慢的路段。

测试

  • 单元测试:增加单元测试覆盖边界情况,就像检查每个零件在极端情况下是否能正常工作。
  • 集成测试:进行集成测试验证完整流程(个人开发者在项目初期,可能因时间成本考虑暂不进行,但项目达到一定规模后应予以考虑),如同测试整个车辆在实际路况下能否正常行驶。

开发习惯

  • 先写测试:对于重要功能,先编写测试用例,就像在建造房屋前先设计好质量检测标准。
  • 提交自测:在提交代码前做好自测,确保每次提交的代码都像经过自检的合格产品。

写在最后

这些bug大多源于一些低级错误,但它们暴露出在代码质量、测试、文档等方面存在的不足。在今后的开发过程中,需注意以下几点:

  1. 仔细检查代码:代码完成后要仔细核查,就像完成作业后认真检查,避免粗心错误。
  2. 编写测试用例:重要功能务必编写测试用例,为代码的正确性提供保障,如同给产品质量上一份保险。
  3. 耐心排查问题:遇到问题要耐心排查,不放过任何疑点,就像解开一道复杂的谜题,每个线索都很重要。
  4. 记录排查过程:将排查过程记录下来,防止重复犯错,如同记录自己走过的弯路,下次不再重蹈覆辙。

希望这些记录能为遇到类似问题的朋友提供帮助,也欢迎大家交流讨论。

阶段总结

至此,项目中的所有模块均已介绍完毕。后续,我可能会考虑将开发日志单独开设一个专栏,以动态的形式呈现每天的开发工作内容,分享给大家。待我确定具体方案后推出,本专栏先暂停更新几日。

相关推荐
猷咪5 分钟前
C++基础
开发语言·c++
IT·小灰灰6 分钟前
30行PHP,利用硅基流动API,网页客服瞬间上线
开发语言·人工智能·aigc·php
快点好好学习吧8 分钟前
phpize 依赖 php-config 获取 PHP 信息的庖丁解牛
android·开发语言·php
秦老师Q9 分钟前
php入门教程(超详细,一篇就够了!!!)
开发语言·mysql·php·db
烟锁池塘柳09 分钟前
解决Google Scholar “We‘re sorry... but your computer or network may be sending automated queries.”的问题
开发语言
是誰萆微了承諾9 分钟前
php 对接deepseek
android·开发语言·php
vx_BS8133013 分钟前
【直接可用源码免费送】计算机毕业设计精选项目03574基于Python的网上商城管理系统设计与实现:Java/PHP/Python/C#小程序、单片机、成品+文档源码支持定制
java·python·课程设计
2601_9498683613 分钟前
Flutter for OpenHarmony 电子合同签署App实战 - 已签合同实现
java·开发语言·flutter
星火开发设计27 分钟前
类型别名 typedef:让复杂类型更简洁
开发语言·c++·学习·算法·函数·知识
qq_1777673739 分钟前
React Native鸿蒙跨平台数据使用监控应用技术,通过setInterval每5秒更新一次数据使用情况和套餐使用情况,模拟了真实应用中的数据监控场景
开发语言·前端·javascript·react native·react.js·ecmascript·harmonyos