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

.NET Core企业微信网页授权登录的更多相关文章

  1. Asp.Net Core 企业微信静默授权

    企业微信接口文档 1.构造授权网页链接 2.回调获取到 Code 通过code+access_token去请求用户信息 3.获取access_token 调试准备工作 -->内网穿透+域名 推荐 ...

  2. JustAuth 1.15.9 版发布,支持飞书、喜马拉雅、企业微信网页登录

    新增 修复并正式启用 飞书 平台的第三方登录 AuthToken 类中新增 refreshTokenExpireIn 记录 refresh token 的有效期 PR 合并 Github #101:支 ...

  3. php 微信登录 公众号 获取用户信息 微信网页授权

    php 微信登录 公众号 获取用户信息 微信网页授权 先自己建立两个文件: index.php  和  getUserInfo.php index.php <?php //scope=snsap ...

  4. PHP微信公众平台oauth2.0网页授权登录类的封装demo

    一.微信授权使用的是OAuth2.0授权的方式.主要有以下简略步骤: 第一步:用户同意授权,获取code 第二步:通过code换取网页授权access_token 第三步:拉取用户信息(需scope为 ...

  5. 玩玩微信公众号Java版之六:微信网页授权

    我们经常会访问一些网站,用微信登录的时候需要用到授权,那么微信网页授权是怎么一回事呢,一起来看看吧!   参考官方文档:https://mp.weixin.qq.com/wiki?t=resource ...

  6. 服务号使用微信网页授权(H5应用等)

    获取授权准备 AppId 服务号已经认证且获取到响应接口权限 设置网页授权域名 公众号设置 - 功能设置 - 网页授权域名.注意事项: 回调页面域名或路径需使用字母.数字及"-"的 ...

  7. Java微信公众平台开发(十六)--微信网页授权(OAuth2.0授权)获取用户基本信息

    转自:http://www.cuiyongzhi.com/post/78.html 好长时间没有写文章了,主要是最近的工作和生活上的事情比较多而且繁琐,其实到现在我依然还是感觉有些迷茫,最后还是决定静 ...

  8. 微信网页授权access_token与基础支持的access_token

    问题1:网页授权access_token与分享的jssdk中的access_token一样吗? 答:不一样.网页授权access_token 是一次性的,而基础支持的access_token的是有时间 ...

  9. Java实现微信网页授权

    开发前的准备: 1.需要有一个公众号(我这里用的测试号),拿到AppID和AppSecret: 2.进入公众号开发者中心页配置授权回调域名.具体位置:接口权限-网页服务-网页账号-网页授权获取用户基本 ...

随机推荐

  1. Java常见的垃圾收集器有哪些?

    守拙者_6a98关注 2020.04.11 22:06:31字数 2,135阅读 394 实际上,垃圾收集器( GC , Garbage Collector )是和具体 JVM 实现紧密相关的,不同厂 ...

  2. bash shell 中的 hash 命令有什么作用?

    linux 命令'hash'管理着一个内置的哈希表,记录了已执行过的命令的完整路径,用该命令可以打印出你所使用过的命令以及执行的次数. [root@localhost ~]# hashhits com ...

  3. SpringMVC常用的注解有哪些?

    @RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上.用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径. @RequestBody:注解实现接收http请求 ...

  4. CAS 的问题 ?

    1.CAS 容易造成 ABA 问题 一个线程 a 将数值改成了 b,接着又改成了 a,此时 CAS 认为是没有变化,其实 是已经变化过了,而这个问题的解决方案可以使用版本号标识,每操作一次 versi ...

  5. 什么是持续集成CI?

    持续集成(CI)是每次团队成员提交版本控制更改时自动构建和测试代码的过程. 这鼓励开发人员通过在每个小任务完成后将更改合并到共享版本控制存储库来共 享代码和单元测试.

  6. SVN报错之“Error: Please execute the 'Cleanup' command. ”

    问题 Error: Please execute the 'Cleanup' command. 需要清理下,注意SVN拉数据的时候别打开其中的问题 解决方案

  7. 解决引用类型为什么打出的是地址值,又怎么改成输出属性值(toString()底层)

    一丶toString的源码解析: 一丶object的toString的源码解析: 集合中toString源码分析: 小结: 改成输出属性值 在父类中重写toString();方法 快捷键:Alt+In ...

  8. Kali Linux 下安装配置MongoDB数据库 ubuntu 下安装配置MongoDB源码安装数据库

    Kali Linux 下安装配置MongoDB数据库   1.下载mongodb.tgz 压缩包: 2.解压到:tar -zxvf mongodb.tgz /usr/local/mongodb 3.创 ...

  9. 印度下架54款中国APP,中东政策逐年收紧,伊拉克成蓝海市场

    2月14日,印度电子和信息技术部以"安全威胁"为由对有中国基因的54款App下达禁令,15日,印度税务部门对某些在印中资企业多个场所进行搜查. 本批下架的App中主要以应用类App ...

  10. web移动开发中如何实现图标点击态的蒙层效果

    webapp开发中经常需要加入点击二态,即用户点击(tap)页面某个部分时该部分的样式进行相应的变化来相应用户的点击操作,这样能够带来更好的用户体验,今天我们要讨论的是如何给图标加上点击的二态效果. ...