天天看點

微信開放平台開發第三方授權登陸(二):PC網頁端一、需求二、開發流程三、開發使用的技術及工具四、具體實作步驟五、測試結果六、應用關鍵參數位置

微信開放平台開發系列文章:

微信開放平台開發第三方授權登陸(一):開發前期準備

微信開放平台開發第三方授權登陸(二):PC網頁端

微信開放平台開發第三方授權登陸(三):Android用戶端

微信開放平台開發第三方授權登陸(四):微信公衆号

微信開放平台開發第三方授權登陸(五):微信小程式

目錄

一、需求

二、開發流程

1.網站應用:(微信用戶端掃碼授權登陸)

三、開發使用的技術及工具

四、具體實作步驟

1、網站應用

1)請求擷取Code

2)使用者同意授權與否

3)擷取access_token

4)通過access_token調用接口擷取使用者個人資訊(UnionID機制)

5)重新整理access_token

五、測試結果

1、網站應用

六、應用關鍵參數位置

微信開放平台第三方授權登陸開發文檔(PC網頁端)

  當微信開放平台開發第三方授權登陸(一):開發前期準備完成後,已經擷取到應用的AppID和AppSecret、且已經成功申請到微信登陸功能。可以進行第三方登陸授權開發。

網站應用微信登入是基于OAuth2.0協定标準建構的微信OAuth2.0授權登入系統。

一、需求

根據需求,需要擁有第三方微信登入功能,并擷取到使用者資訊。

二、開發流程

1.網站應用:(微信用戶端掃碼授權登陸)

1)第三方發起微信授權登入請求,微信使用者允許授權第三方應用後,微信會拉起應用或重定向到第三方網站,并且帶上授權臨時票據code參數;

2)通過code參數加上AppID和AppSecret等,通過API換取access_token;

3)通過access_token進行接口調用,擷取使用者基本資料資源或幫助使用者實作基本操作。

微信開放平台開發第三方授權登陸(二):PC網頁端一、需求二、開發流程三、開發使用的技術及工具四、具體實作步驟五、測試結果六、應用關鍵參數位置

網站應用第三方授權登陸擷取使用者資訊

三、開發使用的技術及工具

1、使用IDEA2017.2進行開發

2、使用SpringBoot進行快速開發

3、使用redis進行緩存。

4、使用fastJson對json資料進行處理

5、使用Maven對項目進行管理

四、具體實作步驟

1、網站應用

建立工程

打開IDEA,建立一個工程,選擇Spring Initializr,Next。然後填寫工程基本資訊

微信開放平台開發第三方授權登陸(二):PC網頁端一、需求二、開發流程三、開發使用的技術及工具四、具體實作步驟五、測試結果六、應用關鍵參數位置

選擇SpringBoot的版本已經需要添加的依賴,也可以直接跳過,手動添加依賴。由于是web工程,需要添加web相關依賴。模闆引擎采用springboot推薦的thymeleaf。最後點選确認,進入工程。

添加相關依賴:

   <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.5</version>
   </dependency>
   <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.4</version>
   </dependency>
   <!-- 添加httpclient支援 -->
   <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
      <version>4.5.2</version>
   </dependency>
   <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>2.8.1</version>
      <type>jar</type>
   </dependency>
           

添加配置檔案

SpringBoot預設會将resources下的application名字開頭的properties作為配置檔案。是以隻需要添加application.properties就可以了

由于是使用thymeleaf作為模闆引擎。可以添加相應的配置:

#thymelea模闆配置
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
spring.thymeleaf.cache=false
spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**
           

為了讓系統更具有通用性,分别建立application-dev.properties、application-prod.properties對不同的環境進行配置。

在application.properties中,使用spring.profiles.active進行配置環境的選擇。

如:spring.profiles.active = prod

如:application-prod.properties

## 微信開放平台
# APPID
wechat.open.appid =
# APPSECRET
wechat.open.appsecret =
# 回調位址
wechat.open.redirect_uri =
           

建立javaBean:WeChatUserInfo.java

@Getter @Setter @ToString
public class WeChatUserInfo {
    String openid;
    String nickname;
    Integer sex;
    String province;
    String city;
    String country;
    String headimgurl;
    String privilege;
    String unionid;

}
           

建立WeChatOpenLoginController.java實作微信開放平台第三方登入的主要邏輯。

根據文檔進行編寫代碼。

1)請求擷取Code

前提:應用已經擷取相應的網頁授權作用域(scope=snsapi_login)

開發:第三方網站引導使用者打開連結

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

參數說明:

參數 必須 說明
appid 應用唯一辨別
redirect_uri 請使用urlEncode對連結進行處理
response_type 填code
scope 應用授權作用域,擁有多個作用域用逗号(,)分隔,網頁應用目前僅填寫snsapi_login即可
state 用于保持請求和回調的狀态,授權請求後原樣帶回給第三方。該參數可用于防止csrf攻擊(跨站請求僞造攻擊),建議第三方帶上該參數,可設定為簡單的随機數加session進行校驗

注意:若提示“該連結無法通路”,請檢查參數是否填寫錯誤,如redirect_uri的域名與稽核時填寫的授權域名不一緻或scope不為snsapi_login。

  @RequestMapping("/login")
    public String openWeChatLogin(HttpServletRequest httpServletRequest) {
        // 防止csrf攻擊(跨站請求僞造攻擊)
        String state = UUID.randomUUID().toString().replaceAll("-", "");
        // 采用redis等進行緩存state 使用sessionId為key 30分鐘後過期,可配置
        RedisPoolUtil.setEx("wechat-open-state-" + httpServletRequest.getSession().getId(), state, Integer.parseInt(env.getProperty("wechat.open.exTime", "1800")));
        String url = "https://open.weixin.qq.com/connect/qrconnect?" +
                "appid=" +
                env.getProperty("wechat.open.pc.appid").trim() +
                "&redirect_uri=" +
                env.getProperty("application.url") +
                env.getProperty("wechat.open.pc.redirect_uri").trim() +
                "&response_type=code" +
                "&scope=snsapi_login" +
                "&state=" +
                state +     // 由背景自動生成
                "#wechat_redirect";
        return "redirect:" + url;
    }
           

2)使用者同意授權與否

使用者允許授權後,将會重定向到redirect_uri的網址上,并且帶上code和state參數

redirect_uri?code=CODE&state=STATE

若使用者禁止授權,則不會重定向到我們提供的回調位址中

成功授權後,将獲得Code,通過Code可以擷取access_token

3)擷取access_token

通過上述方法擷取的code擷取access_token.

Http Get請求

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

參數說明:

參數 是否必須 說明
appid 應用唯一辨別,在微信開放平台送出應用稽核通過後獲得
secret 應用密鑰AppSecret,在微信開放平台送出應用稽核通過後獲得
code 填寫擷取的code參數
grant_type 填authorization_code

請求後,

傳回成功的json串為(樣例):

{

"access_token":"ACCESS_TOKEN",        // 接口調用憑證

"expires_in":7200,                      // access_token接口調用憑證逾時時間,機關(秒)

"refresh_token":"REFRESH_TOKEN",       //使用者重新整理access_token

"openid":"OPENID",                                  //授權使用者唯一辨別

"scope":"SCOPE",                                     //使用者授權的作用域,使用逗号(,)分隔

"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"  //當且僅當該網站應用已獲得該使用者的userinfo授權時,才會出現該字段

}

失敗傳回的樣例:

{"errcode":40029,"errmsg":"invalid code"}

失敗可能原因:  暫時不明

    @RequestMapping("/callback/pc")
    public String openWeChatCallback(HttpServletRequest httpServletRequest, Model model) {
        String code = httpServletRequest.getParameter("code");
        String state = httpServletRequest.getParameter("state");
        String url = null;
        // 判斷state是否合法
        String stateStr = RedisPoolUtil.get("wechat-open-state-" + httpServletRequest.getSession().getId());
        if (StringUtils.isEmpty(code) || StringUtils.isEmpty(stateStr) || !state.equals(stateStr)) {
            throw new WechatParamException("非法參數,請重新登陸", "/");
        }
        url = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
                "appid=" +
                env.getProperty("wechat.open.pc.appid").trim() +
                "&secret=" +
                env.getProperty("wechat.open.pc.appsecret").trim() +
                "&code=" +
                code +
                "&grant_type=authorization_code";
        JSONObject wechatAccessToken = HttpClientUtils.httpGet(url);
        if (wechatAccessToken.get("errcode") != null) {
            throw new WechatParamException("擷取accessToken失敗", "/wechat/open/login");
        }
        String accessToken = (String) wechatAccessToken.get("access_token");
        String openid = (String) wechatAccessToken.get("openid");
        String unionid = (String) wechatAccessToken.get("unionid");
        if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(openid) || StringUtils.isEmpty(unionid)) {
            throw new WechatParamException("擷取accessToken失敗", "/wechat/open/login");
        }
        // TODO:根據Openid或Unionid對資料庫進行查詢,如果查詢到對應的使用者資料,則不需要再向微信伺服器發送請求去傳回資料。
        // TODO: 建議使用Unionid作為查詢條件。
        WeChatUserInfo weChatUserInfo = null;
        wechatAccessToken = null;  // FIXME: 這裡應該是從資料庫中查詢擷取使用者資訊邏輯。
        if (wechatAccessToken == null) {
            // 新使用者
            weChatUserInfo = getUserInfoByAccessToken(accessToken);
            // 資料庫插入的操作
        }
        if (weChatUserInfo != null) {
            model.addAttribute(weChatUserInfo);
            return "wechatUser";
        }
        throw new WechatParamException("擷取使用者資訊失敗", "/wechat/open/login");
    }
           

4)通過access_token調用接口擷取使用者個人資訊(UnionID機制)

前提:

1. access_token有效且未逾時;

2. 微信使用者已授權給第三方應用帳号相應接口作用域(scope)。

Http Get請求:

https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID

參數 是否必須 說明
access_token 調用憑證
openid 普通使用者的辨別,對目前開發者帳号唯一
lang 國家地區語言版本,zh_CN 簡體,zh_TW 繁體,en 英語,預設為zh-CN

傳回成功的json結果(樣例):

{

"openid":"OPENID",        //普通使用者的辨別,對目前開發者帳号唯一

"nickname":"NICKNAME",   //普通使用者昵稱

"sex":1,                                //普通使用者性别,1為男性,2為女性

"province":"PROVINCE",      //普通使用者個人資料填寫的省份

"city":"CITY",                        //普通使用者個人資料填寫的城市

"country":"COUNTRY",         //國家,如中國為CN

"headimgurl": "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",       //使用者頭像,最後一個數值代表正方形頭像大小(有0、46、64、96、132數值可選,0代表640*640正方形頭像),使用者沒有頭像時該項為空

"privilege":[   //使用者特權資訊,json數組,如微信沃卡使用者為(chinaunicom)

"PRIVILEGE1",

"PRIVILEGE2"

],

"unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"   //使用者統一辨別。針對一個微信開放平台帳号下的應用,同一使用者的unionid是唯一的

}

失敗JSON樣例:

{"errcode":40003,"errmsg":"invalid openid"}

注意:在使用者修改微信頭像後,舊的微信頭像URL将會失效,是以開發者應該自己在擷取使用者資訊後,将頭像圖檔儲存下來,避免微信頭像URL失效後的異常情況

最好儲存使用者unionID資訊,以便以後在不同應用中進行使用者資訊互通。

private WeChatUserInfo getUserInfoByAccessToken(String accessToken) {
        if (StringUtils.isEmpty(accessToken)) {
            return null;  //"accessToken為空";
        }
        String get_userInfo_url = null;
        get_userInfo_url = "https://api.weixin.qq.com/sns/userinfo?" +
                "access_token=" +
                accessToken +
                "&openid=" +
                env.getProperty("wechat.open.pc.appid").trim();
        String userInfo_result = HttpClientUtils.httpGet(get_userInfo_url, "utf-8");
        if (!userInfo_result.equals("errcode")) {
            WeChatUserInfo weChatUserInfo = JSON.parseObject(userInfo_result, new TypeReference<WeChatUserInfo>() {
            });
            // TODO: 需要把頭像資訊下載下傳到檔案伺服器,然後替換掉頭像URL。微信的或許不可靠,假設微信使用者更換了頭像,舊頭像URL是否會儲存?而這個URL資訊卻存放在我們的資料庫中,不可靠
            return weChatUserInfo;
        }
        return null;  //"擷取使用者資訊失敗"
    }
           

5)重新整理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失效的後,需要使用者重新授權。

請求方法:

擷取第一步的code後,請求以下連結進行refresh_token:

https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN

參數說明:

參數 是否必須 說明
appid 應用唯一辨別
grant_type 填refresh_token
refresh_token 填寫通過access_token擷取到的refresh_token參數

成功傳回的結果:

{

"access_token":"ACCESS_TOKEN",    //接口調用憑證

"expires_in":7200,                             // access_token接口調用憑證逾時時間,機關(秒)

"refresh_token":"REFRESH_TOKEN",   //使用者重新整理access_token

"openid":"OPENID",                          //授權使用者唯一辨別

"scope":"SCOPE"                              //使用者授權的作用域,使用逗号(,)分隔

}

失敗樣例:

{"errcode":40030,"errmsg":"invalid refresh_token"}

注意:

1、Appsecret 是應用接口使用密鑰,洩漏後将可能導緻應用資料洩漏、應用的使用者資料洩漏等高風險後果;存儲在用戶端,極有可能被惡意竊取(如反編譯擷取Appsecret);

2、access_token 為使用者授權第三方應用發起接口調用的憑證(相當于使用者登入态),存儲在用戶端,可能出現惡意擷取access_token 後導緻的使用者資料洩漏、使用者微信相關接口功能被惡意發起等行為;

3、refresh_token 為使用者授權第三方應用的長效憑證,僅用于重新整理access_token,但洩漏後相當于access_token 洩漏,風險同上。

建議将secret、使用者資料(如access_token)放在App雲端伺服器,由雲端中轉接口調用請求。

五、測試結果

1、網站應用

首先開啟redis。然後運作代碼。由于SpringBoot會使用内置的Tomcat容器進行管理,直接運作就可以了,然後點選微信登入。彈出掃碼登入視窗,手機掃碼同意登入後就可以擷取到使用者資訊了.

微信開放平台開發第三方授權登陸(二):PC網頁端一、需求二、開發流程三、開發使用的技術及工具四、具體實作步驟五、測試結果六、應用關鍵參數位置
微信開放平台開發第三方授權登陸(二):PC網頁端一、需求二、開發流程三、開發使用的技術及工具四、具體實作步驟五、測試結果六、應用關鍵參數位置

六、應用關鍵參數位置

微信開放平台開發第三方授權登陸(二):PC網頁端一、需求二、開發流程三、開發使用的技術及工具四、具體實作步驟五、測試結果六、應用關鍵參數位置

APPID和APPSecret以及回調位址位置,注:需要修改授權回調域,即回調位址

微信開放平台開發第三方授權登陸(二):PC網頁端一、需求二、開發流程三、開發使用的技術及工具四、具體實作步驟五、測試結果六、應用關鍵參數位置