.NET之微信小程序獲取用戶UnionID

前言:

  在實際項目開發中咱們常常會遇到帳號統一的問題,如何在不一樣端或者是不一樣的登陸方式下保證同一個會員或者用戶帳號惟一(便於用戶信息的管理)。這段時間就有一個這樣的需求,以前有個客戶作了一個微信小程序商城(店主端的),而後如今又要作一個會員購物端的小程序商場。首先以前用戶登陸憑證都是使用微信openid來作的惟一標識,而如今客戶需求是要作到用戶在會員端小程序跳轉到到店主端小程序假如以前該用戶微信是在店主端審覈經過的用戶則不須要在進行資料提交審覈操做,直接登陸。因此,因此咱們使用了UnionID來進行關聯,以下是咱們如今項目的基本流程(畫的醜莫見怪)。html

說說UnionID機制:

  若是開發者擁有多個移動應用、網站應用、和公衆賬號(包括小程序),可經過 UnionID 來區分用戶的惟一性,由於只要是同一個微信開放平臺賬號下的移動應用、網站應用和公衆賬號(包括小程序),用戶的 UnionID 是惟一的。換句話說,同一用戶,對同一個微信開放平臺下的不一樣應用,unionid是相同的。web

官方UnionID機制詳細說明:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/union-id.html算法

微信開放平臺綁定小程序流程:

登陸微信開放平臺 — 管理中心 — 小程序 — 綁定小程序(直接使用微信官方圖)json

微信小程序獲取UnoinID的兩種方式:

調用接口 wx.getUserInfo,從解密數據(encryptedData)中獲取 UnionID(推薦使用):

推薦使用緣由:無需關注微信公衆號便可獲取到UnionID。小程序

調用接口wx.getUserInfo前提:用戶容許受權獲取用戶信息!後端

開發者後臺校驗與解密開放數據:

  微信爲了保證用戶信息,把用戶經過wx.getUserInfo接口獲取到的相關敏感信息進行了加密。加密方式對稱加密(後面會提到),首先咱們須要經過微信小程序登陸流程獲取到用戶的session_key(會話密鑰),而後咱們能夠報獲取到的會話密鑰使用緩存存起來,在經過用戶受權獲取用戶相關信息,以下是用戶受權成功獲取到的用戶信息:微信小程序

基本流程圖以下:

 

 

(encryptedData)加密數據解密算法:

開發者如須要獲取敏感數據,須要對接口返回的加密數據(encryptedData) 進行對稱解密。 解密算法以下:api

  1. 對稱解密使用的算法爲 AES-128-CBC,數據採用PKCS#7填充。
  2. 對稱解密的目標密文爲 Base64_Decode(encryptedData)。
  3. 對稱解密祕鑰 aeskey = Base64_Decode(session_key), aeskey 是16字節。
  4. 對稱解密算法初始向量 爲Base64_Decode(iv),其中iv由數據接口返回

很遺憾的是微信竟然沒有爲咱們大.Net提供解密算法demo,實屬讓人不算,最後本身根據網上的資料仍是配上了符合微信對稱加密的解密算法。數組

代碼實現:promise

首先關於session_key(會話密鑰)的獲取,請看下面的wx.login+code2Session 方式

調用接口wx.getUserInfo獲取encryptedData(加密數據)和iv(初始向量):

// 用戶已經受權
wx.getUserInfo({
success: function(res) {
console.log(res);
var userInfo = res.userInfo //用戶基本信息
let sessionKey = wx.getStorageSync("session_key");//臨時會話密鑰,經過小程序登陸流程獲取到的
//請求.net webapi解密接口
wx.request({
 url: 'https://www.xxxtest.com/api/User_oAuth/DecryptSensitiveData',
data: {
sessionKey:sessionKey,
encryptedData:res.encryptedData,
iv:res.iv
},
 header: {
'content-type': 'application/json' // 默認值
},
success (res) {
//解密返回過來的UnionID
console.log(res.data)
}
})
}
})
})

.Net WebApi 解密數據接口:

        /// <summary>
        /// 解密微信對稱加密數據,獲取用戶聯合運營編號
        /// </summary>
        /// <param name="sessionKey">臨時會話祕鑰</param>
        /// <param name="encryptedData">微信用戶敏感加密數據</param>
        /// <param name="iv">解密初始向量</param>
        /// <returns></returns>
        [HttpGet]
        public IHttpActionResult DecryptSensitiveData(string sessionKey,string encryptedData,string iv)
        {
            try
            {
                var getUnionId=DecryptByAesBytes(encryptedData, sessionKey, iv);

                return Json(new { code =1, msg="解密成功",result= getUnionId });
            }
            catch (Exception ex)
            {
                return Json(new { code = 0, msg = "解密失敗,緣由:"+ex.Message });
            }
        }

        #region AES對稱解密
        /// <summary>
        /// AES解密
        /// </summary>
        /// <param name="encryptedData">待解密的字節數組</param>
        /// <param name="sessionKey">解密密鑰字節數組</param>
        /// <param name="iv">IV初始化向量字節數組</param>
        /// <param name="cipher">運算模式</param>
        /// <param name="padding">填充模式</param>
        /// <returns></returns>
        private static string DecryptByAesBytes(string encryptedData, string sessionKey, string iv)
        {
            try
            {
                //非空驗證
                if (!string.IsNullOrWhiteSpace(encryptedData) && !string.IsNullOrWhiteSpace(sessionKey) && !string.IsNullOrWhiteSpace(iv))
                {
                    var decryptBytes = Convert.FromBase64String(encryptedData.Replace(' ', '+'));
                    var keyBytes = Convert.FromBase64String(sessionKey.Replace(' ', '+'));
                    var ivBytes = Convert.FromBase64String(iv.Replace(' ', '+'));

                    var aes = new AesCryptoServiceProvider
                    {
                        Key = keyBytes,
                        IV = ivBytes,
                        Mode = CipherMode.CBC,
                        Padding = PaddingMode.PKCS7
                    };

                    var outputBytes = aes.CreateDecryptor().TransformFinalBlock(decryptBytes, 0, decryptBytes.Length);

                    var decryptResult = Encoding.UTF8.GetString(outputBytes);
                    dynamic decryptData = JsonConvert.DeserializeObject(decryptResult, new { unionid = "" }.GetType());
                    JJHL.Utility.Loghelper.WriteLog("AES對稱解密結果爲:" + decryptResult);
                    return decryptData.unionid;
                }
                else
                {
                    return "";
                }
            }
            catch (Exception e)
            {
                JJHL.Utility.Loghelper.WriteLog("AES對稱解密失敗緣由:" + e.Message);
                return "";
            }
        }

        #endregion

所遇異常:參數使用Convert.FromBase64String轉化時,提示「Base-64字符數組的無效長度」 的問題:

緣由:加密參數中的"+"經過地址欄傳過來時,後臺會解析爲空格(遇到的機率比較小)。

解決:最好的作法是 使用encryptedData.Replace("+", "%2B")先將空格編碼,而後再做爲參數傳給另外一頁面傳遞,這樣頁面在提取參數時纔會將「%2B」解碼爲加號.但這兒爲了簡化,將空格直接還原爲"+"或者是直接在後臺將空格替換爲「+」encryptedData.Replace('  ', '+');

直接經過 wx.login + code2Session 獲取到該用戶 UnionID:

其實這個方式就是實現了小程序的登陸流程,微信官方詳細說明:

https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html

優勢:無需用戶受權。

前提:用戶須要關注該微信公衆號。

小程序端調用接口wx.login獲取code憑證,在經過請求auth.code2Session接口獲取用戶信息(UnionID,openid,session_key會話密鑰)兩種方式:

1.直接經過wx.login請求到code憑證後,在請求該地址獲取用戶信息:
GET:https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code

詳細說明請看微信官方文檔(代碼略):https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html

2.經過請求wx.login獲取code憑證,在向.net webapi後端請求code2Session接口:

緣由:由於咱們須要對獲取的用戶信息作相關業務邏輯處理。

微信小程序端代碼:
/**
*封裝用戶promise登陸,經過code憑證獲取用戶信息(UnionID,openid,session_key會話密鑰)
*/
userLogin: function() {
var that = this;
//定義promise方法
return new Promise(function(resolve, reject) {
//調用登陸接口
wx.login({
success: function(res) {
if (res.code) {
console.log("用戶登陸受權code爲:" + res.code);
//調用wx.request請求傳遞code憑證換取用戶openid,並獲取後臺用戶信息
wx.request({
url: 'https://www.xxxx.xxx.api/User_oAuth/GetUserInfo',//後臺請求用戶信息方法
data: {
code: res.code //code憑證
},
header: {
'content-type':'application/json' // 默認值
},
success(res) {
console.log(res.data)
if (res.data.errcode == 0) {
//存入session緩存中
console.log(res.data.openid);//微信用戶惟一標識
console.log(res.data.UnionID);//微信開發平臺聯合ID
console.log(res.data.session_key);//會話密鑰
//***注意****
//注意:這裏是直接把session_key緩存起來,在上面wx.getUserInfo會使用到
wx.setStorageSync("session_key",res.data.session_key);
//promise機制放回成功數據 
resolve(res.data);
}
else
{ reject(
'error'); }
}, fail: function(res)
{
reject(res);
wx.showToast({ title:
'系統錯誤' })
}, complete: ()
=> { } //complete接口執行後的回調函數,不管成功失敗都會調用
}) } else
{
reject(
"error");
}}
}) })}
.Net WebApi 請求用戶信息接口:
        /// <summary>
        /// 獲取用戶信息
        /// </summary>
        /// <param name="code">信息數據code憑證</param>
        /// <returns></returns>
        [HttpGet]
        public IHttpActionResult GetUserInfo(string code)
        {
            string AppSecret = "小程序祕鑰";
            string AppId = "應用程序標識";

            try
            {
                //請求目標地址和參數(authorization_code受權類型,此處只需填寫 authorization_code)
                string OauthUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=" + AppId + "&secret=" + AppSecret + "&js_code=" + code + "&grant_type=authorization_code";//序列化解析數據
                var Result = HttpGet(OauthUrl);

                return Json(new { openid = Result.openid, errcode = Result.errcode, UnionID = Result.unionid, session_key = Result.session_key });
            }
            catch (Exception ex)
            {

                return Json(new { errcode = 1, msg = "獲取用戶信息失敗" + ex.Message });
            }
        }

        /// <summary>
        /// 請求code2Session接口獲取用戶信息
        /// </summary>
        /// <param name="requestDataAndUrl">目標地址和參數</param>
        /// <returns></returns>
        public WxOauthModle HttpGet(string requestDataAndUrl)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestDataAndUrl);
            request.Method = "GET";
            request.ContentType = "text/html;charset=UTF-8";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            Stream myResponseStream = response.GetResponseStream();
            StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.UTF8);
            string retString = myStreamReader.ReadToEnd();
            myStreamReader.Close();
            myResponseStream.Close();

            return JsonConvert.DeserializeObject<WxOauthModle>(retString);
        }

        public class WxOauthModle
        {
            /// <summary>
            /// 用戶惟一標識
            /// </summary>
            public string openid { get; set; }

            /// <summary>
            /// 會話祕鑰
            /// </summary>
            public string session_key { get; set; }

            /// <summary>
            /// 聯立編號
            /// </summary>
            public string unionid { get; set; }

            /// <summary>
            /// 錯誤碼
            /// </summary>
            public int errcode { get; set; }

            /// <summary>
            /// 錯誤信息
            /// </summary>
            public string errmsg { get; set; }
        }

關於微信網頁開發經過UnionID機制解決用戶在不一樣公衆號,或在公衆號、移動應用之間賬號統一問題:

詳情說明請點擊:http://www.javashuo.com/article/p-vtfudtwt-hw.html

相關文章
相關標籤/搜索