欢迎光临
我们一直在努力

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

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

任意依赖 util-server 的微服务,在方法/类上加一个 @MFACheck 注解即可获得 MFA 二次校验能力,无需写任何实现。
原理:DefaultMfaStatusProvider 已内置,一次查 auth_user_basic_t 的 totp_enabled(已绑定)与 need_totp(强制开关)两列(与 auth-server 完全一致)。

一、接入三步

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

<dependency>
<groupId>com.xxx.cloud.spring</groupId>
<artifactId>hyper-util-server</artifactId>
<version>2.1.4-SNAPSHOT</version>
</dependency>

2. 在敏感操作上加注解

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 生成,无需自己写异常处理):

{ "code": 9000, "message": "需要 MFA 验证", "data": { "mfaChallenge": true, "action": "FW_UPGRADE" } }

前端拿到后:

  • 弹窗让用户输入 Authenticator 动态码;
  • 调 auth-server 的统一二次校验接口(不是本服务):POST {auth-server}/mfa/verifyAction
    Body: { "action": "FW_UPGRADE", "code": "123456" }

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

  • 重放原请求(再调一次本服务的 /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.* 配置绑定(多数用默认即可)

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

  • 默认数据源能读到 auth_user_basic_t(和 auth-server 一样查这张表)。
  • @MapperScan 覆盖 com.xxx.util.*.mapper(否则 AuthUserMfaMapper 注入不到)。@MapperScan(basePackages = {"com.xxx.*.mapper", "com.xxx.util.*.mapper"})
  • 与 auth-server 共享同一个 Redis(二次验证的「已验证」状态要跨服务读到)。
  • 四、可选配置(application.yml,不给就用默认)

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

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

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

    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
    赞(0)
    未经允许不得转载:171主机测评 » 【双因子认证】拦截注解@MFACheck 接入指南(其他微服务如何用)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址