主頁 > .NET開發 > .NET Core企業微信網頁授權登錄

.NET Core企業微信網頁授權登錄

2022-04-29 06:06:04 .NET開發

1.開發前準備

引數獲取

corpid

每個企業都擁有唯一的corpid,獲取此資訊可在管理后臺“我的企業”-“企業資訊”下查看“企業ID”

secret

secret是企業應用里面用于保障資料安全的“鑰匙”,每一個應用都有一個獨立的訪問密鑰,為了保證資料的安全,secret務必不能泄漏,

框架

例子使用yishaadmin開源框架為例

 

2.企業微信OAuth2接入流程

 第一步: 用戶點擊鏈接

   第二步: Index頁取得回呼Code

 第三步: 根據Code和access_token獲取UserID

   第四步: 根據UserID到通訊錄介面獲取其他資訊

 3.構造網頁授權鏈接

假定當前企業CorpID:wxCorpId
訪問鏈接:http://api.3dept.com/cgi-bin/query?action=get

根據URL規范,將上述引數分別進行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進行了一個快取,避免重復獲取.

注意事項:
開發者需要快取access_token,用于后續介面的呼叫(注意:不能頻繁呼叫gettoken介面,否則會受到頻率攔截),當access_token失效或過期時,需要重新獲取,

access_token的有效期通過回傳的expires_in來傳達,正常情況下為7200秒(2小時),有效期內重復獲取回傳相同結果,過期后獲取會回傳新的access_token,
由于企業微信每個應用的access_token是彼此獨立的,所以進行快取時需要區分應用來進行存盤,
access_token至少保留512位元組的存盤空間,
企業微信可能會出于運營需要,提前使access_token失效,開發者應實作access_token失效時重新獲取的邏輯,

獲取Token檔案鏈接https://developer.work.weixin.qq.com/document/path/91039#15074

    /// <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("獲取企業微信Token失敗,請稍后重試!", false);
                    }
                }
            }
            else
            {
                return Tuple.Create(ApplicationContext.Token, true);
            }
        }

獲取用戶ID方法,該方法根據獲取到的token,以及回呼的code進行請求,得到用戶id物體

獲取訪問用戶身份檔案鏈接:https://developer.work.weixin.qq.com/document/path/91023

  /// <summary>
        /// 獲取用戶ID
        /// </summary>
        /// <param name="token">企業微信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呼叫

  本方法是為了企業微信登錄時繞過用戶登錄直接使用企業微信用戶登錄,有其他需求根據需要調整,

  主頁index 中使用code引數獲取回呼傳進來的code,呼叫GetToken方法獲取Token,然后根據Token和Code獲取UserID,最后根據UserID和Token獲取通訊錄的頭像和名稱,需要注意的是我們要對每個用戶最新的code進行快取,在企業微信內部瀏覽器時重繪code引數不會變動,但是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)//企業微信登錄
            {
                //獲取聯系人 從記憶體中取||從介面取
                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//正常網頁登錄
            {

                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.截圖

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/466922.html

標籤:.NET Core

上一篇:C#語法糖系列 —— 第三篇:聊聊閉包的底層玩法

下一篇:C#語法糖系列 —— 第三篇:聊聊閉包的底層玩法

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more