zoukankan      html  css  js  c++  java
  • iOS微信第三方登录实现

    这篇文章主要介绍了iOS微信第三方登录实现的全过程,一步一步告诉大家iOS微信实现第三方登录的方法,感兴趣的小伙伴们可以参考一下
     

    一、接入微信第三方登录准备工作。
    移动应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统。
    在进行微信OAuth2.0授权登录接入之前,在微信开放平台注册开发者帐号,并拥有一个已审核通过的移动应用,并获得相应的AppID和AppSecret,申请微信登录且通过审核后,可开始接入流程。(注意)
    1、下载iOS微信SDK。
    下载地址

    2、将SDK放到工程目录中。

    3、补充导入一些依赖框架。

    4、添加URL Types

    5、添加iOS9 URL Schemes.

    注意:如果没有做这步的话会出现以下错误.

    1
    -canOpenURL: failed for URL: "weixin://app/wx9**********dfd30/" - error: "This app is not allowed to query for scheme weixin"

    6、iOS9中新增App Transport Security(简称ATS)特性, 主要使到原来请求的时候用到的HTTP,都转向TLS1.2协议进行传输。这也意味着所有的HTTP协议都强制使用了HTTPS协议进行传输。需要在Info.plist新增一段用于控制ATS的配置:

    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>

    如果我们在iOS9下直接进行HTTP请求是会收到如下错误提示:

    1
    **App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.**

    7、向微信终端程序注册第三方应用,并在第三方应用实现从微信返回
    在AppDelegate.m中引入"WXApi.h"头文件,然后写入如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #import "AppDelegate.h"
    #import "LoginViewController.h"
    #import "WXApi.h"
     
    #pragma mark - application delegate
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
     
    [WXApi registerApp:@"wxd1931d4a0e46****" withDescription:@"Wechat"];
    return YES;
    }
    // 这个方法是用于从微信返回第三方App
    - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
     
    [WXApi handleOpenURL:url delegate:self];
    return YES;
    }

    8、请求CODE
    开发者需要配合使用微信开放平台提供的SDK进行授权登录请求接入。正确接入SDK后并拥有相关授权域(scope,什么是授权域?)权限后,开发者移动应用会在终端本地拉起微信应用进行授权登录,微信用户确认后微信将拉起开发者移动应用,并带上授权临时票据(code)。

    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
    #import "LoginViewController.h"
    #import "RegisterViewController.h"
    #import "MBProgressHUD.h"
    #import "AFNetworking.h"
    #import "WXApi.h"
     
    #pragma mark - 微信登录
    /*
     目前移动应用上德微信登录只提供原生的登录方式,需要用户安装微信客户端才能配合使用。
     对于iOS应用,考虑到iOS应用商店审核指南中的相关规定,建议开发者接入微信登录时,先检测用户手机是否已经安装
     微信客户端(使用sdk中的isWXAppInstall函数),对于未安装的用户隐藏微信 登录按钮,只提供其他登录方式。
     */
    - (IBAction)wechatLoginClick:(id)sender {
     
    if ([WXApi isWXAppInstalled]) {
        SendAuthReq *req = [[SendAuthReq alloc] init];
        req.scope = @"snsapi_userinfo";
        req.state = @"App";
        [WXApi sendReq:req];
      }
      else {
        [self setupAlertController];
      }
    }
     
    #pragma mark - 设置弹出提示语
    - (void)setupAlertController {
     
      UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"温馨提示" message:@"请先安装微信客户端" preferredStyle:UIAlertControllerStyleAlert];
      UIAlertAction *actionConfirm = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
      [alert addAction:actionConfirm];
      [self presentViewController:alert animated:YES completion:nil];
    }

    执行完上面那一步后,如果客户端安装了微信,那么就会向微信请求相应的授权,图如下:

    还有在实际的使用中我们还要结合需求做一些改变。因为微信授权后access_token(2小时)之类的字段都是有效期的在有效期范围内,我们是没必要让用户再次授权的,很可能你的实现,会如我下面所写的(LoginViewController)

    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
    59
    60
    61
    62
    63
    64
    65
    // LoginViewController.h
    #import <UIKit/UIKit.h>
    @interface LoginViewController : BaseViewController
    /** 通过block去执行AppDelegate中的wechatLoginByRequestForUserInfo方法 */
    @property (copy, nonatomic) void (^requestForUserInfoBlock)();
    @end
     
    // LoginViewController.m
    #pragma mark - 微信登录
    /*
     目前移动应用上德微信登录只提供原生的登录方式,需要用户安装微信客户端才能配合使用。
     对于iOS应用,考虑到iOS应用商店审核指南中的相关规定,建议开发者接入微信登录时,先检测用户手机是否已经安装
     微信客户端(使用sdk中的isWXAppInstall函数),对于未安装的用户隐藏微信 登录按钮,只提供其他登录方式。
     */
    - (IBAction)wechatLoginClick:(id)sender {
      NSString *accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:WX_ACCESS_TOKEN];
      NSString *openID = [[NSUserDefaults standardUserDefaults] objectForKey:WX_OPEN_ID];
      // 如果已经请求过微信授权登录,那么考虑用已经得到的access_token
      if (accessToken && openID) {
        AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
        NSString *refreshToken = [[NSUserDefaults standardUserDefaults] objectForKey:WX_REFRESH_TOKEN];
        NSString *refreshUrlStr = [NSString stringWithFormat:@"%@/oauth2/refresh_token?appid=%@&grant_type=refresh_token&refresh_token=%@", WX_BASE_URL, WXPatient_App_ID, refreshToken];
        [manager GET:refreshUrlStr parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
          NSLog(@"请求reAccess的response = %@", responseObject);
          NSDictionary *refreshDict = [NSDictionary dictionaryWithDictionary:responseObject];
          NSString *reAccessToken = [refreshDict objectForKey:WX_ACCESS_TOKEN];
          // 如果reAccessToken为空,说明reAccessToken也过期了,反之则没有过期
          if (reAccessToken) {
            // 更新access_token、refresh_token、open_id
            [[NSUserDefaults standardUserDefaults] setObject:reAccessToken forKey:WX_ACCESS_TOKEN];
            [[NSUserDefaults standardUserDefaults] setObject:[refreshDict objectForKey:WX_OPEN_ID] forKey:WX_OPEN_ID];
            [[NSUserDefaults standardUserDefaults] setObject:[refreshDict objectForKey:WX_REFRESH_TOKEN] forKey:WX_REFRESH_TOKEN];
            [[NSUserDefaults standardUserDefaults] synchronize];
            // 当存在reAccessToken不为空时直接执行AppDelegate中的wechatLoginByRequestForUserInfo方法
            !self.requestForUserInfoBlock ? : self.requestForUserInfoBlock();
          }
          else {
            [self wechatLogin];
          }
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
          NSLog(@"用refresh_token来更新accessToken时出错 = %@", error);
        }];
      }
      else {
        [self wechatLogin];
      }
    }
    - (void)wechatLogin {
      if ([WXApi isWXAppInstalled]) {
        SendAuthReq *req = [[SendAuthReq alloc] init];
        req.scope = @"snsapi_userinfo";
        req.state = @"GSTDoctorApp";
        [WXApi sendReq:req];
      }
      else {
        [self setupAlertController];
      }
    }
    #pragma mark - 设置弹出提示语
    - (void)setupAlertController {
      UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"温馨提示" message:@"请先安装微信客户端" preferredStyle:UIAlertControllerStyleAlert];
      UIAlertAction *actionConfirm = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
      [alert addAction:actionConfirm];
      [self presentViewController:alert animated:YES completion:nil];
    }

    当有access_token和openID时输出:

    1
    2
    3
    4
    5
    6
    7
    **请求****reAccess****的****response = {**
    **  "access_token" = "OezXcEiiBSKSxW0eoylIeK3BOTSVaRovFSXb5oysH6dewFfLsQRgU3fphSLkKkhOokra9H-JMZuB5nPoM-Iy5YbFeA1nKMRYCbL0fj_s46oFKOluGoRUY8jyTdrdDiiFdgS2fxgo5odEtpnpFk3EXA";**
    **  "expires_in" = 7200;**
    **  openid = oXskgs62CJGFhFX05dSjy9Sjw2KA;**
    **  "refresh_token" = "OezXcEiiBSKSxW0eoylIeK3BOTSVaRovFSXb5oysH6dewFfLsQRgU3fphSLkKkhOOWPTKGEjUtuiueutxRjKOlHGZ9b9ogc3KMbibu4eKc4yTMGzSZayjYPmwQ-c4RJE1RzMLrqvjUWgB5roFnjykw";**
    **  scope = "snsapi_base,snsapi_userinfo,";**
    **}**

    刷新access_token有效期:
    access_token是调用授权关系接口的调用凭证,由于access_token有效期(目前为2个小时)较短,当access_token超时后,可以使用refresh_token进行刷新,access_token刷新结果有两种:

    • 1. 若access_token已超时,那么进行refresh_token会获取一个新的access_token,新的超时时间;
    • 2. 若access_token未超时,那么进行refresh_token不会改变access_token,但超时时间会刷新,相当于续期access_token。

    refresh_token拥有较长的有效期(30天),当refresh_token失效的后,需要用户重新授权。
    让AppDelegate遵守<WXApiDelegate>协议,并实现协议方法onResp:
     我们在该方法中接收请求回来的数据,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //授权后回调
    /*
     http请求方式:GET
     // 根据响应结果中的code获取access_token(要用到申请时得到的AppID和AppSecret)
     正确返回
     {
     "access_token":"ACCESS_TOKEN",
     "expires_in":7200,
     "refresh_token":"REFRESH_TOKEN",
     "openid":"OPENID",
     "scope":"SCOPE",
     "unionid":"o6_bmasdasdsad6_2sgVt7hMZOPfL"
     }
     错误返回样例
     {"errcode":40029,"errmsg":"invalid code"}
     ErrCode ERR_OK = 0(用户同意)
     ERR_AUTH_DENIED = -4(用户拒绝授权)
     ERR_USER_CANCEL = -2(用户取消)
     code  用户换取access_token的code,仅在ErrCode为0时有效
     state  第三方程序发送时用来标识其请求的唯一性的标志,由第三方程序调用sendReq时传入,由微信终端回传,state字符串长度不能超过1K
     lang  微信客户端当前语言
     country 微信用户当前国家信息
     */
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    -(void)showLoginController:(BOOL)shouldAnimation
    {
      LoginViewController *loginController=[[LoginViewController alloc]initWithNibName:@"LoginViewController" bundle:nil];
     
      loginController.requestForUserInfoBlock = ^() {
     
        [[AppDelegate sharedInstance] wechatLoginByRequestForUserInfo];
      };
     
      BaseNavigationController *baseNavController=[[BaseNavigationController alloc]initWithRootViewController:loginController];
      [kAppDelegate.window.rootViewController presentViewController:baseNavController animated:shouldAnimation completion:NULL];
    }
    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
    // 授权后回调
    // AppDelegate.m
    - (void)onResp:(BaseResp *)resp {
      // 向微信请求授权后,得到响应结果
      if ([resp isKindOfClass:[SendAuthResp class]]) {   
        SendAuthResp *temp = (SendAuthResp *)resp;
        AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
        NSString *accessUrlStr = [NSString stringWithFormat:@"%@/oauth2/access_token?appid=%@&secret=%@&code=%@&grant_type=authorization_code", WX_BASE_URL, WXPatient_App_ID, WXPatient_App_Secret, temp.code];
        [manager GET:accessUrlStr parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
          NSLog(@"请求access的response = %@", responseObject);
          NSDictionary *accessDict = [NSDictionary dictionaryWithDictionary:responseObject];
          NSString *accessToken = [accessDict objectForKey:WX_ACCESS_TOKEN];
          NSString *openID = [accessDict objectForKey:WX_OPEN_ID];
          NSString *refreshToken = [accessDict objectForKey:WX_REFRESH_TOKEN];
          // 本地持久化,以便access_token的使用、刷新或者持续
          if (accessToken && ![accessToken isEqualToString:@""] && openID && ![openID isEqualToString:@""]) {
            [[NSUserDefaults standardUserDefaults] setObject:accessToken forKey:WX_ACCESS_TOKEN];
            [[NSUserDefaults standardUserDefaults] setObject:openID forKey:WX_OPEN_ID];
            [[NSUserDefaults standardUserDefaults] setObject:refreshToken forKey:WX_REFRESH_TOKEN];
            [[NSUserDefaults standardUserDefaults] synchronize]; // 命令直接同步到文件里,来避免数据的丢失
          }
          [self wechatLoginByRequestForUserInfo];
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
          NSLog(@"获取access_token时出错 = %@", error);
        }];
      }
    }

    9、通过code获取access_token
    通过上一步获取的code后,请求以下链接获取access_token:

    相关代码上面实现onResp:方法,接收返回的响应。
    参数说明:

    参数           是否必须        说明
    appid           是             应用唯一标识,在微信开放平台提交应用审核通过后获得
    secret          是             应用密钥AppSecret,在微信开放平台提交应用审核通过后获得
    code            是             填写第一步获取的code参数
    grant_type      是             填authorization_code
    返回说明:

    1
    2
    3
    4
    5
    6
    7
    8
    {
    "access_token":"ACCESS_TOKEN", // 接口调用凭证
     "expires_in":7200, // access_token接口调用凭证超时时间,单位(秒)
    "refresh_token":"REFRESH_TOKEN", // 用户刷新access_token
    "openid":"OPENID", // 授权用户唯一标识
    "scope":"SCOPE", // 用户授权的作用域,使用逗号(,)分隔
    "unionid":"o6_bmasdasdsad6_2sgVt7hMZOPfL" // 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段
    }

    错误返回样例:

    {"errcode":40029,"errmsg":"invalid code"}
    10、获取用户个人信息(UnionID机制)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // AppDelegate.m
    // 获取用户个人信息(UnionID机制)
    - (void)wechatLoginByRequestForUserInfo {
      AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
      NSString *accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:WX_ACCESS_TOKEN];
      NSString *openID = [[NSUserDefaults standardUserDefaults] objectForKey:WX_OPEN_ID];
      NSString *userUrlStr = [NSString stringWithFormat:@"%@/userinfo?access_token=%@&openid=%@", WX_BASE_URL, accessToken, openID];
      // 请求用户数据
      [manager GET:userUrlStr parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSLog(@"请求用户信息的response = %@", responseObject);
        // NSMutableDictionary *userDict = [NSMutableDictionary dictionaryWithDictionary:responseObject];
      } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"获取用户信息时出错 = %@", error);
      }];
    }

    返回的Json结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    返回的Json结果
     {
     "openid":"OPENID",
     "nickname":"NICKNAME",
     "sex":1,
     "province":"PROVINCE",
     "city":"CITY",
     "country":"COUNTRY",
     "privilege":[
     "PRIVILEGE1",
     "PRIVILEGE2"
     ],
     "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
     }
     返回错误的Json事例
     {
     "errcode":40003,"errmsg":"invalid openid"
     }

    11、最后
    做到上面一步就应该得到返回微信的基本信息,然后根据你公司后台的基本需求去实现授权后如何登录App.
    资料:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // access_token openid refresh_token unionid
    #define WXDoctor_App_ID @"wxd1931d4a0e462***" // 注册微信时的AppID
    #define WXDoctor_App_Secret @"d0dd6b58da42cbc4f4b715c70e65c***" // 注册时得到的AppSecret
    #define WXPatient_App_ID @"wxbd02bfeea4292***"
    #define WXPatient_App_Secret @"4a788217f363358276309ab655707***"
    #define WX_ACCESS_TOKEN @"access_token"
    #define WX_OPEN_ID @"openid"
    #define WX_REFRESH_TOKEN @"refresh_token"
    #define WX_UNION_ID @"unionid"
    #define WX_BASE_URL @"https://api.weixin.qq.com/sns"

    12.这是我司需求的做法:

    1.首先获取到微信的openID,然后通过openID去后台数据库查询该微信的openID有没有绑定好的手机号.
    2.如果没有绑定,首相第一步就是将微信用户的头像、昵称等等基本信息添加到数据库;然后通过手机获取验证码;最后绑定手机号。然后就登录App.
    3.如果有,那么后台就返回一个手机号,然后通过手机登录App.

    以上就是本文的全部内容,希望对大家的学习有所帮助。

  • 相关阅读:
    macbook 无声音解决方案
    webapck dev server代理请求 json截断问题
    百度卫星地图开启
    服务器 nginx配置 防止其他域名绑定自己的服务器
    记一次nginx php配置的心路历程
    遇到npm报错read ECONNRESET怎么办
    运行svn tortoiseSvn cleanup 命令失败的解决办法
    svn add 命令 递归目录下所有文件
    m4出现Please port gnulib freadahead.c to your platform! Look at the definition of fflush, fread, ungetc on your system, then report this to bug-gnulib."
    Ubuntu下安装GCC,mpc、mpfr、gmp
  • 原文地址:https://www.cnblogs.com/tdalcn/p/6011121.html
Copyright © 2011-2022 走看看