.NET Core企业微信网页授权登录的实现

目录
  • 1.开发前准备
    • 参数获取
  • 2.企业微信OAuth2接入流程
  • 3.构造网页授权链接
  • 4. 调用代码部分
    • 4.1 appsettings配置
    • 4.2 配置IHttpClientFactory调用微信客户端
    • 4.3 类准备
    • 4.4方法准备
    • 4.5调用
  • 5.截图

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

/// <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.截图

到此这篇关于.NET Core企业微信网页授权登录的实现的文章就介绍到这了,更多相关.NET Core企业微信授权登录内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Asp.Net Core 企业微信静默授权的实现

    企业微信接口文档 1. 构造授权网页链接 2.回调获取到 Code 通过code+access_token去 请求用户信息 3. 获取access_token 调试准备工作 -->内网穿透+域名 推荐向日葵有免费的,免费的开发测试够用了 域名的配置成可信用 上代码 Demo下载 [ApiController] [Route("api/[controller]")] public class Auth2Controller : ControllerBase { private re

  • .NET Core企业微信网页授权登录的实现

    目录 1.开发前准备 参数获取 2.企业微信OAuth2接入流程 3.构造网页授权链接 4. 调用代码部分 4.1 appsettings配置 4.2 配置IHttpClientFactory调用微信客户端 4.3 类准备 4.4方法准备 4.5调用 5.截图 1.开发前准备 参数获取 corpid 每个企业都拥有唯一的corpid,获取此信息可在管理后台“我的企业”-“企业信息”下查看“企业ID” secret secret是企业应用里面用于保障数据安全的“钥匙”,每一个应用都有一个独立的访问

  • ajax 实现微信网页授权登录的方法

    项目背景 因为项目采用前后端完全分离方案,所以,无法使用常规的微信授权登录作法,需要采用 ajax 实现微信授权登录. 需求分析 因为本人是一个phper ,所以,微信开发采用的是 EasyWeChat ,所以实现的方式是基于EW的. 其实实现这个也麻烦,在实现之前,我们需要了解一下微信授权的整个流程. 引导用户进入授权页面同意授权,获取code 通过code换取网页授权access_token(与基础支持中的access_token不同) 如果需要,开发者可以刷新网页授权access_toke

  • 微信公众号 网页授权登录及code been used解决详解

    首先微信公众号开发网页授权登录使用环境: 开发工具:eclipse:服务器:tomcat8,开发语言:JAVA. 我写的网页授权登录时用开发者模式自定义view类型按钮点击跳转链接的. 微信网页授权登录首先以官方微信开发文档为准,大体共分为4步: 先说第一步获取code: code说明:code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5扽这未被使用自动过期. 微信公众开发文档给的有获取code的链接,建议直接复制来用,然后替换其中相应的参

  • C#实现的微信网页授权操作逻辑封装示例

    本文实例讲述了C#实现的微信网页授权操作逻辑封装.分享给大家供大家参考,具体如下: 一.微信网页授权登录 前提: 1.已经获取的接口权限,如果是测试账号就已经有权限了 2.配置接口的授权域名 更多说明可以参考方倍工作室:http://www.cnblogs.com/txw1958/p/weixin71-oauth20.html 或者官网API:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html 步骤: 1.用

  • 企业微信扫码登录网页功能实现代码

    企业微信扫码登录网页功能,代码如下所示: //jq写法完善版 <!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <title>信息平台</title> <script src="http://res

  • 微信公众号网页授权登录的超简单实现步骤

    目录 前言 一.微信公众号授权登录到底哪几步 二.回调地址,安全域名怎么配置 三.代码怎么写 四.出现问题怎么办 总结 前言 这篇文章带大家掌握 从0到1实现微信公众平台授权登录 微信公众号授权登录到底哪几步回调地址,安全域名怎么配置代码怎么写出了问题怎么办 一.微信公众号授权登录到底哪几步 官方文档介绍微信开放文档微信开发者平台文档https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_author

  • 微信网页 第三方登录原理详解

    微信开放平台和公众平台的区别 1.公众平台面向的时普通的用户,比如自媒体和媒体,企业官方微信公众账号运营人员使用,当然你所在的团队或者公司有实力去开发一些内容,也可以调用公众平台里面的接口,比如自定义菜单,自动回复,查询功能.目前大多数微信通过认证之后,都在做这个事情. 2.开放平台面向的开发者和第三方独立软件开发商.我觉得开发平台最大的开放就是微信登录.当年腾讯没有花大力气去做统一登录这个事情,导致目前各个网站都要弄一套登录机制.好在他们现在认清了局势.开发者或软件开发商,通过微信开放提供的平

  • VueJs单页应用实现微信网页授权及微信分享功能示例

    在实际开发中,无论是做PC端.WebApp端还是微信公众号等类型的项目的时候,或多或少都会涉及到微信相关的开发,最近公司项目要求实现微信网页授权,并获取微信用户基本信息的功能及微信分享的功能,现在总算完成了,但开发过程中遇到好几个坑.废话不多说了,开始正题. 描述点 微信相关开发知识了解 怎么样实现微信相关功能本地测试 微信网页授权 微信分享 微信相关开发知识了解 微信公众号的appId,AppSecret 当我们注册一个微信公众号后,便能够得到一个appId(每个微信公众号只有一个,一个微信公

  • Java实现微信网页授权的示例代码

    开发前的准备: 1.需要有一个公众号(我这里用的测试号),拿到AppID和AppSecret: 2.进入公众号开发者中心页配置授权回调域名.具体位置:接口权限-网页服务-网页账号-网页授权获取用户基本信息-修改 注意,这里仅需填写全域名(如www.qq.com.www.baidu.com),勿加 http:// 等协议头及具体的地址字段: 我们可以通过使用Ngrok来虚拟一个域名映射到本地开发环境,网址https://www.ngrok.cc/,大家自己去下载学习怎么使用 同时还需要扫一下这个二

随机推荐