热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

登录验证设计解决

你会做Web上的用户登录功能吗?2011年8月25日陈皓发表评论阅读评论78,832人阅读Web上的用户登录功能应该是最基本的功能了,可是在我看过一些

你会做Web上的用户登录功能吗?

2011年8月25日陈皓发表评论阅读评论78,832 人阅读    
 

Web上的用户登录功能应该是最基本的功能了,可是在我看过一些站点的用户登录功能后,我觉得很有必要写一篇文章教大家怎么来做用户登录功能。下面的文章告诉大家这个功能可能并没有你所想像的那么简单,这是一个关系到用户安全的功能,希望大家能从下面的文章中能知道什么样的方法才是一个好的用户登录功能。以下内容,转载时请保持原文一致,并请注明作者和出处

用户名和口令

首先,我们先来说说用户名和口令的事。这并不是本站第一次谈论这个事了。如何管理自己的口令让你知道怎么管理自己的口令,破解你的口令让你知道在现代这样速度的计算速度下,用穷举法破解你的口令可能会是一件很轻松的事。在这里我想告诉从开发者的角度上来做设计这个用户名和口令的事。下面一几件规则:

  • 限制用户输入一些非常容易被破解的口令。如什么qwert,123456, password之类,就像twitter限制用户的口令一样做一个口令的黑名单。另外,你可以限制用户口令的长度,是否有大小写,是否有数字,你可以用你的程序做一下校验。当然,这可能会让用户感到很不爽,所以,现在很多网站都提供了UX让用户知道他的口令强度是什么样的(比如这个有趣的UX),这样可以让用户有一个选择,目的就是告诉用户——要想安全,先把口令设得好一点。
  • 千万不要明文保存用户的口令。正如如何管理自己的口令所说的一样,很多时候,用户都会用相同的ID相同的口令来登录很多网站。所以,如果你的网站明文保存的话,那么,如果你的数据被你的不良员工流传出去那对用户是灾难性的。所以,用户的口令一定要加密保存,最好是用不可逆的加密,如MD5或是SHA1之类的有hash算法的不可逆的加密算法。CSDN曾明文保存过用户的口令。(另,对于国内公司的品行以及有关部门的管理方式,我不敢保证国内网站以加密的方式保存你的口令。我觉得,做为一个有良知的人,我们应该加密保存用户的口令)
 
  • 是否让浏览器保存口令。我们有N多的方法可以不让浏览器保存用户名和口令。但是这可能对用户来说很不爽。因为在真实世界里谁也记得不住那么多的口令。很多用户可能会使用一些密码管理工具来保存密码,浏览器只是其中一种。是否让浏览器保存这个需要你做决定,重点是看一下你的系统的安全级别是否要求比较高,如果是的话,则不要让浏览器保存密码,并在网站明显的位置告诉用户——保存口令最安全的地方只有你的大脑。
  • 口令在网上的传输。因为HTTP是明文协议,所以,用户名和口令在网上也是明文发送的,这个很不安全。你可以看看这篇文章你就明白了。要做到加密传输就必需使用HTTPS协议。但是,在中国还是有很多网站的Web登录方式还在使用ActiveX控件,这可能成为IE6还大量存在的原因。我通常理解为这些ActiveX控件是为了反键盘记录程序的。 不过,我依然觉ActiveX控件不应该存在,因为在国外的众多安全很重要的站点上都看不到ActiveX的控件的身影。

用户登录状态

首先,我想告诉大家的是,因为HTTP是无状态的协议,也就是说,这个协议是无法记录用户访问状态的,其每次请求都是独立的无关联的,一笔是一笔。而我们的网站都是设计成多个页面的,所在页面跳转过程中我们需要知道用户的状态,尤其是用户登录的状态,这样我们在页面跳转后我们才知道是否可以让用户有权限来操作一些功能或是查看一些数据。

所以,我们每个页面都需要对用户的身份进行认证。当然,我们不可能让用户在每个页面上输入用户名和口令,这会让用户觉得我们的网站相当的SB。为了实现这一功能,用得最多的技术就是浏览器的COOKIE,我们会把用户登录的信息存放在客户端的COOKIE里,这样,我们每个页面都从这个COOKIE里获得用户是否登录的信息,从而达到记录状态,验证用户的目的。但是,你真的会用COOKIE吗?下面是使用COOKIE的一些原则。

  • 千万不要在COOKIE中存放用户的密码。加密的密码都不行。因为这个密码可以被人获取并尝试离线穷举。所以,你一定不能把用户的密码保存在COOKIE中。我看到太多的站点这么干了。
  • 正确设计“记住密码”。这个功能简直就是一个安全隐患,我觉得并不是所有的程序员都知道怎么设计这个事。一般的设计 是——一时用户勾选了这个功能,系统会生成一个COOKIE,COOKIE包括用户名和一个固定的散列值,这个固定的散列值一直使用。这样,你就可以在所有的设备和客户上都可以登录,而且可以有多个用户同时登录。这个并不是很安全。下面是一些更为安全的方法供你参考:
    (——更新 2011/08/26,原文中有些小错误,并且说的不清楚,重新调整了一下——

1)在COOKIE中,保存三个东西——用户名登录序列登录token

用户名:明文存放。
登录序列:一个被MD5散列过的随机数,仅当强制用户输入口令时更新(如:用户修改了口令)。
登录token:一个被MD5散列过的随机数,仅一个登录session内有效,新的登录session会更新它。

2)上述三个东西会存在服务器上,服务器的验证用户需要验证客户端COOKIE里的这三个事。

3)这样的设计会有什么样的效果,会有下面的效果,

a)登录token是单实例登录。意思就是一个用户只能有一个登录实例。

b)登录序列是用来做盗用行为检测的。如果用户的COOKIE被盗后,盗用者使用这个COOKIE访问网站时,我们的系统是以为是合法用户,然后更新“登录token”,而真正的用户回来访问时,系统发现只有“用户名”和“登录序列”相同,但是“登录token” 不对,这样的话,系统就知道,这个用户可能出现了被盗用的情况,于是,系统可以清除并更改登录序列  登录token,这样就可以令所有的COOKIE失效,并要求用户输入口令。并给警告用户系统安全。

4)当然,上述这样的设计还是会有一些问题,比如:同一用户的不同设备登录,甚至在同一个设备上使用不同的浏览器保登录。一个设备会让另一个设备的登录token登录序列失效,从而让其它设备和浏览器需要重新登录,并会造成COOKIE被盗用的假象。所以,你在服务器服还需要考虑- IP 地址

a) 如果以口令方式登录,我们无需更新服务器的“登录序列”和 “登录token”(但需要更新COOKIE)。因为我们认为口令只有真正的用户知道。

b) 如果 IP相同 ,那么,我们无需更新服务器的“登录序列”和 “登录token”(但需要更新COOKIE)。因为我们认为是同一用户有同一IP(当然,同一个局域网里也有同一IP,但我们认为这个局域网是用户可以控制的。网吧内并不推荐使用这一功能)。

c) 如果 (IP不同 && 没有用口令登录),那么,“登录token” 就会在多个IP间发生变化(登录token在两个或多个ip间被来来回回的变换),当在一定时间内达到一定次数后,系统才会真正觉得被盗用的可能性很高,此时系统在后台清除“登录序列”和“登录token“,让COOKIE失效,强制用户输入口令(或是要求用户更改口令),以保证多台设备上的COOKIE一致。

  • 不要让COOKIE有权限访问所有的操作。否则就是XSS攻击,这个功能请参看新浪微博的XSS攻击。下面的这些功能一定要用户输入口令:
1)修改口令。
2)修改电子邮件。(电子邮件通常用来找回用户密码,最好通发邮件或是发手机短信的方式修改,或者干脆就不让改一一用电子邮件做帐号名)
3)用户的隐私信息。
4)用户消费功能。
  • 权衡COOKIE的过期时间。如果是永不过期,会有很不错的用户体验,但是这也会让用户很快就忘了登录密码。如果设置上过期期限,比如2周,一个月,那么可能会好一点,但是2周和一个月后,用户依然会忘了密码。尤其是用户在一些公共电脑上,如果保存了永久COOKIE的话,等于泄露了帐号。所以,对于COOKIE的过期时间我们还需要权衡。

找回口令的功能

找回口令的功能一定要提供。但是很多朋友并不知道怎么来设计这个功能。我们有很多找回口令的设计,下面我逐个点评一下。

  • 千万不要使用安全问答。事实证明,这个环节很烦人,而且用户并不能很好的设置安全问答。什么,我的生日啊,我母亲的生日,等等。因为今天的互联网和以前不一样了,因为SNS,今天的互联比以前更真实了,我可以上facebook,开心,人人网,LinkedIn查到你的很多的真实的信息。通过这些信息我可以使用安全问答来重设你的口令。 这里需要说一下 Facebook,Facebook的安全问答很强大,还要你通过照片认人,呵呵。
  • 不要重置用户的密码。因为这有可能让用户的密码遭到恶意攻击。当然,你要发个邮件给用户让其确认,用户点击邮件中的一个链接,你再重置。我并不推荐这样的方法,因为用户一般都会用笔记下来这个很难记的口令,然后登录系统,因为登录系统时使用了“记住密码”的功能,所以导致用户不会去修改密码,从而要么导到被写下来的密码被人盗取,要么又忘记了密码。
  • 好一点的做法——通过邮件自行重置。当用户申请找回口令功能的时候,系统生成一个MD5唯一的随机字串(可通过UID+IP+timestamp+随机数),放在数据库中,然后设置上时限(比如1小时内),给用户发一个邮件,这个连接中包含那个MD5的字串的链接,用户通过点击那个链接来自己重新设置新的口令。
  • 更好一点的做法——多重认证。比如:通过手机+邮件的方式让用户输入验证码。手机+邮件可能还不把握,因为手机要能会丢了,而我的手机可以访问我的邮箱。所以,使用U盾,SecureID(一个会变化的6位数token),或是通过人工的方式核实用户身份。当然,这主要看你的系统的安全级别了。

口令探测防守

  • 使用验证码。验证码是后台随机产生的一个短暂的验证码,这个验证码一般是一个计算机很难识别的图片。这样就可以防止以程序的方式来尝试用户的口令。事实证明,这是最简单也最有效的方式。当然,总是让用户输入那些肉眼都看不清的验证码的用户体验不好,所以,可以折中一下。比如Google,当他发现一个IP地址发出大量的搜索后,其会要求你输入验证码。当他发现同一个IP注册了3个以上的gmail邮箱后,他需要给你发短信方式或是电话方式的验证码。
  • 用户口令失败次数。调置口令失败的上限,如果失败过多,则把帐号锁了,需要用户以找回口令的方式来重新激活帐号。但是,这个功能可能会被恶意人使用。最好的方法是,增加其尝试的时间成本(以前的这篇文章说过一个增加时间成本的解密算法)。如,两次口令尝试的间隔是5秒钟。三次以上错误,帐号被临时锁上30秒,5次以上帐号被锁1分钟,10次以上错误帐号被锁4小时……但是这会导致恶意用户用脚本来攻击,所以最好再加上验证码,验证码出错次数过多不禁止登录而是禁lP。
  • 系统全局防守。上述的防守只针对某一个别用户。恶意者们深知这一点,所以,他们一般会动用“僵尸网络”轮着尝试一堆用户的口令,所以上述的那种方法可能还不够好。我们需要在系统全局域上监控所有的口令失败的次数。当然,这个需要我们平时没有受到攻击时的数据做为支持。比如你的系统,平均每天有5000次的口令错误的事件,那么你可以认为,当口令错误大幅超过这个数后,而且时间相对集中,就说明有黑客攻击。这个时候你怎么办?一般最常见使用的方法是让所有的用户输错口令后再次尝试的时间成本增加。
最后,再说一下,关于用户登录,使用第三方的 OAuth 和 OpenID 也不失为一个很不错的选择。
 
 
 
 
 
 
 
 
eg2

我们知道web是基于HTTP协议传输的,明文传输是极其危险的,随便哪个抓包工具分析下数据包,就over啦,一个加密的传输过程应该包括两部分,一部分为身份认证,用户鉴别这个用户的真伪;另外一部分为数据加密,用于数据的保密。


我大概是这样做的:

(1)生成用户验证token
    用户登录后我会生成一个token,该token可能由如下信息组成:username+ip+expiration+salt【只是举例】,然后将组成信息用可逆加密函数加密得到token,并将该token保存到数据库,写入COOKIE;

(2)最后这样去校验信息,判断用户的登录状态
    将token解密,验证用户username,如果存在,继续;然后验证token是否和存入数据库的token相同,如果相同继续;验证COOKIE的有效期expiration,如果有效继续;验证ip是否变化,若变化跳入登录。。。。。。甚至还可以验证user agent.
 

(3)可以做到单终端登录,可以将token放到数据库,每次登录操作必然会改变token的值,另外一端的用户就会token验证失败下线
 

最后说明:

1.上面保证了token每次登录都会不一样,这回导致之前的token【既COOKIE】失效

2.COOKIE的有效期最好不超过一周

 

 

 

 

eg3

 

作者:残风
链接:https://www.zhihu.com/question/20182967/answer/105234726
来源:知乎
著作权归作者所有,转载请联系作者获得授权。

1. COOKIEs存储session_id. 一般这个地方会另外再存储用户名和其他信息的再次hash值. 用以加强验证session_id. 因为一个session_id很容易被取到伪造, 当网站session数量比较大还可能被穷举到.
2. 举例PHP, session用session_set_save_handler重写, 以便不会被系统默认session中gc回收(默认1440秒). PHP中系统回收不管任何情况, 只按照session存储时间来, 超过1440秒并满足触发条件就可能回收, 所以session目录如果公用一个, 没有单独设置, 一定会被回收.
3. 重写session_set_save_handler的时候用session_id和session_name判断接管访客COOKIEs中是否存在此session_id, 存在进行验证 1 中的附加字段判断是否正确, 正确的话接管, 调出服务器中存储的session信息, 开始使用. 不正确回收浏览器COOKIEs中session_id, 并重新生成COOKIEs中session_id.

大概思路就是上面, 我一般把session存储在redis中. 不要把这东西存数据库, 负载太大.

/**************** 我是分割线 ****************/
2016-06-11更新:

对 @小雷 的方法写了点看法,结果…
以下我详细对小雷的疑问说明下:

以下是你文章中的内容:
-----------------------------
1、将用户信息,比如一个['uid'=>123, 'username'=>'testuser']的数组,序列化后成为字符串,使用可逆加密算法加密该字符串,写到一个Key为userinfo的COOKIE里
2、infodig验证通过后,使用解密算法解密userinfo串,得到用户信息,如果用户信息里的uid存在用户表中,则写SESSION,通过SESSION保持本次会话
-----------------------------

不知道你这里的<<<可逆加密算法加密该字符串>>><<<使用解密算法解密userinfo串>>>是我眼花了吗? 我理解你这样: 解密COOKIEs中的uid用来进行数据库查询判断用户是否存在. 不知道我有没有理解错你的信息?

我们说说问题:

1. COOKIEs不能存可逆加密的任何信息, 即使你另外加了一个不可逆的hash的COOKIEs字段, 也保护不了这个可逆的COOKIEs字段.

2. COOKIEs存储的仅仅是session_id, 也就是默认那个PHPSESSID, 我一般会修改客户端COOKIEs存储名字, 不用PHPSESSID, 用session_name修改为xxx_sessid, xxx为网站识别码, 比如可以用域名中间部分, 这样可以在同域名下区分出不同系统.

3. 用户的uid, username, lifetime, iflogin等信息只能存在session中, 也就是$_SESSION数组中, 不能存在COOKIEs中, 即使是可逆加密, 即使是另外加了一个不可逆的hash字段.

4. 第2点说到COOKIEs存储的仅仅是session_id, 意思是我们完全可以COOKIEs只存储session_id, 其他什么都不存储, session_name定义2中的名字后, session_start()开启会话, 此时session和COOKIEs中session_id已经对接上, 登录成功. 判断服务器$_SESSION中是否存在uid, username, lifetime, iflogin, 判断登录, 开始登录后逻辑.

5. 以上有问题没? 其实也没啥问题, 但是session_id就是一段简单的字符串, 从4中我们可以看出只要客户端存在这个字符串, 系统的SESSION就接收并判断服务器对应存储的会话是否有登录. 这段字符串看着总是太单薄, 所以我们习惯性会额外添加点验证内容:
xxx_sessid ubq4il6v3eiqsbbv9q6uknlpj4
xxx_uid 356a192b7913b04c54595428ab
xxx_uname 385fa818765b90ddc68cd6a

以上xxx_sessid即为session_name自定义的PHPSESSID, 自定义PHPSESSID的好处第2点中讲了.

xxx_uid和xxx_uname是用户id和用户名, 可以用不同于系统密码hash的另外算法进行hash.

用session_name定义好session的环境变量, 这样session_start开启会话会自动判断客户端浏览器是否存在xxx_sessid(PHPSESSID), 不存在会自动生成一个xxx_sessid, 进行登录判断并进入登录步骤, 存在即接管.

此时我们有$_SESSION数组可用了, 我们对服务器的$_SESSION['xxx_uid'], $_SESSION['xxx_uname'] 进行COOKIEs的hash算法的hash, 跟客户端$_COOKIES['xxx_uid'], $_COOKIES['xxx_uname']进行比较, 比对不成功, 回收session和COOKIEs, 因为此会话如果比对不成功说明两者都不安全, 进入登录步骤. 比对一致说明此xxx_sessid没啥问题, 不是穷举而来的, 验证通过. 进入登录后逻辑. 利用$_SESSION['xxx_uid']查出用户信息.

6. xxx_uid不存在怎么办? 比如此用户已经被删掉? 要明白一点: session和COOKIEs管的就是登录会话. 绝对不要混入用户管理逻辑中. 当上面步骤验证登录成功后, 利用$_SESSION['xxx_uid']查出用户信息. 此时发现查询不到用户, 即可回收session和COOKIEs会话, 进入登录步骤. 因为用户不存在了,说明此用户会话需要被回收. 如果我们存储一个$_SESSION['xxx_upass'], 还可以利用登录后查询用户信息的时候比对下用户密码, 这样可以实现修改密码后所有已经登录的会话全部要重新登录. 这些都是在服务器端$_SESSION信息实现的.

7. 以上只是简单说明, 具体使用中还包括很多, 比如COOKIEs生效域, 路径, 时间. session会话前如何用setCOOKIE定义session_name和session_id, session存储在文件中, redis中等的方法和注意点. 再往大了说涉及到不同域名的单点登录如何进行. 还有集群中如何部署并平衡, 就不一一说明了.

你的问题在哪里呢?

1. 你思想上还停留在利用COOKIEs保存用户信息上面, COOKIEs我们仅仅是保存PHPSESSID, 并额外做一点点验证.

2. PHP对session_id, session_name, session_start所作的工作要深刻理解并利用上, 不行就看看手册, 官方手册有比较明了的说明了. 加以理解并自己设计.

3. 实现一个问题的办法很多种, 当半懂不懂的时候是很希望别人来指教一二并改进的, 我有些问题半懂不懂的时候多希望别人能来指教一二. 前几天做PHP扩展的时候有个问题不明白到处求爷爷, 告奶奶. 可人家不鸟你. 人家有人家的忙处, 没那个义务去帮你. 最后在韩老大的Swoole群里有个热心的朋友给我指点了一二, 顿悟, 遂完成那个PHP扩展.

三人行必有我师, 只是07年就研究过这玩意, 还算明了一些. 所以探讨下. 我也很希望看到更好的建设性方法, 我会虚心学习, 并立刻修改我封装的session验证类库.

欢迎拍砖.

点不点赞无所谓, 点赞我学不到东西, 你学到东西.
拍砖我能学到东西.

推荐阅读
  • 如何搭建服务器环境php(2023年最新解答)
    导读:本篇文章编程笔记来给大家介绍有关如何搭建服务器环境php的相关内容,希望对大家有所帮助,一起来看看吧。本文目录一览:1、怎么搭建p ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 加密、解密、揭秘
    谈PHP中信息加密技术同样是一道面试答错的问题,面试官问我非对称加密算法中有哪些经典的算法?当时我愣了一下,因为我把非对称加密与单项散列加 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • macOS Big Sur全新设计大版本更新,10+个值得关注的新功能
    本文介绍了Apple发布的新一代操作系统macOS Big Sur,该系统采用全新的界面设计,包括图标、应用界面、程序坞和菜单栏等方面的变化。新系统还增加了通知中心、桌面小组件、强化的Safari浏览器以及隐私保护等多项功能。文章指出,macOS Big Sur的设计与iPadOS越来越接近,结合了去年iPadOS对鼠标的完善等功能。 ... [详细]
  • 本文介绍了DataTables插件的官方网站以及其基本特点和使用方法,包括分页处理、数据过滤、数据排序、数据类型检测、列宽度自动适应、CSS定制样式、隐藏列等功能。同时还介绍了其易用性、可扩展性和灵活性,以及国际化和动态创建表格的功能。此外,还提供了参数初始化和延迟加载的示例代码。 ... [详细]
  • 本文分享了一位Android开发者多年来对于Android开发所需掌握的技能的笔记,包括架构师基础、高级UI开源框架、Android Framework开发、性能优化、音视频精编源码解析、Flutter学习进阶、微信小程序开发以及百大框架源码解读等方面的知识。文章强调了技术栈和布局的重要性,鼓励开发者做好学习规划和技术布局,以提升自己的竞争力和市场价值。 ... [详细]
  • 一.常见基于身份识别进行反爬1通过headers字段来反爬headers中有很多字段,这些字段都有可能会被对方服务器拿过来进行判断是否为爬虫1.1通过headers中的User-A ... [详细]
  • OAuth2.0指南
    引言OAuth2.0是一种应用之间彼此访问数据的开源授权协议。比如,一个游戏应用可以访问Facebook的用户数据,或者一个基于地理的应用可以访问Foursquare的用户数据等。 ... [详细]
  • PHP连接MySQL的2种方法小结以及防止乱码【PHP】
    后端开发|php教程PHP,MySQL,乱码后端开发-php教程PHP的MySQL配置报错信息:ClassmysqlinotfoundinAnswer:1.在confphp.ini ... [详细]
  • yahoo对付伪造跨站请求的办法是在表单里加入一个叫.crumb的随机串;而facebook也有类似的解决办法,它的表单里常常会有post_form_i ... [详细]
  • vsCode配置通过Github同步(Settings Sync)
    vsCode配置通过Github同步(Settings Sync) ... [详细]
author-avatar
lvyanbo
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有