iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > ASP.NET >ASP.NET Cookie是怎么生成的(推荐)
  • 894
分享到

ASP.NET Cookie是怎么生成的(推荐)

netASP.NETcookieASP 2022-06-07 21:06:49 894人浏览 八月长安
摘要

可能有人知道Cookie的生成由MachineKey有关,machineKey用于决定Cookie生成的算法和密钥,并如果使用多台服务器做负载均衡时,必须指定一致的machin

可能有人知道Cookie的生成由MachineKey有关,machineKey用于决定Cookie生成的算法和密钥,并如果使用多台服务器负载均衡时,必须指定一致的machineKey用于解密,那么这个过程到底是怎样的呢?

如果需要在.net core中使用asp.net Cookie,本文将提到的内容也将是一些必经之路。

抽丝剥茧,一步一步分析
首先用户通过AccountController->Login进行登录:


//
// POST: /Account/Login
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
 if (!ModelState.IsValid)
 {
 return View(model);
 }
 var result = await SignInManager.PassWordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
 switch (result)
 {
 case SignInStatus.Success:
  return RedirectToLocal(returnUrl);
 // ......省略其它代码
 }
}

它调用了SignInManager的PasswordSignInAsync方法,该方法代码如下(有删减):


public virtual async Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)
{
 // ...省略其它代码
 if (await UserManager.CheckPasswordAsync(user, password).WithCurrentCulture())
 {
 if (!await IsTwoFactorEnabled(user))
 {
  await UserManager.ResetAccessFailedCountAsync(user.Id).WithCurrentCulture();
 }
 return await SignInOrTwoFactor(user, isPersistent).WithCurrentCulture();
 }
 // ...省略其它代码
 return SignInStatus.Failure;
}

想浏览原始代码,可参见官方的GitHub链接:

https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Owin/SignInManager.cs#L235-L276

可见它先需要验证密码,密码验证正确后,它调用了SignInOrTwoFactor方法,该方法代码如下:


private async Task<SignInStatus> SignInOrTwoFactor(TUser user, bool isPersistent)
{
 var id = Convert.ToString(user.Id);
 if (await IsTwoFactorEnabled(user) && !await AuthenticationManager.TwoFactorBrowserRememberedAsync(id).WithCurrentCulture())
 {
 var identity = new ClaimsIdentity(DefaultAuthenticationTypes.TwoFactorCookie);
 identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, id));
 AuthenticationManager.SignIn(identity);
 return SignInStatus.RequiresVerification;
 }
 await SignInAsync(user, isPersistent, false).WithCurrentCulture();
 return SignInStatus.Success;
}

该代码只是判断了是否需要做双重验证,在需要双重验证的情况下,它调用了AuthenticationManager的SignIn方法;否则调用SignInAsync方法。SignInAsync的源代码如下:


public virtual async Task SignInAsync(TUser user, bool isPersistent, bool rememberBrowser)
{
 var userIdentity = await CreateUserIdentityAsync(user).WithCurrentCulture();
 // Clear any partial cookies from external or two factor partial sign ins
 AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie, DefaultAuthenticationTypes.TwoFactorCookie);
 if (rememberBrowser)
 {
 var rememberBrowserIdentity = AuthenticationManager.CreateTwoFactorRememberBrowserIdentity(ConvertIdToString(user.Id));
 AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = isPersistent }, userIdentity, rememberBrowserIdentity);
 }
 else
 {
 AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = isPersistent }, userIdentity);
 }
}

可见,最终所有的代码都是调用了AuthenticationManager.SignIn方法,所以该方法是创建Cookie的关键。

AuthenticationManager的实现定义在Microsoft.Owin中,因此无法在ASP.net Identity中找到其源代码,因此我们打开Microsoft.Owin的源代码继续跟踪(有删减):


public void SignIn(AuthenticationProperties properties, params ClaimsIdentity[] identities)
{
 AuthenticationResponseRevoke priorRevoke = AuthenticationResponseRevoke;
 if (priorRevoke != null)
 {
 // ...省略不相关代码
 AuthenticationResponseRevoke = new AuthenticationResponseRevoke(filteredSignOuts);
 }
 AuthenticationResponseGrant priorGrant = AuthenticationResponseGrant;
 if (priorGrant == null)
 {
 AuthenticationResponseGrant = new AuthenticationResponseGrant(new ClaimsPrincipal(identities), properties);
 }
 else
 {
 // ...省略不相关代码
 AuthenticationResponseGrant = new AuthenticationResponseGrant(new ClaimsPrincipal(mergedIdentities), priorGrant.Properties);
 }
}

AuthenticationManager的Github链接如下:Https://github.com/aspnet/AspNetKatana/blob/c33569969e79afd9fb4ec2d6bdff877e376821b2/src/Microsoft.Owin/Security/AuthenticationManager.cs

可见它用到了AuthenticationResponseGrant,继续跟踪可以看到它实际是一个属性:


public AuthenticationResponseGrant AuthenticationResponseGrant
{
 // 省略get
 set
 {
 if (value == null)
 {
  SignInEntry = null;
 }
 else
 {
  SignInEntry = Tuple.Create((IPrincipal)value.Principal, value.Properties.Dictionary);
 }
 }
}

发现它其实是设置了SignInEntry,继续追踪:


public Tuple<IPrincipal, IDictionary<string, string>> SignInEntry
{
 get { return _context.Get<Tuple<IPrincipal, IDictionary<string, string>>>(OwinConstants.Security.SignIn); }
 set { _context.Set(OwinConstants.Security.SignIn, value); }
}

其中,_context的类型为IOwinContext,OwinConstants.Security.SignIn的常量值为"security.SignIn"。

跟踪完毕……

啥?跟踪这么久,居然跟丢啦!?
当然没有!但接下来就需要一定的技巧了。

原来,ASP.NET是一种中间件(Middleware)模型,在这个例子中,它会先处理mvc中间件,该中间件处理流程到设置AuthenticationResponseGrant/SignInEntry为止。但接下来会继续执行CookieAuthentication中间件,该中间件的核心代码在aspnet/AspNetKatana仓库中可以看到,关键类是CookieAuthenticationHandler,核心代码如下:


protected override async Task ApplyResponseGrantAsync()
{
 AuthenticationResponseGrant signin = Helper.LookupSignIn(Options.AuthenticationType);
 // ... 省略部分代码
 if (shouldSignin)
 {
 var signInContext = new CookieResponseSignInContext(
  Context,
  Options,
  Options.AuthenticationType,
  signin.Identity,
  signin.Properties,
  cookieOptions);
 // ... 省略部分代码
 model = new AuthenticationTicket(signInContext.Identity, signInContext.Properties);
 // ... 省略部分代码
 string cookieValue = Options.TicketDataFORMat.Protect(model);
 Options.CookieManager.AppendResponseCookie(
  Context,
  Options.CookieName,
  cookieValue,
  signInContext.CookieOptions);
 }
 // ... 又省略部分代码
}

这个原始函数有超过200行代码,这里我省略了较多,但保留了关键、核心部分,想查阅原始代码可以移步Github链接:https://github.com/aspnet/AspNetKatana/blob/0fc4611e8b04b73f4e6bd68263e3f90e1adfa447/src/Microsoft.Owin.Security.Cookies/CookieAuthenticationHandler.cs#L130-L313

这里挑几点最重要的讲。

与MVC建立关系

建立关系的核心代码就是第一行,它从上文中提到的位置取回了AuthenticationResponseGrant,该Grant保存了Claims、AuthenticationTicket等Cookie重要组成部分:

AuthenticationResponseGrant signin = Helper.LookupSignIn(Options.AuthenticationType);
继续查阅LookupSignIn源代码,可看到,它就是从上文中的AuthenticationManager中取回了AuthenticationResponseGrant(有删减):


public AuthenticationResponseGrant LookupSignIn(string authenticationType)
{
 // ...
 AuthenticationResponseGrant grant = _context.Authentication.AuthenticationResponseGrant;
 // ...
 foreach (var claimsIdentity in grant.Principal.Identities)
 {
 if (string.Equals(authenticationType, claimsIdentity.AuthenticationType, StrinGComparison.Ordinal))
 {
  return new AuthenticationResponseGrant(claimsIdentity, grant.Properties ?? new AuthenticationProperties());
 }
 }
 return null;
}

如此一来,柳暗花明又一村,所有的线索就立即又明朗了。

Cookie的生成

从AuthenticationTicket变成Cookie字节串,最关键的一步在这里:

string cookieValue = Options.TicketDataFormat.Protect(model);
在接下来的代码中,只提到使用CookieManager将该Cookie字节串添加到Http响应中,翻阅CookieManager可以看到如下代码:


public void AppendResponseCookie(IOwinContext context, string key, string value, CookieOptions options)
{
 if (context == null)
 {
 throw new ArgumentNullException("context");
 }
 if (options == null)
 {
 throw new ArgumentNullException("options");
 }
 IHeaderDictionary responseHeaders = context.Response.Headers;
 // 省去“1万”行计算chunk和处理细节的流程
 responseHeaders.AppendValues(Constants.Headers.SetCookie, chunks);
}

有兴趣的朋友可以访问Github看原始版本的代码:https://github.com/aspnet/AspNetKatana/blob/0fc4611e8b04b73f4e6bd68263e3f90e1adfa447/src/Microsoft.Owin/Infrastructure/ChunkingCookieManager.cs#L125-L215

可见这个实现比较……简单,就是往Response.Headers中加了个头,重点只要看TicketDataFormat.Protect方法即可。

逐渐明朗

该方法源代码如下:


public string Protect(TData data)
{
 byte[] userData = _serializer.Serialize(data);
 byte[] protectedData = _protector.Protect(userData);
 string protectedText = _encoder.Encode(protectedData);
 return protectedText;
}

可见它依赖于_serializer、_protector、_encoder三个类,其中,_serializer的关键代码如下:


public virtual byte[] Serialize(AuthenticationTicket model)
{
 using (var memory = new MemoryStream())
 {
 using (var compression = new GZipStream(memory, CompressionLevel.Optimal))
 {
  using (var writer = new BinaryWriter(compression))
  {
  Write(writer, model);
  }
 }
 return memory.ToArray();
 }
}

其本质是进行了一次二进制序列化,并紧接着进行了gzip压缩,确保Cookie大小不要失去控制(因为.NET的二进制序列化结果较大,并且微软喜欢搞xml,更大😂)。

然后来看一下_encoder源代码:


public string Encode(byte[] data)
{
 if (data == null)
 {
 throw new ArgumentNullException("data");
 }
 return Convert.ToBase64String(data).TrimEnd('=').Replace('+', '-').Replace('/', '_');
}

可见就是进行了一次简单的base64-url编码,注意该编码把=号删掉了,所以在base64-url解码时,需要补=号。

这两个都比较简单,稍复杂的是_protector,它的类型是IDataProtector。

IDataProtector

它在CookieAuthenticationMiddleware中进行了初始化,创建代码和参数如下:


IDataProtector dataProtector = app.CreateDataProtector(
 typeof(CookieAuthenticationMiddleware).FullName,
 Options.AuthenticationType, "v1");

注意它传了三个参数,第一个参数是CookieAuthenticationMiddleware的FullName,也就是"Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware",第二个参数如果没定义,默认值是CookieAuthenticationDefaults.AuthenticationType,该值为定义为"Cookies"。

但是,在默认创建的ASP.NET MVC模板项目中,该值被重新定义为ASP.NET Identity的默认值,即"ApplicationCookie",需要注意。

然后来看看CreateDataProtector的源码


public static IDataProtector CreateDataProtector(this IAppBuilder app, params string[] purposes)
{
 if (app == null)
 {
 throw new ArgumentNullException("app");
 }
 IDataProtectionProvider dataProtectionProvider = GetDataProtectionProvider(app);
 if (dataProtectionProvider == null)
 {
 dataProtectionProvider = FallbackDataProtectionProvider(app);
 }
 return dataProtectionProvider.Create(purposes);
}
public static IDataProtectionProvider GetDataProtectionProvider(this IAppBuilder app)
{
 if (app == null)
 {
 throw new ArgumentNullException("app");
 }
 object value;
 if (app.Properties.TryGetValue("security.DataProtectionProvider", out value))
 {
 var del = value as DataProtectionProviderDelegate;
 if (del != null)
 {
  return new CallDataProtectionProvider(del);
 }
 }
 return null;
}

可见它先从IAppBuilder的"security.DataProtectionProvider"属性中取一个IDataProtectionProvider,否则使用DpapiDataProtectionProvider。

我们翻阅代码,在OwinAppContext中可以看到,该值被指定为MachineKeyDataProtectionProvider:

builder.Properties[Constants.SecurityDataProtectionProvider] = new MachineKeyDataProtectionProvider().ToOwinFunction();
文中的Constants.SecurityDataProtectionProvider,刚好就被定义为"security.DataProtectionProvider"。

我们翻阅MachineKeyDataProtector的源代码,刚好看到它依赖于MachineKey:


internal class MachineKeyDataProtector
{
 private readonly string[] _purposes;
 public MachineKeyDataProtector(params string[] purposes)
 {
 _purposes = purposes;
 }
 public virtual byte[] Protect(byte[] userData)
 {
 return MachineKey.Protect(userData, _purposes);
 }
 public virtual byte[] Unprotect(byte[] protectedData)
 {
 return MachineKey.Unprotect(protectedData, _purposes);
 }
}

最终到了我们的老朋友MachineKey。

逆推过程,破解Cookie
首先总结一下这个过程,对一个请求在Mvc中的流程来说,这些代码集中在ASP.NET Identity中,它会经过:

AccountController SignInManager AuthenticationManager

设置AuthenticatinResponseGrant

然后进入CookieAuthentication的流程,这些代码集中在Owin中,它会经过:

CookieAuthenticationMiddleware(读取AuthenticationResponseGrant)
ISecureDataFormat(实现类:SecureDataFormat<T>)
IDataSerializer(实现类:TicketSerializer)
IDataProtector(实现类:MachineKeyDataProtector)
ITextEncoder(实现类:Base64UrlTextEncoder)

这些过程,结果上文中找到的所有参数的值,我总结出的“祖传破解代码”如下:


string cookie = "nZBqV1M-Az7yJezhb6dUzS_urj1urB0GDufSvDjsa0pv27CnDsLHRzMDdpU039j6ApL-VNfrJULfE85yU9RFzGV_aAGXHVkGckYqkCRJUKWV8SqPEjNJ5ciVzW--uxsCBNlG9jOhJI1FJIByRzYJvidjTYABWFQnSSd7XpQRjY4lb082nDZ5lwJVK3gaC_zt6H5Z1k0lUFZRb6afF52laMc___7BdZ0mZSA2kRxTk1QY8h2gQh07HqlR_p0uwTFNKi0vW9NxkplbB8zfKbfzDj7usep3zAeDEnwofyJERtboXgV9GIS21fLjc58O-4rR362IcCi2pYjaKHwZoO4LKWe1bS4r1tyzW0Ms-39Njtiyp7lRTN4HUHMUi9PxacRNgVzkfK3msTA6LkCJA3VwRm_UUeC448Lx5pkcCPCB3lGat_5ttGRjKD_lllI-YE4esXHB5eJilJDIZlEcHLv9jYhTl17H0Jl_H3FqXyPQJR-ylQfh";
var bytes = TextEncodings.Base64Url.Decode(cookie);
var decrypted = MachineKey.Unprotect(bytes,
 "Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware",
 "ApplicationCookie",
 "v1");
var serializer = new TicketSerializer();
var ticket = serializer.Deserialize(decrypted);
ticket.Dump(); // Dump为LINQPad专有函数,用于方便调试显示,此处可以用循环输出代替

运行前请设置好app.config/WEB.config中的machineKey节点,并安装NuGet包:Microsoft.Owin.Security,运行结果如下(完美破解):

总结

学习方式有很多种,其中看代码是我个人非常喜欢的一种方式,并非所有代码都会一马平川。像这个例子可能还需要有一定ASP.NET知识背景。

注意这个“祖传代码”是基于.NET Framework,由于其用到了MachineKey,因此无法在.Net Core中运行。我稍后将继续深入聊聊MachineKey这个类,看它底层代码是如何工作的,然后最终得以在.NET Core中直接破解ASP.NET Identity中的Cookie,敬请期待!

以上所述是小编给大家介绍的ASP.NET Cookie是怎么生成的,希望对大家有所帮助!

您可能感兴趣的文章:详解在ASP.net core 中使用Cookie中间件Asp.net中安全退出时清空Session或Cookie的实例代码ASP.NET中Cookie的使用方法asp.net利用cookie保存用户密码实现自动登录的方法ASP.NET之Response.Cookies.Remove 无法删除COOKIE的原因ASP.NET笔记之页面跳转、调试、form表单、viewstate、cookie的使用说明


--结束END--

本文标题: ASP.NET Cookie是怎么生成的(推荐)

本文链接: https://www.lsjlt.com/news/30779.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • python生成requirements.txt文件的推荐方法
    目录一、什么是requirements.txt文件及作用二、怎么生成requirements.txt 文件1、pip freeze方法(不推荐)2、pipreqs第三方库(推荐)补充...
    99+
    2024-04-02
  • ASP.NET中怎么生成XML
    本篇文章给大家分享的是有关ASP.NET中怎么生成XML,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。1.业务逻辑层:public DataSet ABCD...
    99+
    2023-06-17
  • Vue页面生成PDF的最佳方法推荐
    目录前言安装依赖页面转图片图片转PDFA4打印适配总结前言 最近项目有个需求,将系统统计的数据生成分析报告,然后可以导出成PDF。 这种方法可以有两种,一种是直接调用打印,用户通过浏...
    99+
    2024-04-02
  • ASP.NET中Cookie的作用是什么
    本篇文章给大家分享的是有关ASP.NET中Cookie的作用是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。ASP.NET Cookie在Web开发中应用较多。做用户登录,...
    99+
    2023-06-17
  • MyBatis Generator ORM层面的代码自动生成器(推荐)
    在日常开发工作中,我们往往需要自己去构建各种数据表所对应的持久化对象(POJO)、用于操作数据库的接口(DAO)以及跟 DAO 所绑定的对应 XML。这都是一些重复性的操作,不需要多...
    99+
    2023-01-30
    MyBatis Generator 代码自动生成器 MyBatis Generator 代码生成器
  • ASP.NET中怎么生成HTML静态页面
    ASP.NET中怎么生成HTML静态页面,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。ASP.NET模版生成HTML静态页面方案1:/// <&...
    99+
    2023-06-17
  • 微信短网址在线生成 推荐几个可在在线生成微信短网址的平台
    微信短网址生成工具,之前腾讯在外公开了一个可以供大家使用的微信url短网址API接口,但是在年前这个接口已经失效了,导致现在很多需要使用微信url短链接的朋友没有办法在继续使用了,今天我就给大家推荐几个还可以生成微信url短网址的接口和平台...
    99+
    2023-06-04
  • 网址缩短 短网址链接缩短生成器的试用推荐
    短网址,顾名思义就是在形式上比较短的网址。 还记得前几天发文章回复关键词槽边往事吗,出来的那个网址就是短网址。 先简单说一下短网址原理,按照我的理解就是网址的中转站,点击短网址,经过服务器分析转向到原网址。  很多公众号...
    99+
    2023-06-04
  • ASP.NET控件开发之控件生成器怎么用
    这篇文章给大家分享的是有关ASP.NET控件开发之控件生成器怎么用的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。ASP.NET控件开发之控件生成器1.错误的代码,无法解析首先来看一段简单的代码正确 &n...
    99+
    2023-06-18
  • 怎么在ASP.NET项目中生成一个PDF文档
    本篇文章给大家分享的是有关怎么在ASP.NET项目中生成一个PDF文档,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。安装 DinkToPdf要想安装 DinkToPdf,可以通...
    99+
    2023-06-14
  • 新浪短链接 推荐几个最新的新浪t.cn短链接生成平台
    很多朋友肯定会遇到这些情况,系统生成的推广链接,很多时候都是长长的一串,如果我们拿来做短信推广,链接会占有很大的字符空间,增加了我们的推广成本!如果是发给别人的话,就会显得啰里啰嗦,非常难看,使用起来不方便。其实长链接也是能够变成短链接的,...
    99+
    2023-06-04
  • react推荐函数组件的原因是什么
    这篇文章主要讲解了“react推荐函数组件的原因是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“react推荐函数组件的原因是什么”吧!原因:1、函数组件语法更短、更简单,这使得它更容易...
    99+
    2023-07-04
  • MySQL订单ID是怎么生成的
    本篇内容介绍了“MySQL订单ID是怎么生成的”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!面试官: 小伙子,你低着头笑什么呐。开始面试了,...
    99+
    2023-07-05
  • Python全栈推导式和生成器怎么实现
    本篇内容主要讲解“Python全栈推导式和生成器怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Python全栈推导式和生成器怎么实现”吧!1. 推导式# ### 推导...
    99+
    2023-06-21
  • 新浪短链接 推荐几个最新的新浪t.cn短链接生成的API接口
    新浪很久之前提供了长链接转为短链接的公开API,可以把长链接转为t.cn/xxx这种格式的新浪短链接。但是在去年9月的时候,新浪由于政策上的调整,将之前的接口关闭了!今天就给大家带来几个还可以使用新浪短网址工具:新浪短网址API接口(亲测可...
    99+
    2023-06-04
  • Python异步编程:Linux下的IDE推荐是什么?
    在Python编程中,异步编程已经成为了一个非常重要的概念。异步编程可以让程序在等待某些IO操作的时候不会阻塞,从而提高程序的并发性和响应速度。而在Linux下进行Python异步编程的时候,选择一个好用的IDE也是非常重要的。那么,究竟...
    99+
    2023-09-02
    异步编程 linux ide
  • 为什么Spring官方推荐的@Transational还能导致生产事故
    目录事故原因分析何为长事务?长事务会引发哪些问题?如何避免长事务?声明式事务小结在Spring中进行事务管理非常简单,只需要在方法上加上注解@Transactional,Spring...
    99+
    2024-04-02
  • 推荐系统MostPopular算法的Python怎么实现
    今天小编给大家分享一下推荐系统MostPopular算法的Python怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。...
    99+
    2023-07-02
  • Cookie文件是怎么工作的
    这篇文章主要介绍“Cookie文件是怎么工作的”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Cookie文件是怎么工作的”文章能帮助大家解决问题。1.什么是 cookie 文件 (COOKIES.T...
    99+
    2023-06-26
  • Python推导式、生成器与切片问题怎么解决
    本篇内容介绍了“Python推导式、生成器与切片问题怎么解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、实验要求理解并掌握序列中的常用...
    99+
    2023-06-29
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作