賣家掃碼登陸獲取openid
注:此功能只能是微信公眾帳號能夠使用,個人賬號無此功能。
其所對接的WxMpService都是引入微信第三方sdk
一、打開微信開放平台(與支付階段不同,特別注意!!!),進入網站應用的網站應用微信登陸開發指南。
二、你會發現和之前支付功能一樣,基本步驟如下
1. 第三方發起微信授權登錄請求,微信用戶允許授權第三方應用后,微信會拉起應用或重定向到第三方網站,並且帶上授權臨時票據code參數;
2. 通過code參數加上AppID和AppSecret等,通過API換取access_token;
3. 通過access_token進行接口調用,獲取用戶基本數據資源或幫助用戶實現基本操作。
第一步:請求CODE
第三方使用網站應用授權登錄前請注意已獲取相應網頁授權作用域(scope=snsapi_login),則可以通過在PC端打開以下鏈接:
https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
若提示“該鏈接無法訪問”,請檢查參數是否填寫錯誤,如redirect_uri的域名與審核時填寫的授權域名不一致或scope不為snsapi_login。
參數說明
參數 | 是否必須 | 說明 |
---|---|---|
appid | 是 | 應用唯一標識 |
redirect_uri | 是 | 請使用urlEncode對鏈接進行處理 |
response_type | 是 | 填code |
scope | 是 | 應用授權作用域,擁有多個作用域用逗號(,)分隔,網頁應用目前僅填寫snsapi_login即 |
state | 否 | 用於保持請求和回調的狀態,授權請求后原樣帶回給第三方。該參數可用於防止csrf攻擊(跨站請求偽造攻擊),建議第三方帶上該參數,可設置為簡單的隨機數加session進行校驗 |
第二步:通過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 | 是 | 應用密鑰AppSecret,在微信開放平台提交應用審核通過后獲得 |
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"
}
參數說明
參數 | 說明 |
---|---|
access_token | 接口調用憑證 |
expires_in | access_token接口調用憑證超時時間,單位(秒) |
refresh_token | 用戶刷新access_token |
openid | 授權用戶唯一標識 |
scope | 用戶授權的作用域,使用逗號(,)分隔 |
unionid | 當且僅當該網站應用已獲得該用戶的userinfo授權時,才會出現該字段。 |
錯誤返回樣例:
{"errcode":40029,"errmsg":"invalid code"}
刷新access_token有效期
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,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE"
}
參數說明
參數 | 說明 |
---|---|
access_token | 接口調用憑證 |
expires_in | access_token接口調用憑證超時時間,單位(秒) |
refresh_token | 用戶刷新access_token |
openid | 授權用戶唯一標識 |
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雲端服務器,由雲端中轉接口調用請求。
第三步:通過access_token調用接口
獲取access_token后,進行接口調用,有以下前提:
1. access_token有效且未超時;
2. 微信用戶已授權給第三方應用帳號相應接口作用域(scope)。
對於接口作用域(scope),能調用的接口有以下:
授權作用域(scope) | 接口 | 接口說明 |
---|---|---|
snsapi_base | /sns/oauth2/access_token | 通過code換取access_token、refresh_token和已授權scope |
snsapi_base | /sns/oauth2/refresh_token | 刷新或續期access_token使用 |
snsapi_base | /sns/auth | 檢查access_token有效性 |
snsapi_userinfo | /sns/userinfo | 獲取用戶個人信息 |
其中snsapi_base屬於基礎接口,若應用已擁有其它scope權限,則默認擁有snsapi_base的權限。使用snsapi_base可以讓移動端網頁授權繞過跳轉授權登錄頁請求用戶授權的動作,直接跳轉第三方網頁帶上授權臨時票據(code),但會使得用戶已授權作用域(scope)僅為snsapi_base,從而導致無法獲取到需要用戶授權才允許獲得的數據和基礎功能。
接口調用方法可查閱《微信授權關系接口調用指南》
三、項目實例
注:Spring的@Bean注解用於告訴方法,產生一個Bean對象,然后這個Bean對象交給Spring管理。產生這個Bean對象的方法Spring只會調用一次,隨后這個Spring將會將這個Bean對象放在自己的IOC容器中。@component (把普通pojo實例化到spring容器中,相當於配置文件中的 <bean id="" class=""/>
)
泛指各種組件,就是說當我們的類不屬於各種歸類的時候(不屬於@Controller、@Services等的時候),我們就可以使用@Component來標注這個類。
1、application.yml關於微信賬號的配置
2、在config包中的WechatAccountConfig類中加入字段
package com.imooc.conf
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Map;
@Data
@Component
@ConfigurationProperties(prefix = "wechat")
public class WechatAccountConfig {
/**
* 公眾平台id
*/
private String mpAppId;
/**
* 公眾平台密鑰
*/
private String mpAppSecret;
/**
* 開放平台id
*/
private String openAppId;
/**
* 開放平台密鑰
*/
private String openAppSecret;
/**
* 商戶號
*/
private String mchId;
/**
* 商戶密鑰
*/
private String mchKey;
/**
* 商戶證書路徑
*/
private String keyPath;
/**
* 微信支付異步通知地址
*/
private String notifyUrl;
/**
* 微信模版id
*/
private Map<String, String> templateId;
}
3、往spring中注冊微信開放平台bean(所對接的接口都是WxMpService),注入第三方sdk
package com.imooc.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;
@Component
public class WechatOpenConfig {
@Autowired
private WechatAccountConfig accountConfig;
@Bean
public WxMpService wxOpenService() {
WxMpService wxOpenService = new WxMpServiceImpl();
wxOpenService.setWxMpConfigStorage(wxOpenConfigStorage());
return wxOpenService;
}
@Bean
public WxMpConfigStorage wxOpenConfigStorage() {
WxMpInMemoryConfigStorage wxMpInMemoryConfigStorage = new WxMpInMemoryConfigStorage();
wxMpInMemoryConfigStorage.setAppId(accountConfig.getOpenAppId());
wxMpInMemoryConfigStorage.setSecret(accountConfig.getOpenAppSecret());
return wxMpInMemoryConfigStorage;
}
}
4、在WechatController中定義URL接入
//1、獲取code
@GetMapping("/qrAuthorize")
public String qrAuthorize(@RequestParam("returnUrl") String returnUrl) {
String url = projectUrlConfig.getWechatOpenAuthorize() + "/sell/wechat/qrUserInfo";
String redirectUrl = wxOpenService.buildQrConnectUrl(url, WxConsts.QrConnectScope.SNSAPI_LOGIN, URLEncoder.encode(returnUrl));
return "redirect:"+ redirectUrl;
}
//2、獲取AccessToken,從中取出openId授權用戶唯一標識
@GetMapping("/qrUserInfo")
public String qrUserInfo(@RequestParam("code") String code,
@RequestParam("state") String returnUrl) {
WxMpOAuth2AccessToken wxMpOAuth2AccessToken = new WxMpOAuth2AccessToken();
try {
wxMpOAuth2AccessToken = wxOpenService.oauth2getAccessToken(code);(很重要)
} catch (WxErrorException e) {
log.error("【微信網頁授權】{}", e);
throw new SellException(ResultEnum.WECHAT_MP_ERROR.getCode(), e.getError().getErrorMsg());
}
log.info("wxMpOAuth2AccessToken={}", wxMpOAuth2AccessToken);
String openId = wxMpOAuth2AccessToken.getOpenId();
return "redirect:" + returnUrl + "?openid=" + openId;
}
}