若依AOP

项目结构:

  • ruoyi‑common:存放注解、切面
  • ruoyi‑gateway:yml 配置放行接口
  • ruoyi‑modules‑system:部门 Controller

1、自定义注解 CheckKeyId.java

路径:ruoyi‑common/src/main/java/com/ruoyi/common/annotation/CheckKeyId.java

复制代码
package com.ruoyi.common.annotation;

import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckKeyId {

}

2、AOP 切面 CheckKeyIdAspect.java

路径:ruoyi‑common/src/main/java/com/ruoyi/common/aspect/CheckKeyIdAspect.java

复制代码
package com.ruoyi.common.aspect;

import com.ruoyi.common.annotation.CheckKeyId;
import com.ruoyi.common.core.domain.AjaxResult;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@Aspect
@Component
public class CheckKeyIdAspect {

    /**
     * 切点:拦截标记@CheckKeyId的方法
     */
    @Pointcut("@annotation(com.ruoyi.common.annotation.CheckKeyId)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return AjaxResult.error("无法获取请求上下文");
        }
        HttpServletRequest request = attributes.getRequest();

        // 获取请求头keyid
        String keyid = request.getHeader("keyid");
        final String validKey = "12345678";

        if (keyid == null || !validKey.equals(keyid.trim())) {
            return AjaxResult.error(401, "header keyid校验失败");
        }

        // 校验通过,执行controller原方法
        return joinPoint.proceed();
    }
}

3、SysDeptController 新增部门接口

路径:ruoyi‑modules‑system/src/main/java/com/ruoyi/system/controller/SysDeptController.java

复制代码
import com.ruoyi.common.annotation.CheckKeyId;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.service.ISysDeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/system/dept")
public class SysDeptController {

    @Autowired
    private ISysDeptService deptService;

    /**
     * 新增部门(外部API,跳过登录,使用header keyid鉴权)
     */
    // @RequiresPermissions("system:dept:add") 注释掉权限注解
    @CheckKeyId
    @Log(title = "部门管理", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@Validated @RequestBody SysDept dept)
    {
        if (!deptService.checkDeptNameUnique(dept))
        {
            return AjaxResult.error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
        }
        // 兼容未登录场景,防止SecurityUtils空指针
        String username = SecurityUtils.getUsername();
        dept.setCreateBy(username == null ? "api_external" : username);
        return AjaxResult.toAjax(deptService.insertDept(dept));
    }
}

4、网关 ruoyi‑gateway application.yml 配置【微服务必配!】

路径:ruoyi‑gateway/src/main/resources/application.yml

sa‑token 网关层放行接口,跳过 Sa‑Token 登录校验,请求才能转发到 system 服务,之后才走 AOP 校验 header

复制代码
sa-token:
  # 网关放行路径列表,不校验token
  exclude:
    - /system/dept

📝 Postman 调用示例

  • 请求方式:POST

  • URL:http://网关IP:端口/system/dept

  • 请求头:

    Content-Type: application/json
    keyid: 12345678

  • Body JSON 示例:

    {
    "deptName":"测试外部部门",
    "parentId":0,
    "sort":1,
    "status":"0"
    }

关键注意事项

  1. ruoyi‑modules‑system 必须依赖 ruoyi‑common,默认若依已经依赖无需修改 pom
  2. 鉴权顺序:网关放行 token → 请求进入 system 服务 → AOP 切面校验 header keyid → 执行业务
  3. 没有keyid或者 keyid 不等于12345678,直接返回401 header keyid校验失败,不会进入新增逻辑
  4. 生产环境务必开启 HTTPS,防止 keyid 泄露;上线不需要该接口对外开放时,恢复@RequiresPermissions、删除 yml 放行路径、移除@CheckKeyId

排错清单

  • 切面不执行:确认切面类有@Component,注解全类名写对;
  • 依旧跳登录:检查 gateway 的sa-token.exclude配置是否写对路径,改完重启 gateway 服务;
  • 空指针:确认代码中SecurityUtils.getUsername()做了 null 判断。
相关推荐
是2的10次方啊1 小时前
日志不是越多越好:开发优化、常见坑与线上排查命令
java
ofoxcoding1 小时前
Claude Fable 5.1 API 接入指南:模型标识、五级定价策略、effort 参数与缓存 TTL 配置详解
java·spring·缓存·ai
zww89491111 小时前
相册印刷项目实战指南:从设计到成品的完整技术流程
java·小程序·eclipse
程序猫.1 小时前
双指针问题
java·数据结构·算法
拾光Ծ1 小时前
【MySQL】复合查询、表的连接
java·数据库·sql·mysql
MetaLite2 小时前
Java通用枚举驱动下拉框-元数据接口与前端契约
java·前端·状态模式
文子越来越强2 小时前
线程池使用总结
java
SimonKing2 小时前
Java 图片处理还在用 ImageIO?这个库让你代码从 30 行变 3 行
java·后端·程序员
数据狐(Datafox)2 小时前
mercadolibre.item_get 工程实战:美客多商品详情API技术解析与落地应用
java·人工智能·mysql·json