闪验、创蓝闪验

概述


当前版本:2.2.0.7

发版时间:2019年09月26

文档最后更新时间:2019年09月26日


常见问题请移步至对接问题

崩溃问题请移步至崩溃问题

版本更新:所有版本通用方式——替换SDK静态库, 删除旧版本SDK所有相关的`.framework``.bundle`文件,清除缓存,再导入新版SDK中的所有`.framework``.bundle`文件(.bundle文件注意保留开发者自定义资源),详细说明请移步至「升级指南

如需SDK及相关文档下载请移步至SDK资源下载


一.准备工作


前置条件


  • 闪验SDK支持Xcode 9.4.1,iOS8.0+及以上版本。
  • 闪验SDK支持中国移动、联通、电信4G的取号能力。
  • 闪验SDK支持网络环境为

a.纯数据网络

b.数据网络与wifi网络双开

  • 对于双卡手机,闪验SDK取当前流量卡号


创建应用


提示:一个应用对应一个appid,多个应用(不同bundleID)需在闪验平台创建多个应用以对应多个appid

应用的创建流程及APPID/APPKEY的获取,请查看「账号创建」文档


快速体验Demo

将创建应用时获得的AppID、AppKey填入Demo工程中.pch文件,修改工程BundleID为创建应用时绑定的BundleID即可


开发环境搭建

闪验SDK提供两种集成方式供iOS开发者选择:

  • 通过CocoaPods自动集成
  • 手动集成


通过CocoaPods自动集成

在工程的Podfile里面添加以下代码:

#以下三种版本选择方式示例

#集成最新版闪验SDK:
pod 'CL_ShanYanSDK'

#集成指定版本闪验SDK:
pod 'CL_ShanYanSDK', '2.2.0.5'

#集成指定版本闪验SDK,并在末位小版本范围更新:
pod 'CL_ShanYanSDK', '~> 2.2.0.5'

保存并执行pod install,然后用后缀为.xcworkspace的文件打开工程。

注意:

命令行下执行pod search CL_ShanYanSDK,如显版本不是最新版,或者pod install导入的版本不是最新版,则先执行pod repo update操作更新本地repo的内容

关于CocoaPods的更多信息请查看 CocoaPods官方网站


手动集成

  1. 导入framework: 将闪验SDK压缩包中framework文件夹下所有资源添加到工程中(注意勾选Copy items if needed)

导入资源文件1.jpg


  1. Xcode配置:


  • OtherLinkerFlags中 添加**-ObjC**:xcode->BuildSetting->Other Linker Flags 添加 **-ObjC**

otherLink-ObjC.jpg


  • 添加libc++.1.tbd: 在xcode->General->Linked Frameworks and Libraries中点击 **+** ,搜索并选择添加 **libc++.1.tbd**

libc++.1.tbd.jpg


  1.  Swift工程需要额外添加-force_load:
  • 在xcode->BuildSetting->Other Linker Flags 添加-force_load

swift-force_load.jpg


  • 在-force_load下方添加CL_ShanYanSDK.framework/CL_ShanYanSDK所在路径,具体操作可以将CL_ShanYanSDK.framework拖入空栏,在尾部拼接静态库名称CL_ShanYanSDK,将前缀绝对地址改成相对地址$(SRCROOT),最终为 "$(SRCROOT)/.../CL_ShanYanSDK.framework/CL_ShanYanSDK"形式

swift-sdk-path.jpg



二.SDK使用说明


1.初始化


方法原型


/**初始化*/
+(void)initWithAppId:(NSString *)appId AppKey:(NSString *)appKey complete:(nullable CLComplete)complete;


参数描述

参数

是否必填

类型

说明

appId 

必填

NSString

闪验appID

appKey 

必填

NSString

闪验appKey

complete 

选填

CLComplete

初始化回调block,可以在此回调block中接收初始化情况,也可以不关心初始化结果

接口作用


初始化SDK :传入用户的appID、appKey,获取本机运营商,读取缓存,获取运营商配置,初始化SDK


使用场景


  • 建议在app启动时调用
  • 必须在一键登录前至少调用一次
  • 只需调用一次,多次调用不会多次初始化,与一次调用效果一致


请求示例代码


ObjC:

  1. 导入闪验SDK头文件 #import <CL_ShanYanSDK/CL_ShanYanSDK.h>
  2. 在AppDelegate中的 didFinishLaunchingWithOptions方法中添加初始化代码
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    ...
     //初始化
     [CLShanYanSDKManager initWithAppId:cl_SDK_APPID AppKey:cl_SDK_APPKEY complete:nil];
    ...
}


Swift:

  1. 创建混编桥接头文件并导入闪验SDK头文件 #import <CL_ShanYanSDK/CL_ShanYanSDK.h>
  2. 在AppDelegate中的 didFinishLaunchingWithOptions方法中添加初始化代码


func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // 建议先检测APP登录状态,未登录再使用闪验
   ...
   //初始化        
   CLShanYanSDKManager.initWithAppId("your appID", appKey: "your appKey")
   ...
}


2.预取号


不建议频繁的多次调用和在拉起授权页后调用


方法原型


/**
 * 预取号(获取临时凭证)
 * 建议在判断当前用户属于未登录状态时使用,已登录状态用户请不要调用该方法
 */
+(void)preGetPhonenumber:(nullable CLComplete)complete;


接口作用


电信、联通、移动预取号 :初始化成功后,如果当前为电信/联通/移动,将调用预取号,可以提前获知当前用户的手机网络环境是否符合一键登录的使用条件,成功后将得到用于一键登录使用的临时凭证,默认的凭证有效期60s(电信)/30min(联通)/60min(移动)。


使用场景


  • 建议在执行一键登录的方法前,提前一段时间调用此方法,比如调一键登录的vc的viewdidload中,或者rootVC的viewdidload中,或者app启动后,此调用将有助于提高闪验拉起授权页的速度和成功率
  • 不建议调用后立即调用拉起授权页方法(此方法是异步)
  • 此方法需要1~2s的时间取得临时凭证,因此也不建议和拉起授权页方法一起串行调用
  • 不建议频繁的多次调用和在拉起授权页后调用
  • 建议在判断当前用户属于未登录状态时使用,已登录状态用户请不要调用该方法


请求示例代码


ObjC:


#import <CL_ShanYanSDK/CL_ShanYanSDK.h>
//开发者调拉起授权页的vc
@implementation CustomLoginViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    if (YourAppLoginStatus == NO) {
        //预取号
        [CLShanYanSDKManager preGetPhonenumber:nil];
        ...
    }
}
...
//拉起授权页
- (void)shanYanAuthPageLogin{
    ...
}


3.拉起授权页


在预取号成功后调用预取号失败不建议调用。调用拉起授权页方法后将会调起运营商授权页面。该方法会拉起登录界面,已登录状态请勿调用 。


//闪验一键登录接口
+(void)quickAuthLoginWithConfigure:(CLUIConfigure *)clUIConfigure
           openLoginAuthListener:(CLComplete)openLoginAuthListener
                          oneKeyLoginListener:(CLComplete)oneKeyLoginListener;


参数描述

参数

类型

说明

clUIConfigure  必填

CLUIConfigure

授权页控件属性配置对象

openLoginAuthListener  选填

CLComplete

拉起授权页的回调,拉起页面成功失败均触发

注意:在此回调中用户UI操作必须手动切换到主线程

oneKeyLoginListener 必填

CLComplete

一键登录回调,用于接收一键登录的结果,点一键登录成功失败均触发,点自带的返回按钮也触发

使用场景

  • 用户进行一键登录操作时,调用一键登录方法,如果初始化成功,SDK将会拉起授权页面,用户授权后,SDK将返回取号 token给到应用客户端。
  • 可以在多处调用
  • 需在调用预初始化方法之后调用


一键登录逻辑说明

  • 存在调用预初始化时获取的临时凭证,调用一键登录方法将立即拉起授权页面
  • openLoginAuthListener 拉起授权页监听回调,拉起成功或失败均触发
  • 不存在临时凭证或临时凭证过期时(临时凭证有效期电信60s、联通30min、移动60min),调用一键登录方法,将有一个很短的时延,待取号成功后拉起授权页面
  • 取号失败时,返回失败 


请求示例代码


ObjC:

  1. 导入闪验SDK头文件 #import <CL_ShanYanSDK/CL_ShanYanSDK.h>
  2. 在需要使用一键登录的地方调用闪验一键登录接口
// 用户需要使用闪验一键登录时的方法
- (void)quickLoginBtnClick:(UIButton *)sender {

    __weak typeof(self) weakself = self;

    CGFloat screenScale = [UIScreen mainScreen].bounds.size.width/375.0;

    CLUIConfigure * baseUIConfigure = [CLUIConfigure new];
    baseUIConfigure.viewController = self;
    baseUIConfigure.clLogoImage = [UIImage imageNamed:@"your_app_logo_image"];
    
    //layout 布局
    CLOrientationLayOut * clOrientationLayOutPortrait = [CLOrientationLayOut new];
    clOrientationLayOutPortrait.clLayoutPhoneCenterY = @(0*screenScale);
    clOrientationLayOutPortrait.clLayoutPhoneLeft = @(50*screenScale);
    ...
    baseUIConfigure.clOrientationLayOutPortrait = clOrientationLayOutPortrait;
    
    //开发者自己的loading(注意后面loading的隐藏时机)
    [SVProgressHUD setContainerView:self.view];
    [SVProgressHUD show];

    //闪验一键登录接口(将拉起授权页)
    [CLShanYanSDKManager quickAuthLoginWithConfigure:baseUIConfigure openLoginAuthListener:^(CLCompleteResult * _Nonnull completeResult) {
            注意:在此回调中用户UI操作必须手动切换到主线程
            [SVProgressHUD hide];
            if (completeResult.error) {
                //拉起授权页失败
                NSLog(@"openLoginAuthListener:%@",completeResult.error.userInfo);
            }else{
               //拉起授权页成功
                NSLog(@"openLoginAuthListener:%@",completeResult.yy_modelToJSONObject);
            }
        } oneKeyLoginListener:^(CLCompleteResult * _Nonnull completeResult) {
            __strong typeof(self) strongSelf = weakSelf;
            
            [SVProgressHUD hide];

            if (completeResult.error) {
                //一键登录失败
                NSLog(@"oneKeyLoginListener:%@",completeResult.error.description);
                
                //提示:错误无需提示给用户,可以在用户无感知的状态下直接切换登录方式
                if (completeResult.code == 1011){
                    //用户取消登录(点返回)
                    //处理建议:如无特殊需求可不做处理,仅作为交互状态回调,此时已经回到当前用户自己的页面
                    //点击sdk自带的返回,无论是否设置手动销毁,授权页面都会强制关闭
                }  else{
                    //处理建议:其他错误代码表示闪验通道无法继续,可以统一走开发者自己的其他登录方式,也可以对不同的错误单独处理
                    //1003    一键登录获取token失败
                    //1008    未开启移动网络
                    //1009    未检测到sim卡
                    //其他     其他错误//
                    
                    //关闭授权页,如果授权页还未拉起,此调用不会关闭当前用户vc,即不执行任何代码
                    [CLShanYanSDKManager finishAuthControllerCompletion:nil];
                    
                }
            }else{
                //一键登录获取Token成功
                NSLog(@"oneKeyLoginListener:%@",completeResult.yy_modelDescription);
                
                //SDK成功获取到Token
                /** token置换手机号
                code
                */
            }
        }];
 }



一键登录 quickAuthLoginWith:complete:正确返回示例


completeResult->_data:
{
    accessToken = 2ebf48fbe75a45449ed78ecca9c5d93c;
    appId = xxxxxx;
    device = ZGV2aWNlPWlQaG9uZTd8aXA9MTcyLjE2LjExLjZ8RElEPTBkOGY2NzViZjdkYjRjZDdmMWI1YmM5NDI3NWIyZTQ0fHV1aWQ9MzBjZGU2ZTktYzZmOS00MGNhLTk4MjEtMzI4NGM0ZDViZDNj;
    randoms = "321bfcdb-bb34-4e3e-bbf7-def83d33bd76";
    sign = "sNeHtWihz7B4iTY1Fch5+ztllDA=";
    telecom = CTCC;
    timestamp = 1558771442361;
    version = "2.2.0";
}


字段

类型

含义

appId

String

当前项目的appid

accessToken

String

token,置换令牌,用来和后台置换手机号。一次有效,有效期3min

telecom

String

当前数据流量卡的运营商类型:

CMCC 移动

CTCC  电信

CUCC  联通

timestamp

String

网络时间

randoms

String

随机数

version

String

后台接口版本号

sign

String

签名

device

String

设备型号

ps:请注意返回字段中的accessToken、sign、device字段值为base64字符串,其中包含特殊字符,在客户APP与客户后台交互时应避免使用URL拼接字符串的传值方式,会导致特殊字符丢失,最终导致请求闪验后台的置换手机号接口报“请求非法,签名校验不通过”。


一键登录 quickAuthLoginWith:complete:报错处理


completeResult->_error:

  • 用户取消登录(授权页点击返回) 【处理建议:若无特殊需求可不做处理】
  • 用户选择其他方式登录(点击授权页自带的其他方式登录): 【处理建议:可根据实际情况跳转其他登录方式 】
  • 其他错误 【处理建议:使用闪验通道失败,可根据实际情况跳转其他登录方式 】


授权页销毁


注:sdk拉起授权页成功后,只允许点击一次一键登录按钮。一键登录按钮点击一次后,只有sdk自带的左上角返回按钮可以交互,效果为强制关闭授权页,其他页面元素均会被禁用,以防止多次点击导致多次回调而出现异常。


拉起授权页前 配置授权页面销毁机制属性

// 获取默认参数配置
CLUIConfigure * baseUIConfigure = [CLUIConfigure clDefaultUIConfigure];
baseUIConfigure.viewController = self;
// 是否需要手动销毁授权页面,默认自动销毁, YES->手动销毁
baseUIConfigure.manualDismiss = @(YES);

sdk获取token回调后销毁界面

//关闭页面
[CLShanYanSDKManager finishAuthControllerCompletion:^{
   //如需关闭后present/push新页面,建议在completion回调中执行
    //注:若未拉起授权页,调用此方法,block不会触发
    //用户跳转短信验证
    CustomSmsViewController * smsVc = [[CustomSmsViewController alloc]init];
    CustomNavigationController * smsNav = [[CustomNavigationController alloc]initWithRootViewController:smsVc];
    smsVc.navigationItem.title = @"短信验证";
    [self presentViewController:smsNav animated:YES completion:nil];
}];




4.置换手机号


注:移动、联通、电信分别有自己的置换手机号url,需要根据相对应的运营商url置换手机号码。详情见下方 请求示例代码


使用场景


SDK成功返回token后,开发者需自行调用置换手机号接口,此接口需要开发者调用由服务端接入闪验服务端生成的接口,上线前的调试阶段可以直接使用示例代码中的接口


请求示例代码


ObjC:

[CLShanYanSDKManager quickAuthLoginWithConfigure:baseUIConfigure openLoginAuthListener:^(CLCompleteResult * _Nonnull completeResult) {
    [SVProgressHUD hide];
    if (completeResult.error) {
        //拉起授权页失败
        NSLog(@"openLoginAuthListener:%@",completeResult.error.userInfo);
    }else{
       //拉起授权页成功
        NSLog(@"openLoginAuthListener:%@",completeResult.yy_modelToJSONObject);
    }
} oneKeyLoginListener:^(CLCompleteResult * _Nonnull completeResult) {
    [SVProgressHUD dismiss];
    if (completeResult.error) {
        ...
    }else{
        //SDK成功获取到Token
      
        NSString * telecom = [completeResult.data valueForKey:@"telecom"];
        //urlStr:用户后台对接闪验后台后配置的API,以下为Demo提供的调试API及调用示例,在调试阶段可暂时调用此API,也可用此API验证后台API是否正确配置
        NSString * urlStr = nil;
        if ([telecom isEqualToString:@"CMCC"]) {
          urlStr = [cl_SDK_URL stringByAppendingString:@"open/flashsdk/mobile-query-m"];
        }else if ([telecom isEqualToString:@"CUCC"]) {
          urlStr = [cl_SDK_URL stringByAppendingString:@"open/flashsdk/mobile-query-u"];
        }else if ([telecom isEqualToString:@"CTCC"]) {
          urlStr = [cl_SDK_URL stringByAppendingString:@"open/flashsdk/mobile-query-t"];
        }else{
          //失败
          return;
        }

        if (urlStr) {
          NSLog(@"tokenParamr:%@",completeResult.data);

          [NetWorkManager POST_url:urlStr withParameter:completeResult.data complete:^(NSDictionary * _Nonnull responseObject, NSError * _Nonnull error) {
              if (error) {
                  NSLog(@"%@",error);
              }else{
                  NSInteger code = [[responseObject valueForKey:@"code"] integerValue];
                  if (code == 200000) {
                      NSString * mobileName = responseObject[@"data"][@"mobileName"];
                      NSString * mobileCode = [mobileName decryptUseDESKey:cl_SDK_APPKEY];
                      dispatch_async(dispatch_get_main_queue(), ^{
                          [SVProgressHUD showSuccessWithStatus:[NSString stringWithFormat:@"免密登录成功,手机号:%@",mobileCode]];
                      });
                      NSLog(@"免密登录成功,手机号:%@",mobileCode);
                  } else {
                      dispatch_async(dispatch_get_main_queue(), ^{
                          [SVProgressHUD showSuccessWithStatus:[NSString stringWithFormat:@"免密登录失败"]];
                      });
                      NSLog(@"免密登录失败:%@",responseObject);
                  }
              }
          }];
        }
    }
}];


Swift:

CLShanYanSDKManager.quickAuthLogin(with: clUIConfigure, openLoginAuthListener: { (completeResult) in
    ...
}) { (completeResult) in 

    if completeResult.error != nil {
        ...
    }else{
      //SDK成功获取到Token

        NSLog("quickAuthLogin Success:%@",completeResult.data ?? "")

        //urlStr:用户后台对接闪验后台后配置的API,以下为Demo提供的调试API及调用示例,在调试阶段可暂时调用此API,也可用此API验证后台API是否正确配置
        var urlStr : String?
        let APIString = "https://api.253.com/"

        if let telecom = completeResult.data?["telecom"] as! String?{
            switch telecom {
            case "CMCC":
                urlStr = APIString.appendingFormat("open/flashsdk/mobile-query-m")
                break
            case "CUCC":
                urlStr = APIString.appendingFormat("open/flashsdk/mobile-query-u")
                break
            case "CTCC":
                urlStr = APIString.appendingFormat("open/flashsdk/mobile-query-t")
                break
            default:
                break
            }
        }

        if let urlStr = urlStr{
            let dataDict = completeResult.data as! Parameters

            Alamofire.request(urlStr, method:.post, parameters:dataDict, encoding:URLEncoding.default, headers:[:]).responseJSON(completionHandler: { (response) in
                if response.result.isSuccess {
                    if let json = response.result.value{
                        let jsonDict = JSON(json)
                        if jsonDict["code"].intValue == 200000{
                            let mobileName = jsonDict["data"]["mobileName"].stringValue
                            let mobileCode = StringDecryptUseDES.decryptUseDESString(mobileName, key: "tDo3Ml2K")//appKey
                            DispatchQueue.main.async(execute: {
                                SVProgressHUD.showSuccess(withStatus: ("免密登录成功,手机号:\(mobileCode)"))
                            })
                            print(("免密登录成功,手机号:\(mobileCode)"))
                            return;
                        }
                    }
                }
                DispatchQueue.main.async(execute: {
                    SVProgressHUD.showInfo(withStatus: ("免密登录失败:\(response.description)"))
                    print(("免密登录失败:\(response.description)"))
                })
            })
        }
    }
  }
}


token置换手机号 正确返回示例

{
    "chargeStatus": 1,
    "message": "成功",
    "data": {
      "fanqizha": "1",
        "tag": "",
        "tradeNo": "18112115031414011",
        "mobileName": "OaLj8kkXPwFGp/eRk+3vQQ\u003d\u003d"
    },
    "code": "200000"
}



字段

类型

含义

code

string

200000成功,其他失败

chargeStatus

string

1:收费  0:不收费

tradeNo

string

交易流水号

mobileName

string

DES加密的真实手机号,使用CBC模式,PKCS5Padding填充原则。CBC模式对应的iv偏移向量为:byte[] iv = { 1, 2, 3, 4, 5, 6, 7, 8 },key为appKey

fanqizha/tag

string

防羊毛识别标识,此功能需要在后台开通

fanqizha:int 1:白名单 2:黑名单 3:可信任度低 4:未找到

tag:标签属性,判断的依据信息,用于黑名单。样例:批量操作/系统环境异常/信用低/小号养号/号码风险模型评分中/可能存在作弊行为 

5.手动关闭授权页

当开发者设置点击一键登录或者自定义控件不自动销毁授权页时,将需要自行调用此方法主动销毁授权页,建议在置换手机号成功后销毁。如在得到回调后未销毁授权页而,使用拉起授权页方法再次拉起授权页,此页面将无法响应任何按键(除了导航栏的返回按钮)。


  • 关闭授权页时机

      a.SDK拉起授权页方法 直接回调失败时

      b.置换手机号有返回结果时


  • 当前页面直接销毁
//方式1
[self.PresentedViewController dismissViewControllerAnimated:YES completion:nil];

//方式二
[CLShanYanSDKManager finishAuthControllerCompletion:^{
   //如需关闭后present/push新页面,建议在completion回调中执行
    //注:若未拉起授权页,调用此方法,block不会触发
    //用户跳转短信验证
    CustomSmsViewController * smsVc = [[CustomSmsViewController alloc]init];
    CustomNavigationController * smsNav = [[CustomNavigationController alloc]initWithRootViewController:smsVc];
    smsVc.navigationItem.title = @"短信验证";
    [self presentViewController:smsNav animated:YES completion:nil];
}];


  • 找到topVC进行dismiss
dispatch_async(dispatch_get_main_queue(), ^{
    //建议使用授权页面配置对象传入的viewcontroller 调 dismiss
    if (self.navigationController.viewControllers.lastObject.navigationController) {
        [self.navigationController.viewControllers.lastObject dismissViewControllerAnimated:YES completion:nil];
    } else {
        UIViewController *topRootViewController = [[UIApplication  sharedApplication] keyWindow].rootViewController;
        // 在这里加一个这个样式的循环
        while (topRootViewController.presentedViewController) {
            // 这里固定写法
            topRootViewController = topRootViewController.presentedViewController;
        }
        // 然后再进行present操作
        [topRootViewController dismissViewControllerAnimated:YES completion:nil];
    }
});



三.授权界面修改


设计规范

闪验220授权页规范.jpg开发者不得通过任何技术手段,将授权页面的隐私栏、品牌露出内容隐藏、覆盖,对于接入闪验SDK并上线的应用,我方和运营商会对上线的应用授权页面做审查,如果有出现未按要求设计授权页面,将隐私栏、运营商品牌、授权登录按钮隐去不可见的设计,我方有权将应用的登录功能下线。


页面可调整属性

注:授权页基本控件均支持上、下、左、右、宽、高、水平中心、竖直中心布局设置,布局通过布局对象设置,布局定位更加方便快捷,建议使用最新布局对象进行设置。


//要拉起授权页的vc [必填项] (注:SDK不持有接入方VC)
UIViewController * viewController;

/**
 *外部手动管理关闭界面
 *BOOL,default is NO
 *eg.@(YES)
 */
NSNumber * manualDismiss;

/**授权页-背景图片*/
UIImage *clBackgroundImg;

//导航栏
/**导航栏 是否隐藏 BOOL default is NO, 设置优先级高于clNavigationBackgroundClear eg.@(NO)*/
NSNumber * clNavigationBarHidden;
/**导航栏 背景透明 BOOL eg.@(YES)*/
NSNumber * clNavigationBackgroundClear;
/**导航栏标题*/
NSAttributedString * clNavigationAttributesTitleText;
/**导航栏右侧自定义按钮*/
UIBarButtonItem * clNavigationRightControl;
/**导航栏左侧自定义按钮*/
UIBarButtonItem * clNavigationLeftControl;
/**导航栏左侧返回按钮图片*/
UIImage   * clNavigationBackBtnImage;
/**导航栏左侧自定义按钮隐藏设置,默认显示 BOOL eg.@(YES)*/
NSNumber  * clNavigationBackBtnHidden;
/**导航栏分割线 是否隐藏 default is NO, BOOL eg.@(YES) */
NSNumber * clNavigationBottomLineHidden;
/**导航栏 文字颜色*/
UIColor  * clNavigationTintColor;
/**导航栏 背景色 default is white*/
UIColor  * clNavigationBarTintColor;
/**导航栏 背景图片*/
UIImage  * clNavigationBackgroundImage;
/**导航栏 配合背景图片设置,用来控制在不同状态下导航栏的显示(横竖屏是否显示) UIBarMetrics eg.@(UIBarMetricsCompact)*/
NSNumber * clNavigationBarMetrics;
/**导航栏 导航栏底部分割线(图片)*/
UIImage  * clNavigationShadowImage;
/**
 *NavigationBar.barStyle:默认UIBarStyleBlack
 *Info.plist: View controller-based status bar appearance = YES
 *导航栏隐藏时:
 *状态栏黑白由 statusBarStyle 决定:
 *UIStatusBarStyleLightContent:状态栏显示 黑
 *UIStatusBarStyleLightContent:状态栏显示 白
 *导航栏显示时:
 *状态栏黑白由 navigation_barStyle 决定
 *UIBarStyleDefault:状态栏显示 黑
 *UIBarStyleBlack:状态栏显示 白
 *
 *eg. @(UIBarStyleBlack)
 */
NSNumber * clNavigationBarStyle;

//LOGO图片
/**LOGO图片*/
UIImage  * clLogoImage;
/**LOGO圆角 CGFloat eg.@(2.0)*/
NSNumber * clLogoCornerRadius;
/**LOGO显隐 BOOL eg.@(NO)*/
NSNumber * clLogoHiden;

/**手机号显示控件*/
/**手机号颜色*/
UIColor  * clPhoneNumberColor;
/**手机号字体*/
UIFont   * clPhoneNumberFont;
/**手机号对齐方式 NSTextAlignment eg.@(NSTextAlignmentCenter)*/
NSNumber * clPhoneNumberTextAlignment;

/*一键登录按钮 控件
 注: 一键登录授权按钮 不得隐藏
 **/
/**按钮文字*/
NSString   * clLoginBtnText;
/**按钮文字颜色*/
UIColor  * clLoginBtnTextColor;
/**按钮背景颜色*/
UIColor  * clLoginBtnBgColor;
/**按钮文字字体*/
UIFont   * clLoginBtnTextFont;
/**按钮背景图片*/
UIImage  * clLoginBtnNormalBgImage;
/**按钮边框颜色*/
UIColor  * clLoginBtnBorderColor;
/**按钮偏圆角 CGFloat eg.@(5)*/
NSNumber * clLoginBtnCornerRadius;
/**按钮偏边框 CGFloat eg.@(2.0)*/
NSNumber * clLoginBtnBorderWidth;

/*隐私条款Privacy
 注: 运营商隐私条款 不得隐藏
 用户条款不限制
 **/
/**隐私条款名称颜色:@[基础文字颜色UIColor*,条款颜色UIColor*] eg.@[[UIColor lightGrayColor],[UIColor greenColor]]*/
NSArray<UIColor*> *clAppPrivacyColor;
/**隐私条款文字字体*/
UIFont  * clAppPrivacyTextFont;
/**隐私条款--APP名称简写 默认取CFBundledisplayname*/
NSString  * clAppPrivacyAbbreviatedName;
/**隐私条款文字对齐方式 NSTextAlignment eg.@(NSTextAlignmentCenter)*/
NSNumber * clAppPrivacyTextAlignment;
/**运营商隐私条款书名号 默认NO 不显示 BOOL eg.@(YES)*/
NSNumber * clAppPrivacyPunctuationMarks;
/**隐私条款一:需同时设置Name和UrlString eg.@[@"条款一名称":@"条款一URL"]*/
NSArray<NSString*> * clAppPrivacyFirst;
/**隐私条款二:需同时设置Name和UrlString eg.@[@"条款一名称":@"条款一URL"]*/
NSArray<NSString*> * clAppPrivacySecond;
/**隐私协议WEB页面导航栏标题 NSAttributedString*/
NSAttributedString * clAppPrivacyWebAttributesTitle;
/**隐私协议WEB页面导航返回按钮图片*/
UIImage * clAppPrivacyWebBackBtnImage;
/**多行时行距 CGFloat eg.@(2.0)*/
NSNumber* clAppPrivacyLineSpacing;
/**是否需要sizeToFit,设置后与宽高约束的冲突请自行考虑 BOOL eg.@(YES)*/
NSNumber* clAppPrivacyNeedSizeToFit;

/*SLOGAN
 注: 运营商品牌标签,不得隐藏
 **/
/**slogan文字字体*/
UIFont   * clSloganTextFont;
/**slogan文字颜色*/
UIColor  * clSloganTextColor;
/**slogan文字对齐方式 NSTextAlignment eg.@(NSTextAlignmentCenter)*/
NSNumber * clSlogaTextAlignment;

/*CheckBox
 *协议勾选框,默认选中且在协议前显示
 *可在sdk_oauth.bundle中替换checkBox_unSelected、checkBox_selected图片
 *也可以通过属性设置选中和未选择图片
 **/
/**协议勾选框(默认显示,放置在协议之前)BOOL eg.@(YES)*/
NSNumber *clCheckBoxHidden;
/**协议勾选框默认值(默认不选中)BOOL eg.@(YES)*/
NSNumber *clCheckBoxValue;
/**协议勾选框 尺寸 CGSize eg.[NSValue valueWithCGSize:CGSizeMake(25, 25)]*/
NSValue *clCheckBoxSize;
/**协议勾选框 UIButton.image图片缩进 UIEdgeInset eg.[NSValue valueWithUIEdgeInsets:UIEdgeInsetsMake(2, 2, 2, 2)]*/
NSValue *clCheckBoxImageEdgeInsets;
/**协议勾选框 设置CheckBox顶部与隐私协议控件顶部对齐 YES或大于0生效 eg.@(YES)*/
NSNumber *clCheckBoxVerticalAlignmentToAppPrivacyTop;
/**协议勾选框 设置CheckBox顶部与隐私协议控件竖向中心对齐 YES或大于0生效 eg.@(YES)*/
NSNumber *clCheckBoxVerticalAlignmentToAppPrivacyCenterY;
/**协议勾选框 非选中状态图片*/
UIImage  *clCheckBoxUncheckedImage;
/**协议勾选框 选中状态图片*/
UIImage  *clCheckBoxCheckedImage;

/*Loading*/
/**Loading 大小 CGSize eg.[NSValue valueWithCGSize:CGSizeMake(50, 50)]*/
NSValue *clLoadingSize;
/**Loading 圆角 float eg.@(5) */
NSNumber *clLoadingCornerRadius;
/**Loading 背景色 UIColor eg.[UIColor colorWithRed:0.8 green:0.5 blue:0.8 alpha:0.8]; */
UIColor *clLoadingBackgroundColor;
/**UIActivityIndicatorViewStyle eg.@(UIActivityIndicatorViewStyleWhiteLarge)*/
NSNumber *clLoadingIndicatorStyle;
/**Loading Indicator渲染色 UIColor eg.[UIColor greenColor]; */
UIColor *clLoadingTintColor;
/**授权页自定义Loading
 - containerView为loading的全屏蒙版view
 - 请自行在containerView添加自定义loading
 - 设置block后,上述loading属性将无效
 */
void(^loadingView)(UIView * containerView);

//添加自定义控件
/**可设置背景色及添加控件*/
void(^customAreaView)(UIView * customAreaView);

//竖屏布局配置对象 -->创建一个布局对象,设置好控件约束属性值,再设置到此属性中
/**竖屏:UIInterfaceOrientationPortrait|UIInterfaceOrientationPortraitUpsideDown
 *由于运营商限制不同,外部自动旋转条件满足时,电信联通页面自动旋转,均使用此约束;移动强制竖屏
 *eg.   CLUIConfigure * baseUIConfigure = [CLUIConfigure new];
 *      CLOrientationLayOut * clOrientationLayOutPortrait = [CLOrientationLayOut new];
 *      clOrientationLayOutPortrait.clLayoutPhoneCenterY = @(0);
 *      clOrientationLayOutPortrait.clLayoutPhoneLeft = @(50*screenScale);
 *      ...
 *      baseUIConfigure.clOrientationLayOutPortrait = clOrientationLayOutPortrait;
 */
@property (nonatomic,strong) CLOrientationLayOut * clOrientationLayOutPortrait;



/*CLOrientationLayOut:
 *横竖屏布局配置对象
 *配置页面布局相关属性
 */
/**LOGO图片*/
// 约束均相对vc.view
NSNumber * clLayoutLogoLeft;
NSNumber * clLayoutLogoTop;
NSNumber * clLayoutLogoRight;
NSNumber * clLayoutLogoBottom;
NSNumber * clLayoutLogoWidth;
NSNumber * clLayoutLogoHeight;
NSNumber * clLayoutLogoCenterX;
NSNumber * clLayoutLogoCenterY;

/**手机号显示控件*/
//layout 约束均相对vc.view
NSNumber * clLayoutPhoneLeft;
NSNumber * clLayoutPhoneTop;
NSNumber * clLayoutPhoneRight;
NSNumber * clLayoutPhoneBottom;
NSNumber * clLayoutPhoneWidth;
NSNumber * clLayoutPhoneHeight;
NSNumber * clLayoutPhoneCenterX;
NSNumber * clLayoutPhoneCenterY;

/*一键登录按钮 控件
 注: 一键登录授权按钮 不得隐藏
 **/
//layout 约束均相对vc.view
NSNumber * clLayoutLoginBtnLeft;
NSNumber * clLayoutLoginBtnTop;
NSNumber * clLayoutLoginBtnRight;
NSNumber * clLayoutLoginBtnBottom;
NSNumber * clLayoutLoginBtnWidth;
NSNumber * clLayoutLoginBtnHeight;
NSNumber * clLayoutLoginBtnCenterX;
NSNumber * clLayoutLoginBtnCenterY;

/*隐私条款Privacy
 注: 运营商隐私条款 不得隐藏, 用户条款不限制
 **/
//layout 约束均相对vc.view
NSNumber * clLayoutAppPrivacyLeft;
NSNumber * clLayoutAppPrivacyTop;
NSNumber * clLayoutAppPrivacyRight;
NSNumber * clLayoutAppPrivacyBottom;
NSNumber * clLayoutAppPrivacyWidth;
NSNumber * clLayoutAppPrivacyHeight;
NSNumber * clLayoutAppPrivacyCenterX;
NSNumber * clLayoutAppPrivacyCenterY;

/*Slogan 运营商品牌标签:"认证服务由中国移动/联通/电信提供" label
 注: 运营商品牌标签,不得隐藏
 **/
//layout 约束均相对vc.view
NSNumber * clLayoutSloganLeft;
NSNumber * clLayoutSloganTop;
NSNumber * clLayoutSloganRight;
NSNumber * clLayoutSloganBottom;
NSNumber * clLayoutSloganWidth;
NSNumber * clLayoutSloganHeight;
NSNumber * clLayoutSloganCenterX;
NSNumber * clLayoutSloganCenterY;


添加自定义控件示例


ObjC:

// 快捷登录
- (void)quickLoginBtnClick:(UIButton *)sender {
    ...    

    CLUIConfigure * baseUIConfigure = [CLUIConfigure new];
    baseUIConfigure.viewController = self;
    CGFloat screenScale = [UIScreen mainScreen].bounds.size.width/375.0;
    baseUIConfigure.customAreaView = ^(UIView * _Nonnull customAreaView) {

        UIButton * button = [[UIButton alloc]init];
        [button setTitle:@"其他方式登录" forState:(UIControlStateNormal)];
        button.titleLabel.font = [UIFont systemFontOfSize:14];
        [button setTitleColor:[UIColor grayColor] forState:(UIControlStateNormal)];
        [button addTarget:self action:@selector(otherLoginWayBtnCliced:) forControlEvents:(UIControlEventTouchUpInside)];
        [customAreaView addSubview:button];

        [button mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.right.mas_equalTo(0);
            make.top.mas_equalTo(280*screenScale);
            make.height.mas_equalTo(40);
        }];
    };  
    ...
}
//授权页 点击自定义控件绑定的方法
-(void)otherLoginWayBtnCliced:(UIButton *)sender{

    //关闭页面
    [CLShanYanSDKManager finishAuthControllerCompletion:^{
       //如需关闭后present/push新页面,建议在completion回调中执行 
    }];
    [SVProgressHUD showInfoWithStatus:@"用户使用其他方式进行注册登录"];
}


Swift:


@IBAction func quickLogin(_ sender: UIButton) {
      ...

      let baseUIConfigure = CLUIConfigure()
      //requried
      baseUIConfigure.viewController = self
      //Optional:
      let screenScale = UIScreen.main.bounds.size.width/375.0;
      baseUIConfigure.customAreaView = {
          [unowned self] view in
          let otherLoginButtonWay = UIButton()
          otherLoginButtonWay.setTitle("其他方式登录", for: .normal)
          otherLoginButtonWay.setTitleColor(UIColor.gray, for: .normal)
          otherLoginButtonWay.titleLabel?.font = UIFont.systemFont(ofSize: 15)
          view.addSubview(otherLoginButtonWay)
          otherLoginButtonWay.snp.makeConstraints { (make) in
              make.centerX.equalToSuperview()
              make.top.equalTo(280*screenScale)
              make.width.equalToSuperview()
              make.height.equalTo(40)
          }
          otherLoginButtonWay.addTarget(self, action: #selector(self.otherLoginButtonWay(_:)), for: .touchUpInside)
      }
      ...
  }

//授权页 点击其他方式登录
@objc func otherLoginButtonWay(_ sender: UIButton) {
    DispatchQueue.main.async(execute: {
        SVProgressHUD.showSuccess(withStatus: ("用户选择其他方式登录"))
    })
}


四.返回码对照

外层错误码

同一外层码可能对应不同的内层码

外层返回码返回码描述
1000一键登录成功,解析result,可得到网络请求参数
1011用户取消免密登录(点击返回按钮)
1001SDK初始化失败
1023预取号/取号失败
1003拉起授权页失败/一键登录失败/获取token失败
1008未开启移动网络
1009未检测/识别到sim卡
其他其他错误

内层错误码


联通返回码

状态码信息(msg)示例说明
100成功
101鉴权失败公钥或者apiKey不正确
103成功(无数据返回)
104系统繁忙
100000通用,未明确定义的其他错误
100001网络出错apiKey配置错误或者404错误
100002参数错误服务未初始化
100003数据格式错误服务器返回结果在客户端解析出错
100004无结果服务器返回null
100005数据不匹配短信上行的认证结果与填入的不匹配计为认证失败
100006包名不匹配服务器返回的结果非本app计为失败
100007apikey为空应用未填入apiKey密钥
100008超时取号或认证时间超过超时时间
100009取消用户取消操作,计为失败
100012无公钥应用未填入公钥
100016公钥出错
2001取号/认证失败
5001序列号不存在seq过期或已使用(seq只能使用一次)
6001参数出错配置信息填写错误
6002应用信息错误
6003SDK信息错误
6005接入信息错误
6006获取接入信息流控类型错误
6008流控超限
6009设备信息未注册
6010应用秘钥信息不匹配
7004公司或应用ID信息出错
8001无支付权限
8002余额不足

电信返回码

返回码返回码描述
0请求成功
-64permission-denied(无权限访问)
-65API-request-rates-Exceed-Limitations(调用接口超限)
-10001取号失败
-10002参数错误
-10003解密失败
-10004ip受限
-10005异网取号回调参数异常
-10006Mdn取号失败,且属于电信网络
-10007重定向到异网取号
-10008超过预设取号阈值
-10009时间戳过期
-20005sign-invalid(签名错误)
-7999服务不可用(其他错误,默认返回值)
-8000responseCode非200(网络错误,http状态码错误)
-8001请求网络异常
-8100无网络连接(网络错误)
-8101获取失败(切换失败)
-9999网络故障(networkauth-fail)
-30003topClass-invalid
-8003请求超时

移动返回码

返回码返回码描述
103000成功
102101无网络
102102网络异常
102103未开启数据网络
102121用户取消登录
102203输入参数错误
102223数据解析异常
102507请求超时
102508数据网络切换失败
200002手机未安装sim卡
200005用户未授权(READ_PHONE_STATE)
200006用户未授权(SEND_SMS)
200007authType仅使用短信验证码认证
2000081. authType参数为空;2. authType参数不合法;
200009应用合法性校验失败(包名包签名未填写正确)
200010预取号时imsi获取失败或者没有sim卡
200012取号失败,跳短信验证码登录
200013短信上行发送短信失败(短信上行)
200014手机号码格式错误(短验)
200015短信验证码格式错误
200016更新KS失败
200017非移动卡不支持短信上行
200018不支持网关登录
200019不支持短信验证码登录
200020用户取消登录
200021数据解析异常(服务器异常可重新尝试)
200022无网络状态
200023请求超时
200024数据网络切换失败
200025未知错误一般出现在线程捕获异常,请配合异常打印分析
200026输入参数错误
200027预取号时未开启数据流量
200028网络请求出错(根据日志分析)
200029请求出错,上次请求未完成
200030没有初始化参数
200031生成token失败
200032KS缓存不存在
200033复用中间件获取Token失败
200034预取号token失效
200035协商ks失败
200036预取号失败
200037获取不到openid
200038电信重定向失败
200039电信取号接口返回失败
200040UI资源加载异常
200042授权页弹出异常

200050

EOF异常:Socket创建或发送接收数据失败或用户未授权蜂窝权限

200060用户点击“切换账号”且使用应用自己的短信验证码时返回
200062联通号码不支持预取号
200063电信号码不支持预取号
获取手机号码接口返回码
返回码返回码描述
103000返回成功
103101签名错误
103102包名/包签名/bundle id错误
103106输入手机号不合法(短验)
103107已存在相同的随机数(短验)
103108短信验证码错误
103109短信验证码超时
103111wap网关IP错误
103112错误的请求
103113token校验错误
103119appid不合法
103122btid不存在
103125手机号格式错误(短验)
103127证书验证:版本过期
103133sourceid不合法
103138appid已存在
103139sourceid已存在
103202缓存用户不存在或者验证短信输入失败次数过多
103203缓存用户不存在(短信上行)
103204缓存随机数不存
103205服务器异常
103207发送短信失败
103211其他错误
103214访问缓存或数据库错误
103401消息签名为空
103404加密失败
103408此sourceId注册已达上限99次
103409query参数为空
103412无效的请求
103414参数效验异常
103505重放攻击
103511源IP不合法
103810token校验失败,接口版本不一致
103811token为空
103901短信验证码下发次数已达上限 (一分钟内最多5次,一天最多10次)
103902临时取号凭证scrip校验失败
103911请求过于频繁(短信校验)
103922自动升级文件没找到
104201token已经被校验过或者超时失效
104202短信验证失败过多
105001联通网关取号失败
105002移动网关取号失败
105003电信网关取号失败
105005短信上行发送信息为空
105007手机号码格式错误(短信上行)
105008短信内容为空(短信上行)
105010script失效或者非法
105011参数加密的私钥失效或者非法
105012不支持电信取号
105013不支持联通取号
105018用户权限不够
105019应用未授权(未勾选能力)
105302AppId不在白名单

五.已知问题


更多问题查询请移步至【常见问题】


1.ATS开关(Http与Https)

目前运营商个别接口为http请求,对于全局禁用Http的项目,需要设置Http白名单。以下为运营商http接口host名单:

*.cmpassport.com、id6.me、123.125.99.8:9001ms.zzx9.cnmdn.open.wo.cn10.99.255.231,*为通配符,建议按以下方式配置Info.plist


<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>zzx9.cn</key>
            <dict>
                <key>NSIncludesSubdomains</key>
                <true/>
                <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
                <true/>
            </dict>
            <key>cmpassport.com</key>
            <dict>
                <key>NSIncludesSubdomains</key>
                <true/>
                <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
                <true/>
            </dict>
            <key>id6.me</key>
            <dict>
                <key>NSIncludesSubdomains</key>
                <true/>
                <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
                <true/>
            </dict>
            <key>wostore.cn</key>
            <dict>
                <key>NSIncludesSubdomains</key>
                <true/>
                <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
                <true/>
            </dict>
            <key>mdn.open.wo.cn</key>
            <dict>
                <key>NSIncludesSubdomains</key>
                <true/>
                <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
                <true/>
            </dict>
        </dict>
    </dict>



2.移动报 获取移动token失败 Code=1003 “(null)” UserInfo={desc=错误的请求签名, resultCode=103101}

【获取token失败、错误的请求签名、103101】

工程中的Info.plist中添加bundleId对应的key-value。搜索Xcode项目工程中的.strings国际化文件,如果app存在本地国际化文件请在每个本地国际化文件中添加CFBundleIdentifier,各个语言版本文件中都需要添加。



3.联通预取号崩溃【预取号+联通+崩溃、NSDictionary + setObject、空指针】

联通SDK内部会读取校验info.plist文件中Bundle identifier、Bundle name、Bundle versions string, short、Bundle version字段,如未配置相关字段Value会导致以下崩溃