若依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 判断。
相关推荐
欣栀♡1 小时前
JVM 频繁 Full GC 先看日志括号里的 cause,jstat 的 FGC 在 JDK 8 上会骗人
java·jvm
IT研究室2 小时前
最新计算机毕业设计选题推荐-基于spring boot的葡萄酒文化宣传网站-网站-文档指导-Java-springboot
java·spring boot·课程设计
慧都小项2 小时前
MyEclipse 中创建和测试 JAX-RS REST 接口:从项目配置到 REST Explorer
java·restful·myeclipse·jax-rs
Wang's Blog2 小时前
Java 项目实战: 交友项目 - 入门案例:编写 Dubbo 服务提供者
java·dubbo·交友
秋饼2 小时前
Spring AI 2.0 结构化输出企业级实战:从 BeanOutputConverter 源码到生产级解析容错
java·ai·技术分享·后端开发
摇滚侠2 小时前
《计算机是如何工作的:人人都能懂的计算机软硬件工作原理》第 9 章 机器码与汇编语言 阅读笔记 9
java·开发语言·笔记
Cc.Y3 小时前
Java零基础入门:深入Java核心:文件IO与序列化全解
java·开发语言·python
拿本唠嗑AI研究3 小时前
本体工程实战指南:从建模到可维护的完整落地路径
java·人工智能·python·jena·ontology
lambo mercy3 小时前
Day3:结构化输出
java·数据库·oracle
程序员清风3 小时前
最短路径算法:BFS、Dijkstra 与 Floyd
java·算法·宽度优先