【iOS】实现微信第三方登录
TIP
移动应用微信登录是基于 OAuth2.0 协议标准构建的微信 OAuth2.0 授权登录系统。 在进行微信 OAuth2.0 授权登录接入之前,在微信开放平台注册开发者帐号,并拥有一个已审核通过的移动应用,并获得相应的 AppID 和 AppSecret,申请微信登录且通过审核后,可开始接入流程。
这篇文章主要介绍了 iOS 微信第三方登录实现的全过程,一步一步告诉大家 iOS 微信实现第三方登录的方法,感兴趣的小伙伴们可以参考一下
1.下载 iOS 微信 SDK
2.把 SDK 放到工程中(直接拖进去即可)
3.导入一些依赖框架和手动添加 AFNetworking 框架
最后是这样
4.添加 URL Types,添加 IOS9 URLSchemes,添加 ATS
IOS9 中新增 App Transport Security(ATS) 特性,主要使 dao 原来请求的时候用到的 HTTP,都转向 TLS1.2 协议进行传输。这也意味着所有的 HTTP 协议都强制使用了 HTTPS 协议进行传输。需要在 Info.plist 新增一段用于控制 ATS 的配置;
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
5.在 AppDelegate.h 中声明一个变量来保存用户成功登陆后的个人信息,声明一个方法来能够外部调用(为了不 DRY)
@property (nonatomic,retain)NSDictionary*userInfo;
-(void) weixinLoginByRequestForUserInfo;
6.新建一个 weixinInfo.h 的文件来保存微信的 id,secret 等相关的信息,然后在其他文件里导入这个头文件就能使用这些变量了,以后如果有相关的修改的话,这样比较便捷和安全
7.向微信终端程序注册第三方应用,并在第三方应用实现从微信返回
在 AppDelegate.m 中引入“WXApi.h”头文件,然后写入如下:
#import "AppDelegate.h"
#import "AFNetworking.h"
#import "WXApi.h"
#import "weixinInfo.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//Override point for customization after application launch.
[WXApi registerApp:WXPatient_App_ID withDescription:@"Wechat"];
return YES;
}
// 这个方法是用于从微信返回第三方App
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
return [WXApihandleOpenURL:urldelegate:self];
}
8.请求 code
开发者需要配合使用微信开放平台提供的 sdk 进行授权登陆请求接入。正确接入 SDK 后并拥有相关授权域(scope)权限后,开发者移动应用会在终端本地拉起微信应用进行授权登陆,微信用户确认后微信将拉起开发者移动应用,并带上授权临时票据(code)
/*
目前移动应用上德微信登录只提供原生的登录方式,需要用户安装微信客户端才能配合使用。
对于iOS应用,考虑到iOS应用商店审核指南中的相关规定,建议开发者接入微信登录时,先检测用户手机是否已经安装
微信客户端(使用sdk中的isWXAppInstall函数),对于未安装的用户隐藏微信登录按钮,只提供其他登录方式。
*/
- (IBAction)weixinLoginClick:(UIButton *)sender {
if([WXApiisWXAppInstalled]){
SendAuthReq *req = [[SendAuthReqalloc]init];
req.scope = WX_SCOPE;
req.state = WX_STATE; //可省,不影响功能
[WXApi sendReq:req];
}else{
[self setupAlertController];
}
}
#pragma mark - 设置弹出提示语
- (void)setupAlertController {
UIAlertController *alert = [UIAlertControlleralertControllerWithTitle:@"温馨提示"message:@"请先安装微信客户端"preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *actionConfirm = [UIAlertActionactionWithTitle:@"确定"style:UIAlertActionStyleDefaulthandler:nil];
[alert addAction:actionConfirm];
[self presentViewController:alert animated:YEScompletion:nil];
}
执行完上面那一步后,如果客户端安装了微信,那么就会向微信请求相应的授权,图如下:
(此图用的别人的)
PS: 还有在实际的使用中我们还要结合需求做一些改变。因为微信授权后 access_token(2 小时)之类的字段都是有效期的在有效期范围内,我们是没必要让用户再次授权的,很可能你的实现,会如我下面所写的。
/*
// ViewController.h
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
/** 通过block去执行AppDelegate中的weixinLoginByRequestForUserInfo方法 */
@property (copy,nonatomic)void(^weixinLoginByRequestForUserInfo)();
@end
// ViewController.m
目前移动应用上德微信登录只提供原生的登录方式,需要用户安装微信客户端才能配合使用。
对于iOS应用,考虑到iOS应用商店审核指南中的相关规定,建议开发者接入微信登录时,先检测用户手机是否已经安装
微信客户端(使用sdk中的isWXAppInstall函数),对于未安装的用户隐藏微信登录按钮,只提供其他登录方式。
*/
- (IBAction)weixinLoginClick:(UIButton *)sender {
NSString *accessToken = [[NSUserDefaultsstandardUserDefaults]objectForKey:WX_ACCESS_TOKEN];
NSString *openID = [[NSUserDefaultsstandardUserDefaults]objectForKey:WX_OPEN_ID];
// 如果已经请求过微信授权登录,那么考虑用已经得到的access_token
if (![accessToken isEqualToString:@"access_token"]|| ![openIDisEqualToString:@"openid"]) {
[self isLoginedAlertController];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManagermanager];
manager.responseSerializer = [AFHTTPResponseSerializerserializer];
NSString *refreshToken = [[NSUserDefaultsstandardUserDefaults]objectForKey:WX_REFRESH_TOKEN];
NSString *refreshUrlStr = [NSStringstringWithFormat:@"%@/oauth2/refresh_token?appid=%@&grant_type=refresh_token&refresh_token=%@",WX_BASE_URL,WXPatient_App_ID, refreshToken];
[manager GET:refreshUrlStrparameters:nilsuccess:^(AFHTTPRequestOperation *operation,id responseObject) {
NSLog(@"请求reAccess的response = %@", responseObject);
//对数据进行转码
// ASCII to NSString
NSString * refreshDictStr = [[NSStringalloc]initWithData: responseObjectencoding:NSUTF8StringEncoding];
NSLog(@"\n\n refreshDict = %@",refreshDictStr);
//字符串再生成NSData
NSData * data = [refreshDictStrdataUsingEncoding:NSUTF8StringEncoding];
//再解析
NSDictionary *refreshDict = [NSJSONSerializationJSONObjectWithData:dataoptions:NSJSONReadingMutableLeaveserror:nil];
NSString *reAccessToken = [refreshDictobjectForKey:WX_ACCESS_TOKEN];
// 如果reAccessToken为空,说明reAccessToken也过期了,反之则没有过期
if (reAccessToken) {
// 更新access_token、refresh_token、open_id
[[NSUserDefaultsstandardUserDefaults]setObject:reAccessTokenforKey:WX_ACCESS_TOKEN];
[[NSUserDefaultsstandardUserDefaults]setObject:[refreshDictobjectForKey:WX_OPEN_ID]forKey:WX_OPEN_ID];
[[NSUserDefaultsstandardUserDefaults]setObject:[refreshDictobjectForKey:WX_REFRESH_TOKEN]forKey:WX_REFRESH_TOKEN];
[[NSUserDefaultsstandardUserDefaults]synchronize];
// 当存在reAccessToken不为空时直接执行AppDelegate中的weixinLoginByRequestForUserInfo方法
AppDelegate *appDelegate = (AppDelegate *)[[UIApplicationsharedApplication]delegate];
[appDelegate weixinLoginByRequestForUserInfo];
}
else {
[self weixinLogin];
}
} failure:^(AFHTTPRequestOperation *operation,NSError *error) {
NSLog(@"用refresh_token来更新accessToken时出错 = %@", error);
}];
}
else {
[self weixinLogin];
}
}
9.授权后回调
// 授权后回调 AppDelegate.m
- (void)onResp:(BaseResp *)resp {
// 向微信请求授权后,得到响应结果
if ([resp isKindOfClass:[SendAuthResp class]]) {
SendAuthResp *temp = (SendAuthResp *)resp;
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManagermanager];
manager.responseSerializer = [AFHTTPResponseSerializerserializer];
NSString *accessUrlStr = [NSStringstringWithFormat:@"%@/oauth2/access_token?appid=%@&secret=%@&code=%@&grant_type=authorization_code",WX_BASE_URL,WXPatient_App_ID,WXPatient_App_Secret, temp.code];
NSLog(@"%@",accessUrlStr);
[manager GET:accessUrlStrparameters:nilsuccess:^(AFHTTPRequestOperation *operation,id responseObject) {
NSLog(@"请求access的response = %@", responseObject);
// ASCII to NSString
NSString * accessStr = [[NSStringalloc]initWithData: responseObjectencoding:NSUTF8StringEncoding];
NSLog(@"nsstring = %@",accessStr);
//字符串再生成NSData
NSData * data = [accessStrdataUsingEncoding:NSUTF8StringEncoding];
//再解析
NSDictionary *accessDict = [NSJSONSerializationJSONObjectWithData:dataoptions:NSJSONReadingMutableLeaveserror:nil];
NSString *accessToken = [accessDictobjectForKey:WX_ACCESS_TOKEN];
NSString *openID = [accessDictobjectForKey:WX_OPEN_ID];
NSString *refreshToken = [accessDictobjectForKey:WX_REFRESH_TOKEN];
// 本地持久化,以便access_token的使用、刷新或者持续
if (accessToken && ![accessTokenisEqualToString:@""] && openID && ![openIDisEqualToString:@""]) {
[[NSUserDefaultsstandardUserDefaults]setObject:accessTokenforKey:WX_ACCESS_TOKEN];
[[NSUserDefaultsstandardUserDefaults]setObject:openIDforKey:WX_OPEN_ID];
[[NSUserDefaultsstandardUserDefaults]setObject:refreshTokenforKey:WX_REFRESH_TOKEN];
[[NSUserDefaultsstandardUserDefaults]synchronize];//命令直接同步到文件里,来避免数据的丢失
}
[self weixinLoginByRequestForUserInfo];
} failure:^(AFHTTPRequestOperation *operation,NSError *error) {
NSLog(@"获取access_token时出错 = %@", error);
}];
}
}
10.获取用户信息(UnionID 机制)
// 获取用户个人信息(UnionID机制)AppDelegate.m
- (void)weixinLoginByRequestForUserInfo {
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManagermanager];
manager.responseSerializer = [AFHTTPResponseSerializerserializer];
NSString *accessToken = [[NSUserDefaultsstandardUserDefaults]objectForKey:WX_ACCESS_TOKEN];
NSString *openID = [[NSUserDefaultsstandardUserDefaults]objectForKey:WX_OPEN_ID];
NSString *userUrlStr = [NSStringstringWithFormat:@"%@/userinfo?access_token=%@&openid=%@",WX_BASE_URL, accessToken, openID];
// 请求用户数据
[manager GET:userUrlStr parameters:nil success:^(AFHTTPRequestOperation *operation,id responseObject) {
NSLog(@"请求用户信息的response = %@", responseObject);
// ASCII to NSString
NSString * accessStr = [[NSStringalloc]initWithData: responseObjectencoding:NSUTF8StringEncoding];
NSLog(@"nsstring = %@",accessStr);
//字符串再生成NSData
NSData * data = [accessStrdataUsingEncoding:NSUTF8StringEncoding];
//再解析
_userInfo = [NSJSONSerializationJSONObjectWithData:dataoptions:NSJSONReadingMutableLeaveserror:nil];
NSLog(@"\n\n userInfo= %@",_userInfo);
} failure:^(AFHTTPRequestOperation *operation,NSError *error) {
NSLog(@"获取用户信息时出错 = %@", error);
}];
}
11.总结
至此微信登陆的流程已经结束了,用户的信息已经保存在了 userInfo 变量中,在需要使用到用户变量的地方,先创建 AppDelegate 的实例,然后 AppDelegate.userInfo 即可调用了。
我主要借鉴了这篇文章里的方法,http://www.jb51.net/article/78916.htm。 然后对其中一些数据的格式转换等进行了调整,使之能够成功运行。
做的 demo 链接:http://download.csdn.net/detail/cooldiok/9584859, 下载下来后需要在 weixinInfo.h 里写上自己的帐号相关信息才能正常运行。
样图: