app实现阿里云手机号一键登录最佳实践

4,580 阅读5分钟

该篇blog主要记录博主实现app手机号一键登录功能,集成的是阿里云号码认证服务,主要简单记录一下实现的过程,先附上实现最后app一键登录的效果:

一键登录.png

在记录一键登录实现逻辑之前,你应该了解一下阿里的认证方案一键登录本机号校验:

前提条件

整体实现流程

步骤一:开通号码认证服务

  1. 访问号码认证服务产品详情页
  2. 单击立即开通或访问产品控制台。
  3. 进入控制台首页,勾选我已阅读并同意《号码认证服务协议》。
  4. 单击立即开通,完成产品开通。开通号码认证-框

步骤二:添加认证方案

具体操作,请参见 认证方案管理

认证方案管理

认证方案用于标识App下的认证场景,一般一个认证方案对应一个App包名/包签名或者BundleId。系统调用过程中需要使用对应的方案Code。本文为您介绍如何添加认证方案、修改相关配置如认证方式,以及删除认证方案。

  1. 登录号码认证产品控制台

  2. 在左侧导航栏上,选择号码认证服务 > 认证方案管理。

  3. 根据相应功能填写信息,添加认证方案。

    • 一键登录和本机号码校验、活体认证、短信认证:

      1. 选择iOS或Android页签,再单击+添加认证方案。认证方案管理

      2. 填写方案名称、App名称等信息。Android操作系统需要填写应用包名及包签名,iOS操作系统需要填写BundleID。

        短信认证功能还需在认证方式一栏勾选短信验证码,绑定对应签名。建议使用您的App名称作为签名,提高签名审核通过率。若需要添加新的签名,可单击创建签名。若没有可用的短信签名,可绑定赠送的签名进行测试,待正式签名审核通过后再修改绑定签名,详情请参见修改方案配置

        **

        说明 方案名称和App名称建议输入实际上线的App名称。

        多个功能认证方案-框

代码逻辑实现

pom依赖

<dependency> 
    <groupId>com.aliyun</groupId> 
    <artifactId>dypnsapi20170525</artifactId> 
    <version>1.0.2</version> 
</dependency>

核心接口

package org.jeecg.modules.base.service;

import com.aliyun.dypnsapi20170525.models.GetMobileResponse;
import com.aliyun.dypnsapi20170525.models.VerifyMobileResponse;

import javax.servlet.http.HttpServletRequest;

/**
 * 开放API接口 服务类
 *
 * @author: jacklin
 * @since: 2022/03/28 9:38
 **/
public interface IOpenAPIService {

    /**
     * 调用GetMobile完成一键登录取号
     *
     * @param accessToken APP端SDK获取的登录token,必填
     * @param outId       外部流水号,非必填
     * @author: jacklin
     * @since: 2021/4/17 9:41
     **/
    GetMobileResponse getMobile(String accessToken, String outId, HttpServletRequest request);

    /**
     * 调用verifyMobile完成本机号码校验认证
     *
     * @param accessCode  APP端SDK获取的登录token,必填
     * @param phoneNumber 手机号,必填
     * @param outId       外部流水号,非必填
     * @author: jacklin
     * @since: 2021/4/17 11:18
     **/
    VerifyMobileResponse verifyMobile(String accessCode, String phoneNumber, String outId, HttpServletRequest request);

    /**
     * 根据键名查询参数配置信息
     *
     * @param configKey 参数键名
     * @return configValue  参数键值
     * @author: jacklin
     * @since: 2022/1/15 15:43
     **/
    String selectConfigValueByKey(String configKey, HttpServletRequest request);

}

接口实现类

package org.jeecg.modules.base.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.aliyun.dypnsapi20170525.models.GetMobileRequest;
import com.aliyun.dypnsapi20170525.models.GetMobileResponse;
import com.aliyun.dypnsapi20170525.models.VerifyMobileRequest;
import com.aliyun.dypnsapi20170525.models.VerifyMobileResponse;
import com.aliyun.teaopenapi.models.Config;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.api.dto.SystemConfigDTO;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.global.GlobalWebSiteValue;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.modules.base.mapper.BaseCommonMapper;
import org.jeecg.modules.base.service.IOpenAPIService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * 开放API接口 服务实现类
 *
 * @author: jacklin
 * @since: 2021/03/29 9:39
 **/
@Slf4j
@Service
public class OpenAPIServiceImpl implements IOpenAPIService {

    @Autowired
    private RedisUtil redisUtil;
    @Resource
    BaseCommonMapper baseCommonMapper;

    /**
     * 使用AK&SK初始化账号Client
     **/
    public static com.aliyun.dypnsapi20170525.Client createClient(String accessKeyId, String accessKeySecret) throws Exception {
        Config config = new Config()
                .setAccessKeyId(accessKeyId)
                .setAccessKeySecret(accessKeySecret);
        // 访问的域名
        config.endpoint = "dypnsapi.aliyuncs.com";
        return new com.aliyun.dypnsapi20170525.Client(config);
    }


    /**
     * 响应内容格式:
     * {
     * "Message": "请求成功",
     * "RequestId": 8906582,
     * "Code": "OK",
     * "GetMobileResultDTO": {
     * "Mobile": 121343241
     * }
     * }
     **/
    @Override
    public GetMobileResponse getMobile(String accessToken, String outId, HttpServletRequest request) {
        com.aliyun.dypnsapi20170525.Client client = null;
        GetMobileResponse response = new GetMobileResponse();
        try {

            //从数据库查询获取短信全局配置数据,存在从缓存里取
            String accessKeyId = this.selectConfigValueByKey(CommonConstant.SMS_ACCESS_KEY_ID, request);
            String accessKeySecret = this.selectConfigValueByKey(CommonConstant.SMS_ACCESS_KEY_SECRET, request);
            if (StringUtils.isNoneEmpty(accessKeyId, accessKeySecret)) {
                client = createClient(accessKeyId, accessKeySecret);
                GetMobileRequest mobileRequest = new GetMobileRequest();
                mobileRequest.setAccessToken(accessToken);
                mobileRequest.setOutId(outId);
                response = client.getMobile(mobileRequest);
            } else {
                log.error("阿里云号码认证(一键登录)失败,获取系统配置秘钥为空!");
            }
        } catch (Exception e) {
            log.error(response.body.getMessage());
        }
        return response;
    }


    /**
     * 响应格式:
     * {
     * "Message": "请求成功",
     * "RequestId": 8906582,
     * "Code": "OK"
     * "GateVerifyResultDTO": {
     * "VerifyResult": "PASS",
     * "VerifyId": 121343241
     * }
     * }
     **/
    @Override
    public VerifyMobileResponse verifyMobile(String accessCode, String phoneNumber, String outId, HttpServletRequest request) {
        com.aliyun.dypnsapi20170525.Client client = null;
        VerifyMobileResponse verifyMobileResponse = new VerifyMobileResponse();
        try {

            //从数据库查询获取短信全局配置数据,存在从缓存里取
            String accessKeyId = this.selectConfigValueByKey(CommonConstant.SMS_ACCESS_KEY_ID, request);
            String accessKeySecret = this.selectConfigValueByKey(CommonConstant.SMS_ACCESS_KEY_SECRET, request);
            if (StringUtils.isNoneEmpty(accessKeyId, accessKeySecret)) {
                client = createClient(accessKeyId, accessKeySecret);
                VerifyMobileRequest verifyMobileRequest = new VerifyMobileRequest();
                verifyMobileRequest.setAccessCode(accessCode);
                verifyMobileRequest.setPhoneNumber(phoneNumber);
                verifyMobileRequest.setOutId(outId);
                verifyMobileResponse = client.verifyMobile(verifyMobileRequest);
            } else {
                log.error("本机号码校验认证失败,获取系统配置短信秘钥信息为空");
            }

        } catch (Exception e) {
            log.error(verifyMobileResponse.body.getMessage());
        }

        return verifyMobileResponse;
    }

    @Override
    public String selectConfigValueByKey(String configKey, HttpServletRequest request) {
        Integer tenantId = GlobalWebSiteValue.getTenantId(request);
        String cacheConfigKey = "TENANT_ID_" + tenantId + "_" + configKey;
        //如果缓存存在走缓存取
        String configValue = Convert.toStr(redisUtil.get(cacheConfigKey));
        if (StringUtils.isNotBlank(configValue)) {
            //log.info("短信秘钥缓存存在,从缓存获取~");
            return configValue;
        }
        SystemConfigDTO systemConfigDTO = baseCommonMapper.selectSystemConfigByConfigKey(configKey);
        if (ObjectUtil.isNotNull(systemConfigDTO)) {
            //入缓存
            redisUtil.set(cacheConfigKey, systemConfigDTO.getConfigValue());
            return systemConfigDTO.getConfigValue();
        }
        return StringUtils.EMPTY;
    }

}

只需要在你的登录接口调用IOpenAPIService.getMobile()方法,比如:

GetMobileResponse response = openApiService.getMobile(loginToken, "", request);

到这步实际上就已经实现一键登录的整个过程了。