关于登录注册那些事

写这篇文章还是中午吃饭的时候刷到一个关于登录注册面试,看完觉得还是比较有学习价值的,之前在公司参与的项目大部分都是对内的,在登录注册方面其实相对于所看到的文章并没有考虑的那么完善,一直觉得登录注册其实就是一个很简单的小功能,但是当我们真的去各种情况下考虑的时候,还是有很多值得我们去深思的逻辑,下面就是某大佬很早之前朋友的面试题,我主要做一个记录以及自己的思考

登录注册的业务设计

问题1:如果用户登录时,忘记了密码怎么办呢?

自己思考:其实这个业务逻辑相对于简单一点,在实际业务中通常我们会在登录页面的上有一个忘记密码的接口,当然前提我们的注册会有一个用户名或者手机号的填写,这两个数据在我们表中是设计的唯一值,那么我们就可以根据找回密码页面用户传来的用户名或者手机号从表中查到该用户,然后根据前端传来的新密码进行数据库数据的修改,当然密码加密这些就属于各个不同的方法了

作者回答:

对于这点是无需担心的,因为在登录页面上,提供了找回密码的入口,前面注册时必须要填写邮箱或手机号的,用户可以通过「手机验证码或邮箱验证码」的方式找回密码

问题2: 那用户登录一次之后,第二次登录时还需要重新输入密码吗?

自己思考:其实这也是个普遍的业务逻辑,登录页面都会有记住密码这一个选项,如果用户选中了这个选项,那么在用户登录成功以后我们项目一般会把用户的一些信息重新返回到前端,包含token等,然后前端会把这些信息存储起来,这样避免用户第二次登录还需要输入用户名和密码

作者回答:

这个要看具体情况来区分,因为在登录的时候,提供了一个「记住密码」的选项,如果用户登录时勾选了该选项,这时在浏览器发出的「登录请求」中,除开基本的用户登录信息外,还会额外传递一个标识。在后端判断用户输入的「用户名、密码」正确后,如果请求中存在该标识,则会生成一个Cookie信息,将「用户名、密码」保存在Cookie中返回,客户端在收到该信息后,会自动把Cookie存储在浏览器的本地缓存中,所以当用户第二次登录时,避免了再次重复输入「用户名、密码」的工作

问题3:你认为这种方式会存在什么问题? 

自我思考:首先我们知道这个问的是把用户信息存在浏览器的行为会造成什么问题,那么我们就需要从浏览器出发去思考,第一点肯定是信息存在浏览器,别人打开了这个浏览器就可以从Cookie里面找到所存储的用户信息,账号密码可能会被盗,当然前提得密码没加密的情况,加密后的密码安全性还是会相对较高一点,第二点就是如果设备给别人使用或者出售给别人,虽然密码加密破解难度高,但是之前登录的网站还是可以正常访问,就会造成网站信息泄露的风险(特别是一些私密性比较高存储网站)

作者回答:

有两个隐患,一方面由于保存的「用户名、密码」存在浏览器的本地记录中,所以如果在本地找到了对应的Cookie记录,用户密码是有被盗取的风险。同时,如果并非用户本人操作电脑时,其他人通过「开发者工具」把input元素的类型从password类型修改成text这种,依旧有可能造成用户密码泄露。

问题4:用户注册时填好了一部分信息,但因为有事走开了,最后电脑没电关机,用户重启电脑后,再次打开注册界面,需要重填信息吗?

个人思考:这个其实属于业务设计的问题,对于需不需要重新填写信息其实都是可以的,我们很早之前的项目用户填写的信息就会丢失掉,需要用户再重新填写,其实想要用户不重新填写也是比较简单的,可以前端做一个存储的操作,如果用户没有点击注册按钮,会把这些信息先存储在前端,点了注册把信息发往后端,清空存储的就可以了

作者回答:

 在我们当时的项目中,如果出现这种情况,由于电脑已经重启了,因此用户上次填写的信息会丢失,需要用户重新从头填写注册信息

 在用户填写数据的时候,前端可以通过「光标移出事件」来获取用户当前填写的数据,接着将其保存在本地的Cookie中,如果用户点击了「注册」按钮后,则主动去删除Cookie中的信息,毕竟提交注册后这些保存的信息就失去了作用。
但如果用户写到一半,电脑突然没电关机了,重启后再次打开注册页面,那这里又可以在「页面加载事件」中,从本地Cookie中将原本保存的数据读出来,然后赋值给对应的文本框即可,从而避免用户重复填写数据。

问题5:假设有两个用户在同时注册,并且输入的用户名相同,同时提交注册会出现什么情况呢? 

个人思考:其实这个问题当我们在表中设计用户名是唯一的时候就已经可以避免这个问题了,用户名相同同时注册,我们暂且不考虑网络延迟的问题,就极限情况下一起操作,因为数据库唯一索引的限制,也只会插入一条成功,我们不能确定是哪一个人成功,但是保证了只有一个人注册成功,另一个失败,当失败的再次注册时候会提示用户名已存在

作者回答:

首先这种情况出现的几率比中彩票都小,同时就算出现了也没关系,因为不同地段的网速肯定有差距,所以两个注册请求到达服务器的时机也不同,同时在设计数据库的用户表时,对用户名加了唯一索引,所以两个用户同时注册时,就算输入的用户名相同,也只会有一个注册成功,并不会出现用户名重复的情况。

问题6: 项目除开通过注册账号登录外,还有没有什么其他方式呢?

这个我之前的项目都是内部使用的,确实没有开通过其他的登录

作者回答:

还有第三方联合登录的实现,主要就是QQ、微信这两种社交账号的联合登录,是通过腾讯本身提供的API来实现的,如果用户选择这种第三方登录,会直接去调用腾讯的登录API。用户扫码登录成功后,会触发我们平台登录成功的回调接口,为其自动在平台注册一个账号,最终实现第三方账号的联合登录。

登录注册敏感词

问题1:在做注册业务的时候,有没有考虑过,如若用户填写的「昵称/用户名」涵盖敏感信息怎么办呢?比如填写的昵称存在传播色情、违反政策规定、存在侮辱性含义等情况

自我考虑:之前公司内部的项目,没有思考过这个方面的问题,自己简单思考一下可以做一个敏感词的集合,用户注册填写账号的时候项目本身会有一个是否是昵称已存在的判断,在这个判断之前我们再加一个判断,判断用户所填写的昵称是否包含在敏感词集合中,如果包含的话我们之前就返回一个信息,或者我们python本身可以在序列化器进行反序列化的时候在局部钩子中对这个字段进行同样的校验

作者回答:

对于这块问题,当时在开发时并未考虑完全,因为这个平台属于定制化开发的,所以用户注册量也不算太大,因此在设计时也没往这块多想

问题:那假设现在我让你去解决这个问题,你会如何下手呢?

首先我会在数据库里设计一张表,或者在后端里面创建一个Map、Set这类的容器,专门用来存储「违规敏感词」,当用户注册时,在填写好「昵称」后,前端采用Ajax异步请求的方式,将用户输入的「昵称」发给后端进行敏感词检测。

   后端收到前端发送的Ajax请求后,拿着用户的昵称去和「违规敏感词」进行匹配,如果用户输入的昵称中包含敏感词,那就让前端显示一下「昵称违规,请重新输入」,反之则通过验证,允许用户正常注册。

问题:那如果用户的昵称有七个字,但其中有两个字组成的词语属于敏感词,请问如何检测出来呢?

   首先肯定需要先把用户输入的昵称分开,然后再进行敏感词检测,但由于个人未处理过该问题,所以目前不清楚具体的做法(其实具体方案是可以借助ElasticSearch对用户输入的昵称做分词处理,然后再对分词后的结果进行敏感词检测,或者可以通过DFA算法的方式进行敏感词检测)

 问题2:如果有人通过机器手段,如爬虫技术对平台进行账号的批量注册怎么办?

自我思考:其实这个时候就体验出来表设计好的重要性了,这个问题同样也是我们表字段对用户名,手机号进行唯一索引的限制,用户名可能会通过机器手段随机出很多个,但是对于手机号来说现在实名制一个人不会有太多,而且还因为有注册验证码的存在已经能够很好的去防范这个问题了

作者回答:

这点不必担心啊,因为前面说过的,在注册时用户必须要填写「手机号、或邮箱地址」,然后后端会先向对应的手机号或邮箱发送「验证码」,用户必须要输入正确的「验证码」之后,才能继续注册的,而手机号也好、邮箱也罢,基本上同一个人不会有太多个,所以通过「验证码」的方式,能够有效阻止机器批量注册。也包括这个平台其实还支持第三方账号注册,也就是通过QQ、微信的方式快捷注册,这种账号和「手机号、邮箱」类似,都具备一定的稀缺性,同一个人不会有太多的账号,所以基于这类稀缺性账号实现注册功能,都能有效的避免机器批量注册。

问题3:一个手机号或者邮箱允许注册多个账号么? 

自我思考:这个毋庸置疑的,我们一般都是只允许注册一个,还是因为我们表中唯一索引的限制

作者回答:

这个是不行的,因为在后端有做唯一性判断,一个「手机号、邮箱」注册一次之后,就无法再利用它进行二次注册了

爬虫恶意调用短信接口做轰炸

问题1:假设有人通过逆向分析,调试出了你们「发送短信验证码」的接口,接着用爬虫技术批量调用该接口轰炸别人怎么办?

自我思考:其实面试官想问的就是怎么防止别人利用我们的接口做短信轰炸,之前确实是没有思考过这个问题,直接用户点击验证码就发送验证码,这样的情况肯定会被别人利用成轰炸工具,自己思考了一下觉得应该从发送验证码入手,可以在发送短信验证码之前再加一层校验,比如现在大部分网站比较流行的,在发送短信验证码的时候会输入一个普通的验证码,然后普通验证码进行后端对比,如果一致的情况下才可以调用短信发送接口,这个普通的验证码我们可以后台自己生成或者利用第三方的滑块各种都可以,只是为了多一层校验

作者回答:

咳咳,对于这个问题嘛,其实也不必担心,因为当用户点击了「发送验证码」的按钮之后,首先会弹出来一个「滑块验证码」,只有当用户通过「滑块验证」之后,才会颁发一个调用接口的「数字签名」,如果不具备这个签名,直接调用「发短信验证码」的接口时就会返回「权限不足」的提示。而作为一个正常人,通过「滑块验证」自然不成问题,所以当一个“人类用户”注册时,肯定是先拿到签名再调用「发短信」接口,如果出现未携带「数字签名」的请求,自然无法通过调用前的校验,因此通过这种滑块验证码的方式,就能有效防止爬虫的暴力调用问题

问题:那如果对方通过Selenium这种自动化技术,通过了你们平台的「滑块验证」,又或者说对方又调试出了「数字签名」的生成接口,从而得到了签名,依旧可以正常调用「短信」接口怎么办?

答:

这个当时没有考虑到,毕竟前面跟您说过的,这个平台属于定制化程序,上线后面对的用户量并不算大,因此也没有考虑设计反爬虫机制,但您所说的这个问题也很好解决,对于一些较为“珍贵”的接口资源,比如目前所说到的短信接口,因为每条发出的短信都需要付费,所以通常情况下都会做调用限制,比如限制十分钟内只允许调用三次这类的。

关于更多验证码限流可以参考原作者的回答:追忆四年前:一段关于我被外企CTO用登录注册吊打的不堪往事 - 掘金

API接口的幂等性问题

问题1:如果一个用户在注册时,网络比较卡顿,所以提交注册后迟迟没有反应,因此他又连续点击了多次「注册」按钮,此时会发生什么情况呢?

自我思考:这个问题问的是接口幂等性问题,首先就要回忆一下是什么接口幂等性,在编程中指的是一个接口无论调用多少次他所产生的影响都是一样的,我们的查询和删除接口就是天然幂等性,但是我们的新增和修改接口就不是,当我们新增点击多次的情况下,会发多个新增请求,每一个请求都会新增,所以这个问题在没有做限制的情况下,点击多次注册按钮,会向服务端发送多个新增请求,用户表没有做唯一限制还会新增出多条用户记录

解决方案:对于注册来说,用户点击注册按钮之后会直接重定向到登录页面可以防止,如果是非注册页面之前项目是采用token的形式,所有的非接口幂等性页面,当用户进入这个页面的时候前端会发送一个ajax请求到后端,后端会产生一个随机的字符串token,按照用户名和其他组合产生一个key,然后会将这个key和token作为value存到我们的redi缓存数据库中,同时也会将这个key:value返回到前端,当用户发过来一个post或者put请求会携带之前发给前端的token,在数据操作之前进行前后端的对比,如果一致则删除缓存中的该数据,允许本次操作,如果不一致或者没有该key:value则直接不允许操作

作者回答:

如果没有做任何限制,理论上会向服务端发出多次请求,如果数据库的表结构设计不合理,那么还会出现同一用户的注册信息,在用户表中被插入多次。

问:你们当时是怎么处理呢?

答:我们当时处理方案比较简单,首先在前端做了一定限制,也就是当用户首次点击了「注册」按钮后,「注册」按钮就会变成灰色,也就是用户再次点击时,并不会再次发送Post请求向后端提交表单数据

问:那如果用户看点击注册按钮后迟迟没反应,按F5刷新或浏览器的后退键,接着再次点了「注册」按钮怎么办?

答:但其实现在想来,解决的思想也比较简单,除开在原本将按钮变灰的基础上,再加上一个「重定向页面」即可,这样做的好处在于:重定向操作发生后,当用户再次刷新网页,或者通过浏览器的回退键,回到原本的界面时,之前表单中填写的信息并不会保存。这样做的好处在于:用户想要再次点击注册按钮,就只能再次重新输入信息

作者也提供了接口幂等性的最佳设计:追忆四年前:一段关于我被外企CTO用登录注册吊打的不堪往事 - 掘金 

下一个问题是用户账号合并的问题,因为之前从来没考虑过这方面的,还是学习作者的回答,可以参考原贴

 登录的夺命五连问

问题1:用户登录成功之后,第二天再次打开网站需要重新登录吗?

自我思考:其实这个问题在于python中就是比较简单了,因为python有一个django框架可以使用rest_framework_jwt这样的一个库,他可以通过我们当前登录的用户来产生一个荷载,通过荷载产生一个token,在用户登录成功以后我们会将这个token返回给前端Cookie中存起来,我们在配置文件中配置好token的过期时间,如果token没有过期的情况下,都不需要用户再次进行登录了

作者回答:

如果用户登录成功之后,第二天再次打开网站无需再次登录,但「免登录」存在时效限制,一般情况下为7天,也就是距离用户上次登录的时间超出七天后,用户再次访问网站就需要再次登录

问题2:用户登录成功之后,其他的子系统如何得知该用户登录了? 

自我思考:其实这个这个时候就要区分一下这个子系统下,是该域名下的子系统么,如果是的话,那证明是同一个项目,我们可以使用rest_framework_jwt的登录认证,他会自动帮我们认证已登录的用户,如果不是该域名下的子系统,那么他就不是同一个项目,就无法用到之前的登录认证,就需要重新登录

作者回答:

因为不同的子系统都有权限控制,一个用户在主站登录成功之后,服务端会向客户端颁发Token,客户端可以通过该Token在主站域名下“活跃”,但当客户端尝试访问其他不同域名的子系统时,由于浏览器的本地数据(缓存、Cookies等)是按域名区分存储的,所以访问其他子系统时并不会携带前面主站颁发的Token,最终客户端的访问会遭到拒绝。

   现如今业务线愈加复杂,因此都会引入分布式概念拆分出不同的子系统,并且不同的业务子系统会采用不同的域名部署,所以想要保证「用户一次登录,全线都能访问」的功能,就需要实现单点登录功能。在我们项目中,当时通过OAuth2.0整合JWT实现了SSO认证服务,从而最终实现了单点登录的功能。

问题3:用户复制一个登录后才能访问的链接,然后粘贴到另一个页面上会怎样? 

自我思考:其实这个还是分情况的,区别在与粘贴的是是否是同一个浏览器,如果是同一个浏览器的话他还是可以正常访问的,如果是不同的浏览器,他就无法从浏览器的Cookie中获取当前用户的token,那么就无法通过后端的登录认证校验,会重新到登录页面进行登录签发token在新的浏览器上面就能正常访问了

作者回答:

 这要分情况,如果用户复制链接之后,粘贴在同一个浏览器的其他页面,此时该用户是可以正常访问的。但如果用户复制链接粘贴到其他浏览器上,在其他浏览器未登录过的情况下,本次访问都会遭到拒绝。
这是因为后端都对用户做了权限控制,如果未登录账号的客户端,在我们平台属于游客级别,而登录了账号的客户端,则属于正常用户的级别,不同的用户级别对应不同角色,不同角色则又对应不同权限,以此来实现权限的精准控制。

问题4:用户点击登录之后把当前页面关了会发生什么? 

自我思考:这个问题当时想的是点击登录了,请求发送了,那肯定就是登录成功了,但是读了作者的回答,其实这个是不一定的,主要还是看浏览器进程是否还存在能接受到后端的响应不

作者回答:

站在现在的角度思考:
结论:是否会登录成功要分实际情况来决定,看用户关闭的是当前网页,还是当前的浏览器。
用户关闭了当前网页,结果是会登录成功。用户关闭了当前浏览器,结果是不一定登录成功。

原理分析:
关闭当前网页:因为用户点击登录按钮之后,登录(账号、密码)的请求已经发往了服务器,所以服务端处理完登录请求后,最终会返回一个Token或登录凭证,此时由于浏览器进程还在,这也就意味着浏览器自带的网络进程并未消失,所以登录效验成功之后的操作,如在Cookie中植入Token、各类凭证等操作依旧能正常完成,所以理论上会登录成功。
关闭当前浏览器:这种情况下,用户点击登录按钮后,依旧会向服务器发出登录请求,但由于浏览器已经被关闭了,所以相应的网络进程也会消失,最终就会出现一种特殊现象:「当服务端处理完登录请求后,向客户端返回响应结果时,由于客户端的网络进程已经销毁,所以浏览器无法接收响应结果,也就自然无法在Cookie中植入各种登录凭证,最终结果就不一定登录成功」。

疑惑解答:
为什么关闭浏览器之后无法接收服务端的响应结果?
因为HTTP/HTTPS协议的底层是TCP协议,TCP是一种双向通信的网络协议,当通信的一端出现故障时,两端之间的网络数据就无法正常传输,期间TCP的发送方会多次重新发送数据包,但由于接收端的网络进程已销毁,所以无法收到响应结果。
为什么关闭浏览器的结果是不一定登录成功?
因为存在不稳定因素,毕竟大多数进程在退出时,采取的措施都是优雅关闭,也就是会先处理完目前正在执行的任务后,才会真正将所有后台进程退出(也就是大家关闭一个程序之后,电脑管家都会提醒你XX软件还有残留进程可清理的原因)。
如果关闭浏览器之后,网络进程没有立马销毁,在这期间可能会正常收到服务端的响应结果,最终就会登录成功。
但如果服务端的响应时间比较慢,或者用户安装了电脑管家之类的程序,在进程退出后也许会自动清理残留进程,这种情况下就会彻底销毁网络进程,此时结果就是登录失败。

问题5:用户点击登录之后把网线拔了,你认为结果是怎么样的?

自我思考:感觉这个也是看响应有没有在网站拔了之前发过来,因为响应发到前端还需要经历公网IP地址的机器转发给我们的用户,那么如果用户在这个之前拔了那肯定就收不到响应,如果在之后拔了就没问题,作者的回答也是涉及到这一方面

作者回答:

具体要看用户拔网线的时机,结果依旧可能是登录成功或登录失败。
如果用户在响应结果回来之后拔了网线,结果是登录成功。但如若响应回来之前拔了网线,结果是失败。

原理分析:
这个问题其实和上一个问题类似,但实际情况又存在很大差异,因为不管什么时候拔网线,本质上浏览器的网络进程都不会消失,问题在于网络传输链路出了问题。
对于接收到响应结果之后才拔网线的情况,理解起来也比较容易,毕竟响应结果都拿到了,剩下的工作自然也能进行,最终结果就必然是登录成功。但此时重点要说明的另一种情况,也就是:为什么在响应结果回来之前,拔掉网线的结果是登录失败?
想要明白这个问题,本质上与计算机网络的基础脱不了干系,众所周知的一点,现如今的互联网是由一个个局域网组成的(不了解的小伙伴回去看《计网基础:漫谈计算机网络》),由于IP属于珍贵性资源,所以并不是每台网络设备都具备公网IP,而恰恰远距离的网络通信需要公网IP,此时又该怎么办呢?那也就是多台网络设备共享一个公网IP,这些共享一个公网IP的多台机器,会组成一个小的局域网(如果理解比较困难,可以这样理解:插同一个路由器网线、连同一个路由器WiFi的设备,都可以看成是一个局域网内的设备)。
有了上述知识的简单储备后,接着再回到问题本身进行探讨,当用户的浏览器发出登录请求,并且服务端将用户的登录请求处理完成后,经一系列处理会产生一个数据报文,该报文的目标地址就是发出登录请求的那台机器(实际上是那台机器所在的局域网的公网IP),接着响应报文会先来到机器所在的局域网,但此刻问题来了!
响应报文已经抵达了局域网,不过此刻用户的电脑网线被拔,也就是对应设备会退出这个局域网,那么局域网的路由器在“派送数据报文”时,就无法找到具体的派送目标,但此时用户电脑上的浏览器网络进程依旧存在,只是由于传输链路出现故障,所以无法接收到响应结果,最终导致登录失败。
这种情况就相当于买快递,原本你写的是收货地址A,当快递送到A小区的菜鸟驿站时,结果你搬家搬去了B小区,这时A小区的驿站派送员,就无法根据收货地址将快递送货上门。
当然,还有一种特殊情况,也就是用户把网线拔了之后,又立马插上去了,这时理论上还是会登录成功的,因为HTTP底层的TCP协议,是一种可靠性传输协议,在传输失败的情况下会有重发机制

令人窒息的多IP并发操作

问题1:一个账号在多台电脑上同时点击登录按钮,最后会出现什么情况呢?

自我思考:之前项目中其实没有做限制IP登录,按照这样的情况其实会出现多端登录的情况,如果我们想要防止这种情况,可以在用户名加一条记录用户最后一次登录的IP地址,然后在登录认证中判断当前登录ip是否和之前一致,线下之前的ip,重新记录现在的ip地址

问题2:那假设一个账号在两个IP上登录了,同时修改昵称会发生什么变化?

自我思考:这个其实按照正常逻辑,两个账号都会给后端发送put请求,但是因为网络延迟的原因,会有一个先一个后,先的会先修改昵称,后的会在先修改昵称之后再次修改昵称,暂时没有想到解决的办法,只能参考作者的

作者回答:

有一个IP上修改的昵称,会被另外一个IP上的昵称替代掉。因为就算两个IP同时修改、同时提交,最终到数据库执行update语句时,都会被串行化,因为两个事务并发修改同一行数据时,需要先获取行锁资源,这也就意味着这两个修改操作最终都有前后之分,前一个IP修改的昵称总会被后一个IP修改的昵称覆盖掉。

问:那在不限制多IP登录的情况下,你有什么好的办法结果这个问题吗?

可以加入一个中间状态,也就是在用户表中多设计一个状态字段,0代表正常状态,1代表审核状态,当用户的信息发生变化后,对应的用户记录都会被改成「审核中状态」,而执行语句时只允许修改正常状态的用户记录

后面两个暂时自己没有想到合适的思路,觉得作者的最合适,借鉴作者的

问题3:如果现在有一个签到领积分的功能,两个不同IP的同一账号同时签到,会不会领到双倍积分?

作者回答: 

如果没有做任何限制措施,这种情况下应该会领到双倍积分,但前提是两个IP是以绝对手段进行同时操作的才行,也就是服务端中同一时间内,两条线程并行处理两个IP的签到请求。

问题4:那如果你项目中有订单功能,一个IP删除订单,一个IP结算订单,两个操作同一时刻内进行,结果是什么呢?

自我思考:作者这里使用到了状态机,跟我最近公司的项目还是比较类似的,刚接手的时候发现有很多状态,其实到现在只是觉得用于区分,当看到这个问题以及答案的时候有点豁然开朗的感觉,对于并发操作有一定的限制性

作者回答:

会出现问题,导致一个账号上的订单数据错乱。

解决方案:解决方案为:状态机!啥意思呢?其实和之前「并发修改昵称」的方案差不多,单独的靠加锁无法解决此问题,问题并不在这上面,这同样是个业务逻辑的问题,应该在订单表上面也设计一个status状态字段

有了上述这个状态机字段后,再回过头来看「删除订单、结算订单」这两个业务操作,本质上都是执行update操作,删除是将状态改为9,结算是将状态改为1也就是直接通过状态字段限制其他并发操作,无论「删除订单、结算订单」谁先执行,另一个操作都无法继续执行。

引用文章:追忆四年前:一段关于我被外企CTO用登录注册吊打的不堪往事 - 掘金 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值