對接後臺登入流程
微信官方早都已經禁止開發者直接透過 api 獲取使用者資訊資料了,大家拿個使用者的 openid 註冊好,剩下的讓使用者填寫就行了。
先上官方的經典登入流程圖:
步驟拆分解析:
- 前端透過 呼叫官方 API
wx.login
,將回撥中的code
臨時登陸憑證傳遞給(請求)後臺 - 後臺去請求微信的介面
https://api.weixin.qq.com/sns/jscode2session
。具體用法參考 官方文件 - 後臺請求該介面的響應成功的資料中可以拿到兩個值
openid
和session_key
。其中openid
是唯一值,可以當作使用者標識,比如判斷該使用者是否存在,是否註冊該使用者等。session_key
自行處理。
如果需要獲取使用者資訊(現在使用者資訊毛都沒有,獲取也沒用,別獲取了),需要在 wx.login
之前呼叫 wx.getUserProfile
將 rawData
和 signature
聯合 code 一併傳遞給後臺,後臺透過 session_key
和 rawData
來反解析 signature
的正確性。
根據現在微信官方的說法,註冊時新增微信使用者真實資訊(頭像暱稱)是不可能的,前後端互動時,透過
wx.login
傳遞一個 code 僅註冊就可以,其它引數目前完全沒用
前端程式碼
不是我不用 wx.getUserProfile
,而是這個 api 真沒啥資訊可獲取了,老老實實拿個 openid 註冊就行了。
wx.login({
success(res) {
wx.request({
url: 'http://localhost:8888/testApi/wx/login',
data: {
// encryptedData 和 iv 是用來解析私密使用者資訊的,但是現在啥也獲取不到了,所以沒用
code: res.code,
// encryptedData: InfoRes.encryptedData,
// iv: InfoRes.iv,
// rawData: InfoRes.rawData,
// signature: InfoRes.signature,
},
}).then((res) => {
// 登入認證成功,後臺一般會返回登陸憑證(token),儲存使用即可
});
},
fail(err) {
console.log(err.errMsg);
},
});
後端程式碼
請求第三方(微信)示例
請求第三方介面,這裡給出兩種獲取第三方介面的程式碼方式,任選其中一種即可。
- pom 中整合 httpclient 包。
HttpClientUtils.java
public class HttpClientUtil {
public static String doGet(String url, Map<String, String> param) {
// 建立Httpclient物件
CloseableHttpClient httpclient = HttpClients.createDefault();
String resultString = "";
CloseableHttpResponse response = null;
try {
// 建立uri
URIBuilder builder = new URIBuilder(url);
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();
// 建立http GET請求
HttpGet httpGet = new HttpGet(uri);
// 執行請求
response = httpclient.execute(httpGet);
// 判斷返回狀態是否為200
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doGet(String url) {
return doGet(url, null);
}
public static String doPost(String url, Map<String, String> param) {
// 建立Httpclient物件
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 建立Http Post請求
HttpPost httpPost = new HttpPost(url);
// 建立引數列表
if (param != null) {
List<NameValuePair> paramList = new ArrayList<>();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, param.get(key)));
}
// 模擬表單
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
httpPost.setEntity(entity);
}
// 執行http請求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doPost(String url) {
return doPost(url, null);
}
public static String doPostJson(String url, String json) {
// 建立Httpclient物件
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 建立Http Post請求
HttpPost httpPost = new HttpPost(url);
// 建立請求內容
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// 執行http請求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
}
使用
Map<String, String> params = new HashMap<>();
// .... 引數新增 params.put("...","...")
String s = HttpClientUtil.doPost(url, params);
SONObject object = JSON.parseObject(s);
- 使用 Springboot 自帶的 RestTemplate
RestTemplateUtil
public class RestTemplateUtil {
public static JSONObject doPost(String url, MultiValueMap<String, Object> param){
RestTemplate restTemplate=new RestTemplate();
String s = restTemplate.postForObject(url, param, String.class);
return JSONObject.parseObject(s);
}
}
使用
MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
// .... 引數新增 params.add("...","...")
JSONObject object = RestTemplateUtil.doPost(url, params);
登入流程對接
// 現在 rawData 和 signature 已經沒什麼用了,所以這兩個引數相關的邏輯可以刪除,此處保留僅做記錄
public R wxLogin(String code) {
String url = "https://api.weixin.qq.com/sns/jscode2session";
MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
params.add("appid",appid);
params.add("secret",secret);
params.add("js_code",code);
params.add("grant_type",grantType);
JSONObject object = RestTemplateUtil.doPost(url, params);
System.out.println(object);
//接收微信介面服務 獲取返回的引數,這裡拿到 openid 就足夠了,後邊的簽名校驗沒啥必要
String openid = object.getString("openid");
String sessionKey = object.getString("session_key");
//校驗簽名 小程式傳送的簽名signature與伺服器端生成的簽名signature2 = sha1(rawData + sessionKey)
// String signature2 = DigestUtils.sha1Hex(rawData + sessionKey);
// System.out.println(signature2);
// System.out.println(openid);
// if(!signature.equals(signature2)){
// return R.fail("簽名校驗失敗");
// }
// SONObject rawDataJson = JSONObject.parseObject(rawData);
// return R.ok(rawDataJson);
// --------這裡可以使用 openid 判斷使用者是否註冊,將使用者資訊插入資料庫
}
獲取使用者資訊
透過 API 呼叫方式直接獲取使用者資訊的方式已經完全沒有了,只能讓使用者自己手動完善個人資訊,類似於表單填寫,但在此基礎上,微信官方提供了相應的開放能力。
此處使用原生的小程式做示例,Taro 和 uniapp 的使用邏輯相同。
獲取使用者暱稱
小程式的 input 元件配置 type="nickname"
<input type="nickname" model:value="{{ nameValue }}" placeholder="請輸入暱稱" />
Page({
data: {
nameValue: '',
},
onLoad(options) {
const { name } = options;
this.setData({
nameValue: name,
});
},
onSubmit() {
// 此處可將填充的使用者暱稱提交給後端做更新
console.log(this.data.nameValue);
},
});
獲取使用者頭像
小程式的 button 元件需設定 open-type="chooseAvatar"
,並繫結對應的事件,獲取使用者的(上傳)頭像臨時地址,將其傳給後端進行資訊儲存。
<button open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar">
<image src="{{ avatarUrl }}" />
</button>
const defaultAvatarUrl = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0';
Page({
data: {
avatarUrl: defaultAvatarUrl,
},
async onChooseAvatar(e) {
const { avatarUrl = '' } = e.detail;
const { data, code } = await uploadImg(avatarUrl); // 此處將頭像上傳到後端,並返回圖片 url 地址
code === 200 &&
this.setData({
avatarUrl: data,
});
},
});