最近在做这个登录功能接口,记录一下
1、小程序端调用wx.login方法获取code,后端使用WeChatAuth方法请求auth.code2Session接口使用appid、secret、js_code、grant_type:默认authorization_code获取session_key、openid
auth.code2Session官方地址:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html
2、后端通过redis保存session_key、openid自定义登录状态并返回小程序端(也可以直接将两个参数传回小程序端,但是不安全)
3、小程序端使用button让用户进行授权获取到encryptedData、iv加密数据和自定义状登录状态传回后端WeChatAuthUserInfo方法,判断登录状态
4、后端使用Decrypt方法将加密数据进行解密,并反序列化为PhoneInfo类获取到手机号进行数据库操作
1 /// <summary> 2 /// 微信小程序-获取登陆凭证code 3 /// </summary> 4 /// <param name="_"></param> 5 /// <returns></returns> 6 public Response WeChatAuth(dynamic _) 7 { 8 9 WechatLoginInfo req = this.GetReq<WechatLoginInfo>(); 10 string Auth_code = req.code; 11 if (string.IsNullOrEmpty(Auth_code)) 12 { 13 return Fail("未获取到Auth_code"); 14 } 15 string AppId = Config.GetValue("WeChatAppId"); 16 string AppSecret = Config.GetValue("WeChatAppSecret"); 17 18 19 WebClient wc = new WebClient(); 20 wc.Encoding = System.Text.Encoding.UTF8; 21 22 23 //第一步:通过code换取网页授权access_token 24 NameValueCollection tokennvc = new NameValueCollection(); 25 tokennvc.Add("appid", AppId); 26 tokennvc.Add("secret", AppSecret); 27 tokennvc.Add("js_code", Auth_code); 28 tokennvc.Add("grant_type", "authorization_code"); 29 string tokenresult = System.Text.Encoding.UTF8.GetString(wc.UploadValues("https://api.weixin.qq.com/sns/jscode2session?", tokennvc)); 30 if (tokenresult.Contains("errcode")) 31 { 32 AccessError accessError = Newtonsoft.Json.JsonConvert.DeserializeObject<AccessError>(tokenresult); 33 return Fail(accessError.errmsg); 34 } 35 //反序列化 36 AuthAccessToken auth = Newtonsoft.Json.JsonConvert.DeserializeObject<AuthAccessToken>(tokenresult); 37 38 TimeSpan ts = new TimeSpan(48, 0, 0); 39 string token_session = Guid.NewGuid().ToString(); 40 41 //写入Redis 42 redisCache.Write<AuthAccessToken>("token_session_"+ token_session, auth, ts, CacheId.client); 43 44 return Success(token_session); 45 } 49 77 78 /// <summary> 79 /// 根据微信小程序平台提供的解密算法解密数据 80 /// </summary> 81 /// <param name="encryptedData">加密数据</param> 82 /// <param name="iv">初始向量</param> 83 /// <param name="sessionKey">从服务端获取的SessionKey</param> 84 /// <returns></returns> 85 public PhoneInfo Decrypt(WechatLoginInfo loginInfo,string session_key) 86 { 87 88 //创建解密器生成工具实例 89 AesCryptoServiceProvider aes = new AesCryptoServiceProvider(); 90 91 //设置解密器参数 92 93 aes.Mode = CipherMode.CBC; 94 95 aes.BlockSize = 128; 96 97 aes.Padding = PaddingMode.PKCS7; 98 99 //格式化待处理字符串 100 101 byte[] byte_encryptedData = Convert.FromBase64String(loginInfo.encryptedData); 102 103 byte[] byte_iv = Convert.FromBase64String(loginInfo.iv); 104 105 byte[] byte_sessionKey = Convert.FromBase64String(session_key); 106 107 aes.IV = byte_iv; 108 109 aes.Key = byte_sessionKey; 110 111 //根据设置好的数据生成解密器实例 112 113 ICryptoTransform transform = aes.CreateDecryptor(); 114 115 //解密 116 117 byte[] final = transform.TransformFinalBlock(byte_encryptedData, 0, byte_encryptedData.Length); 118 119 //生成结果 120 121 string result = Encoding.UTF8.GetString(final); 122 123 //反序列化结果,生成用户信息实例 124 125 var phoneInfo = Newtonsoft.Json.JsonConvert.DeserializeObject<PhoneInfo>(result); 126 127 return phoneInfo; 128 129 } 130 131 132 /// <summary> 133 /// 获取微信小程序用户信息 134 /// </summary> 135 /// <param name="_">The .</param> 136 /// <returns></returns> 137 public Response WeChatAuthUserInfo(dynamic _) 138 { 139 WechatLoginInfo req = this.GetReq<WechatLoginInfo>(); 140 var AuthLogin = redisCache.Read<AuthAccessToken>("token_session_"+req.token_session, CacheId.clientuser); 141 if (AuthLogin== null) 142 { 143 return Fail("找不到登录密钥"); 144 } 145 146 //数据解密,解密手机号等重要信息 147 PhoneInfo userinfojson = Decrypt(req,AuthLogin.session_key); 148 149 if (userinfojson==null) 150 { 151 return Fail("数据解密失败"); 152 } 153 154 string Token = Guid.NewGuid().ToString().Substring(0,8);
156 string UserId = ""; 157 158 #region 159 //查询数据库userinfo是否有openid(没有的话insert,有的话更新)openid是微信用户唯一标识符
160 if (userinfo != null) 161 { 162 userinfo.Phone = userinfojson.phoneNumber; 163 userinfo.OpenId = AuthLogin.openid; 164 //进行update实例userinfo 165 UserId = userinfo.Id; 166 } 167 else 168 { 169 //进行insert entity171 entity.Nick_Name = Nick_Name; 172 entity.Phone = userinfojson.phoneNumber; 173 entity.OpenId = AuthLogin.openid;176 177 UserId = entity.Id; 178 } 179 //UserInfo实例类 180 UserInfo userentity = new UserInfo(); 181 userentity.Id = UserId; 182 userentity.Token = Token; 183 userentity.Nick_Name = Nick_Name; 184 userentity.Phone = userinfojson.phoneNumber; 185 userentity.AddTime = DateTime.Now; 186 userentity.OpenId = AuthLogin.openid; 187 //有效期 188 TimeSpan ts = new TimeSpan(48, 0, 0); 189 190 //写入Redis 191 redisCache.Write<ResUserInfo>(Token, userentity, ts, CacheId.userinfo); 192 193 #endregion 194 195 return Success(userentity); 196 } 197 198 199 /// <summary> 200 /// 错误信息 201 /// </summary> 202 public class AccessError 203 { 204 /// <summary> 205 /// 错误码 206 /// </summary> 207 public string errcode { get; set; } 208 /// <summary> 209 /// 错误信息 210 /// </summary> 211 public string errmsg { get; set; } 212 } 213 214 /// <summary> 215 /// 获取AccessToken 216 /// </summary> 217 public class AuthAccessToken 218 { 219 //会话密钥, 220 public string session_key { get; set; } 221 /// <summary> 222 /// access_token接口调用凭证超时时间,单位(秒) 223 /// </summary> 224 public string expires_in { get; set; } 225 /// <summary> 226 /// 获取到的凭证 227 /// </summary> 228 public string access_token { get; set; } 229 /// <summary> 230 /// 用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID 231 /// </summary> 232 public string openid { get; set; } 233 234 } 235 299 300 /// <summary> 301 /// 加密的数据 302 /// </summary> 303 public class Watermark 304 305 { 306 public string appid { get; set; } 307 308 public string timestamp { get; set; } 309 310 } 311 312 /// <summary> 313 /// 登录获取信息请求参数 314 /// </summary> 315 public class WechatLoginInfo 316 { 317 //自定义登录状态, 318 public string token_session { get; set; } 319 320 public string code { get; set; } 321 322 /// <summary> 323 ///加密数据 324 /// </summary> 325 /// <value> 326 /// </value> 327 public string encryptedData { get; set; } 328 329 /// <summary> 330 /// 加密算法的初始向量 331 /// </summary> 332 /// <value> 333 /// The iv. 334 /// </value> 335 public string iv { get; set; } 336 337 /// <summary> 338 /// 不包括敏感信息的原始数据字符串,用于计算签名 339 /// </summary> 340 /// <value> 341 /// The raw data. 342 /// </value> 343 public WeChatUserInfo rawData { get; set; } 344 345 /// <summary> 346 /// 校验用户信息 347 /// </summary> 348 /// <value> 349 /// The signature. 350 /// </value> 351 public string signature { get; set; } 352 353 public WeChatUserInfo UserInfo { get; set; } 354 355 /// <summary> 356 /// 语言. 357 /// </summary> 358 /// <value> 359 /// The language. 360 /// </value> 361 public string language { get; set; } 362 363 } 364 365 /// <summary> 366 /// 手机号信息 367 /// </summary> 368 public class PhoneInfo 369 { 370 371 /// <summary> 372 /// 用户的唯一标识 373 /// </summary> 374 public string openid { get; set; } 375 /// <summary> 376 /// 用户绑定的手机号 377 /// </summary> 378 /// <value> 379 /// The phone number. 380 /// </value> 381 public string phoneNumber { get; set; } 382 /// <summary> 383 /// 没有区号的手机号 384 /// </summary> 385 /// <value> 386 /// The pure phone number. 387 /// </value> 388 public string purePhoneNumber { get; set; } 389 /// <summary> 390 /// 区号 391 /// </summary> 392 /// <value> 393 /// The country code. 394 /// </value> 395 public string countryCode { get; set; } 396 397 398 public Watermark watermark { get; set; } 399 }