本文最后更新于 2023年9月17日 上午
作者:Mintimate
博客:https://www.mintimate.cn
Mintimate’s Blog,只为与你分享
短信登录
一个登录模块,无法就是一个鉴权。现代社会,大家手机不离身,使用手机进行鉴权,并完成后续单点登录,无疑是一个不错的方法。
本次就使用Springboot配合腾讯云的SMS服务,完成后台短信登录逻辑。
准备工作
首先,我们看看准备工作:
当然,你想开通腾讯云的SMS服务,前置条件:
否则,是无法使用腾讯云短信功能的。
短信开通
首先,我们要进入SMS控制台,开通短信功能:
创建短信签名
其次,我们开通了短信服务功能后,需要创建短信签名,创建短信签名
比如,我有一个备案域名(flyinbug.cn
),所以我创建的短信签名:
短信正文模板
现在,我们就可以创建正文模版:
可以看到,我这里创建一个短信登录的模版,其中{1}
和Nginx的$1
类似,用于后续Springboot使用时的传参。
创建提交后,就可以等审核了:
在等审核的过程中,你可以先去完善后台逻辑。
审核通过:
到此,你会得到:
- signName:短信签名名;
- templateId:短信正文模版id;
Maven依赖
我们使用腾讯云的SDK进行封装业务,所以需要引用:
1 2 3 4 5
| <dependency> <groupId>com.tencentcloudapi</groupId> <artifactId>tencentcloud-sdk-java</artifactId> <version>4.0.11</version> </dependency>
|
最新版本号参考:https://search.maven.org/artifact/com.tencentcloudapi/tencentcloud-sdk-java-sms
另外,还有Redis:
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
|
到此,前期准备就完成了。我们可以进行Java逻辑部署了。
Springboot操作
Springboot内,我们肯定是需要写一个工具包,这个工具包实现方法很多;比如:IOC注入、static静态方法等。
为了方便,我这里使用static静态方法。
官方文档
首先查看官方文档:https://cloud.tencent.com/document/product/382/43194
观察可以看到,我们需要参数:
- secretId:腾讯云账号机密id;
- secretKey:腾讯云账号机密key;
- signName:前文的短信签名名;
- templateId:短信正文模版id;
其中:signName
和templateId
上文已经获取;而secretId
和secretKey
,我们可以在腾讯云控制台内获取:https://console.cloud.tencent.com/cam/capi
为此分离配置。
创建常量类
我们把在官方文档内看到的常量参数进行封装,定义一个constant常量类,进行存储:
1 2 3 4 5 6 7 8 9 10 11 12
|
public class tencentSmsConst { public static final String secretId = ""; public static final String secretKey = ""; public static final String sdkAppId = ""; public static final String SignName = ""; }
|
本来想用IOC注入的,结果我的Springboot更新一下,有些问题…… 还没解决;索性就用constant类来实现。
创建枚举类
想想一下,我们还需要传templateId
,也就是短信的模版ID,如果只有一个短信模版情况下,可以直接写为final static
类型作为constant属性。可是,如果你有多个模版呢?
所以,我这里定义一个枚举类型,方便多模版切换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
@Getter @AllArgsConstructor public enum SmsTemplateEnum {
LOGIN("1******4","短信登录模版"), private final String TemplateID; private final String TemplateDesc; }
|
其中,使用TemplateDesc
,方便我使用log4j2打印日志……
静态工具包
最后,我们就可以写这个静态工具包了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
|
public class tencentSmsUtil {
public static boolean sendMessage(String telephoneNumber, String TemplateID, String... templateParamSet) {
Credential cred = new Credential(secretId, secretKey); SendSmsRequest req = new SendSmsRequest(); SmsClient client = new SmsClient(cred, "ap-guangzhou");
req.setSmsSdkAppId(sdkAppId);
req.setSignName(SignName);
String sessionContext = "xxx"; req.setSessionContext(sessionContext); req.setTemplateId(TemplateID); req.setPhoneNumberSet(new String[]{"+86" + telephoneNumber}); req.setTemplateParamSet(templateParamSet);
SendSmsResponse res = null; try { res = client.SendSms(req); } catch (TencentCloudSDKException ex) { ex.printStackTrace(); return false; } System.out.println(SendSmsResponse.toJsonString(res)); System.out.println(res.getRequestId()); return true; } }
|
其实就是对官方的模版进行简单封装和简化。
演示使用
这里就进行简单的测试使用,设计的简单逻辑是:
Service层逻辑
首先,我写了一个Service层的调用代码来测试:
1 2 3 4 5 6 7 8 9 10
| @Override public boolean sendSMS(String teleNumber,SmsTemplateEnum smsTemplateEnum) { String flag = VerifyCodeUntil.getVerifyCodes(6); if (redisUtil.set(teleNumber + "_verifyCodesTest", flag, 300)) { return tencentSmsUtil.sendMessage(teleNumber, smsTemplateEnum.getTemplateID(), flag); } return false; }
|
其中VerifyCodeUntil
为我自己的工具包,用来生成6位存数字验证码:
1 2 3 4 5 6 7 8 9 10 11
| public class VerifyCodeUntil { private final static String sources = "5678012349"; public static String getVerifyCodes(int longSize){ Random rand = new Random(); StringBuffer flag = new StringBuffer(); for (int j = 0; j < 6; j++) { flag.append(sources.charAt(rand.nextInt(9)) + ""); } return String.valueOf(flag); } }
|
这样,就可以业务层发送测试代码了。不过,我的Redis还没完善,暂时使用这样的形式。
Controller层
之后,写个Controller测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
@GetMapping("/getVerifyCodesByTelephone") public Result getVerifyCodesByTelephone( @RequestParam(value = "telephone") String telephone) { User user = userService.getOne(new LambdaQueryWrapper<User>() .eq(User::getUserTele,telephone)); if (Objects.isNull(user)) { return Result.fail(); } if (userService.sendSMS(telephone,SmsTemplateEnum.LOGIN)){ return Result.ok(); } return Result.fail(); }
@PostMapping("/VerifyCodesByTelephone") public Result VerifyCodesByTelephone( @RequestParam(value = "telephone") String telephone, @RequestParam(value = "verifyCodes") String verifyCodes) { Object redisCodes = redisUtil.get(telephone + "_verifyCodesTest"); if (redisCodes == null) { return Result.fail(); } if (redisCodes.equals(verifyCodes)) { return Result.ok(userService.loginByTelephone(telephone)); } else { return Result.fail(); } }
|
其中:
Result
:我自定义的返回结果,你可以理解为ResponseEntity。
API测试
之后,使用PAW或者Postman进行测试:
测试成功:
之后就是前端编写了。比如我的前端:
有点丑,后续再完善一下。
END
到此,我们的Springboot整合短信登录的大体框架就完成了。大家可以更具需要,补全业务结构。
另外,我是没有使用Spring Security,按道理使用Spring Security整合SMS会更好,有机会和大家介绍。