强制门户认证

摘要  

强制网络门户(captive portal)就是手机在连接上某个WiFi的时候,自动弹出一个页面,这个页面通常可以用来配置网络、验证用户信息等,对于公司的产品可以使用此功能实现WEB运维后台。

  • 强制门户原理

强制门户技术的核心是重定向,重定向的方法有多种,比如基于MAC或IP的重定向、DNS重定向、HTTP重定向、WPAD等等,当然重定向的作用范围远不止强制门户技术。无论是何种重定向,本质上是利用通信协议的机制和规则,实现特定数据流的转发

对于强制门户使用的主要是DNS重定向和HTTP重定向结合的方案,通过DNS重定向,将用户的外网访问请求指定到管理地址,然后HTTP进程接收特定的HTTP请求后,返回特定的HTTP重定向报文,客户端浏览器以重定向报文中指定的URL再次发起请求,以此将用户强制到网关的登陆页面。

图1 强制门户重定向交互流程

  • DNS应用层协议
    1. DNS资源记录

在介绍DNS层协议之前,先了解一下DNS服务器存储的资源记录(Resource RecordsRRs,一条资源记录(RR)记载着一个映射关系。每条RR通常包含如下表所示的一些信息:

表1 RR信息类型

字段

含义

NAME

名字

TYPE

类型

CLASS

TTL

生存时间

RDLENGTH

RDATA所占的字节数

RDATA

数据

NAME和RDATA表示的含义根据TYPE的取值不同而不同,常见的:

    1. 若TYPE=A,则name是主机名,value是其对应的ip;
    2. 若TYPE=NS,则name是一个域,value是一个权威DNS服务器的主机名。该记录表示name域的域名解析将由value主机名对应的DNS服务器来做;
    3. 若TYPE=CNAME,则value是别名为name的主机对应的规范主机名;
    4. 若TYPE=MX,则value是别名为name的邮件服务器的规范主机名;
    5. ……

TYPE实际上还有其他类型,所有可能的type及其约定的数值表示如下:

表2 TYPE类型以及含义

TYPE

value

meaning

A

1

a host address

NS

2

an authoritative name server

MD

3

a mail destination (Obsolete - use MX)

MF

4

a mail forwarder (Obsolete - use MX)

CNAME

5

the canonical name for an alias

SOA

6

marks the start of a zone of authority

MB

7

a mailbox domain name (EXPERIMENTAL)

MG

8

a mail group member (EXPERIMENTAL)

MR

9

a mail rename domain name (EXPERIMENTAL)

NULL

10

a null RR (EXPERIMENTAL)

WKS

11

a well known service description

PTR

12

a domain name pointer

HINFO

13

host information

MINFO

14

mailbox or mail list information

MX

15

mail exchange

TXT

16

text strings

    1. DNS整体说明

DNS请求与响应的格式是一致的,其整体分为Header、Question、Answer、Authority、Additional5部分,如下图所示:

https://blog-1251252991.cos.ap-chengdu.myqcloud.com/2017-04-15%2003-02-39%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE.png

图2 DNS协议整体结构

    1. Header部分说明

Header部分是一定有的,长度固定为12个字节;其余4部分可能有也可能没有,并且长度也不一定,这个在Header部分中有指明。Header的结构如下:

https://blog-1251252991.cos.ap-chengdu.myqcloud.com/2017-04-15%2003-06-31%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE.png

图3 Header结构

下面说明一下各个字段的含义:

  1. ID:占16位。该值由发出DNS请求的程序生成,DNS服务器在响应时会使用该ID,这样便于请求程序区分不同的DNS响应。
  2. QR:占1位。指示该消息是请求还是响应。0表示请求;1表示响应。
  3. OPCODE:占4位。指示请求的类型,有请求发起者设定,响应消息中复用该值。0表示标准查询;1表示反转查询;2表示服务器状态查询。3~15目前保留,以备将来使用。
  4. AA(Authoritative Answer,权威应答):占1位。表示响应的服务器是否是权威DNS服务器。只在响应消息中有效。
  5. TC(TrunCation,截断):占1位。指示消息是否因为传输大小限制而被截断。
  6. RD(Recursion Desired,期望递归):占1位。该值在请求消息中被设置,响应消息复用该值。如果被设置,表示希望服务器递归查询。但服务器不一定支持递归查询。
  7. RA(Recursion Available,递归可用性):占1位。该值在响应消息中被设置或被清除,以表明服务器是否支持递归查询。
  8. Z:占3位。保留备用。
  9. RCODE(Response code):占4位。该值在响应消息中被设置。取值及含义如下:
  • 0:No error condition,没有错误条件;
  • 1:Format error,请求格式有误,服务器无法解析请求;
  • 2:Server failure,服务器出错。
  • 3:Name Error,只在权威DNS服务器的响应中有意义,表示请求中的域名不存在。
  • 4:Not Implemented,服务器不支持该请求类型。
  • 5:Refused,服务器拒绝执行请求操作。
  • 6~15:保留备用。
  1. QDCOUNT:占16位(无符号)。指明Question部分的包含的实体数量。
  2. ANCOUNT:占16位(无符号)。指明Answer部分的包含的RR(Resource Record)数量。
  3. NSCOUNT:占16位(无符号)。指明Authority部分的包含的RR(Resource Record)数量。
  4. ARCOUNT:占16位(无符号)。指明Additional部分的包含的RR(Resource Record)数量。
    1. Question部分说明

Question部分的每一个实体的格式如下图所示:

https://blog-1251252991.cos.ap-chengdu.myqcloud.com/2017-04-15%2003-08-09%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE.png

图4 Question结构

  1. QNAME:字节数不定,以0x00作为结束符。表示查询的主机名。注意:众所周知,主机名被"."号分割成了多段标签。在QNAME中,每段标签前面加一个数字,表示接下来标签的长度。比如:api.sina.com.cn表示成QNAME时,会在"api"前面加上一个字节0x03,"sina"前面加上一个字节0x04,"com"前面加上一个字节0x03,而"cn"前面加上一个字节0x02;
  2. QTYPE:占2个字节。表示RR类型,见上述RR介绍;
  3. QCLASS:占2个字节。表示RR分类,见上述RR介绍。
    1. AnswerAuthorityAdditional部分

Answer、Authority、Additional部分格式一致,每部分都由若干实体组成,每个实体即为一条RR,之前有过介绍,格式如下图所示:

https://blog-1251252991.cos.ap-chengdu.myqcloud.com/2017-04-15%2003-11-32%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE.png

图4 Answer&Authority&Additional结构

  1. NAME:长度不定,可能是真正的数据,也有可能是指针(其值表示的是真正的数据在整个数据中的字节索引数),还有可能是二者的混合(以指针结尾)。若是真正的数据,会以0x00结尾;若是指针,指针占2个字节,第一个字节的高2位为11。
  2. TYPE:占2个字节。表示RR的类型,如A、CNAME、NS等,见上述RR介绍;
  3. CLASS:占2个字节。表示RR的分类,见上述RR介绍;
  4. TTL:占4个字节。表示RR生命周期,即RR缓存时长,单位是秒;
  5. RDLENGTH:占2个字节。指定RDATA字段的字节数;
  6. RDATA:即上述介绍的value,含义与TYPE有关,见上述RR介绍。
  • HTTP重定向

URL重定向(也称为 URL转发)是一种为页面、表单或者整个Web站点/应用提供多个URL地址的技术。HTTP对此操作有一种特殊类型的响应,称为 HTTP重定向(HTTP redirect)。

  1. 方案简述

在HTTP中,服务器可以通过返回一个重定向响应来进行重定向。这个重定向响应有一个以3开头的状态码,并且有一个 Location 头字段表示要重定向到的位置。

浏览器接收到这个重定向之后,会立即加载 Location中指定的URL。通常这一过程耗时极短,用户基本注意不到这个过程。

重定向过程如下图所示:

图5 HTTP重定向过程

    1. 重定向状态码及含义

表3 HTTP重定向状态码

状态码

状态短语

状态含义

300

Multiple Choices

当请求的URL对应有多个资源时(如同一个HTML的不同语言的版本),返回这个代码时,可以返回一个可选列表,这样用户可以自行选择。通过Location头字段可以自定首选内容。

301

Moved Permanetly

当前请求的资源已被移除时使用,响应的Location 头字段会提供资源现在的URL。直接使用GET方法发起新情求。

302

Found

301类似,但客户端只应该将Location返回的URL当做临时资源来使用,将来请求时,还是用老的URL。直接使用GET方法发起新情求。

303

See Other

用于在PUT或者POST请求之后进行重定向,这样在结果页就不会再次触发重定向了。

304

Not Modified

资源未修改,表示本地缓存仍然可用。

305

Use Proxy

用来表示必须通过一个代理来访问资源,代理的位置有Location头字段给出

306

Switch Proxy

在最新版的规范中,306状态码已不再被使用。最初是指后续请求应使用指定的代理

307

Temporary Redirect

302类似,但是使用原请求方法发起新情求。

308

Permanent Redirect

301 类似,但是使用原请求方法发起新情求。

这9种状态码可以分成3大类,分别是:

  • 永久重定向:301308
  • 临时重定向:302303307
  • 特殊重定向:300304305306

对于强制门户,使用一般都为临时重定向,下面进一步说明临时重定向处理方法以及应用场景。

表4 临时重定向使用场景

状态码

处理方法

典型应用场景

302

GET方法不会发生变更。其他方法有可能会变更为GET方法。

由于不可预见的原因该页面暂不可用。

303

GET方法不会发生变更,其他方法会变更GET方法(消息主体丢失)。

用于PUTPOST请求完成之后重定向,来防止由于页面刷新导致的操作的重复触发。

307

方法和消息主体都不发生变化。

由于不可预见的原因该页面暂不可用。当站点支持非GET方法的链接或操作的时候,该状态码优于302状态码。

由于我们的状态HTTP服务器仅支持GET方法,所以选用302进行HTTP重定向。

  • 相关案例分析

在了解了上述强制门户的原理以及相关基础后,下面,使用Wireshark抓取路由器强制门户的实际案例。

(dns || tcp || http) && !tcp.analysis.out_of_order && !tcp.analysis.retransmission && !tcp.analysis.duplicate_ack


其中WireShark过滤规则可以参考如下规则:

  1. dns || tcp || http:
  • dns:显示所有DNS协议的数据包。
  • tcp:显示所有TCP协议的数据包。
  • http:显示所有HTTP协议的数据包。
  • 逻辑操作符||表示“或”,即只要数据包属于DNS、TCP或HTTP协议之一,就会被显示出来。
  1. !tcp.analysis.out_of_order && !tcp.analysis.retransmission && !tcp.analysis.duplicate_ack :
  • tcp.analysis.out_of_order:标识为“TCP out of order”的数据包。这些数据包表示TCP流中的数据包到达顺序与发送顺序不一致。
  • tcp.analysis.retransmission:标识为“TCP Retransmission”的数据包。这些数据包表示数据包由于丢失或错误而被重新发送。
  • tcp.analysis.duplicate_ack:标识为“TCP Dup ACK”的数据包。这些数据包表示接收方收到了重复的ACK(确认)数据包,通常表示之前的某个数据包没有正确接收。
  • !:表示“非”或“排除”,即排除掉上述所有标记的数据包。
  • 2、DNS解析

分析其DNS解析部分,可以看到路由器将客户端的DNS请求,解析为了172.31.255.254这个IP地址,后续客户端将使用此IP去打开socket。

图6 DNS解析报文

  1. 建立TCP连接并且进行HTTP重定向

图7 HTTP重定向报文

  1. 建立TCP连接

在通过DNS获取到目标IP(172.31.255.254)后,客户端将与目标IP建立TCP连接,其端口为HTTP的默认端口:80;

  1. HTTP请求以及重定向

建立TCP连接后,客户端请求HTTP服务,服务使用307将HTTP请求进行重定向到http://192.168.8.1/html/index.html?origin=xxx,此url为路由器的管理网站;

  1. 关闭TCP连接

因为重定向后的IP(192.168.1.1)与DNS解析的IP(172.31.255.254)不一致,所以客户端断开TCP连接。随后,客户端将使用重定向后的IP重新建立TCP连接;

    1. 登录管理页面

图8 登录管理界面报文

  1. 建立TCP连接

客户端将与重定向后的IP(192.168.1.1)建立TCP连接,其端口为HTTP的默认端口:80;

  1. HTTP请求与回复

建立TCP连接后,客户端使用重定向的url(/html/index.html?origin=xxx)发起HTTP请求,服务端回复管理页面的html文件;

至此,客户端WEB已成功打开管理页面,强制门户认证过程完成。

    1. 对比ESP32门户网站案例

在分析完路由器的案例后,又抓取了ESP32门户网站的相关报文。

发现两者之间存在以下差异点:

  1. 对于DNS请求的处理不同
  • 路由器将客户端所有的DNS请求统一定向到172.31.255.254;

图9 路由器DNS请求与回复

  • ESP32仅对部分域名(type A)的DNS请求返回了IP(192.168.4.1),但是对其余的DNS请求,进行了特殊的回复(Unused);

图10 ESP32DNS请求与回复

其特殊回复实现主要是将Type值设置为了0,如下图所示:

图10 ESP32回复Unused

查看DNS的标准规范DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION并未找到对Type值为0的明确定义,但在DNS的相关规范中Domain Name System (DNS) IANA Considerations找到了如下说明:

说明了RRTYPE值为0的特殊用途,即在某些特定的情况下(例如SIG(0)记录中)使用,而不应用于普通的DNS查询。虽未并未明确说明Type0可以在用于这种情况,但在此暂且进行深究,若有兴趣可以继续研读相关规范。

图11 对于Type0的相关说明

  1. HTTP重定向状态码不同

图12 HTTP重定位内容对比

  • 路由器使用307进行重定向回复
  • ESP32使用302进行重定向回复

参照上述表4对于状态码的描述,可以看到,ESP32重定位后仅支持GET方法,而路由器重定位后所支持更多的请求方法。

  1. HTTP重定位内容差异
  • 路由器重定位时,不仅指定新资源的url:/html/index.html?origin=xxx,同时,指定了新资源的IP地址: 192.168.8.1;
  • ESP32重定位时,仅指定新资源的url:/

路由器将所有客户端的DNS请求解析到一个特定的IP地址(172.31.255.254),这是一个中间服务器,此IP不是真正的目标网站,而是一个内部IP,路由器用它来捕获所有的流量。但是,此操作需要多打开一个TCP服务器,对于资源要求较高。

    1. 正常WiFi测试

图13 WIFI测试

  1. DNS请求
    • 系统首先会进行 DNS 查询,解析 `www.msftconnecttest.com` 这个域名。由于该域名是一个 CNAME 记录,实际解析指向的是 `ncsi-geo.trafficmanager.net`。
    •  系统通过 DNS 查询获得 `ncsi-geo.trafficmanager.net` 的 IP 地址
  2. HTTP 请求
    • -在成功解析到 IP 地址后,系统会尝试通过 HTTP 协议访问 `http://www.msftconnecttest.com/connecttest.txt`
    • 这是一个简单的 HTTP GET 请求,没有复杂的参数或数据发送。请求的目标是获取一个预定义的静态文件。
  3. 接收响应
    •  如果请求成功,服务器会返回一个 HTTP 200 OK 的状态码以及一个包含预期文本内容的响应体,通常这个文件内容是 `Microsoft Connect Test`。
    • 如果能够成功接收到这个内容,系统就会判断当前网络连接能够访问互联网,并在网络状态图标上显示“已连接互联网”。
  • 方案确定
    1. 方案总述

基于上述分析,确定最终的门户认证方案,其流程如下: ​​​​​​​

图13 强制门户流程

  • 因资源限制,只能开启一个TCP服务器,所以在域名解析时仅将部分域名直接解析为最终的IP(192.168.1.1),并且,对其余的DNS请求,进行特殊回复(Unused);
  • 因仅支持GET请求,所以HTTP重定义时使用302进行回复;
    1. 疑问点分析

若要按照上述步骤实施,要解决下面两个问题:

Q1:DNS解析时,将哪些域名解析为:192.168.1.1?

A1:在案例分析中看到路由器对所有的DNS请求都进行解析,而ESP32仅正常解析Type A记录查询,也可以实现强制门户,这是因为Type A应用于网站访问。那么,域名解析的范围是否可以进一步缩小呢?

市场上的各类设备通常会访问特定域名,用于检测互联网连接状态,为了确保强制门户跳转(Captive Portal)能够在常见的设备上生效,就需要穷举出这些域名:

表5 各设备域名表

设备类型

域名

Windows设备

www.msftconnecttest.com

connectivitycheck.gstatic.com

联想设备

connectivitycheck.lenovo.com

苹果设备

captive.apple.com

www.apple.com/library/test/success.html

www.apple.com

安卓设备

connectivitycheck.android.com

clients3.google.com/generate_204

鸿蒙设备

connectivitycheck.hicloud.com

华为设备

connectivitycheck.platform.hicloud.com

小米设备

www.miui.com/generate_204

connect.rom.miui.com/generate_204

Vivo设备

devel.vivo.com.cn/generate_204

三星设备

config.samsungcloudsolution.com/generate_204

OPPO设备

connectivitycheck.oppo.com

一加设备

connectivitycheck.oneplus.net

索尼设备

connectivitycheck.sonymobile.com

归纳一下上述表格,得到以下通用字段:

msftconnecttestconnectivitycheckgenerate_204apple

经过上述分析,确定正常解析的域名需同时满足以下两个条件:

  • 条件一:DNS查询类型为Type A;
  • 条件二:域名中包含通用字段;

*注意:虽然上述的字段可以包含市面常见设备,但无法保证对支持所有设备,在资源足够的情况下,不建议使用条件二进行过滤!

Q2:若TCP服务器只能打开1个socket,如何保证跳转成功率?

A2:首先分析路由器和ESP32的相关报文:

  • 在成功跳转到ESP32门户网站前,设备依然保持其余3个socket连接,总共耗费了4个socket资源,才成功跳转到了门户网站;

图14 路由器socket连接状态

  • 在成功跳转到ESP32门户网站前,设备依然保持其余2个socket连接,总共耗费了3个socket资源,才成功跳转到了门户网站。

虽然从表现上ESP32相对路由器少消耗1个socket资源,但是,深入分析发现,这只是因为设备DNS请求的顺序不一致导致的,因此,其现象不存在必然性。

图14 ESP32 socket连接状态

按照上面分析,要成功跳转到门户网站一般需要3-4个socket资源,但因设备资源限制(共5个socket资源,其余4个socket用作它用),仅有1个socket资源用于门户网站,所以,必须通过优化相关过程,才能实现强制门户跳转。

按照上面分析,主要可以从下面两方面进行流程优化:

优化一:提高Socket资源准确性

  1. 如上面A1所述,通过增加DNS解析条件,使Socket资源更精确地用于门户网站,从而提升Socket资源的使用准确性。

优化二:提高Socket资源使用率

  1. 缩短重定向后资源的有效时长:
  1. DNS重定向:当通过DNS解析将请求重定向至强制门户时,将重定向后资源的有效时长设定为5秒。这意味着在DNS缓存或TTL过期后,客户端将再次进行DNS查询,有助于快速恢复正常的网络访问。
  2. HTTP重定向:在需要重定向至门户页面时,使用HTTP状态码302(临时重定向),指示客户端临时访问强制门户。
  1. 服务器主动关闭非门户网站连接:
  1. 在检测到Socket连接未成功打开门户网站后,服务器将主动关闭该连接。这种措施有助于释放无效的Socket资源,避免长时间占用资源而导致其他用户无法访问门户页面。
  2. 通过监控Socket连接的状态和活动情况,服务器可以判断哪些连接未正确访问门户,并及时进行处理,以优化资源利用率。

这些优化措施旨在确保Socket资源被准确、高效地使用,从而提升强制门户方案的整体性能和用户体验。

### 回答1: 强制门户认证是一种在ESP8266上实施的网络安全措施。它要求连接到ESP8266的设备在访问网络之前必须经过身份验证。 要实现强制门户认证,以下是一种可能的方法: 1. 配置ESP8266作为门户认证服务器:首先,将ESP8266配置为一个门户认证服务器,可以使用Arduino IDE或其他适用的开发环境。确定ESP8266的IP地址和端口号。 2. 创建门户认证页面:创建一个HTML页面,用于门户认证。这个页面应包含一个表单,要求用户输入凭据(例如用户名和密码)进行身份验证。 3. 配置WiFi网络:在ESP8266上配置WiFi网络,将其设置为访问点模式(Access Point Mode)。确保网络名称(SSID)和密码设置为您选择的值。 4. 实施门户认证逻辑:在ESP8266的代码中实施门户认证逻辑。当设备连接到ESP8266的WiFi网络时,通过重定向设备到门户认证页面来强制进行身份验证。一旦用户输入凭据并通过认证,ESP8266可以允许设备继续访问网络。 5. 存储认证凭据:为了实现更长时间的持久认证,可以在ESP8266上存储认证凭据。例如,可以使用EEPROM来存储凭据和其他必要的信息,以便在重新启动时恢复认证状态。 强制门户认证可提高ESP8266的网络安全性,防止未经授权的设备访问网络。但需要注意的是,实施门户认证可能会增加设备连接时的处理时间和复杂性,因此需要权衡安全性和设备性能之间的平衡。 ### 回答2: ESP8266是一种常用的无线模块,可以用于连接到互联网并进行通信。门户认证是一种常见的网络安全措施,用于验证用户的身份和提供访问控制。强制门户认证是指强制为所有通过ESP8266连接到网络的用户进行门户认证。 为了实现强制门户认证,我们需要在ESP8266上设置相关的功能和配置。首先,我们需要配置该模块作为一个访问点(AP)并启用门户认证功能。这可以通过编程来实现,例如使用Arduino集成开发环境(IDE)和ESP8266库。 在编程中,我们可以使用WiFi库来配置ESP8266作为一个AP,并设置门户认证选项。我们可以指定要显示的认证页面的内容和样式,以及用户成功认证后应该重定向到的页面。 在门户认证页面上,我们可以要求用户输入他们的凭据(例如用户名和密码),然后将其传输到服务器进行验证。服务器可以是本地服务器,也可以是远程服务器。一旦用户的凭据被验证,服务器可以向该模块发送一个认证的确认消息,然后模块可以将用户重定向到指定的页面或提供访问权限。 要实现强制门户认证,还需要处理连接请求和页面重定向的逻辑。当一个新的用户连接到ESP8266时,我们可以使用WiFi事件处理程序来检测连接事件,并将用户重定向到门户认证页面。一旦用户成功认证,我们可以使用重定向功能将其重定向到指定的页面。 总之,强制门户认证ESP8266的实现需要配置模块为AP并启用门户认证选项,编程设置认证页面和重定向逻辑,并在服务器端进行用户凭据验证。这样可以确保所有通过该模块连接到网络的用户都需要进行门户认证。 ### 回答3: 强制门户认证是一种安全措施,用于限制设备连接到特定的WiFi网络。如何实现强制门户认证在ESP8266上呢? 首先,ESP8266是一款WiFi模块,可以使用Arduino或MicroPython进行编程。要实现强制门户认证,可以按照以下步骤进行操作: 1. 设置连接的WiFi网络:首先,在代码中设置ESP8266连接到目标WiFi网络。可以编写代码将WiFi SSID和密码作为参数传递给模块的WiFi库,以确保连接成功。 2. 创建门户页面:在ESP8266上,可以使用HTML和CSS来创建一个门户认证页面。这个页面将显示给用户,要求输入凭据来进行认证。可以包括用户名和密码输入框,并使用CSS样式来美化页面。 3. 设置门户服务器:创建一个Web服务器,用于处理ESP8266与门户页面之间的通信。在服务器代码中,可以监听由ESP8266发送的POST请求,并验证输入的凭据。如果认证成功,服务器可以响应一个成功消息。否则,可以返回一个失败消息和一个重定向到门户页面的URL。 4. 实现认证流程:在ESP8266代码中,可以使用WiFiClient库与门户服务器进行通信。首先,设备将发送一个POST请求,包含用户名和密码。然后,ESP8266将等待服务器的响应。如果收到成功消息,则可以继续设置设备功能。如果收到失败消息或重定向URL,则ESP8266可以重新加载门户页面来重新认证。 这样,通过强制门户认证,我们可以确保只有经过授权的用户才能连接到ESP8266设备所在的WiFi网络。这种安全措施防止了未经授权的访问和潜在的网络攻击。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

识自本心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值