需求:微信公眾號登錄網站,獲取用戶信息
前提條件#
- 微信公眾號已經認證
- 擁有伺服器,可以接收微信伺服器的回調
步驟#
1、獲取微信公眾號的 appid 和 appsecret#
2、接入伺服器,接收微信伺服器的回調,需要有域名,可以使用內網穿透工具,例如 cpolar#
首先需要準備好圖中的資源#
說明:
-
URL 表示微信驗證服務端是否存在,需要按照要求進行配置,例如我這裡的地址是
https://13a34511.r1.cpolar.top/api/user/wxcheck所以必須在自己的服務上準備好響應的接口
//外層還有/user @GetMapping("wxcheck") public String wxCheck(@RequestParam("signature") String signature, @RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce, @RequestParam("echostr") String echostr) { if (userService.wxCheck(signature, timestamp, nonce, wxOpenConfig.getToken())) { return echostr; } else { return "error"; } }需要按照微信的要求返回數據,要求如下:
1)將 token、timestamp、nonce 三個參數進行字典序排序
2)將三個參數字符串拼接成一個字符串進行 sha1 加密
3)開發者獲得加密後的字符串可與 signature 對比,標識該請求來源於微信
@Override public Boolean wxCheck(String signature, String timestamp, String nonce, String token) { // 微信公眾號接入校驗 // 將token、timestamp、nonce三個參數進行字典序排序 String[] arr = new String[]{token, timestamp, nonce}; Arrays.sort(arr); // 將三個參數字符串拼接成一個字符串進行sha1加密 StringBuilder content = new StringBuilder(); for (String s : arr) { content.append(s); } String tmpStr = MYDigestUtils.sha1DigestAsHex(content.toString()); log.info("微信公眾號接入校驗,signature = {}, tmpStr = {}", signature, tmpStr); return signature.equals(tmpStr); }接著提交上面的表單後會提示驗證通過
3、接下來就是獲取 access_token 了#
Mermaid Loading...
- 需要根據要求返回訪問微信資源的 token,訪問微信中的資源必須要這個 token,例如後續的二維碼
- 官方文檔開始開發 / 獲取 Access token (qq.com)
- 代碼:
@Override
public String getAccessToken() {
// 獲取access_token
String url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
wxOpenConfig.getAppId(), wxOpenConfig.getAppSecret());
// 發送請求
String result = restTemplate.getForObject(url, String.class);
log.info("獲取access_token,result = {}", result);
return result;
}
4、生成帶 Ticket 二維碼#
@Override
public String getQRCode(String accessToken) {
// 獲取二維碼
String url = String.format("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=%s", accessToken);
// 5分鐘過期
String body = WXUtils.getQRCodeBody("300", "QR_STR_SCENE");
String result = restTemplate.postForObject(url, body, String.class);
log.info("獲取二維碼,result = {}", result);
return result;
}
5、將二維碼的 ticket 返回給前端,前端請求到二維碼圖片展示給用戶#
6、用戶掃碼後會將響應的事件推送給上面的 URL, 只是將 GET 請求轉換為 POST 請求,格式為:#
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event>
<EventKey><![CDATA[qrscene_123123]]></EventKey>
<Ticket><![CDATA[TICKET]]></Ticket>
</xml>
當然有很多事件類型,我們可以定義策略模式分別處理
@Slf4j
public class DispatchEventHandle {
private final Map<String, EventHandle> eventHandleMap = new HashMap<>();
{
eventHandleMap.put("subscribe", new SubscribeEventHandle());
eventHandleMap.put("SCAN", new ScanEventHandle());
}
public void dispatch(JSONObject jsonObject) {
String eventType = jsonObject.getJSONObject("xml").getStr(WxEventHandleConstants.EVENT_TYPE_KEY);
log.info("事件類型:{}", eventType);
EventHandle eventHandle = eventHandleMap.get(eventType);
if (eventHandle == null) {
log.error("未找到對應的事件處理器");
return;
}
eventHandle.handle(jsonObject);
}
}
接收請求的接口定義為
@PostMapping("wxcheck")
public String event(@RequestBody String xml) {
String msgType = WXUtils.getMsgType(xml);
if ("event".equals(msgType)) {
DispatchEventHandle dispatchEventHandle = new DispatchEventHandle();
dispatchEventHandle.dispatch(JSONUtil.xmlToJson(xml));
}
return xml;
}
7、前端在展示二維碼後記得要輪詢伺服器,看是否已經掃描,後端可以在處理完成後將 ticket 作為 key,JWT 作為值快取,前端請求查詢到後順利登錄;#
此文由 Mix Space 同步更新至 xLog 原始鏈接為 https://me.liuyaowen.club/posts/default/1