【双因子认证】拦截注解@MFACheck 接入指南(其他微服务如何用)

@MFACheck 接入指南(其他微服务如何用)

任意依赖 hyper-util-server 的微服务,在方法/类上加一个 @MFACheck 注解 即可获得 MFA 二次校验能力,无需写任何实现 。

原理:DefaultMfaStatusProvider 已内置,一次查 auth_user_basic_t 的 totp_enabled(已绑定)与 need_totp(强制开关)两列(与 auth-server 完全一致)。

一、接入三步

1. 确认依赖(一般已有)

xml 复制代码
<dependency>
    <groupId>com.xxx.cloud.spring</groupId>
    <artifactId>hyper-util-server</artifactId>
    <version>2.1.4-SNAPSHOT</version>
</dependency>

2. 在敏感操作上加注解

java 复制代码
import com.xxx.util.mfa.annotation.MFACheck;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/firmware")
public class FirmwareController {

    /** 固件升级:每次都要 MFA(STRICT) */
    @MFACheck(action = "FW_UPGRADE", level = MFACheck.Level.STRICT)
    @PostMapping("/upgrade")
    public Result<Void> upgrade(@RequestBody UpgradeDto dto) {
        // ... 业务逻辑 ...
        return ResultUtil.ok();
    }

    /** 批量配置:5 分钟窗口内验过一次即可(LIGHT) */
    @MFACheck(action = "BATCH_CONFIG", level = MFACheck.Level.LIGHT, skipWindowSeconds = 300)
    @PostMapping("/batchConfig")
    public Result<Void> batchConfig(@RequestBody ConfigDto dto) {
        // ... 业务逻辑 ...
        return ResultUtil.ok();
    }
}

也可标在类上,对该类所有方法生效。

3. 前端处理 -9000(二次验证闭环)

被拦截时,本服务自动返回(由 util 的 MfaExceptionHandler 生成,无需自己写异常处理):

json 复制代码
{ "code": -9000, "message": "需要 MFA 验证", "data": { "mfaChallenge": true, "action": "FW_UPGRADE" } }

前端拿到后:

  1. 弹窗让用户输入 Authenticator 动态码;

  2. 调 auth-server 的统一二次校验接口(不是本服务):

    复制代码
    POST {auth-server}/mfa/verifyAction
    Body: { "action": "FW_UPGRADE", "code": "123456" }

    通过后,auth-server 会把「该用户该 action 已验证」写入共享 Redis (mfa:act:{uid}:{action})。

  3. 重放原请求 (再调一次本服务的 /firmware/upgrade)→ 切面读到共享 Redis 里已验证 → 放行。

-9001(强制 MFA 但用户未绑定:need_totp=1 且 totp_enabled!=1)→ 前端引导用户去 auth-server /mfa/init + /mfa/enable 绑定。

判定优先级:MFA 认证 > 管理员操作密码 。已绑定(totp_enabled=1)走 MFA;未绑定且 need_totp=1 引导绑定;未绑定且 need_totp=0 放行,回退既有二次密码链。

二、你自动获得了什么(零代码)

引入 util-server 后,以下组件自动装配 (组件扫描 + MfaAutoConfiguration):

组件 作用
MFACheckAspect 拦截 @MFACheck,做 MFA 判定
DefaultMfaStatusProvider 默认查 auth_user_basic_t 的 totp_enabled + need_totp(@ConditionalOnMissingBean,可被覆盖)
MfaExceptionHandler 把 MFA 异常转成 code=-9000/-9001 + data.action
MfaSessionManager 共享 Redis 上的「窗口内已验证」状态(跨服务复用)
MfaProperties mfa.* 配置绑定(多数用默认即可)

三、前置条件(必须满足,否则默认实现会失效)

  1. 默认数据源能读到 auth_user_basic_t(和 auth-server 一样查这张表)。

  2. @MapperScan 覆盖 com.xxx.util.*.mapper (否则 AuthUserMfaMapper 注入不到)。

    java 复制代码
    @MapperScan(basePackages = {"com.xxx.*.mapper", "com.xxx.util.*.mapper"})
  3. 与 auth-server 共享同一个 Redis(二次验证的「已验证」状态要跨服务读到)。

四、可选配置(application.yml,不给就用默认)

yaml 复制代码
mfa:
  pending:
    ttlSeconds: 300   # 两步登录凭证有效期(秒)
  # 其余 totp/recovery/rateLimit 项主要给 auth-server 用,消费方一般不用配

五、特殊服务:查不到 auth_user_basic_t 时覆盖默认

若某服务(独立 DB / 不连 auth 库)无法直接查该表,自定义一个 MfaStatusProvider bean,MfaAutoConfiguration 的默认实现会自动让位:

java 复制代码
import com.xxx.util.mfa.spi.MfaStatusProvider;
import com.xxx.util.mfa.spi.MfaUserStatus;
import org.springframework.stereotype.Component;

@Component
public class RemoteMfaStatusProvider implements MfaStatusProvider {
    @Override
    public MfaUserStatus getStatus(String uid) {
        // 例如:Feign 回调 auth-server,或查本服务用户表
        // 一次返回 totp_enabled(enabled) 与 need_totp(required),避免两次查库
        boolean enabled = authFeignClient.isMfaEnabled(uid);
        boolean required = authFeignClient.isMfaRequired(uid);
        return new MfaUserStatus(enabled, required);
    }

    @Override
    public boolean isMfaEnabled(String uid) {
        return getStatus(uid).isEnabled();
    }
}

六、依赖 auth-server 提供的端点(MFA 生命周期)

消费方不需要实现这些,由 auth-server 统一提供:

端点 用途
POST /mfa/init 生成绑定二维码(首次绑定)
`POST /mfa/enable
相关推荐
蛋蛋的就会顺顺的8 小时前
hot100 p0——栈,贪心算法,动态规划
java·前端·javascript·算法·leetcode·力扣
需要8268 小时前
Spring Bean 生命周期:从实例化到销毁的完整钩子
java·后端·spring
小 黄 鸡8 小时前
HashMap 面试必会知识点详解(JDK 8)
java·面试·职场和发展
伯恩bourne10 小时前
Spring Boot Bean 注册的两种方式:@Component 家族 vs @Configuration + @Bean
java·spring boot·后端
silvamux_10 小时前
curl 测试 OpenAI 兼容 API 的完整流程
java·人工智能·缓存·ai·oneapi
省钱兄--zs11 小时前
深圳24小时自助健身房软硬件解决方案实战指南与开发流程详解
java·数据仓库·spring boot·小程序·架构
顽疲11 小时前
Java vue 养老系统源码详解:月账单 care_month_bill 与 Quartz 扣费 Job 实践
java·开发语言·vue.js·vue养老·springboot养老·java养老
Wang's Blog11 小时前
Java 项目实战: 交友项目 - 安装开发工具与环境准备
java·开发语言·交友
用户212968352616811 小时前
开源了一个 Java 多智能体 LLM 框架 Baton:一个依赖,把 Agent 循环、工具调用、记忆、编排全配齐
java·llm
helmsail12 小时前
05 管理活动与选品
java