news 2026/9/12 16:26:07

Java框架快速入门: Spring Security+OAuth2之云服务集成与多因子认证设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java框架快速入门: Spring Security+OAuth2之云服务集成与多因子认证设计

纲要

  • 云服务认证基础
    • AccessKey IDAccessKey Secret的密钥对模型
    • 短信服务要素:签名、模板
    • 跨平台对照:阿里云、Leancloud
  • 邮件发送方案
    • SMTPWeb API的对比与选型
    • 邮件服务中的API Key鉴权
  • 多因子认证(MFA)架构设计
    • 用户实体扩展:mfaEnabledtotpSecret字段
    • TOTP密钥生成与持久化
    • 两步验证的交互流程
    • 利用请求 ID 与缓存实现无状态关联
  • 代码实现
    • 项目结构树
    • 用户实体、TOTP 服务、MFA 服务
    • 阿里云短信客户端封装
  • 可视化流程
    • MFA 认证时序图
    • 请求 ID 与缓存映射类图

云服务认证与基础概念

绝大多数云服务都通过一对密钥来标识调用方身份并完成请求签名。常见命名形式为AccessKey IDAccessKey Secret,也可能称为AppID / AppSecretAPI Key / Secret KeyID作为公开标识,Secret用于生成签名,两者缺一不可。

以阿里云短信服务为例,需在 RAM 控制台创建AccessKey,获得上述密钥对。同时还需要配置:

  • 短信签名:标识发送者身份,如“XX科技”,需提前审核。
  • 短信模板:定义短信正文结构及占位符,不同业务场景使用不同模板,通过TemplateCode指定。

Leancloud 等 BaaS 平台同样提供AppIDAppKey,部分还提供MasterKey用于管理操作。其短信服务也要求签名和模板,并建议绑定自定义 API 域名以避免默认域名的限流问题。

邮件服务:SMTP 与 Web API

方式原理优点缺点
SMTP直连邮件服务器端口,按协议投递标准化、通用性强连接耗时、资源占用高
Web API调用 RESTful 接口,异步发送响应快、解耦业务系统依赖服务商 API 稳定性

生产环境推荐Web API。服务商(如 SendGrid)通常提供一个API Key,在 SMTP 场景作为密码,在 API 场景作为Bearer Token使用。这种方式交互时间短,不会阻塞应用线程。

多因子认证(MFA)设计

用户模型扩展

在用户实体中增加两个关键字段:

  • mfaEnabled(布尔型):标识是否启用多因子认证。可由用户自主开启,也可由管理员统一控制,灵活性高。
  • totpSecret(字符串):存储该用户的 TOTP 共享密钥,每个用户唯一。密钥生成后持久化到数据库,用于每次验证码校验。

两步验证流程

用户使用用户名/密码完成第一级认证后,若mfaEnabled=true,服务端不直接颁发令牌,而是返回一个特殊响应:

HTTP 401 Unauthorized X-Auth-Identity: MFA_REQUIRED, requestId=<uuid>

其中requestId是一个临时凭证,服务端将其与已认证的用户信息绑定存入缓存(如 Redis),设置较短过期时间。客户端引导用户输入 TOTP 验证码,再次请求验证接口,并附上requestId和验证码。服务端根据requestId从缓存取出用户主体,校验 TOTP,成功后生成正式令牌并清除缓存。整个过程无需在第二次请求中重复传递密码,保持了无状态服务的简洁性。

项目结构树

src/main/java/com/example/security/ ├── config/ │ ├── SecurityConfig.java │ └── RedisConfig.java ├── entity/ │ └── User.java ├── repository/ │ └── UserRepository.java ├── service/ │ ├── TotpService.java │ ├── SmsService.java │ └── MfaService.java ├── filter/ │ └── MfaAuthenticationFilter.java └── dto/ ├── MfaRequiredResponse.java └── MfaVerifyRequest.java

核心代码实现

用户实体

packagecom.example.security.entity;importjakarta.persistence.*;@Entity@Table(name="users")publicclassUser{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)privateLongid;@Column(nullable=false,unique=true)privateStringusername;@Column(nullable=false)privateStringpassword;@Column(nullable=false)privatebooleanmfaEnabled=false;@Column(length=64)privateStringtotpSecret;// 省略 getter/setter}

TOTP 密钥生成与验证服务

packagecom.example.security.service;importcom.eatthepath.otp.TimeBasedOneTimePasswordGenerator;importorg.apache.commons.codec.binary.Base32;importjavax.crypto.spec.SecretKeySpec;importjava.security.Key;importjava.security.SecureRandom;importjava.time.Instant;publicclassTotpService{privatestaticfinalTimeBasedOneTimePasswordGeneratortotp=newTimeBasedOneTimePasswordGenerator();publicStringgenerateSecret(){SecureRandomrandom=newSecureRandom();byte[]bytes=newbyte[20];random.nextBytes(bytes);returnnewBase32().encodeToString(bytes);}publicbooleanvalidateCode(Stringsecret,Stringcode){try{byte[]decodedKey=newBase32().decode(secret);Keykey=newSecretKeySpec(decodedKey,"HmacSHA1");Stringexpected=totp.generateOneTimePassword(key,Instant.now());returnexpected.equals(code);}catch(Exceptione){returnfalse;}}}

MFA 服务与缓存交互

packagecom.example.security.service;importcom.example.security.dto.MfaRequiredResponse;importcom.example.security.entity.User;importcom.example.security.repository.UserRepository;importorg.springframework.data.redis.core.StringRedisTemplate;importjava.util.UUID;importjava.util.concurrent.TimeUnit;publicclassMfaService{privatefinalStringRedisTemplateredisTemplate;privatefinalTotpServicetotpService;publicMfaService(StringRedisTemplateredisTemplate,TotpServicetotpService){this.redisTemplate=redisTemplate;this.totpService=totpService;}publicMfaRequiredResponserequireMfa(Useruser){StringrequestId=UUID.randomUUID().toString();redisTemplate.opsForValue().set("mfa:"+requestId,user.getUsername(),5,TimeUnit.MINUTES);returnnewMfaRequiredResponse("MFA_REQUIRED",requestId);}publicbooleanverifyMfa(StringrequestId,Stringcode,UserRepositoryuserRepo){Stringusername=redisTemplate.opsForValue().get("mfa:"+requestId);if(username==null){returnfalse;}Useruser=userRepo.findByUsername(username).orElse(null);if(user==null||!user.isMfaEnabled()){returnfalse;}booleanvalid=totpService.validateCode(user.getTotpSecret(),code);if(valid){redisTemplate.delete("mfa:"+requestId);}returnvalid;}}

阿里云短信客户端示例

packagecom.example.security.service;importcom.aliyun.dysmsapi20170525.Client;importcom.aliyun.dysmsapi20170525.models.SendSmsRequest;importcom.aliyun.teaopenapi.models.Config;publicclassSmsService{privatefinalClientclient;privatefinalStringsignName;privatefinalStringtemplateCode;publicSmsService(StringaccessKeyId,StringaccessKeySecret,StringsignName,StringtemplateCode)throwsException{Configconfig=newConfig().setAccessKeyId(accessKeyId).setAccessKeySecret(accessKeySecret);config.endpoint="dysmsapi.aliyuncs.com";this.client=newClient(config);this.signName=signName;this.templateCode=templateCode;}publicvoidsend(StringphoneNumber,Stringcode){SendSmsRequestrequest=newSendSmsRequest().setPhoneNumbers(phoneNumber).setSignName(signName).setTemplateCode(templateCode).setTemplateParam("{\"code\":\""+code+"\"}");client.sendSms(request);}}

流程可视化

MFA 认证时序图

RedisAuthServerClientRedisAuthServerClientalt[校验通过][校验失败]alt[用户未启用 MFA][用户已启用 MFA]POST /login (username, password)验证用户名/密码200 返回 access_tokenSET mfa:{requestId} = username (TTL 5min)401 X-Auth-Identity: MFA_REQUIRED, requestId弹出二次验证输入框POST /auth/mfa/verify (requestId, code)GET mfa:{requestId}username加载用户, 校验 TOTPDEL mfa:{requestId}200 返回 access_token403 验证码错误

请求 ID 与缓存键结构

写入

读取并校验

加载

LoginStep1

+String username

+String password

MfaRequiredResponse

+String header: X-Auth-Identity

+String requestId

RedisCache

+key: "mfa:" + requestId

+value: username

+ttl: 5min

MfaVerifyRequest

+String requestId

+String code

User

+String totpSecret

+boolean mfaEnabled

总结

本文梳理了云服务集成中的认证术语与 MFA 架构设计思路。通过扩展用户实体、引入 TOTP 密钥,并借助请求 ID 与 Redis 缓存,可在 Spring Security + OAuth2 体系下实现无状态的两步验证流程。

文中给出的阿里云短信封装、TOTP 校验等代码可作为实际开发的起点,开发者可根据具体云平台接入对应的 SDK。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 16:25:33

Substance Painter智能法线贴花库快速制作方案

1. 项目概述&#xff1a;Substance贴花库快速制作方案 在三维材质制作领域&#xff0c;法线贴图&#xff08;Normal Map&#xff09;一直是提升模型细节表现的关键技术。传统手工绘制法线贴图不仅耗时耗力&#xff0c;对美术人员的专业技能要求也极高。最近在Substance Painter…

作者头像 李华
网站建设 2026/9/12 16:22:53

GAIA实践指南:用5大维度和3项指标看清AI助手的真实能力

GAIA实践指南&#xff1a;用5大维度和3项指标看清AI助手的真实能力 【免费下载链接】agents-course This repository contains the Hugging Face Agents Course. 项目地址: https://gitcode.com/GitHub_Trending/ag/agents-course 同一个复杂任务丢给三个 AI 助手&…

作者头像 李华