若是用戶在微信客戶端中訪問第三方網頁,公衆號能夠經過微信網頁受權機制,來獲取用戶基本信息,進而實現業務邏輯。咱們在進行公衆號網頁開發的時候,想要獲取用戶的基本信息,首先得獲取到access_token,從access_token裏咱們要拿出用戶的openid來做爲用戶在咱們系統中的惟一標識,以及經過openid能夠保證該用戶的只能訪問到與其openid相對應的數據,防止越權漏洞。所以,咱們須要對網頁進行受權,不然是沒法在獲取到用戶的openid的。java
先上官方的文檔,微信官方文檔地址以下:git
爲了可以與微信進行聯調,因此咱們須要使用到內網穿透工具,讓外網可以訪問到咱們內網的接口地址。我以前寫了一篇關於如何使用natapp進行內網穿透的文章,這裏就再也不過多贅述這些基本的工具使用了:github
在本文中會介紹兩種獲取openid的方式:本身根據文檔接口手寫代碼獲取、使用第三方已經封裝好的SDK來獲取。第三方SDK的GitHub地址:web
說明與注意:spring
(1)網頁受權分爲兩種:json
(2)你的公衆號必須爲認證的訂閱號或者認證的服務號或申請官方提供的測試號,不然沒有此接口權限。api
(3)你要配置好回調域名:即用戶點擊網址獲取用戶信息後打開哪一個域名。瀏覽器
咱們先來申請一個官方的測試號,微信測試帳號申請地址以下:bash
登陸成功後,便可獲取測試號信息:微信
下拉頁面找到測試號二維碼那一欄,而後使用微信掃描二維碼關這個注測試公衆號。關注成功後,稍等一會就會顯示出用戶列表。以下:
配置完測試號後,繼續下拉頁面找到網頁賬號的設置,點擊修改:
而後將咱們的在natapp裏註冊外網域名配置進去:
注:因爲是測試號,這一塊不會嚴格去檢測這個域名,若是是使用真實的公衆帳號進行配置時,會對配置的域名進行檢測。可是即使是測試,也要保證這個域名是可用的,否則就沒法進行聯調了。
(1)第一步,用戶贊成受權,獲取code參數:
完成以上測試帳號的配置及微信網頁受權後,建立一個SpringBoot的Web工程,並打開natapp的客戶端。關於獲取code這一步要仔細查看這部分文檔,我就不過多解釋了:
從文檔中能夠看到,須要讓用戶在訪問頁面的過程當中打開一個特定的連接,而後用戶受權成功後會跳轉回調redirect_uri參數裏指定的連接,這樣咱們就能獲取code參數了。so,在工程中建立controller包,在該包裏新建一個 WeixinController 控制器,咱們來寫一個這樣的跳轉回調接口給微信進行回調,看看可否獲取到回調時傳遞的code參數:
package org.sell.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * @program: sell * @description: 微信網頁受權,獲取用戶openid * @author: 01 * @create: 2018-05-15 13:35 **/ @RestController @RequestMapping("/weixin") @Slf4j public class WeixinController { @GetMapping("/auth") public void auth(@RequestParam("code") String code) { log.info("進入auth方法..."); log.info("code = {}", code); } }
編寫完接口代碼以後,咱們來基於文檔中給出的連接進行修改,我這裏填寫了appid、redirect_uri以及scope三個參數,其他的參數我保持了默認。因此填寫完成後的鏈接以下:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx37617xxxxxxc6c76&redirect_uri=https://zero.mynatapp.cc/sell/weixin/auth&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect
若是須要獲取用戶信息的話,則須要把scope參數改成snsapi_userinfo,用戶訪問這個鏈接時就會提示用戶登陸。可是若是是使用的測試帳號的appid,則不會彈出登陸界面。使用真實的公衆號帳號的appid纔會彈出登陸界面,咱們使用的是測試帳戶因此是不會彈出登陸界面的:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx376172f171ac6c76&redirect_uri=https://zero.mynatapp.cc/sell/weixin/auth&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect
而後複製該連接到微信中打開,必定要在微信中打開,使用pc端的瀏覽器或手機端的瀏覽器是沒法打開該鏈接的,打開後也會提示你在微信中打開。在微信訪問該地址成功後,控制檯會輸出以下內容,其中code參數獲取成功就表明這一步測試是成功的:
2018-05-15 13:47:02,365 - 進入auth方法... 2018-05-15 13:47:02,365 - code = 0219vS7617imVS1dqe861eXP7619vS7j
可是code是一次性的,不能直接使用,因此接下來的第二步咱們須要拿這個code去換取access_token。
關於使用code換取access_token這一步要仔細查看這部分文檔:
官方文檔中說,獲取code後,請求如下連接來獲取access_token:
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
一樣的,咱們也是需求根據文檔去填寫連接中的參數,例如我這裏就填寫了appid、secret以及code。因爲code參數咱們須要在auth接口被微信調用時獲取,因此修改auth接口的代碼以下:
@GetMapping("/auth") public void auth(@RequestParam("code") String code) { log.info("進入auth方法..."); log.info("code = {}", code); String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=wx3761xxxxx1ac6c76&secret=f12caef4d31axxxxx819c4c405541f4&code=" + code + "&grant_type=authorization_code"; RestTemplate restTemplate = new RestTemplate(); String response = restTemplate.getForObject(url, String.class); log.info("response = {}", response); }
從新啓動SpringBoot,而後再次到微信裏訪問以前那個獲取code參數的連接,訪問成功後,微信會進行會回調,咱們的接口就會接收到一個json格式的返回信息,其中就有咱們須要的openid。我這裏是訪問成功的,因此獲取到了這個返回信息,將其格式化後以下:
{ "access_token": "9_lZjiw19HhAga4cZ9C4rmtN50AkLzCwLVnlp8msI9xvCCWMIR88CmZAtB-IN1SGOwR_nh76V100vaTF8qihmBnfDx7XYynqHurb4UiqSZTDo", "expires_in": 7200, "refresh_token": "9_oMBSt0Bu-UUbVajpfo4OdyPwIfYbN1lut8KHjT4JEMTTbITtPnBFcQopP-SIzYvWHQuEdeQRcVHt1pRJHwvDhecDrd5pGz5HPckOCeHvmxg", "openid": "ok_HP0txxYBxxxxxxd1kZGz6A", "scope": "snsapi_base" }
從回調的數據中能夠看到,openid包含在了這個數據裏,這樣咱們就獲取到了用戶的openid,到此爲止,咱們手工獲取OpenID的方式就演示完成了。接下來咱們固然就是上工具了,有一個很優秀的第三方SDK,在文章的開頭也給出了GitHub的連接,秉承一向不重複造輪子能用現成的就用現成的良(hou)好(yan)習(wu)慣(chi),下一小節中咱們將介紹如何使用該SDK來對接微信網頁受權。
首先須要將SDK集成到咱們的項目中,在pom.xml文件中加入以下依賴配置:
<dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-mp</artifactId> <version>3.0.0</version> </dependency>
注:工程中還使用了lombok,若是沒有了解過lombok的話,可能須要瞭解一下才能看懂一些註解的做用,關於lombok的使用及配置能夠參考我以前寫的一篇文章:Lombok快速入門
關於該SDK的一些文檔地址:
因爲可能會出現異常,在咱們的工程裏新建一個enums包,用於存放枚舉類,在該包中新建一個 ResultEnum 枚舉類,代碼以下:
package org.sell.enums; import lombok.AllArgsConstructor; import lombok.Getter; @AllArgsConstructor @Getter public enum ResultEnum { WECHAT_MP_ERROR(20, "微信公衆帳號出現異常"), ; private Integer code; private String msg; }
新建一個exception包,自定義一個異常類。代碼以下:
package org.sell.exception; import org.sell.enums.ResultEnum; /** * @program: sell * @description: 自定義異常 * @author: 01 * @create: 2018-05-09 15:45 **/ public class SellException extends RuntimeException { private Integer code; public SellException(Integer code, String msg) { super(msg); this.code = code; } public SellException(ResultEnum resultEnum) { super(resultEnum.getMsg()); this.code = resultEnum.getCode(); } }
將默認的applicationContext.properties文件改成applicationContext.yml文件,而後在該文件中配置咱們的AppId以及AppSecret。以下:
wechat: mpAppId: wx37617xxxxxac6c76 mpAppSecret: f12caef4d31adxxxxxxc4c405541f4
在工程中新建config包,用於存放一些配置類,在該包下新建 WechatAccountConfig 類,用於加載咱們的配置在.yml文件裏的AppId以及AppSecret屬性。代碼以下:
package org.sell.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * @program: sell * @description: 測試號相關信息的配置類 * @author: 01 * @create: 2018-05-15 15:50 **/ @Data @Component @ConfigurationProperties(prefix = "wechat") // 載入配置文件裏前綴爲wechat的配置信息 public class WechatAccountConfig { private String mpAppId; private String mpAppSecret; }
接着再建立一個 WechatMpConfig 類,配置微信網頁受權時所需的信息。代碼以下:
package org.sell.config; import me.chanjar.weixin.mp.api.WxMpConfigStorage; import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; /** * @program: sell * @description: 微信網頁受權信息配置類 * @author: 01 * @create: 2018-05-15 15:40 **/ @Component public class WechatMpConfig { @Autowired private WechatAccountConfig wechatAccountConfig; /** * 配置WxMpService所需信息 * @return */ @Bean // 此註解指定在Spring容器啓動時,就執行該方法並將該方法返回的對象交由Spring容器管理 public WxMpService wxMpService(){ WxMpService wxMpService = new WxMpServiceImpl(); // 設置配置信息的存儲位置 wxMpService.setWxMpConfigStorage(wxMpConfigStorage()); return wxMpService; } /** * 配置appID和appsecret * @return */ @Bean public WxMpConfigStorage wxMpConfigStorage(){ // 使用這個實現類則表示將配置信息存儲在內存中 WxMpInMemoryConfigStorage wxMpInMemoryConfigStorage = new WxMpInMemoryConfigStorage(); wxMpInMemoryConfigStorage.setAppId(wechatAccountConfig.getMpAppId()); wxMpInMemoryConfigStorage.setSecret(wechatAccountConfig.getMpAppSecret()); return wxMpInMemoryConfigStorage; } }
在controller包下,新建一個 WechatController 控制器類,該類的接口用於對接微信網頁受權,受權完成後,需跳轉到咱們指定的網頁中。代碼以下:
package org.sell.controller; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; import org.sell.enums.ResultEnum; import org.sell.exception.SellException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import java.net.URLEncoder; /** * @program: sell * @description: 對接微信網頁受權 * @author: 01 * @create: 2018-05-15 15:35 **/ @Controller @RequestMapping("/wechat") @Slf4j public class WechatController { @Autowired private WxMpService wxMpService; /** * 獲取code參數 * * @param returnUrl 須要跳轉的url * @return */ @GetMapping("/authorize") public String authorize(@RequestParam("returnUrl") String returnUrl) { // 暫時將咱們的回調地址硬編碼在這裏,方便一會調試 String url = "https://zero.mynatapp.cc/sell/wechat/userInfo"; // 獲取微信返回的重定向url String redirectUrl = wxMpService.oauth2buildAuthorizationUrl(url, WxConsts.OAuth2Scope.SNSAPI_BASE, URLEncoder.encode(returnUrl)); log.info("【微信網頁受權】獲取code,redirectUrl = {}", redirectUrl); return "redirect:" + redirectUrl; } /** * 使用code參數換取access_token信息,並獲取到openid * * @param code * @param returnUrl * @return */ @GetMapping("/userInfo") public String userInfo(@RequestParam("code") String code, @RequestParam("state") String returnUrl) { WxMpOAuth2AccessToken wxMpOAuth2AccessToken; try { // 使用code換取access_token信息 wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code); } catch (WxErrorException e) { log.error("【微信網頁受權】異常,{}", e); throw new SellException(ResultEnum.WECHAT_MP_ERROR.getCode(), e.getError().getErrorMsg()); } // 從access_token信息中獲取到用戶的openid String openId = wxMpOAuth2AccessToken.getOpenId(); log.info("【微信網頁受權】獲取openId,openId = {}", openId); // 重定向到咱們要跳轉的頁面 return "redirect:" + returnUrl + "?openid=" + openId; } }
完成以上代碼的編寫後,從新啓動SpringBoot,啓動完成後,在微信裏訪問以下連接,我這裏隨便指定了一個須要跳轉的url,是個人51cto博客地址:
https://zero.mynatapp.cc/sell/wechat/authorize?returnUrl=https://blog.51cto.com/zero01
我這裏在微信訪問該鏈接後是跳轉成功的,跳轉到了咱們在連接裏指定的51cto博客地址:
控制檯輸出的日誌內容以下:
2018-05-15 17:09:03,543 - Initializing Spring FrameworkServlet 'dispatcherServlet' 2018-05-15 17:09:03,544 - FrameworkServlet 'dispatcherServlet': initialization started 2018-05-15 17:09:03,557 - FrameworkServlet 'dispatcherServlet': initialization completed in 13 ms 2018-05-15 17:09:03,580 - 【微信網頁受權】獲取code,redirectUrl = https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx376172xxxxc76&redirect_uri=https%3A%2F%2Fzero.mynatapp.cc%2Fsell%2Fwechat%2FuserInfo&response_type=code&scope=snsapi_base&state=http%3A%2F%2Fblog.51cto.com%2Fzero01#wechat_redirect 2018-05-15 17:09:05,453 - 【微信網頁受權】獲取openId,openId = ok_HP0txxYBgxxxxxx9d1kZGz6A
能夠看到,openid成功獲取,而且頁面也成功跳轉了,咱們這一步的測試也就經過了,以上就是如何使用這個第三方的SDK獲取openid。到此爲止,兩種獲取用戶openid的方式都介紹了,至於在實際項目裏使用哪種方式就看本身的實際狀況了,我這裏是使用第三方的SDK,畢竟真實的項目模塊比較多,涉及的微信特性也比較多,若是沒有特殊要求的話也不必重複去造輪子。