阿里云短信认证(SpringBoot+Vue)
一.短信认证服务概述短信认证服务是阿里云为个人和企业用户提供的验证服务您可免申请资质、签名和模板通过API直接使用。短信认证服务由平台提供资源对接成本低、成功率高特别适合无法提供企业资质但有验证码场景需求的个人用户。了解使用流程短信认证服务提供多个可选签名和短信模板模板中包含“验证码”和“有效期”两个变量。您可根据验证场景需求通过API指定签名、模板、验证码及有效期来发送短信。其中验证码支持按设定的规则自动生成也可由您直接输入自定义内容。二.发送短信验证码1.接口说明由于运营商近期加强对短信签名的管控。您自定义的签名面临下发失败问题推荐您使用号码认证控制台赠送的短信签名和模板进行短信认证。系统赠送签名必须搭配系统赠送模板使用。请确保在使用该接口前已充分了解号码认证服务产品的收费方式和价格短信认证服务仅收取短信发送费用按运营商回执状态计费短信提交成功但运营商回执失败时不计费核验服务免费。2.授权信息下表是API对应的授权信息可以在RAM权限策略语句的Action元素中使用用来给RAM用户或RAM角色授予调用此API的权限。具体说明如下操作是指具体的权限点。访问级别是指每个操作的访问级别取值为写入Write、读取Read或列出List。资源类型是指操作中支持授权的资源类型。具体说明如下对于必选的资源类型用前面加 * 表示。对于不支持资源级授权的操作用全部资源表示。条件关键字是指云产品自身定义的条件关键字。关联操作是指成功执行操作所需要的其他权限。操作者必须同时具备关联操作的权限操作才能成功。操作访问级别资源类型条件关键字关联操作dypns:SendSmsVerifyCodecreate\*全部资源*无无3.请求参数名称类型必填描述示例值SchemeNamestring否方案名称如果不填则为“默认方案”。最多不超过 20 个字符。测试方案CountryCodestring否号码国家编码。默认为 86目前也仅支持国内号码发送。86PhoneNumberstring是短信接收方手机号。130\\\\0000SignNamestring是签名名称。暂不支持使用自定义签名请使用系统赠送的签名您可在赠送签名配置页面选择需要下发的签名。恒创联众TemplateCodestring是短信模板 CODE。参数SignName选择赠送签名时必须搭配赠送模板下发短信。您可在赠送模板配置页面选择适用您业务场景的模板。100001TemplateParamstring是短信模板参数。验证码位置有两种传值方式 - 可使用##code##替代由参数 CodeType 指定验证码生成规则 - 也可直接传入具体的验证码值直接下发至接收方。 示例如模板内容为“您的验证码是${code}有效期${min}分钟请勿告诉他人。”。重要上文中的 code 请替换成您实际申请的验证码模板中的参数名称 - 该字段可传入{code:##code##,min:5}由系统根据规则生成验证码 - 或直接传入指定的验证码值{code:123456,min:5}。说明- {code:##code##,min:5}验证码是 api 动态生成的阿里云接口可以完成校验。 - {code:123456,min:5}验证码是用户配置的不是 api 动态生成阿里云接口无法校验。说明- 如果 JSON 中需要带换行符请参照标准的 JSON 协议处理。 - 模板变量规范请参见短信模板规范。{code:##code##,min:5}SmsUpExtendCodestring否上行短信扩展码。上行短信指发送给通信服务提供商的短信用于定制某种服务、完成查询或是办理某种业务等需要收费按运营商普通短信资费进行扣费。说明扩展码是生成签名时系统自动默认生成的不支持自行传入。无特殊需要此字段的用户请忽略此字段。如需使用请联系您的商务经理。1213123OutIdstring否外部流水号。外部流水号透传CodeLengthinteger否验证码长度支持 48 位长度默认是 4 位。4ValidTimeinteger否验证码有效时长单位秒默认为 300 秒。300DuplicatePolicyinteger否核验规则当有效时间内对同场景内的同号码重复发送验证码时旧验证码如何处理。 - 1覆盖处理默认即旧验证码会失效掉。 - 2保留即多个验证码都是在有效期内都可以校验通过。枚举值- 1 : 覆盖 - 2 : 保留1Intervalinteger否时间间隔单位秒。即多久间隔可以发送一次验证码用于频控默认 60 秒。60CodeTypeinteger否生成的验证码类型。当参数 TemplateParam 传入占位符时此参数必填将由系统根据指定的规则生成验证码。取值 - 1纯数字默认。 - 2纯大写字母。 - 3纯小写字母。 - 4大小字母混合。 - 5数字大写字母混合。 - 6数字小写字母混合。 - 7数字大小写字母混合。枚举值- 1 : 纯数字 - 2 : 纯大写字母 - 3 : 纯小写字母 - 4 : 大小字母混合 - 5 : 数字大写字母混合 - 6 : 数字小写字母混合 - 7 : 数字大小写字母混合1ReturnVerifyCodeboolean否是否返回验证码。取值 -true返回。 -false不返回。trueAutoRetryinteger否是否自动替换签名重试默认开启可取值 - 1 开启自动重试功能开启后在验证码有效期内当运营商返回明确的失败状态时允许阿里云尽可能的尝试使用其他方式发送验证码以提升发送成功率。其他方式包括且不限于通过其他运营商重试、更换签名重试等 - 0 不开启自动重试是否自动重试4.返回参数名称类型描述示例值objectAccessDeniedDetailstring访问被拒绝详细信息。无Messagestring状态码的描述。成功RequestIdstring请求 ID。CC3BB6D2-2FDF-4321-9DCE-B38165CE4C47Modelobject请求结果数据。VerifyCodestring验证码。4232RequestIdstring请求 ID。a3671ccf-0102-4c8e-8797-a3678e091d09OutIdstring外部流水号。1231231313BizIdstring业务 ID。112231421412414124123^4Codestring请求状态码。返回 OK 代表请求成功。其他错误码请参见返回码列表。OKSuccessboolean请求是否成功。 -true请求成功。 -false请求失败。true5.示例正常返回示例{ AccessDeniedDetail: 无, Message: 成功 , RequestId: CC3BB6D2-2FDF-4321-9DCE-B38165CE4C47, Model: { VerifyCode: 4232, RequestId: a3671ccf-0102-4c8e-8797-a3678e091d09, OutId: 1231231313, BizId: 112231421412414124123^4 }, Code: OK, Success: true }6.错误码HTTP status code错误码错误信息描述400MOBILE\NUMBER\ILLEGALThe mobile number is illegal.手机号码格式错误400BUSINESS\LIMIT\CONTROLThe number has exceeded the limit for the day.触发号码天级流控400FREQUENCY\_FAILCheck frequency fail.频控校验未通过400INVALID\_PARAMETERSparameter is not valid.非法参数400FUNCTION\NOT\OPENEDYou have not opened this function.没有开通融合认证功能三.核验短信验证码1.授权信息下表是API对应的授权信息可以在RAM权限策略语句的Action元素中使用用来给RAM用户或RAM角色授予调用此API的权限。具体说明如下操作是指具体的权限点。访问级别是指每个操作的访问级别取值为写入Write、读取Read或列出List。资源类型是指操作中支持授权的资源类型。具体说明如下对于必选的资源类型用前面加 * 表示。对于不支持资源级授权的操作用全部资源表示。条件关键字是指云产品自身定义的条件关键字。关联操作是指成功执行操作所需要的其他权限。操作者必须同时具备关联操作的权限操作才能成功。操作访问级别资源类型条件关键字关联操作dypns:CheckSmsVerifyCodenone\*全部资源*无无2.请求参数名称类型必填描述示例值SchemeNamestring否方案名称如果不填则为“默认方案”。最多不超过 20 个字符。重要如果发送接口的方案名称不为空请确保该参数不为空且与发送接口的方案名称参数一致测试方案CountryCodestring否号码国家编码默认为 86。86PhoneNumberstring是手机号。186\\\\0000OutIdstring否外部流水号。12123231VerifyCodestring是验证码。说明- SendSmsVerifyCode 接口的字段 TemplateParam配置方式有 2 种 - - {code:##code##,min:5} - - {code:123456,min:5} - {code:##code##,min:5}验证码是 api 动态生成的阿里云接口可以完成校验。 - {code:123456,min:5}验证码是用户配置的不是 api 动态生成阿里云接口无法校验。 - 请您按照实际情况传入对应的验证码。1231CaseAuthPolicyinteger否验证码大小写字母核验策略。取值 - 1不区分大小写。 - 2区分大小写。13.返回参数名称类型描述示例值objectAccessDeniedDetailstring访问被拒绝详细信息。无Messagestring状态码的描述。成功Modelobject请求结果数据。OutIdstring外部流水号。1212312VerifyResultstring短信验证码核验结果。取值 - PASS短信验证码核验成功。 - UNKNOWN短信验证码核验失败。PASSCodestring接口请求状态码。 - 返回 OK 代表请求成功。 - 其他错误码请参见返回码。重要接口请求成功不代表短信验证码核验成功短信验证码核验结果仅以Model.VerifyResult参数返回值为准。OKSuccessboolean接口调用是否成功。取值 - true接口调用成功。 - false接口调用失败。重要接口调用成功不代表短信验证码核验成功短信验证码核验结果仅以Model.VerifyResult参数返回值为准。trueRequestIdstringCF8854E5-DB21-3E5D-A9B1-DDC752FD73844.示例正常返回示例{ AccessDeniedDetail: 无, Message: 成功, Model: { OutId: 1212312, VerifyResult: PASS }, Code: OK, Success: true, RequestId: CF8854E5-DB21-3E5D-A9B1-DDC752FD7384 }四.整合SpringBoot1.引入依赖!-- 阿里云号码认证服务 V2.0 SDK (包含短信认证新 API) -- dependency groupIdcom.aliyun/groupId artifactIddypnsapi20170525/artifactId version2.0.0/version !-- 请以阿里云 Maven 仓库最新版为准 -- /dependency2.配置application.ymlaliyun: auth-sms: access-key-id: access-key-secret: sign-name: 恒创联众 template-code: 1000013.AliyunSmsPropertiesimport lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; Data Configuration ConfigurationProperties(prefix aliyun.auth-sms) public class AliyunSmsProperties { private String accessKeyId; private String accessKeySecret; private String signName; private String templateCode; }4.AliyunSmsHelperimport cn.hutool.json.JSONUtil; import com.aliyun.dypnsapi20170525.Client; import com.aliyun.dypnsapi20170525.models.CheckSmsVerifyCodeRequest; import com.aliyun.dypnsapi20170525.models.CheckSmsVerifyCodeResponse; import com.aliyun.dypnsapi20170525.models.SendSmsVerifyCodeRequest; import com.aliyun.dypnsapi20170525.models.SendSmsVerifyCodeResponse; import com.aliyun.tea.TeaException; import com.aliyun.teaopenapi.models.Config; import com.hmdp.config.AliyunSmsProperties; import com.hmdp.exception.BusinessException; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.HashMap; import java.util.Map; Component Slf4j public class AliyunSmsHelper { Resource private AliyunSmsProperties aliyunSmsProperties; private Client client; PostConstruct public void init() throws Exception { if (aliyunSmsProperties null || !StringUtils.hasText(aliyunSmsProperties.getAccessKeyId()) || !StringUtils.hasText(aliyunSmsProperties.getAccessKeySecret())) { throw new IllegalStateException(Aliyun SMS AccessKey properties are not configured properly!); } Config config new Config() .setAccessKeyId(aliyunSmsProperties.getAccessKeyId()) .setAccessKeySecret(aliyunSmsProperties.getAccessKeySecret()); config.endpoint dypnsapi.aliyuncs.com; this.client new Client(config); } /** * 发送短信验证码 * param phone 目标手机号 */ public void sendSmsCode(String phone) { try { // 使用 Map 构造并使用 JSONUtil 转为 JSON 串避免不规范的硬编码转义字符 MapString, String paramMap new HashMap(2); paramMap.put(code, ##code##); paramMap.put(min, 5); String templateParam JSONUtil.toJsonStr(paramMap); SendSmsVerifyCodeRequest request new SendSmsVerifyCodeRequest() .setPhoneNumber(phone) .setSignName(aliyunSmsProperties.getSignName()) .setTemplateCode(aliyunSmsProperties.getTemplateCode()) .setTemplateParam(templateParam) .setCodeLength(6L) .setValidTime(300L); SendSmsVerifyCodeResponse response client.sendSmsVerifyCode(request); if (OK.equals(response.getBody().getCode())) { log.info(短信验证码发送成功目标手机号{}, phone); } else { log.error(短信发送失败响应Code {}, Message {}, response.getBody().getCode(), response.getBody().getMessage()); throw new BusinessException(验证码发送失败); } } catch (BusinessException e) { throw e; } catch (Exception e) { log.error(调用短信发送接口遇到未知异常, e); throw new BusinessException(程序出错请过一段时间再试); } } /** * 校验短信验证码是否正确 * param phone 手机号 * param verifyCode 用户输入的验证码 * return 是否通过 */ public boolean verifySmsCode(String phone, String verifyCode) { try { CheckSmsVerifyCodeRequest request new CheckSmsVerifyCodeRequest() .setPhoneNumber(phone) .setVerifyCode(verifyCode); CheckSmsVerifyCodeResponse response client.checkSmsVerifyCode(request); if (OK.equals(response.getBody().getCode()) response.getBody().getModel() ! null PASS.equals(response.getBody().getModel().getVerifyResult())) { log.info(验证码核验通过手机号{}, phone); return true; } else { log.warn(验证码核验失败响应Code {}, Message {}, response.getBody().getCode(), response.getBody().getMessage()); return false; } } catch (TeaException e) { log.error(调用核验接口失败 - 阿里云异常信息: Code {}, Message {}, e.getCode(), e.getMessage()); // 阿里云核验验证码不匹配时抛出的是 code: 400message: 验证失败。此处应妥善作为校验失败返回 if (400.equals(e.getCode()) || (e.getMessage() ! null e.getMessage().contains(验证失败))) { return false; } throw new BusinessException(程序出错请过一段时间再试); } catch (Exception e) { log.error(调用核验接口遇到未知网络/系统异常, e); throw new BusinessException(程序出错请过一段时间再试); } } }5.在UserController中使用Override public ResultString sendCode(String phone, HttpSession session) { if (RegexUtils.isPhoneInvalid(phone)) { throw new BusinessException(手机号格式错误); } // 调用封装好的阿里云短信助手发送验证码 aliyunSmsHelper.sendSmsCode(phone); return Result.success(); } Override public ResultString login(LoginFormDTO loginForm, HttpSession session) { String phone loginForm.getPhone(); String userInputCode loginForm.getCode(); if (RegexUtils.isPhoneInvalid(phone)) { throw new BusinessException(手机号格式错误); } // 调用封装好的阿里云短信助手核验验证码 boolean verifyPass aliyunSmsHelper.verifySmsCode(phone, userInputCode); if (!verifyPass) { throw new BusinessException(验证码核验失败); } User user userMapper.getByPhone(phone); if (user null) { user new User(); user.setPhone(phone); user.setNickName(user_ RandomUtil.randomNumbers(10)); userMapper.save(user); } session.setAttribute(user, user); return Result.success(session-token-placeholder); }