.NET?Core企業(yè)微信網(wǎng)頁授權(quán)登錄的實(shí)現(xiàn)
1.開發(fā)前準(zhǔn)備
參數(shù)獲取
corpid
每個(gè)企業(yè)都擁有唯一的corpid,獲取此信息可在管理后臺(tái)“我的企業(yè)”-“企業(yè)信息”下查看“企業(yè)ID”
secret
secret是企業(yè)應(yīng)用里面用于保障數(shù)據(jù)安全的“鑰匙”,每一個(gè)應(yīng)用都有一個(gè)獨(dú)立的訪問密鑰,為了保證數(shù)據(jù)的安全,secret務(wù)必不能泄漏。
框架
例子使用yishaadmin開源框架為例
2.企業(yè)微信OAuth2接入流程
第一步: 用戶點(diǎn)擊連接
第二步: Index頁取得回調(diào)Code
第三步: 根據(jù)Code和access_token獲取UserID
第四步: 根據(jù)UserID到通訊錄接口獲取其他信息

3.構(gòu)造網(wǎng)頁授權(quán)鏈接
假定當(dāng)前企業(yè)CorpID:wxCorpId
訪問鏈接:http://api.3dept.com/cgi-bin/query?action=get
根據(jù)URL規(guī)范,將上述參數(shù)分別進(jìn)行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
然后新建應(yīng)用,將鏈接放入,配置應(yīng)用可信域名。

官方文檔鏈接:https://developer.work.weixin.qq.com/document/path/91335
4. 調(diào)用代碼部分
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調(diào)用微信客戶端
public static IHttpClientFactory httpClientFactory { get; set; }
Startup添加以下內(nèi)容
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 類準(zhǔn)備
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 用于刷新時(shí)code不更新問題
/// </summary>
public string Code { get; set; }
}
}ApplicationContext用于緩存Token 過期時(shí)間以及用戶集合避免多次調(diào)用微信接口提高響應(yīng)速度
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>
/// 用于多點(diǎn)登錄的微信用戶
/// </summary>
public const string WxUser = "taskUser";
/// <summary>
/// 用于多點(diǎn)登錄的微信密碼
/// </summary>
public const string WxPassWord = "123456";
/// <summary>
/// 過期時(shí)間
/// </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返回實(shí)體
using System;
using System.Collections.Generic;
using System.Text;
namespace YiSha.Entity.OAManage
{
public class GetTokenResult
{
/// <summary>
/// 錯(cuò)誤編號(hào)
/// </summary>
public int errcode { get; set; }
/// <summary>
/// 錯(cuò)誤信息
/// </summary>
public string errmsg { get; set; }
/// <summary>
/// Token
/// </summary>
public string access_token { get; set; }
/// <summary>
/// 過期時(shí)間
/// </summary>
public int expires_in { get; set; }
}
}獲取用戶id返回實(shí)體
using System;
using System.Collections.Generic;
using System.Text;
namespace YiSha.Entity.OAManage
{
//獲取用戶ID
public class GetUserInfoResult
{
/// <summary>
/// 錯(cuò)誤編號(hào)
/// </summary>
public int errcode { get; set; }
/// <summary>
/// 錯(cuò)誤信息
/// </summary>
public string errmsg { get; set; }
/// <summary>
/// 用戶ID
/// </summary>
public string UserID { get; set; }
}
}獲取用戶通訊錄返回實(shí)體
using System;
using System.Collections.Generic;
using System.Text;
namespace YiSha.Entity.OAManage
{
public class GetUserResult
{
/// <summary>
/// 錯(cuò)誤編號(hào)
/// </summary>
public int errcode { get; set; }
/// <summary>
/// 錯(cuò)誤信息
/// </summary>
public string errmsg { get; set; }
/// <summary>
/// 名稱
/// </summary>
public string name { get; set; }
/// <summary>
/// 頭像
/// </summary>
public string avatar { get; set; }
}
}4.4方法準(zhǔn)備
獲取Token方法,該方法對(duì)Token進(jìn)行了一個(gè)緩存,避免重復(fù)獲取.
注意事項(xiàng):
開發(fā)者需要緩存access_token,用于后續(xù)接口的調(diào)用(注意:不能頻繁調(diào)用gettoken接口,否則會(huì)受到頻率攔截)。當(dāng)access_token失效或過期時(shí),需要重新獲取。
access_token的有效期通過返回的expires_in來傳達(dá),正常情況下為7200秒(2小時(shí)),有效期內(nèi)重復(fù)獲取返回相同結(jié)果,過期后獲取會(huì)返回新的access_token。
由于企業(yè)微信每個(gè)應(yīng)用的access_token是彼此獨(dú)立的,所以進(jìn)行緩存時(shí)需要區(qū)分應(yīng)用來進(jìn)行存儲(chǔ)。
access_token至少保留512字節(jié)的存儲(chǔ)空間。
企業(yè)微信可能會(huì)出于運(yùn)營需要,提前使access_token失效,開發(fā)者應(yīng)實(shí)現(xiàn)access_token失效時(shí)重新獲取的邏輯。
獲取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是否在有效期內(nèi)
if(string.IsNullOrEmpty(ApplicationContext.Token) || ApplicationContext.TimeOutDate > DateTime.Now)
{
//構(gòu)造請(qǐng)求鏈接
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失敗,請(qǐng)稍后重試!", false);
}
}
}
else
{
return Tuple.Create(ApplicationContext.Token, true);
}
}獲取用戶ID方法,該方法根據(jù)獲取到的token,以及回調(diào)的code進(jìn)行請(qǐng)求,得到用戶id實(shí)體
獲取訪問用戶身份文檔鏈接:https://developer.work.weixin.qq.com/document/path/91023
/// <summary>
/// 獲取用戶ID
/// </summary>
/// <param name="token">企業(yè)微信Token</param>
/// <param name="code">構(gòu)造請(qǐng)求的回調(diào)code</param>
/// <returns>Item1 UserId;Item2 是否成功</returns>
public Tuple<string, bool> GetUserID(string token,string code)
{
//構(gòu)造請(qǐng)求鏈接
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失敗,請(qǐng)稍后重試!", false);
}
}
}獲取用戶通訊錄方法,該方法可以通過token和userid進(jìn)行獲取用戶頭像等信息,按需要調(diào)用
讀取成員接口文檔:https://developer.work.weixin.qq.com/document/path/90196
/// <summary>
/// 獲取用戶通訊錄
/// </summary>
/// <returns>Item1 頭像,獲取失敗時(shí)為錯(cuò)誤信息;Item2 名稱;Item3 是否成功</returns>
public Tuple<string,string, bool> GetUserByID(string token, string userid)
{
//構(gòu)造請(qǐng)求鏈接
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失敗,請(qǐng)稍后重試!","", false);
}
}
}4.5調(diào)用
本方法是為了企業(yè)微信登錄時(shí)繞過用戶登錄直接使用企業(yè)微信用戶登錄,有其他需求根據(jù)需要調(diào)整。
主頁index 中使用code參數(shù)獲取回調(diào)傳進(jìn)來的code,調(diào)用GetToken方法獲取Token,然后根據(jù)Token和Code獲取UserID,最后根據(jù)UserID和Token獲取通訊錄的頭像和名稱。需要注意的是我們要對(duì)每個(gè)用戶最新的code進(jìn)行緩存,在企業(yè)微信內(nèi)部瀏覽器時(shí)刷新code參數(shù)不會(huì)變動(dòng),但是code只能使用一次會(huì)導(dǎo)致接口調(diào)用失敗。
[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)系人 從內(nèi)存中取||從接口取
string username, portrait = default;
bool issuccess2 = default;
//緩存最近的一次code 用于刷新URL時(shí)重復(fù)code請(qǐng)求失敗
var codeCache = ApplicationContext.UserCache.FirstOrDefault(o => o.Code == code);
if(codeCache == null)
{
//獲取token Token時(shí)間為過期時(shí)間減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);
}
//構(gòu)建前端返回的用戶名 以及頭像
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);
}
}
//授權(quán)篩選
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調(diào)整

5.截圖

到此這篇關(guān)于.NET Core企業(yè)微信網(wǎng)頁授權(quán)登錄的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān).NET Core企業(yè)微信授權(quán)登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在Repeater控件中通過Eval的方式綁定Style樣式代碼
這篇文章主要介紹了如何在Repeater控件中通過Eval的方式綁定Style樣式,需要的朋友可以參考下2014-04-04
ASP.net中網(wǎng)站訪問量統(tǒng)計(jì)方法代碼
這篇文章介紹了ASP.net中網(wǎng)站訪問量統(tǒng)計(jì)方法代碼,有需要的朋友可以參考一下2013-11-11
asp.net下用Aspose.Words for .NET動(dòng)態(tài)生成word文檔中的數(shù)據(jù)表格的方法
導(dǎo)出word 文檔,要求這個(gè)文檔的格式不是固定的,用戶可以隨便的調(diào)整,導(dǎo)出內(nèi)容中的數(shù)據(jù)表格列是動(dòng)態(tài)的,例如要求導(dǎo)出姓名和性別,你就要導(dǎo)出這兩列的數(shù)據(jù),而且這個(gè)文檔不是導(dǎo)出來之后再調(diào)整而是導(dǎo)出來后已經(jīng)是調(diào)整過了的。2010-04-04
iis訪問出現(xiàn)各種問題(Vs訪問正常)的部分處理方法詳細(xì)整理
在vs中調(diào)試都可以正常,但是在iis訪問就會(huì)出現(xiàn)各種問題,很是疑惑索性把這些問題整理一下,這樣更容易的可以處理方法說明清楚,感興趣的朋友可以了解下2013-01-01
ADO.NET中的五個(gè)主要對(duì)象的詳細(xì)介紹與應(yīng)用
ADO.NET中的五個(gè)主要對(duì)象:Connection、Command、DataAdapter DataSet、DataReader詳細(xì)介紹與應(yīng)用,感興趣的朋友可以參考下2012-12-12
合并兩個(gè)DataSet的數(shù)據(jù)內(nèi)容的方法
合并兩個(gè)DataSet的數(shù)據(jù)內(nèi)容的方法,需要的朋友可以參考一下2013-03-03
asp.net實(shí)現(xiàn)圖片以二進(jìn)制流輸出的兩種方法
這篇文章主要介紹了asp.net實(shí)現(xiàn)圖片以二進(jìn)制流輸出的兩種方法,以簡(jiǎn)單實(shí)例形式分析了asp.net實(shí)現(xiàn)以二進(jìn)制流形式讀寫圖片文件的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-12-12

