.NET?Core企業(yè)微信網(wǎng)頁授權登錄的實現(xiàn)
1.開發(fā)前準備
參數(shù)獲取
corpid
每個企業(yè)都擁有唯一的corpid,獲取此信息可在管理后臺“我的企業(yè)”-“企業(yè)信息”下查看“企業(yè)ID”
secret
secret是企業(yè)應用里面用于保障數(shù)據(jù)安全的“鑰匙”,每一個應用都有一個獨立的訪問密鑰,為了保證數(shù)據(jù)的安全,secret務必不能泄漏。
框架
例子使用yishaadmin開源框架為例
2.企業(yè)微信OAuth2接入流程
第一步: 用戶點擊連接
第二步: Index頁取得回調Code
第三步: 根據(jù)Code和access_token獲取UserID
第四步: 根據(jù)UserID到通訊錄接口獲取其他信息
3.構造網(wǎng)頁授權鏈接
假定當前企業(yè)CorpID:wxCorpId
訪問鏈接:http://api.3dept.com/cgi-bin/query?action=get
根據(jù)URL規(guī)范,將上述參數(shù)分別進行UrlEncode,得到拼接的OAuth2鏈接為:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxCorpId&redirect_uri=http%3a%2f%2fapi.3dept.com%2fcgi-bin%2fquery%3faction%3dget&response_type=code&scope=snsapi_base&state=#wechat_redirect
然后新建應用,將鏈接放入,配置應用可信域名。
官方文檔鏈接:https://developer.work.weixin.qq.com/document/path/91335
4. 調用代碼部分
4.1 appsettings配置
"Wx": { "corpid": "", "corpsecret": "", "baseurl": "https://qyapi.weixin.qq.com", "getUserByCode": "/cgi-bin/user/getuserinfo?access_token={0}&code={1}", "getToken": "/cgi-bin/gettoken?corpid={0}&corpsecret={1}", "getUserByUserId": "/cgi-bin/user/get?access_token={0}&userid={1}" }
4.2 配置IHttpClientFactory調用微信客戶端
public static IHttpClientFactory httpClientFactory { get; set; }
Startup添加以下內容
public void ConfigureServices(IServiceCollection services) { services.AddHttpClient("WxClient", config => { config.BaseAddress = new Uri(Configuration["Wx:baseurl"]); config.DefaultRequestHeaders.Add("Accept", "application/json"); }); }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { GlobalContext.httpClientFactory = app.ApplicationServices.GetService<IHttpClientFactory>(); }
4.3 類準備
UserCache 類保存用戶id,頭像,用戶名,以及code,按需新增。
using System; using System.Collections.Generic; using System.Text; namespace YiSha.Model.Result { public class UserCache { /// <summary> /// 用戶id /// </summary> public string UserID { get; set; } /// <summary> /// 頭像 /// </summary> public string Portrait { get; set; } /// <summary> /// 用戶名 /// </summary> public string Username { get; set; } /// <summary> /// 緩存最近一次Code 用于刷新時code不更新問題 /// </summary> public string Code { get; set; } } }
ApplicationContext用于緩存Token 過期時間以及用戶集合避免多次調用微信接口提高響應速度
using?System;using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using YiSha.Model.Result; namespace YiSha.Admin.Web.App_Code { public static class ApplicationContext { /// <summary> /// 用于多點登錄的微信用戶 /// </summary> public const string WxUser = "taskUser"; /// <summary> /// 用于多點登錄的微信密碼 /// </summary> public const string WxPassWord = "123456"; /// <summary> /// 過期時間 /// </summary> public static DateTime TimeOutDate { get; set; } /// <summary> /// Token /// </summary> public static string Token { get; set; } /// <summary> /// 緩存UserID Name 頭像 /// </summary> public static List<UserCache> UserCache { get; set; } = new List<UserCache>(); } }
獲取Token返回實體
using System; using System.Collections.Generic; using System.Text; namespace YiSha.Entity.OAManage { public class GetTokenResult { /// <summary> /// 錯誤編號 /// </summary> public int errcode { get; set; } /// <summary> /// 錯誤信息 /// </summary> public string errmsg { get; set; } /// <summary> /// Token /// </summary> public string access_token { get; set; } /// <summary> /// 過期時間 /// </summary> public int expires_in { get; set; } } }
獲取用戶id返回實體
using System; using System.Collections.Generic; using System.Text; namespace YiSha.Entity.OAManage { //獲取用戶ID public class GetUserInfoResult { /// <summary> /// 錯誤編號 /// </summary> public int errcode { get; set; } /// <summary> /// 錯誤信息 /// </summary> public string errmsg { get; set; } /// <summary> /// 用戶ID /// </summary> public string UserID { get; set; } } }
獲取用戶通訊錄返回實體
using System; using System.Collections.Generic; using System.Text; namespace YiSha.Entity.OAManage { public class GetUserResult { /// <summary> /// 錯誤編號 /// </summary> public int errcode { get; set; } /// <summary> /// 錯誤信息 /// </summary> public string errmsg { get; set; } /// <summary> /// 名稱 /// </summary> public string name { get; set; } /// <summary> /// 頭像 /// </summary> public string avatar { get; set; } } }
4.4方法準備
獲取Token方法,該方法對Token進行了一個緩存,避免重復獲取.
注意事項:
開發(fā)者需要緩存access_token,用于后續(xù)接口的調用(注意:不能頻繁調用gettoken接口,否則會受到頻率攔截)。當access_token失效或過期時,需要重新獲取。
access_token的有效期通過返回的expires_in來傳達,正常情況下為7200秒(2小時),有效期內重復獲取返回相同結果,過期后獲取會返回新的access_token。
由于企業(yè)微信每個應用的access_token是彼此獨立的,所以進行緩存時需要區(qū)分應用來進行存儲。
access_token至少保留512字節(jié)的存儲空間。
企業(yè)微信可能會出于運營需要,提前使access_token失效,開發(fā)者應實現(xiàn)access_token失效時重新獲取的邏輯。
獲取Token文檔鏈接https://developer.work.weixin.qq.com/document/path/91039
/// <summary> /// 獲取Token /// </summary> /// <returns>Item1 Token;Item2 是否成功</returns> public Tuple<string,bool> GetToken() { //判斷Token是否存在 以及Token是否在有效期內 if(string.IsNullOrEmpty(ApplicationContext.Token) || ApplicationContext.TimeOutDate > DateTime.Now) { //構造請求鏈接 var requestBuild = GlobalContext.Configuration["Wx:getToken"]; requestBuild = string.Format(requestBuild, GlobalContext.Configuration["Wx:corpid"], GlobalContext.Configuration["Wx:corpsecret"] ); using (var wxClient = GlobalContext.httpClientFactory.CreateClient("WxClient")) { var httpResponse = wxClient.GetAsync(requestBuild).Result; if(httpResponse.StatusCode == System.Net.HttpStatusCode.OK) { var dynamic= JsonConvert.DeserializeObject<GetTokenResult>( httpResponse.Content.ReadAsStringAsync().Result ); ApplicationContext.Token = dynamic.access_token; //過期5分鐘前刷新Token var expires_in = Convert.ToDouble(dynamic.expires_in - 5 * 60); ApplicationContext.TimeOutDate = DateTime.Now.AddSeconds(expires_in); return Tuple.Create(ApplicationContext.Token,true); } else { return Tuple.Create("獲取企業(yè)微信Token失敗,請稍后重試!", false); } } } else { return Tuple.Create(ApplicationContext.Token, true); } }
獲取用戶ID方法,該方法根據(jù)獲取到的token,以及回調的code進行請求,得到用戶id實體
獲取訪問用戶身份文檔鏈接:https://developer.work.weixin.qq.com/document/path/91023
/// <summary> /// 獲取用戶ID /// </summary> /// <param name="token">企業(yè)微信Token</param> /// <param name="code">構造請求的回調code</param> /// <returns>Item1 UserId;Item2 是否成功</returns> public Tuple<string, bool> GetUserID(string token,string code) { //構造請求鏈接 var requestBuild = GlobalContext.Configuration["Wx:getUserByCode"]; requestBuild = string.Format(requestBuild,token,code); using (var wxClient = GlobalContext.httpClientFactory.CreateClient("WxClient")) { var httpResponse = wxClient.GetAsync(requestBuild).Result; if (httpResponse.StatusCode == System.Net.HttpStatusCode.OK) { var dynamic = JsonConvert.DeserializeObject<GetUserInfoResult>( httpResponse.Content.ReadAsStringAsync().Result ); return Tuple.Create(dynamic.UserID, true); } else { return Tuple.Create("獲取用戶ID失敗,請稍后重試!", false); } } }
獲取用戶通訊錄方法,該方法可以通過token和userid進行獲取用戶頭像等信息,按需要調用
讀取成員接口文檔:https://developer.work.weixin.qq.com/document/path/90196
/// <summary> /// 獲取用戶通訊錄 /// </summary> /// <returns>Item1 頭像,獲取失敗時為錯誤信息;Item2 名稱;Item3 是否成功</returns> public Tuple<string,string, bool> GetUserByID(string token, string userid) { //構造請求鏈接 var requestBuild = GlobalContext.Configuration["Wx:getUserByUserId"]; requestBuild = string.Format(requestBuild, token, userid); //建立HttpClient using (var wxClient = GlobalContext.httpClientFactory.CreateClient("WxClient")) { var httpResponse = wxClient.GetAsync(requestBuild).Result; if (httpResponse.StatusCode == System.Net.HttpStatusCode.OK) { var dynamic = JsonConvert.DeserializeObject<GetUserResult>( httpResponse.Content.ReadAsStringAsync().Result ); return Tuple.Create(dynamic.avatar, dynamic.name, true); } else { return Tuple.Create("獲取用戶ID失敗,請稍后重試!","", false); } } }
4.5調用
本方法是為了企業(yè)微信登錄時繞過用戶登錄直接使用企業(yè)微信用戶登錄,有其他需求根據(jù)需要調整。
主頁index 中使用code參數(shù)獲取回調傳進來的code,調用GetToken方法獲取Token,然后根據(jù)Token和Code獲取UserID,最后根據(jù)UserID和Token獲取通訊錄的頭像和名稱。需要注意的是我們要對每個用戶最新的code進行緩存,在企業(yè)微信內部瀏覽器時刷新code參數(shù)不會變動,但是code只能使用一次會導致接口調用失敗。
[HttpGet] public async Task<IActionResult> Index(string code) { OperatorInfo operatorInfo = default; TData<List<MenuEntity>> objMenu = await menuBLL.GetList(null); List<MenuEntity> menuList = objMenu.Data; menuList = menuList.Where(p => p.MenuStatus == StatusEnum.Yes.ParseToInt()).ToList(); if (code != null)//企業(yè)微信登錄 { //獲取聯(lián)系人 從內存中取||從接口取 string username, portrait = default; bool issuccess2 = default; //緩存最近的一次code 用于刷新URL時重復code請求失敗 var codeCache = ApplicationContext.UserCache.FirstOrDefault(o => o.Code == code); if(codeCache == null) { //獲取token Token時間為過期時間減5分鐘 var (token, issuccess) = GetToken(); if (!issuccess) return RedirectToAction("error1", new { errormessage = token }); //獲取userid var (userid, issuccess1) = GetUserID(token, code); if (!issuccess1) return RedirectToAction("error1", new { errormessage = userid }); var useridCache = ApplicationContext.UserCache.FirstOrDefault(o => o.UserID == userid); if (useridCache == null)//不存在緩存中 { (portrait, username, issuccess2) = GetUserByID(token, userid); if (!issuccess2) return RedirectToAction("error1", new { errormessage = portrait }); //加緩存 ApplicationContext.UserCache.Add(new UserCache() { Code = code, Username = username, Portrait = portrait, UserID = userid }); //保存登錄日志 var log = logLoginBLL.SaveForm(new LogLoginEntity { Remark = username, ExtraRemark = token + ":" + userid }); } else//從緩存中獲取用戶信息 { username = useridCache.Username; portrait = useridCache.Portrait; //更新最新code useridCache.Code = code; } } else { username = codeCache.Username; portrait = codeCache.Portrait; } //模擬登錄 TData<UserEntity> userObj = await userBLL.CheckLogin(ApplicationContext.WxUser , ApplicationContext.WxPassWord , (int)PlatformEnum.Web); if (userObj.Tag == 1) { await new UserBLL().UpdateUser(userObj.Data); await Operator.Instance.AddCurrent(userObj.Data.WebToken); var op = await Operator.Instance.Current(); AuthorizeListWhere(op); } //構建前端返回的用戶名 以及頭像 operatorInfo = new OperatorInfo(); operatorInfo.RealName = username; operatorInfo.UserName = username; operatorInfo.Portrait = portrait; } else//正常網(wǎng)頁登錄 { operatorInfo = await Operator.Instance.Current(); if (operatorInfo == null) return RedirectToAction("Login"); if (operatorInfo.IsSystem != 1) { AuthorizeListWhere(operatorInfo); } } //授權篩選 void AuthorizeListWhere(OperatorInfo info) { TData<List<MenuAuthorizeInfo>> objMenuAuthorize = menuAuthorizeBLL.GetAuthorizeList(info).Result; List<long?> authorizeMenuIdList = objMenuAuthorize.Data.Select(p => p.MenuId).ToList(); menuList = menuList.Where(p => authorizeMenuIdList.Contains(p.Id)).ToList(); } new CookieHelper().WriteCookie("UserName", operatorInfo.UserName, false); new CookieHelper().WriteCookie("RealName", operatorInfo.RealName, false); ViewBag.OperatorInfo = operatorInfo; ViewBag.MenuList = menuList; return View(); }
Index.Html調整
5.截圖
到此這篇關于.NET Core企業(yè)微信網(wǎng)頁授權登錄的實現(xiàn)的文章就介紹到這了,更多相關.NET Core企業(yè)微信授權登錄內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
在Repeater控件中通過Eval的方式綁定Style樣式代碼
這篇文章主要介紹了如何在Repeater控件中通過Eval的方式綁定Style樣式,需要的朋友可以參考下2014-04-04ASP.net中網(wǎng)站訪問量統(tǒng)計方法代碼
這篇文章介紹了ASP.net中網(wǎng)站訪問量統(tǒng)計方法代碼,有需要的朋友可以參考一下2013-11-11asp.net下用Aspose.Words for .NET動態(tài)生成word文檔中的數(shù)據(jù)表格的方法
導出word 文檔,要求這個文檔的格式不是固定的,用戶可以隨便的調整,導出內容中的數(shù)據(jù)表格列是動態(tài)的,例如要求導出姓名和性別,你就要導出這兩列的數(shù)據(jù),而且這個文檔不是導出來之后再調整而是導出來后已經(jīng)是調整過了的。2010-04-04iis訪問出現(xiàn)各種問題(Vs訪問正常)的部分處理方法詳細整理
在vs中調試都可以正常,但是在iis訪問就會出現(xiàn)各種問題,很是疑惑索性把這些問題整理一下,這樣更容易的可以處理方法說明清楚,感興趣的朋友可以了解下2013-01-01