1. 程式人生 > >iOS 【微信登入流程以及遇到的一些坑】

iOS 【微信登入流程以及遇到的一些坑】

最近公司中為了降低登入門檻,讓使用者不再每次都輸入賬號密碼進行登入。所以引入了微信登入和QQ登入,讓使用者更加便捷的登入操作。大多數的第三方登入都是相仿的,按照整合文件一步步的整合就好了,仔細閱讀文件一般沒有太大問題。整合的步驟我不一一贅述,將我遇到的一些坑列舉出來,供大家參考。

微信登入整合的主要流程:

授權微信三方登入,請求 code ——> 通過 code 獲取 access_token 以及 openId ——> 拿到 openId 進行登入

再次請求 ——> access_token 未過期 ——> 直接拿到 openId 進行登入

或 再次請求 ——> access_token 過期 ——> 重新通過 refresh_token 進行重新整理 access_token 和 openId ——> 再次拿到 openId 進行登入

或 再次請求 ——>  access_token 過期 && refresh_token 過期 ——> 重新進行授權請求

(code 相當於是一個兌換碼,而 access_token 相當於是一個驗證碼,為了安全,才有這兩個設定。而 openId 才是我們要的,因為他是使用者的唯一標示符。這裡注意,在同一個微信開發者賬戶下如果同時集成了 安卓 和 iOS 的程式,這在不同平臺上的兩個程式是完全不同的,我們用同一個 微信 或者 QQ 分別去登入安卓程式和iOS程式,得到的 openId 是完全不同的,這時需要開發者賬戶去傳送一個郵件去資料打通,用於關聯 iOS 和 安卓 兩個程式,用於保證用同一第三方賬號登入兩個程式時返回的 openId 是一致的。申請方式如下圖。)


上面整合流程一般用於只有第三方登入的 app,請求步驟完全交由第三方平臺認證。我司 app 含自有賬號密碼進行登入,所以整合步驟並沒有那麼複雜。

我司整合步驟:

① 首先獲取到微信的 openId,然後通過 openId 去後臺數據庫查詢該微信的 openId 有沒有繫結好的我司自有賬號;

② 如果沒有繫結我司自有賬號,就跳轉註冊繫結介面,通過手機獲取驗證碼,填寫密碼,最後繫結手機號(手機號作為我司自有賬號)。然後就登入App。

③ 如果該微信使用者 openId 已經繫結我司自有賬號。 那麼就通過 openId 和 authName(授權型別“weixin”或“QQ”)進行登入 或者 通過 我司自有賬號 和 密碼 進行登入。(也就是將 openId 和 authName 與 自有賬號 和 密碼 建立一對一的關係,隨便二選一進行登入)

這樣做的原因是降低了使用者二次登入的門檻,不需要輸入自有賬號和密碼進行登入。每次登入請求 openId,拿到 openId 進行登入。實質上是在我們自有賬號和微信登入之間建立唯一橋樑罷了。方便了使用者的操作。

遇到的一點坑:

① onResp 這個方法是在向微信請求授權之後回撥的,但可能程式碼按照文件整合之後並沒有來到這個方法。如果遇到這種情況應先檢查 onResp 的位置,一般來說是在 AppDelegate.m 進行呼叫的,因為代理是在 AppDelegate.m 的方法中設定的,具體方法如下:

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url NS_DEPRECATED_IOS(2_0, 9_0, "Please use application:openURL:options:") __TVOS_PROHIBITED;
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(nullable NSString *)sourceApplication annotation:(id)annotation NS_DEPRECATED_IOS(4_2, 9_0, "Please use application:openURL:options:") __TVOS_PROHIBITED;

在文件中宣告要同時整合上面兩個方法,但是這兩個方法只支援到 iOS 9,在方法中也提醒我們了:

"Please use application:openURL:options:"
所以說如今我們應該整合如下方法:
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options NS_AVAILABLE_IOS(9_0); // no equiv. notification. return NO if the application can't open for some reason
建議大家將這三個方法同時整合,避免一些未知的麻煩。整合方法如下:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
    if ([TencentOAuth HandleOpenURL:url]) {
        return [TencentOAuth HandleOpenURL:url];
    }
    return [WXApi handleOpenURL:url delegate:self];
}

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
    if ([TencentOAuth HandleOpenURL:url]) {
        return [TencentOAuth HandleOpenURL:url];
    }
    return [WXApi handleOpenURL:url delegate:self];
}

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary*)options {
    if ([TencentOAuth HandleOpenURL:url]) {
        return [TencentOAuth HandleOpenURL:url];
    }
    return [WXApi handleOpenURL:url delegate:self];
}
同時整合QQ和微信三方登入時要注意區分不同的第三方授權回撥。

② 在 onResp 中,我們會拿到用於請求 access_token 和 openId 的 code,然後傳送網路請求,如下程式碼:

/*
 * 傳送一個sendReq後,收到微信的迴應
 */
- (void)onResp:(BaseResp *)resp {
    NSLog(@"onResp");
    // 向微信請求授權後,得到響應結果
    if ([resp isKindOfClass:[SendAuthResp class]]) {
        SendAuthResp *authResp = (SendAuthResp *)resp;
        NSLog(@"code --- %@", authResp.code);
        if (authResp.code && authResp.code.length != 0) { // 微信取消授權後會再次呼叫這個方法,然後code為空,必須判斷一下,不然崩掉。
            NSString *path = @"https://api.weixin.qq.com/sns/oauth2/access_token";
            NSDictionary *params = @{
                                     @"appid":@"412b********08w9",
                                     @"secret":@"dh32********g09l",
                                     @"code":authResp.code,
                                     @"grant_type":@"authorization_code",
                                     };
            AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
            manager.requestSerializer.timeoutInterval = 10;
            manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html", @"text/json", @"text/javascript", @"text/plain", nil];
            __weak typeof(self) weakSelf = self;
            [manager GET:path parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                NSLog(@"responseObject -- %@", responseObject);
                
                NSString *openId = responseObject[@"openid"];
                NSString *authName = @"weixin";
                NSString *access_token = responseObject[@"access_token"];
                
                if (access_token && [access_token length] != 0) { // 通過驗證才可繼續登入,如果只有 openId 是不行的,保險起見。
                    BPBLoginViewController *loginVc = [[BPBLoginViewController alloc] init];
                    weakSelf.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:loginVc];
                    // 第三方登入
                    [loginVc thirdLoginWithOpenId:openId authName:authName];
                }
                
            } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                [SVProgressHUD showErrorWithStatus:[NSString stringWithFormat:@"錯誤碼:%ld", error.code]];
            }];
        
        } else {
            [SVProgressHUD showErrorWithStatus:@"您取消了微信登入授權!"];
        }
    }
}

上面有幾處需要注意的地方:

① 獲取 code 的物件應是 BaseResp 的子類。

SendAuthResp *authResp = (SendAuthResp *)resp;

② acceptableContentTypes 要設定上 @"text/plain" 和 @"text/html",便於伺服器端的解析。如果不設定,可能會返回如下亂碼:

<7b226163 63657373 5f746f6b 656e223a 226d5965 725a3165 65484858 43396776 355a4933 35684146 34535f5a 2d632d79 6d724668 416e594e 774e704c 41776f6c 526d4b6b 70716b57 746f5748 394f6351 424f736e 58327947 4231756d 49447068 51395f50 33616252 7258772d 65445632 42526650 5f72366a 4f684834 222c2265 78706972 65735f69 6e223a37 3230302c 22726566 72657368 5f746f6b 656e223a 222d766f 5a7a4530 796d6a50 6a356455 59686a55 67555763 434c7659 30564b32 59365f2d 42547531 67373342 50796f55 65564363 4a7a674e 37724962 576a7678 4a327747 55466e71 61566b7a 4945334e 6f4f5834 576d4e51 7a49566c 49424f4b 67383851 31595a49 57444967 222c226f 70656e69 64223a22 6f324c34 56755074 77732d6b 4d71306f 63454b4e 66434f50 65797563 222c2273 636f7065 223a2273 6e736170 695f7573 6572696e 666f222c 22756e69 6f6e6964 223a226f 61686233 747a6659 43583143 4f4a6852 4f73326a 51526959 6a766722 7d>
③ 要對 code 進行判空操作。 ④ 要對 access_token 進行判空操作。