TNWX-微信公衆號授權獲取用戶信息

簡介

TNW: TypeScript(The) + Node.js(Next) + WeChat 微信公衆號開發腳手架,支持任何 Node.js 的服務端框架(Express、Nest、egg 等)

什麼是 OAuth2.0

微信公衆平臺OAuth2.0授權詳細步驟

配置授權回調頁面域名

請參考之前寫的文章 微信公衆號開發之授權獲取用戶信息-Java版 此篇文章已有 2.4 w+ 的閱讀量

授權用戶信息的一些說明

關於網頁授權的兩種scope的區別說明

1、以 snsapi_base 爲 scope 發起的網頁授權,是用來獲取進入頁面的用戶的 openid 的,並且是靜默授權並自動跳轉到回調頁的。用戶感知的就是直接進入了回調頁(往往是業務頁面)

2、以 snsapi_userinfo 爲 scope 發起的網頁授權,是用來獲取用戶的基本信息的。但這種授權需要用戶手動同意,並且由於用戶同意過,所以無須關注,就可在授權後獲取該用戶的基本信息。

3、用戶管理 類接口中的 獲取用戶基本信息接口,是在用戶和公衆號產生消息交互或關注後事件推送後,才能根據用戶 openid 來獲取用戶基本信息。這個接口,包括其他微信接口,都是需要該用戶(即openid)關注了公衆號後,才能調用成功的。

關於特殊場景下的靜默授權

1、上面已經提到,對於以snsapi_base爲scope的網頁授權,就靜默授權的,用戶無感知;

2、對於已關注公衆號的用戶,如果用戶從公衆號的會話或者自定義菜單進入本公衆號的網頁授權頁,即使是scope爲snsapi_userinfo,也是靜默授權,用戶無感知。

具體而言,網頁授權流程分爲四步:

1、引導用戶進入授權頁面同意授權,獲取code

2、通過 code 換取網頁授權 access_token(與基礎支持中的access_token不同)

3、如果需要,開發者可以刷新網頁授權 access_token,避免過期

4、通過網頁授權 access_token 和 openid 獲取用戶基本信息(支持UnionID機制)

用戶同意授權,獲取code

引導關注者打開如下授權的頁面URL:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

若提示“該鏈接無法訪問”,請檢查參數是否填寫錯誤,是否擁有scope參數對應的授權作用域權限。

尤其注意:

  1. 由於授權操作安全等級較高,所以在發起授權請求時,微信會對授權鏈接做正則強匹配校驗,如果鏈接的參數順序不對,授權頁面將無法正常訪問
  2. 跳轉回調redirect_uri,應當使用https鏈接來確保授權code的安全性且必須有在MP配置回調頁面的域名。

用戶同意授權後

如果用戶同意授權,頁面將跳轉至 redirect_uri/?code=CODE&state=STATE。

特別注意 : code作爲換取 access_token 的票據,每次用戶授權帶上的 code 將不一樣,code 只能使用一次,5分鐘未被使用自動過期。

通過code換取網頁授權access_token

刷新access_token(如果需要)

拉取用戶信息(需scope爲 snsapi_userinfo)

檢驗授權憑證(access_token)是否有效

TNW 中的封裝

export class SnsAccessTokenApi {

    private static authorizeUrl: string = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s";
    private static accessTokenUrl: string = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code"
    private static refreshTokenUrl: string = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s"
    private static userInfoUrl: string = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=%s";
    private static checkTokenUrl: string = "https://api.weixin.qq.com/sns/auth?access_token=%s&openid=%s";

    /**
     * 獲取授權鏈接
     * @param redirectUri 回調地址
     * @param scope 
     * @param state 
     */
    public static getAuthorizeUrl(redirectUri: string, scope: ScopeEnum, state?: string): string {
        let url = util.format(this.authorizeUrl, ApiConfigKit.getApiConfig.getAppId, urlencode(redirectUri), scope);
        if (state) {
            url = url + "&state=" + state;
        }
        return url + "#wechat_redirect";
    }
    /**
     * 通過code換取網頁授權access_token
     * @param code 
     */
    public static async getSnsAccessToken(code: string) {
        let url = util.format(this.accessTokenUrl, ApiConfigKit.getApiConfig.getAppId,
            ApiConfigKit.getApiConfig.getAppScrect, code);
        return HttpKit.getHttpDelegate.httpGet(url);
    }
    /**
     * 刷新access_token
     * @param refreshToken 
     */
    public static async refreshAccessToken(refreshToken: string) {
        let url = util.format(this.refreshTokenUrl, ApiConfigKit.getApiConfig.getAppId, refreshToken);
        return HttpKit.getHttpDelegate.httpGet(url);
    }
    /**
     * 檢驗授權憑證(access_token)是否有效
     * @param accessToken 通過code換取的access_token
     * @param openId 
     */
    public static async checkAccessToken(accessToken: string, openId: string) {
        let url = util.format(this.checkTokenUrl, accessToken, openId);
        return HttpKit.getHttpDelegate.httpGet(url);
    }
    /**
     * 拉取用戶信息(需scope爲 snsapi_userinfo)
     * @param accessToken 
     * @param openId 
     * @param lang 
     */
    public static async getUserInfo(accessToken: string, openId: string, lang: Lang) {
        let url = util.format(this.userInfoUrl, accessToken, openId, lang);
        return HttpKit.getHttpDelegate.httpGet(url);
    }

}

export enum ScopeEnum {
    SNSAPI_BASE = "snsapi_base",
    SNSAPI_USERINFO = "snsapi_userinfo"
}

export enum Lang {
    ZH_CN = "zh_CN",
    ZH_TW = "zh_TW",
    EN = "en"
}

TNW 案例

訪問:http/https://域名/toAuth

回調:http/https://域名/auth

app.get('/toAuth', (req, res) => {
    let url = SnsAccessTokenApi.getAuthorizeUrl("http://xxx/auth", ScopeEnum.SNSAPI_USERINFO, "IJPay");
    console.log("授權URL:", url);
    res.redirect(url);
});
// 授權回調
app.get('/auth', (req, res) => {
    let code = req.query.code;
    let state = req.query.state;
    console.log("code:", code, " state:", state);

    SnsAccessTokenApi.getSnsAccessToken(code).then(data => {
        let temp = JSON.parse(data.toString());
        // 判斷 access_token 是否獲取成功
        if (temp.errcode) {
            // access_token 獲取失敗
            res.send(temp);
            return;
        }

        let access_token = temp.access_token;
        let openid = temp.openid;
        let scope = temp.scope;
        if (scope == ScopeEnum.SNSAPI_USERINFO) {
            // 獲取用戶信息
            SnsAccessTokenApi.getUserInfo(access_token, openid, Lang.ZH_CN).then(data => {
                res.send(data);
            });
        } else {
            res.send(temp);
        }
    })
});

常見錯誤

1、請在微信客戶端中打開

授權獲取用戶信息必須在微信客戶端中打開或者使用微信提供的 微信開發者工具

2、redirect_url 參數錯誤

請檢查appId對應的公衆平臺中設置的授權域名是否與你項目中配置的域名保持一致

3、測試號測試時提示未關注測試號

測試號測試授權是必須先關注的測試的號,官方這做是爲了安全。正式環境微信認證的服務號是不用關注就可以獲取用戶的信息。

開源推薦

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章