3.3 第二步:通過 code 獲取 openid 和 access_token
1. 概述
項目源碼:基於 Spring Boot 和 WxJava 實現網站接入微信掃碼登錄
如果項目存在什麼問題,歡迎大家提 issue,期待你的來訪
demo 使用了Spring Boot 和 WxJava (微信開發 Java SDK,支持包括微信支付、開放平臺、小程序、企業微信/企業號和公衆號等的後端開發)
網站微信掃碼登錄相關接口還不能在微信測試號上使用,因此如果是剛入門WxJava的同學,可以轉到以下這篇文章,我寫了簡單的demo,大家可以簡單體會下使用WxJava進行Java開發的便利。
Spring Boot微信開發(基於SDK-WxJava),實現了簡單的消息處理、生成二維碼及掃碼事件處理
WxJava
WxJava 是使用 Java 進行微信開發的一款廣受好評的 SDK,GitHub star 15K+,文檔和 demo 豐富。簡單易用,極大地較少了微信開發的工作量和時間成本。感興趣的同學可到 GitHub 下載其源碼進行學習。本 Demo 僅使用到其
公衆號模塊-weixin-java-mp
最終實現效果:網站登錄 —> 引導到微信授權登錄頁—> 微信掃碼進行授權
2. 前置工作
網站應用微信登錄是基於 OAuth2.0
協議標準構建的微信OAuth2.0
授權登錄系統。 在進行微信 OAuth2.0
授權登錄接入之前,在微信開放平臺註冊開發者帳號,並擁有一個已審覈通過的網站應用,並獲得相應的 AppID
和 AppSecret
,申請微信登錄且通過審覈後,可開始接入流程。
2.1 微信開放平臺認證流程
-
進入微信開放平臺。
-
使用帳號登錄後進入到帳號中心–》開發者資格認證–》認證(需要年費300RMB,另外認證需要提交公司營業執照一些信息)此處按要求填寫即可。認證成功與否一般會在兩日內回覆結果。此處不再贅述。
-
認證成功後就可以對我們現有的網站系統進行開發了。資源中心中有官方給的一些參考 demo。可以自行查看。
-
進入到管理中心–》網站應用–》創建網站應用–》填寫基本信息–》填寫網站信息。需要說明的是開發信息中有個授權回調域,此處填寫的是我們項目所在的域名(此域名可以修改,其他信息修改需要重新審覈)。
-
提交成功後官方會對所申請的網站應用進行審覈(我到第七天才審覈通過)。審覈成功後我們就能看到我們開發所需要的 AppID 和 AppSecret(第一次使用使用需要用綁定的微信號掃碼生成)。
2.1 項目引入 WxJava
pom.xml
<properties>
<!-- more -->
<!-- WeChat -->
<weixin-java-mp.version>3.4.0</weixin-java-mp.version>
</properties>
<dependencies>
<!-- more -->
<!-- WeChatQrCode -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>${weixin-java-mp.version}</version>
</dependency>
</dependencies>
application.yml
wx:
open:
config:
# 填寫自己的 appid
appid: xxxxxxxxxxxxxxxxxxxxxxx
# 填寫自己的 secret
secret: xxxxxxxxxxxxxxxxxxxxxxxxx
redirectUrl: http://www.xxx.com/wxCallBack
csrfKey: WeChat_WxJava_Demo
Config
@Configuration
@Data
public class WxOpenConfig {
/**
* 設置雲視訊的appid
*/
@Value("${wx.open.config.appid}")
private String appid;
/**
* 設置雲視訊的app secret
*/
@Value("${wx.open.config.secret}")
private String secret;
@Bean
public WxMpService wxMpService() {
WxMpService service = new WxMpServiceImpl();
WxMpInMemoryConfigStorage configStorage = new WxMpInMemoryConfigStorage();
configStorage.setAppId(appid);
configStorage.setSecret(secret);
service.setWxMpConfigStorage(configStorage);
return service;
}
}
3. 與微信平臺交互的步驟以及實現
微信 OAuth2.0
授權登錄讓微信用戶使用微信身份安全登錄第三方應用或網站,在微信用戶授權登錄已接入微信 OAuth2.0
的第三方應用後,第三方可以獲取到用戶的接口調用憑證(access_token
),通過access_token
可以進行微信開放平臺授權關係接口調用,從而可實現獲取微信用戶基本開放信息和幫助用戶實現基礎開放功能等。 微信 OAuth2.0
授權登錄目前支持 authorization_code
模式,適用於擁有 server
端的應用授權。該模式整體流程爲:
第三方發起微信授權登錄請求,微信用戶允許授權第三方應用後,微信會拉起應用或重定向到第三方網站,並且帶上授權臨時票據
code
參數;通過code參數加上
AppID
和AppSecret
等,通過API
換取access_token
;通過
access_token
進行接口調用,獲取用戶基本數據資源或幫助用戶實現基本操作。
3.1 獲取 access_token 時序圖
3.2 第一步:請求 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 進行校驗 |
返回說明
用戶允許授權後,將會重定向到 redirect_uri
的網址上,並且帶上 code
和 state
參數
redirect_uri?code=CODE&state=STATE
若用戶禁止授權,則重定向後不會帶上 code
參數,僅會帶上 state
參數
redirect_uri?state=STATE
防止跨站請求僞造策略
3.2.1 代碼實現
獲取微信二維碼頁面的地址,這個地址一定要前端發請求給後端。因爲 redirectUrl 配置在後端且 state 參數在後端生成。
Controller
@RestController
public class WeChatController {
@Autowired
private WeChatService weChatService;
/**
* 獲取微信登陸二維碼地址
* @return
*/
@GetMapping("/getQRCodeUrl")
public RestResult getQRCodeUrl() {
return RestResultGenerator.createOkResult(weChatService.getQRCodeUrl());
}
// 省略
}
Service
@Service
@Slf4j
public class WeChatServiceImpl implements WeChatService {
@Autowired
private WxMpService wxMpService;
@Value("${wx.open.config.redirectUrl}")
private String wxRedirectUrl;
@Value("${wx.open.config.csrfKey}")
private String CSRF_KEY;
@Override
public String getQRCodeUrl() {
// 生成 state 參數,用於防止 csrf
String date = DateUtil.format(new Date(), "yyyyMMdd");
String state = MD5Utils.generate(CSRF_KEY + date);
return wxMpService.buildQrConnectUrl(wxRedirectUrl, Constant.WeChatLogin.SCOPE, state);
}
// 省略
}
3.3 第二步:通過 code 獲取 openid 和 access_token
通過 code
獲取 openid
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"
}
3.3.1 代碼實現
注意:回調地址一定是要在微信開放平臺裏我們配置的授權回調域下的接口
Controller
/**
* 微信掃碼回調處理
* 使用 @Valid + BindingResult 進行 controller 參數校驗,實現斷路器。大家可以根據自己的喜好來,不必一定要跟我這樣做
* @param input
* @param bindingResult
* @return
*/
@GetMapping("/wxCallBack")
public String wxCallBack(@RequestBody @Valid LoginProtocol.WeChatQrCodeCallBack.Input input, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "failedPage";
}
if (weChatService.wxCallBack(input)) {
return "successPage";
} else {
return "failedPage";
}
}
Service
@Override
public Boolean wxCallBack(LoginProtocol.WeChatQrCodeCallBack.Input input) {
String code = input.getCode();
String state = input.getState();
String openid = null;
String token = null;
if (code == null) {
return false;
}
if (code != null && state != null) {
// 驗證 state,防止跨站請求僞造攻擊
String date = DateUtil.format(new Date(), "yyyyMMdd");
Boolean isNotCsrf = MD5Utils.verify(CSRF_KEY + date, state);
if (!isNotCsrf) {
return false;
}
// 獲取 openid
try {
WxMpOAuth2AccessToken accessToken =wxMpService.oauth2getAccessToken(code);
openid = accessToken.getOpenId();
token = accessToken.getAccessToken();
// 拿到 openid 後做自己的業務, 獲取用 token 進一步獲取用戶信息
// 用 access_token 獲取用戶的信息
WxMpUser user = wxMpService.oauth2getUserInfo(accessToken, null);
} catch (WxErrorException e) {
log.error(e.getMessage(), e);
}
return true;
}
return false;
}
請大家支持原創,轉載請註明出處,謝謝。