1. 程式人生 > >微信小程式之使用者登入(獲取使用者資訊,openid,unionld) java後臺版

微信小程式之使用者登入(獲取使用者資訊,openid,unionld) java後臺版

參考文章: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 獲取頭像暱稱,不會彈框
wx.getUserInfo({ success: function (res) { that.globalData.userInfo = res.userInfo console.log(that.globalData.userInfo) }, fail: function () { wx.redirectTo({ url: '../../pages/login/login', })
} }) }else{ //未授權, 跳轉登入頁面 wx.redirectTo({ url: '../../pages/login/login', }) } } }) }, globalData: { userInfo: null, baseUrl: 'XXXXXXXXXXX', imageUrl: 'XXXXXXXX' }})

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"));  
}