微信小程式之使用者登入(獲取使用者資訊,openid,unionld) java後臺版
阿新 • • 發佈:2019-01-02
參考文章:https://blog.csdn.net/guochanof/article/details/80189935;感謝作者給的思路與大部分問題解決辦法
由於微信官方api的更改,wx.getuserinfo()方法無法在無授權的情況下直接使用,參考文中作者是直接可以拉取授權,但到我這裡失效了,檢視錯誤資訊是
fail scope unauthorized
獲取授權資訊失敗;檢視官方文件https://developers.weixin.qq.com/blogdetail?action=get_post_info&lang=zh_CN&token=384460955&docid=000aee01f98fc0cbd4b6ce43b56c01後得到解決辦法,需要使用者手動點選button後呼叫授權,更改後成功.具體程式碼及後臺如下:
1.微信端
app.js:
//app.jsApp({ onLaunch: function () { // 展示本地儲存能力 var logs = wx.getStorageSync('logs') || [] logs.unshift(Date.now()) wx.setStorageSync('logs', logs) var that = this; // 獲取使用者資訊 wx.getSetting({ success: res => { if (res.authSetting['scope.userInfo']) { // 已經授權,可以直接呼叫 getUserInfo 獲取頭像暱稱,不會彈框login頁面:
wxml:
<view class="login-container"> <view class="login" wx:if="{{ !logged }}"> <view class="app-info"> <image class="app-logo" src="{{logo}}" /> <text class="app-name">{{title}}</text> </view> <view class="alert"> <view class="alert-title">尊敬的使用者,請確認授權以下資訊</view> <view class="alert-desc"> <view class="alert-text">獲得你的公開資訊(暱稱、頭像等)</view> </view> </view> <button class="weui-btn" type="primary" open-type="getUserInfo" bindgetuserinfo="login">確認登入</button> </view> <view class="logged" wx:else> <image class="logged-icon" src="../../images/iconfont-weixin.png" /> <view class="logged-text">近期你已經授權登陸過{{title}}</view> <view class="logged-text">自動登入中</view> </view></view>js:
const app = getApp();var API_URL = app.globalData.baseUrl + '/ZZTG/wXLoginController.do?decodeUserInfo';Page({ onLoad: function () { }, login:function(e){ wx.login({ success: function (r) { var code = r.code;//登入憑證 console.log(code) if (code) { //2、呼叫獲取使用者資訊介面 wx.getUserInfo({ success: function (res) { console.log({ encryptedData: res.encryptedData, iv: res.iv, code: code }) //3.請求自己的伺服器,解密使用者資訊 獲取unionId等加密資訊 wx.request({ url: API_URL,//自己的服務介面地址 method: 'POST', header: { 'content-type': 'application/x-www-form-urlencoded' }, data: { encryptedData: res.encryptedData, iv: res.iv, code: code }, success: function (data) {//4.解密成功後 獲取自己伺服器返回的結果 if (data.data.status == 1) { var userInfo_ = data.data.userInfo; app.globalData.userInfo = userInfo_; console.log(userInfo_) } else { console.log('解密失敗') }
}, fail: function () { console.log('系統錯誤') } }) }, fail: function () { console.log('獲取使用者資訊失敗') } })
} else { console.log('獲取使用者登入態失敗!' + r.errMsg) } }, fail: function () { console.log('登陸失敗') } }) }})
wxss:
.login-container { height: 100%; padding: 10px 30px; background: #fff;}.app-info { position: relative; padding: 20px; text-align: center;}
.app-info:after { content: " "; position: absolute; left: 0; bottom: 0; right: 0; height: 1px; border-bottom: 1px solid #E5E5E5; color: #E5E5E5; -webkit-transform-origin: 0 100%; transform-origin: 0 100%; -webkit-transform: scaleY(0.5); transform: scaleY(0.5);}
.app-info .app-logo { display: block; width: 64px; height: 64px; margin: 10px auto; border-radius: 4px;}
.app-info .app-name { font-weight: bold; font-size: 18px; color: #000;}
.alert { margin: 20px 0 30px;}
.alert .alert-title { font-weight: 400; font-size: 16px; color: #000; margin-bottom: 10px;}
.alert-desc { display: block; list-style: disc inside none;}
.alert .alert-text { display: list-item; text-align: -webkit-match-parent; font-size: 14px; color: #999;}
.logged { margin-top: 100px;}
.logged .logged-icon { display: block; width: 64px; height: 64px; margin: 20px auto;}
.logged .logged-text { font-size: 14px; color: #000; text-align: center; margin: 10px 0;}
json:
{ "navigationBarBackgroundColor": "#000000", "navigationBarTitleText": "微信登入", "disableScroll": true}後臺java端:
控制層:參考文中並沒有給出Controller所引的包,在這裡貼出
package com.liruan.zztg.controller;
import java.util.HashMap;
import java.util.Map;
import org.activiti.engine.impl.util.json.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.liruan.zztg.util.AesCbcUtil;
import com.liruan.zztg.util.HttpRequest;
@Controller
@RequestMapping("/wXLoginController")
public class WXLoginController {
@RequestMapping(params = "decodeUserInfo")
@ResponseBody
public Map decodeUserInfo(String encryptedData, String iv, String code) {
Map map = new HashMap();
// 登入憑證不能為空
if (code == null || code.length() == 0) {
map.put("status", 0);
map.put("msg", "code 不能為空");
return map;
}
// 小程式唯一標識 (在微信小程式管理後臺獲取)
String wxspAppid = "wxd61ae972bca3dce6";
// 小程式的 app secret (在微信小程式管理後臺獲取)
String wxspSecret = "8871bee8857cb7d997bcdbfb9d0c8438";
// 授權(必填)
String grant_type = "authorization_code";
//////////////// 1、向微信伺服器 使用登入憑證 code 獲取 session_key 和 openid
//////////////// ////////////////
// 請求引數
String params = "appid=" + wxspAppid + "&secret=" + wxspSecret + "&js_code=" + code + "&grant_type="
+ grant_type;
// 傳送請求
String sr = HttpRequest.sendGet("https://api.weixin.qq.com/sns/jscode2session", params);
// 解析相應內容(轉換成json物件)
JSONObject json = new JSONObject(sr);
// 獲取會話金鑰(session_key)
String session_key = json.get("session_key").toString();
// 使用者的唯一標識(openid)
String openid = (String) json.get("openid");
//////////////// 2、對encryptedData加密資料進行AES解密 ////////////////
try {
String result = AesCbcUtil.decrypt(encryptedData, session_key, iv, "UTF-8");
if (null != result && result.length() > 0) {
map.put("status", 1);
map.put("msg", "解密成功");
JSONObject userInfoJSON = new JSONObject(result);
Map userInfo = new HashMap();
userInfo.put("openId", userInfoJSON.get("openId"));
userInfo.put("nickName", userInfoJSON.get("nickName"));
userInfo.put("gender", userInfoJSON.get("gender"));
userInfo.put("city", userInfoJSON.get("city"));
userInfo.put("province", userInfoJSON.get("province"));
userInfo.put("country", userInfoJSON.get("country"));
userInfo.put("avatarUrl", userInfoJSON.get("avatarUrl"));
// 解密unionId & openId;
if (!userInfoJSON.isNull("unionId")) {
userInfo.put("unionId", userInfoJSON.get("unionId"));
}
map.put("userInfo", userInfo);
} else {
map.put("status", 0);
map.put("msg", "解密失敗");
}
} catch (Exception e) {
e.printStackTrace();
} return map;
}
}
引用的兩個工具類:AesCbcUtil.java和HttpRequest.java
重點標識的這個jar包commons.codec.jar需要根據自己的jdk版本做對應的引入,我的是1.7的jdk,但程式裡引入的1.3和1.9,一直報錯,刪除後引入1.7解決
package com.liruan.zztg.util;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; import java.security.*; import java.security.spec.InvalidParameterSpecException; /** * Created by yfs on 2018/3/25. * <p> * AES-128-CBC 加密方式 * 注: * AES-128-CBC可以自己定義“金鑰”和“偏移量“。 * AES-128是jdk自動生成的“金鑰”。 */ public class AesCbcUtil { static { //BouncyCastle是一個開源的加解密解決方案,主頁在http://www.bouncycastle.org/ Security.addProvider(new BouncyCastleProvider()); } /** * AES解密 * * @param data //密文,被加密的資料 * @param key //祕鑰 * @param iv //偏移量 * @param encodingFormat //解密後的結果需要進行的編碼 * @return * @throws Exception */ public static String decrypt(String data, String key, String iv, String encodingFormat) throws Exception { // initialize(); //被加密的資料 byte[] dataByte = Base64.decodeBase64(data); //加密祕鑰 byte[] keyByte = Base64.decodeBase64(key); //偏移量 byte[] ivByte = Base64.decodeBase64(iv); try { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); SecretKeySpec spec = new SecretKeySpec(keyByte, "AES"); AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES"); parameters.init(new IvParameterSpec(ivByte)); cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化 byte[] resultByte = cipher.doFinal(dataByte); if (null != resultByte && resultByte.length > 0) { String result = new String(resultByte, encodingFormat); return result; } return null; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidParameterSpecException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return null; } }package com.liruan.zztg.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;
public class HttpRequest {
public static void main(String[] args) {
//傳送 GET 請求
String s=HttpRequest.sendGet("http://v.qq.com/x/cover/kvehb7okfxqstmc.html?vid=e01957zem6o", "");
System.out.println(s);
// //傳送 POST 請求
// String sr=HttpRequest.sendPost("http://www.toutiao.com/stream/widget/local_weather/data/?city=%E4%B8%8A%E6%B5%B7", "");
// JSONObject json = JSONObject.fromObject(sr);
// System.out.println(json.get("data"));
}
/**
* 向指定URL傳送GET方法的請求
*
* @param url
* 傳送請求的URL
* @param param
* 請求引數,請求引數應該是 name1=value1&name2=value2 的形式。
* @return URL 所代表遠端資源的響應結果
*/
public static String sendGet(String url, String param) {
String result = "";
BufferedReader in = null;
try {
String urlNameString = url + "?" + param;
URL realUrl = new URL(urlNameString);
// 開啟和URL之間的連線
URLConnection connection = realUrl.openConnection();
// 設定通用的請求屬性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 建立實際的連線
connection.connect();
// 獲取所有響應頭欄位
Map<String, List<String>> map = connection.getHeaderFields();
// 遍歷所有的響應頭欄位
for (String key : map.keySet()) {
System.out.println(key + "--->" + map.get(key));
}
// 定義 BufferedReader輸入流來讀取URL的響應
in = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("傳送GET請求出現異常!" + e);
e.printStackTrace();
}
// 使用finally塊來關閉輸入流
finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result;
}
/**
* 向指定 URL 傳送POST方法的請求
*
* @param url
* 傳送請求的 URL
* @param param
* 請求引數,請求引數應該是 name1=value1&name2=value2 的形式。
* @return 所代表遠端資源的響應結果
*/
public static String sendPost(String url, String param) {
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
// 開啟和URL之間的連線
URLConnection conn = realUrl.openConnection();
// 設定通用的請求屬性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 傳送POST請求必須設定如下兩行
conn.setDoOutput(true);
conn.setDoInput(true);
// 獲取URLConnection物件對應的輸出流
out = new PrintWriter(conn.getOutputStream());
// 傳送請求引數
out.print(param);
// flush輸出流的緩衝
out.flush();
// 定義BufferedReader輸入流來讀取URL的響應
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("傳送 POST 請求出現異常!"+e);
e.printStackTrace();
}
//使用finally塊來關閉輸出流、輸入流
finally{
try{
if(out!=null){
out.close();
}
if(in!=null){
in.close();
}
}
catch(IOException ex){
ex.printStackTrace();
}
}
return result;
}
}
關於unionId
這個資訊是隻給符合條件的使用者下發,如不符合,則沒有這個資料,在呼叫時需要做相應的判斷,否則直接取值會報錯,判斷方法:
if (!userInfoJSON.isNull("unionId")) {
userInfo.put("unionId", userInfoJSON.get("unionId"));
}