持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情
一键登录
什么是一键登录
现在很多手机 APP 都已经提供了一键登录的功能,相比验证码登录,少了输入验证码的一步,对用户来说更加方便快捷。
一键登录能不能做,取决于运营商是否开放相关服务,这也是为什么过去没有开发出一键登录功能,直到最近三大运营商都有了自己的开放平台:
要使用一键登录,需要接入运营商的 SDK。
一键登录的流程
整个流程:SDK初始化 -> 唤起授权页面 -> 同意授权并登录 -> 取号(获取手机号码) -> 登录成功
三大运营商使用了同一套授权流程:
主要步骤如下:
- SDK 初始化
调用 SDK 的初始化方法,传入项目在平台上的 AppKey 和 AppSecret。
- 唤起授权页
调用 SDK 唤起授权接口。SDK 会先向运营商发起获取手机号掩码的请求,请求成功后跳转到授权页。授权页会显示手机号掩码以及运营商协议给用户确认。
- 同意授权并登录
用户同意相关协议,点击授权页面的登录按钮,SDK 会请求本次取号的 token,请求成功后将 token 返回给客户端。
- 取号
将获取到的 token 发送到我们自己的服务器,由服务器携带 token 调用运营商一键登录的接口,调用成功就返回手机号码了。服务器用手机号进行登录或注册操作,返回操作结果给客户端,完成一键登录。
项目中添加一键登录时,可以使用如下流程:
注意事项
(1)授权页
授权页是 SDK 里定义的页面,以安卓为例,接入 SDK 后我们需要在 AndroidManifest 里注册好这个页面。我们不能也不应该跳过授权页面,未经用户允许就获取用户手机号。
由于授权页是第三方的页面,我们无法自由修改页面样式,但 SDK 里提供了足够丰富的接口给我们自定义样式。
在授权页显示之前需要先请求到手机号掩码,用于在授权页展示。为了增加授权页的跳转速度,减少用户等待的时间,通常 SDK 会提供一个预取号的接口给我们,这个接口取的就是手机号掩码。我们可以在打开 APP 的时候就调用这个接口,在已经取得掩码的情况下,用户点击跳转授权页面就不需要再等待了。算是对用户体验的一个小优化。
(2)网络
在认证过程中,会把网络切换为移动蜂窝网络,目前支持的制式有中国移动 2G/3G/4G、中国联通 3G/4G、中国电信 4G。当然最好是使用 4G,使用 2G、3G 会降低认证的成功率。
在没有插电话卡,或者关闭移动蜂窝网络的情况下,是无法完成认证的。所以就算接入了一键登录,我们也要兼容传统的登录方式,允许用户在认证失败的情况下,手动输入手机号登录。
(3)兼容三大运营商
三家运营商都拥有自己的 SDK,但各自的 SDK 不一定支持别家的认证。这导致目前许多接入了一键登录的产品,只支持某一个运营商。
如果要兼容三大运营商,就需要分别接入三个 SDK。所以一般会选择接入一个整合了三大运营商认证能力的第三方 SDK,目前主要有以下几家,原理和使用方法基本都是一样的,具体大家可以查看它们的文档。
(4)关于计费
认证一次的价格集中在 4 到 6 分钱不等。一般是成功置换到手机号算一次计费,调用预取号接口和认证失败,都是不计费的。
总的来说,一键登录和发送验证短信的价格差不多。发送短信是发送一条就计费一次,但用户存在需要多次获取验证码才能登录成功的情况。而一键登录能置换到手机号,基本就意味着登录成功了。所以从次数上考虑,接入一键登录应该还能稍微省一点。
SpringBoot 接入阿里云一键登录
首先需要 app 端拿到认证的 Token ,然后把这个 token 发给后端,我们需要做的是,使用这个 token 去发送请求,获取实际手机号,获取到手机号后,进行登录 / 注册的流程,然后系统生成用户 token,发给 app 端,这样就登录成功了。
1.引入依赖
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.4.4</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dypnsapi</artifactId>
<version>1.0.4</version>
</dependency>
2.后端接口中获取手机号码:
@RequestMapping(value = "oneClickLogin", method = RequestMethod.POST)
public Result oneClickLogin(@RequestParam(name = "token", required = true) String token,
@RequestParam(name = "platform", required = true) Integer platform,
@RequestParam(name = "deviceId", required = false) String deviceId,
HttpServletRequest request,HttpServletResponse response) {
if (StringUtils.isBlank(token) || platform == null) {
return Result.error(5000, "一键登录失败,缺少必要参数");
}
DefaultProfile profile = DefaultProfile.getProfile(REGIONID, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
IAcsClient client = new DefaultAcsClient(profile);
GetMobileRequest getMobileRequest = new GetMobileRequest();
getMobileRequest.setSysRegionId(REGIONID);
getMobileRequest.setAccessToken(token);
getMobileRequest.setSysConnectTimeout(5000);// 设置连接超时为5000毫秒
getMobileRequest.setSysReadTimeout(5000);// 设置读超时为5000毫秒
Dto dto = new BaseDto();
try {
GetMobileResponse getMobileResponse = client.getAcsResponse(getMobileRequest);
logger.debug("getMobileResponse=" + getMobileResponse);
if (getMobileResponse == null) {
return Result.error(5000, "一键登录失败");
}
logger.debug("getMobileResponse=" + new Gson().toJson(getMobileResponse));
String code = getMobileResponse.getCode();
if ("OK".equals(code)) { // 获取成功
GetMobileResultDTO mobileRequest = getMobileResponse.getGetMobileResultDTO();
String phone = mobileRequest.getMobile();
logger.debug("获取手机号:" + phone);
// 拿到手机号就可以进行后续的业务处理了,比如说用户已经注册过,直接登陆,没注册则走注册流程。
} else {
return Result.error(5000, "一键登录失败");
}
} catch (ClientException e) {
logger.error("一键获取号码失败:token:{},error:{}", token, e.getMessage());
return Result.error(5000, "一键登录失败");
}
}
参考: