一、接入微信第三方登錄準(zhǔn)備工作。
移動(dòng)應(yīng)用微信登錄是基于oauth2.0協(xié)議標(biāo)準(zhǔn)構(gòu)建的微信oauth2.0授權(quán)登錄系統(tǒng)。
在進(jìn)行微信oauth2.0授權(quán)登錄接入之前,在微信開(kāi)放平臺(tái)注冊(cè)開(kāi)發(fā)者帳號(hào),并擁有一個(gè)已審核通過(guò)的移動(dòng)應(yīng)用,并獲得相應(yīng)的appid和appsecret,申請(qǐng)微信登錄且通過(guò)審核后,可開(kāi)始接入流程。(注意)
1、下載ios微信sdk。
下載地址
2、將sdk放到工程目錄中。
3、補(bǔ)充導(dǎo)入一些依賴框架。
4、添加url types
5、添加ios9 url schemes.
注意:如果沒(méi)有做這步的話會(huì)出現(xiàn)以下錯(cuò)誤.
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(簡(jiǎn)稱ats)特性, 主要使到原來(lái)請(qǐng)求的時(shí)候用到的http,都轉(zhuǎn)向tls1.2協(xié)議進(jìn)行傳輸。這也意味著所有的http協(xié)議都強(qiáng)制使用了https協(xié)議進(jìn)行傳輸。需要在info.plist新增一段用于控制ats的配置:
<key>nsapptransportsecurity</key>
<dict>
<key>nsallowsarbitraryloads</key>
<true/>
</dict>
如果我們?cè)趇os9下直接進(jìn)行http請(qǐng)求是會(huì)收到如下錯(cuò)誤提示:
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、向微信終端程序注冊(cè)第三方應(yīng)用,并在第三方應(yīng)用實(shí)現(xiàn)從微信返回
在appdelegate.m中引入"wxapi.h"頭文件,然后寫(xiě)入如下:
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; } // 這個(gè)方法是用于從微信返回第三方app - ( bool )application:(uiapplication *)application handleopenurl:(nsurl *)url { [wxapi handleopenurl:url delegate:self]; return yes; } |
8、請(qǐng)求code
開(kāi)發(fā)者需要配合使用微信開(kāi)放平臺(tái)提供的sdk進(jìn)行授權(quán)登錄請(qǐng)求接入。正確接入sdk后并擁有相關(guān)授權(quán)域(scope,什么是授權(quán)域?)權(quán)限后,開(kāi)發(fā)者移動(dòng)應(yīng)用會(huì)在終端本地拉起微信應(yīng)用進(jìn)行授權(quán)登錄,微信用戶確認(rèn)后微信將拉起開(kāi)發(fā)者移動(dòng)應(yīng)用,并帶上授權(quán)臨時(shí)票據(jù)(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 - 微信登錄 /* 目前移動(dòng)應(yīng)用上德微信登錄只提供原生的登錄方式,需要用戶安裝微信客戶端才能配合使用。 對(duì)于ios應(yīng)用,考慮到ios應(yīng)用商店審核指南中的相關(guān)規(guī)定,建議開(kāi)發(fā)者接入微信登錄時(shí),先檢測(cè)用戶手機(jī)是否已經(jīng)安裝 微信客戶端(使用sdk中的iswxappinstall函數(shù)),對(duì)于未安裝的用戶隱藏微信 登錄按鈕,只提供其他登錄方式。 */ - (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 - 設(shè)置彈出提示語(yǔ) - ( void )setupalertcontroller { uialertcontroller *alert = [uialertcontroller alertcontrollerwithtitle:@ "溫馨提示" message:@ "請(qǐng)先安裝微信客戶端" preferredstyle:uialertcontrollerstylealert]; uialertaction *actionconfirm = [uialertaction actionwithtitle:@ "確定" style:uialertactionstyledefault handler:nil]; [alert addaction:actionconfirm]; [self presentviewcontroller:alert animated:yes completion:nil]; } |
執(zhí)行完上面那一步后,如果客戶端安裝了微信,那么就會(huì)向微信請(qǐng)求相應(yīng)的授權(quán),圖如下:
還有在實(shí)際的使用中我們還要結(jié)合需求做一些改變。因?yàn)槲⑿攀跈?quán)后access_token(2小時(shí))之類的字段都是有效期的在有效期范圍內(nèi),我們是沒(méi)必要讓用戶再次授權(quán)的,很可能你的實(shí)現(xiàn),會(huì)如我下面所寫(xiě)的(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 /** 通過(guò)block去執(zhí)行appdelegate中的wechatloginbyrequestforuserinfo方法 */ @property (copy, nonatomic) void (^requestforuserinfoblock)(); @end // loginviewcontroller.m #pragma mark - 微信登錄 /* 目前移動(dòng)應(yīng)用上德微信登錄只提供原生的登錄方式,需要用戶安裝微信客戶端才能配合使用。 對(duì)于ios應(yīng)用,考慮到ios應(yīng)用商店審核指南中的相關(guān)規(guī)定,建議開(kāi)發(fā)者接入微信登錄時(shí),先檢測(cè)用戶手機(jī)是否已經(jīng)安裝 微信客戶端(使用sdk中的iswxappinstall函數(shù)),對(duì)于未安裝的用戶隱藏微信 登錄按鈕,只提供其他登錄方式。 */ - (ibaction)wechatloginclick:(id)sender { nsstring *accesstoken = [[nsuserdefaults standarduserdefaults] objectforkey:wx_access_token]; nsstring *openid = [[nsuserdefaults standarduserdefaults] objectforkey:wx_open_id]; // 如果已經(jīng)請(qǐng)求過(guò)微信授權(quán)登錄,那么考慮用已經(jīng)得到的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(@ "請(qǐng)求reaccess的response = %@" , responseobject); nsdictionary *refreshdict = [nsdictionary dictionarywithdictionary:responseobject]; nsstring *reaccesstoken = [refreshdict objectforkey:wx_access_token]; // 如果reaccesstoken為空,說(shuō)明reaccesstoken也過(guò)期了,反之則沒(méi)有過(guò)期 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]; // 當(dāng)存在reaccesstoken不為空時(shí)直接執(zhí)行appdelegate中的wechatloginbyrequestforuserinfo方法 !self.requestforuserinfoblock ? : self.requestforuserinfoblock(); } else { [self wechatlogin]; } } failure:^(afhttprequestoperation *operation, nserror *error) { nslog(@ "用refresh_token來(lái)更新accesstoken時(shí)出錯(cuò) = %@" , 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 - 設(shè)置彈出提示語(yǔ) - ( void )setupalertcontroller { uialertcontroller *alert = [uialertcontroller alertcontrollerwithtitle:@ "溫馨提示" message:@ "請(qǐng)先安裝微信客戶端" preferredstyle:uialertcontrollerstylealert]; uialertaction *actionconfirm = [uialertaction actionwithtitle:@ "確定" style:uialertactionstyledefault handler:nil]; [alert addaction:actionconfirm]; [self presentviewcontroller:alert animated:yes completion:nil]; } |
當(dāng)有access_token和openid時(shí)輸出:
1
2
3
4
5
6
7
|
**請(qǐng)求****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是調(diào)用授權(quán)關(guān)系接口的調(diào)用憑證,由于access_token有效期(目前為2個(gè)小時(shí))較短,當(dāng)access_token超時(shí)后,可以使用refresh_token進(jìn)行刷新,access_token刷新結(jié)果有兩種:
-
1. 若access_token已超時(shí),那么進(jìn)行refresh_token會(huì)獲取一個(gè)新的access_token,新的超時(shí)時(shí)間;
-
2. 若access_token未超時(shí),那么進(jìn)行refresh_token不會(huì)改變access_token,但超時(shí)時(shí)間會(huì)刷新,相當(dāng)于續(xù)期access_token。
refresh_token擁有較長(zhǎng)的有效期(30天),當(dāng)refresh_token失效的后,需要用戶重新授權(quán)。
讓appdelegate遵守<wxapidelegate>協(xié)議,并實(shí)現(xiàn)協(xié)議方法onresp:
我們?cè)谠摲椒ㄖ薪邮照?qǐng)求回來(lái)的數(shù)據(jù),如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
//授權(quán)后回調(diào) /* http請(qǐng)求方式:get // 根據(jù)響應(yīng)結(jié)果中的code獲取access_token(要用到申請(qǐng)時(shí)得到的appid和appsecret) https://api.weixin.qq.com/sns/oauth2/access_token?appid=appid&secret=secret&code=code&grant_type=authorization_code 正確返回 { "access_token":"access_token", "expires_in":7200, "refresh_token":"refresh_token", "openid":"openid", "scope":"scope", "unionid":"o6_bmasdasdsad6_2sgvt7hmzopfl" } 錯(cuò)誤返回樣例 {"errcode":40029,"errmsg":"invalid code"} errcode err_ok = 0(用戶同意) err_auth_denied = -4(用戶拒絕授權(quán)) err_user_cancel = -2(用戶取消) code 用戶換取access_token的code,僅在errcode為0時(shí)有效 state 第三方程序發(fā)送時(shí)用來(lái)標(biāo)識(shí)其請(qǐng)求的唯一性的標(biāo)志,由第三方程序調(diào)用sendreq時(shí)傳入,由微信終端回傳,state字符串長(zhǎng)度不能超過(guò)1k lang 微信客戶端當(dāng)前語(yǔ)言 country 微信用戶當(dāng)前國(guó)家信息 */ |
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
|
// 授權(quán)后回調(diào) // appdelegate.m - ( void )onresp:(baseresp *)resp { // 向微信請(qǐng)求授權(quán)后,得到響應(yīng)結(jié)果 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(@ "請(qǐng)求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的使用、刷新或者持續(xù) 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]; // 命令直接同步到文件里,來(lái)避免數(shù)據(jù)的丟失 } [self wechatloginbyrequestforuserinfo]; } failure:^(afhttprequestoperation *operation, nserror *error) { nslog(@ "獲取access_token時(shí)出錯(cuò) = %@" , error); }]; } } |
9、通過(guò)code獲取access_token
通過(guò)上一步獲取的code后,請(qǐng)求以下鏈接獲取access_token:
1
|
https: //api.weixin.qq.com/sns/oauth2/access_token?appid=appid&secret=secret&code=code&grant_type=authorization_code |
相關(guān)代碼上面實(shí)現(xiàn)onresp:方法,接收返回的響應(yīng)。
參數(shù)說(shuō)明:
參數(shù) 是否必須 說(shuō)明
appid 是 應(yīng)用唯一標(biāo)識(shí),在微信開(kāi)放平臺(tái)提交應(yīng)用審核通過(guò)后獲得
secret 是 應(yīng)用密鑰appsecret,在微信開(kāi)放平臺(tái)提交應(yīng)用審核通過(guò)后獲得
code 是 填寫(xiě)第一步獲取的code參數(shù)
grant_type 是 填authorization_code
返回說(shuō)明:
1
2
3
4
5
6
7
8
|
{ "access_token" : "access_token" , // 接口調(diào)用憑證 "expires_in" :7200, // access_token接口調(diào)用憑證超時(shí)時(shí)間,單位(秒) "refresh_token" : "refresh_token" , // 用戶刷新access_token "openid" : "openid" , // 授權(quán)用戶唯一標(biāo)識(shí) "scope" : "scope" , // 用戶授權(quán)的作用域,使用逗號(hào)(,)分隔 "unionid" : "o6_bmasdasdsad6_2sgvt7hmzopfl" // 只有在用戶將公眾號(hào)綁定到微信開(kāi)放平臺(tái)帳號(hào)后,才會(huì)出現(xiàn)該字段 } |
錯(cuò)誤返回樣例:
{"errcode":40029,"errmsg":"invalid code"}
10、獲取用戶個(gè)人信息(unionid機(jī)制)
1
2
|
http請(qǐng)求方式:get https: //api.weixin.qq.com/sns/userinfo?access_token=access_token&openid=openid |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// appdelegate.m // 獲取用戶個(gè)人信息(unionid機(jī)制) - ( 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]; // 請(qǐng)求用戶數(shù)據(jù) [manager get:userurlstr parameters:nil success:^(afhttprequestoperation *operation, id responseobject) { nslog(@ "請(qǐng)求用戶信息的response = %@" , responseobject); // nsmutabledictionary *userdict = [nsmutabledictionary dictionarywithdictionary:responseobject]; } failure:^(afhttprequestoperation *operation, nserror *error) { nslog(@ "獲取用戶信息時(shí)出錯(cuò) = %@" , error); }]; } |
返回的json結(jié)果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
返回的json結(jié)果 { "openid" : "openid" , "nickname" : "nickname" , "sex" :1, "province" : "province" , "city" : "city" , "country" : "country" , "headimgurl" : "http://wx.qlogo.cn/mmopen/g3monuztnhkdmzicilibx6iafqac56vxlsufpb6n5wksyvy0chqkkiajsgq1dzutogvllrhjberqq4emsv84eavhiaiceqxibjxcfhe/0" , "privilege" :[ "privilege1" , "privilege2" ], "unionid" : " o6_bmasdasdsad6_2sgvt7hmzopfl" } 返回錯(cuò)誤的json事例 { "errcode" :40003, "errmsg" : "invalid openid" } |
11、最后
做到上面一步就應(yīng)該得到返回微信的基本信息,然后根據(jù)你公司后臺(tái)的基本需求去實(shí)現(xiàn)授權(quán)后如何登錄app.
資料:
1
2
3
4
5
6
7
8
9
10
|
// access_token openid refresh_token unionid #define wxdoctor_app_id @"wxd1931d4a0e462***" // 注冊(cè)微信時(shí)的appid #define wxdoctor_app_secret @"d0dd6b58da42cbc4f4b715c70e65c***" // 注冊(cè)時(shí)得到的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,然后通過(guò)openid去后臺(tái)數(shù)據(jù)庫(kù)查詢?cè)撐⑿诺膐penid有沒(méi)有綁定好的手機(jī)號(hào).
2.如果沒(méi)有綁定,首相第一步就是將微信用戶的頭像、昵稱等等基本信息添加到數(shù)據(jù)庫(kù);然后通過(guò)手機(jī)獲取驗(yàn)證碼;最后綁定手機(jī)號(hào)。然后就登錄app.
3.如果有,那么后臺(tái)就返回一個(gè)手機(jī)號(hào),然后通過(guò)手機(jī)登錄app.
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。