一、注意事项(先了解微信小程序的规则,知己知彼,才能解决问题)
1、微信小程序获取用户手机号,每次都会弹窗询问
2、获取用户信息(包括昵称、性别、城市、openid等),只弹窗一次 (根据手机号做授权验证用不到这里)
3、微信小程序可以利用缓存,但是有时间限制
4、获取手机号和openid—>是需要到自己服务器后台解密—>解密需要用到session_key—>session_key需要微信login接口(从前端)获取code然后传到后台服务器调用微信接口获取
5、通过自己服务器后台使用code获取数据之后,code则会失效
二、前端登录获取Code
<template>
<view class="content">
<button @click="login">Login</button>
<button open-type="getPhoneNumber" @getphonenumber="onGetPhoneNumber">getPhoneNumber</button>
</view>
</template>
<script>
export default {
data() {
return {}
},
methods: {
login(e) {
uni.login({
success: (res) => {
console.log("login", res);
}
})
},
onGetPhoneNumber(e) {
console.log("onGetPhoneNumber", e);
console.log(e.detail.errMsg);
console.log(e.detail.iv);
console.log(e.detail.encryptedData);
uni.showModal({
title: "onGetPhoneNumber",
content: e.detail.errMsg
})
}
}
}
</script>
三、后端解密
ASP.NET WEBAPI作服务端开发小程序实现微信授权用户登录实例
- 版本1
- 版本2
using AIOWeb.Models; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Linq; using System.Web; namespace AIOWeb { /// <summary> /// wxapi 的摘要说明 /// </summary> public class wxapi : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; string code = ""; string iv = ""; string encryptedData = ""; try { code = HttpContext.Current.Request.QueryString["code"].ToString(); iv = HttpContext.Current.Request.QueryString["iv"].ToString(); encryptedData = HttpContext.Current.Request.QueryString["encryptedData"].ToString(); } catch (Exception ex) { context.Response.Write(ex.ToString()); } string Appid = "wxdb2641f85b04f1b3"; string Secret = "8591d8cd7197b9197e17b3275329a1e7"; string grant_type = "authorization_code"; //向微信服务端 使用登录凭证 code 获取 session_key 和 openid string url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + Appid + "&secret=" + Secret + "&js_code=" + code + "&grant_type=" + grant_type; string type = "utf-8"; AIOWeb.Models.GetUsersHelper GetUsersHelper = new AIOWeb.Models.GetUsersHelper(); string j = GetUsersHelper.GetUrltoHtml(url, type);//获取微信服务器返回字符串 //将字符串转换为json格式 JObject jo = (JObject)JsonConvert.DeserializeObject(j); result res = new result(); try { //微信服务器验证成功 res.openid = jo["openid"].ToString(); res.session_key = jo["session_key"].ToString(); } catch (Exception) { //微信服务器验证失败 res.errcode = jo["errcode"].ToString(); res.errmsg = jo["errmsg"].ToString(); } if (!string.IsNullOrEmpty(res.openid)) { //用户数据解密 GetUsersHelper.AesIV = iv; GetUsersHelper.AesKey = res.session_key; string result = GetUsersHelper.AESDecrypt(encryptedData); //存储用户数据 JObject _usrInfo = (JObject)JsonConvert.DeserializeObject(result); userInfo userInfo = new userInfo(); userInfo.openId = _usrInfo["openId"].ToString(); try //部分验证返回值中没有unionId { userInfo.unionId = _usrInfo["unionId"].ToString(); } catch (Exception) { userInfo.unionId = "unionId"; } userInfo.nickName = _usrInfo["nickName"].ToString(); userInfo.gender = _usrInfo["gender"].ToString(); userInfo.city = _usrInfo["city"].ToString(); userInfo.province = _usrInfo["province"].ToString(); userInfo.country = _usrInfo["country"].ToString(); userInfo.avatarUrl = _usrInfo["avatarUrl"].ToString(); object watermark = _usrInfo["watermark"].ToString(); object appid = _usrInfo["watermark"]["appid"].ToString(); object timestamp = _usrInfo["watermark"]["timestamp"].ToString(); #region //创建连接池对象(与数据库服务器进行连接) SqlConnection conn = new SqlConnection("server=127.0.0.1;database=Test;uid=sa;pwd=1"); //打开连接池 conn.Open(); //创建命令对象 string Qrystr = "SELECT * FROM WeChatUsers WHERE openId='" + userInfo.openId + "'"; SqlCommand cmdQry = new SqlCommand(Qrystr, conn); object obj = cmdQry.ExecuteScalar(); if ((Object.Equals(obj, null)) || (Object.Equals(obj, System.DBNull.Value))) { string str = "INSERT INTO WeChatUsers ([UnionId] ,[OpenId],[NickName],[Gender],[City],[Province],[Country],[AvatarUrl],[Appid],[Timestamp],[Memo],[counts])VALUES('" + userInfo.unionId + "','" + userInfo.openId + "','" + userInfo.nickName + "','" + userInfo.gender + "','" + userInfo.city + "','" + userInfo.province + "','" + userInfo.country + "','" + userInfo.avatarUrl + "','" + appid.ToString() + "','" + timestamp.ToString() + "','来自微信小程序','1')"; SqlCommand cmdUp = new SqlCommand(str, conn); // 执行操作 try { int row = cmdUp.ExecuteNonQuery(); } catch (Exception ex) { context.Response.Write(ex.ToString()); } } else { //多次访问,记录访问次数counts 更新unionId是预防最初没有,后期关联后却仍未记录 string str = "UPDATE dbo.WeChatUsers SET counts = counts+1,UnionId = '" + userInfo.unionId + "' WHERE OpenId='" + userInfo.openId + "'"; SqlCommand cmdUp = new SqlCommand(str, conn); int row = cmdUp.ExecuteNonQuery(); } //关闭连接池 conn.Close(); #endregion //返回解密后的用户数据 context.Response.Write(result); } else { context.Response.Write(j); } } public bool IsReusable { get { return false; } } } } GetUsersHelper 帮助类 using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; namespace AIOWeb.Models { public class GetUsersHelper { /// <summary> /// 获取链接返回数据 /// </summary> /// <param name="Url">链接</param> /// <param name="type">请求类型</param> /// <returns></returns> public string GetUrltoHtml(string Url, string type) { try { System.Net.WebRequest wReq = System.Net.WebRequest.Create(Url); // Get the response instance. System.Net.WebResponse wResp = wReq.GetResponse(); System.IO.Stream respStream = wResp.GetResponseStream(); // Dim reader As StreamReader = New StreamReader(respStream) using (System.IO.StreamReader reader = new System.IO.StreamReader(respStream, Encoding.GetEncoding(type))) { return reader.ReadToEnd(); } } catch (System.Exception ex) { return ex.Message; } } #region 微信小程序用户数据解密 public static string AesKey; public static string AesIV; /// <summary> /// AES解密 /// </summary> /// <param name="inputdata">输入的数据encryptedData</param> /// <param name="AesKey">key</param> /// <param name="AesIV">向量128</param> /// <returns name="result">解密后的字符串</returns> public string AESDecrypt(string inputdata) { try { AesIV = AesIV.Replace(" ", "+"); AesKey = AesKey.Replace(" ", "+"); inputdata = inputdata.Replace(" ", "+"); byte[] encryptedData = Convert.FromBase64String(inputdata); RijndaelManaged rijndaelCipher = new RijndaelManaged(); rijndaelCipher.Key = Convert.FromBase64String(AesKey); // Encoding.UTF8.GetBytes(AesKey); rijndaelCipher.IV = Convert.FromBase64String(AesIV);// Encoding.UTF8.GetBytes(AesIV); rijndaelCipher.Mode = CipherMode.CBC; rijndaelCipher.Padding = PaddingMode.PKCS7; ICryptoTransform transform = rijndaelCipher.CreateDecryptor(); byte[] plainText = transform.TransformFinalBlock(encryptedData, 0, encryptedData.Length); string result = Encoding.UTF8.GetString(plainText); return result; } catch (Exception) { return null; } } #endregion } } ===============================================后补:实体类======================================== using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace AIOWeb.Models { public class wechat { } #region 实体类 /// <summary> /// 微信用户类 /// </summary> public class userInfo { public string openId { get; set; } public string nickName { get; set; } public string gender { get; set; } public string city { get; set; } public string province { get; set; } public string country { get; set; } public string avatarUrl { get; set; } public string unionId { get; set; } public data watermark { get; set; } } /// <summary> /// 微信用户数据水印 /// </summary> public class data { public string appid { get; set; } public string timestamp { get; set; } } /// <summary> /// 微信小程序验证返回结果 /// </summary> public class result { public string openid { get; set; } public string session_key { get; set; } public string errcode { get; set; } public string errmsg { get; set; } } #endregion }
- 版本3
微信小程序获取手机号需要完成认证,才能获取得到
否则会出现下列情况
一、搭建后台服务器(WebApI)
新建webapi2空控制器 OnLoginController
using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Security.Cryptography; using System.Text; using System.Web.Http; using WebApplication1.Models;//引入实体类命名空间 using System.Web.Http.Cors;//跨域请求命名空间 namespace WebApplication1.Controllers { //跨域请求 [EnableCors(origins:"*",headers:"*",methods:"*")] [RoutePrefix("api/OnLogin")] public class OnLoginController : ApiController { [Route("Get")] [HttpGet] public object Get(string code) { string cod = code; string html = string.Empty; string url = "https://api.weixin.qq.com/sns/jscode2session?appid=wxb2f63b12a7371f2a&secret=110dc84617c235b5809a7f5d8d43af3a&js_code="+cod+"&grant_type=authorization_code"; HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url); request.Method = "GET"; request.ContentType = "text/html;charset=UTF-8"; HttpWebResponse response = request.GetResponse() as HttpWebResponse; Stream ioStream = response.GetResponseStream(); StreamReader sr = new StreamReader(ioStream, Encoding.UTF8); html = sr.ReadToEnd(); sr.Close(); ioStream.Close(); response.Close(); RepParamrep rep = JsonConvert.DeserializeObject<RepParamrep>(html); return rep; } } }
-
新建实体类RepParamrep ,保存session_key和open_id
namespace WebApplication1.Models { public class RepParamrep { public string session_key { get; set; } public string openid { get; set; } } }
解析手机号码
[Route(“Get”)]
[HttpGet]
public string Get(string encryptedData, string iv, string session_key)
{string _telPhone = getPhoneNumber(encryptedData, iv, session_key); return _telPhone; } private string getPhoneNumber(string encryptedData, string IV, string Session_key) { try { byte[] encryData = Convert.FromBase64String(encryptedData); RijndaelManaged rijndaelCipher = new RijndaelManaged(); rijndaelCipher.Key = Convert.FromBase64String(Session_key); rijndaelCipher.IV = Convert.FromBase64String(IV); rijndaelCipher.Mode = CipherMode.CBC; rijndaelCipher.Padding = PaddingMode.PKCS7; ICryptoTransform transform = rijndaelCipher.CreateDecryptor(); byte[] plainText = transform.TransformFinalBlock(encryData, 0, encryData.Length); string result = Encoding.Default.GetString(plainText); dynamic model = Newtonsoft.Json.Linq.JToken.Parse(result) as dynamic; return model.phoneNumber; } catch (Exception ex) { return ""; } }
二、设计前端界面及逻辑
//index.wxss <view> <button class="cu-btn bg-blue margin-tb-xs lg" hidden="{{hid1}}" open-type="getUserInfo" bindgetuserinfo="bindGetUserInfo">授权</button> <button class="cu-btn bg-blue margin-tb-xs lg" hidden="{{hid2}}" open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">进入</button> </view>
//index.js const app = getApp(); Page({ data: { userPhone: '', userNicheng: '', hid1: false, hid2: true }, bindGetUserInfo: function(e) { this.setData({ userNicheng: e.detail.userInfo.nickName, hid1: true, hid2: false }) }, getPhoneNumber: function(e) { var that = this; console.log("getPhoneNumberok" + e.detail.errMsg); if (e.detail.errMsg == "getPhoneNumber:ok") { //端口号是由后端服务器生成 wx.request({ url: 'https://localhost:44358/api/OnLogin/Get', data: { encryptedData: e.detail.encryptedData, iv: e.detail.iv, session_key: app.globalData.session_key }, method: "get", success: function(res) { console.log(res) that.setData({ userPhone: res.data }) }, fail:function(res){ console.log(res.errMsg) } }) } } })
//app.js App({ globalData:{ session_key:'', openid:'' }, onLaunch: function() { wx.login({ success: res => { // 发送 res.code 到后台换取 openId, sessionKey, unionId console.log(res) if (res.code) { console.log("code========" + JSON.stringify(res)); //发起网络请求 wx.request({ url: 'https://localhost:44358/api/OnLogin/Get', data: { code: res.code }, success: data => { console.log("data" + JSON.stringify(data.data)); this.globalData.session_key = data.data.session_key; this.globalData.openid = data.data.openid; console.log("openid" + this.globalData.openid); }, fail:data=>{ console.log(data) } }) } else { console.log('登录失败!' + res.errMsg) } } }) } })
- 版本4
<% @ webhandler language="C#" class="AverageHandler" %>
using System;
using System.Net;
using System.Web;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using LitJson;
public class AverageHandler : IHttpHandler
{
public bool IsReusable
{ get { return true; } }
public void ProcessRequest(HttpContext ctx)
{
ctx.Response.ContentType = "application/json";
HttpRequest Request = ctx.Request;
string text = Request["encryptedData"];
string IV = Request["iv"];
//小程序appid和appsecret配置
string appid = "小小星星亮晶晶";
string secret = "小小星星亮晶晶";
string code = Request["code"];//微信获取登录的口令
Stream s_re = WebRequest.Create("https://api.weixin.qq.com/sns/jscode2session?appid="+appid+"&secret="+secret+"&js_code="+code+"&grant_type=authorization_code").GetResponse().GetResponseStream();
StreamReader sr = new StreamReader(s_re, Encoding.UTF8);
string strLine = sr.ReadToEnd();
sr.Close();
try
{
JsonData jo = JsonMapper.ToObject(strLine);
string Key = jo["session_key"].ToString();
//string weixinID = jo["openid"].ToString();
string str= AES_decrypt(text,Key,IV);
ctx.Response.Write(str);
}
catch (Exception ex)
{
//return "";
}
}
public string AES_decrypt(string encryptedDataStr, string key, string iv)
{
RijndaelManaged rijalg = new RijndaelManaged();
//-----------------
//设置 cipher 格式 AES-128-CBC
rijalg.KeySize = 128;
rijalg.Padding = PaddingMode.PKCS7;
rijalg.Mode = CipherMode.CBC;
rijalg.Key = Convert.FromBase64String(key);
rijalg.IV = Convert.FromBase64String(iv);
byte[] encryptedData= Convert.FromBase64String(encryptedDataStr);
//解密
ICryptoTransform decryptor = rijalg.CreateDecryptor(rijalg.Key, rijalg.IV);
string result;
using (MemoryStream msDecrypt = new MemoryStream(encryptedData))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
result = srDecrypt.ReadToEnd();
}
}
}
return result;
}
}