分享
 
 
 

如何基于discuz开发网站通行证的功能

王朝html/css/js·作者佚名  2006-12-16
窄屏简体版  字體: |||超大  

通行证是个大题目,说白了也就是如何维护客户的会话,在php中所能用的方法无非几种:

● Cookie

● Session

● POST或GET的方法传值

一般中小型的单服务器应用程序站点,如Discuz论坛,采用的cookie+session+mysql的方法来维持用户会话。

考虑到网站规模和复杂性,例如多台服务器多个应用程序,例如163的通行证系统,登陆成功后会在客户端写一个cookie,里面保存用户名等信息。其他应用程序,都是通过读取这个Cookie重新将客户带入自己的会话中,重而实现通行证功能。163的这种做法不失为一种好办法。

Discuz! Passport 接口技术文档

从 Discuz! 4.0.0 RC4 版本开始,Discuz! 内嵌了一个独特的 Passport(通行证) 接口,利用此接口,用户将很容易将论坛与其他应用程序整合,而实现统一登录与退出、用户数据共享、积分同步等功能。可以整合的应用程序包括内容管理系统(CMS)、商城系统、游戏系统等等,如您对这方面功能有兴趣或有需求,请继续阅读本文档。

Discuz! Passport 的优点

Discuz! Passport 系统使用了 Discuz! 独有的技术,并不等同于以往使用过的一些方法,与传统的实现方式相比,具备(不限于)以下优势:

基于私有密匙的低相关性可逆加密算法,配合 MD5 校检码技术,使得暴力破解或伪造几乎不可能。

应用程序可与论坛放置于不同的服务器及不同的域名下。可基于不同操作系统、不同程序语言和不同数据库平台,具备真正的平台无关性。

不需要任何形式的数据库连接、或强制把两套应用程序的数据放在同一数据库甚至同一数据表中。论坛与应用程序都有各自的用户数据表,只是在需要时进行无缝同步操作。

对应用程序的代码改动简便易行,可最快速的完成应用程序与论坛间的整合。

Discuz! Passport 的局限

您在开始利用 Discuz! Passport 进行二次开发时,需要了解这个系统的局限性,以对未来的工作进行正确的评估与安排。

只能工作在用户密码不加密、可逆加密或 MD5 加密的情况下,否则论坛后台无法登录。

只能与一种应用程序关联,即二方关联。不能实现三方关联或与更多的应用程序进行关联。

应用程序需具有独立的注册、登录、退出页面和链接,否则需要自行修改论坛中的相应表单或程序。

由于论坛的注册人数可能很多,例如百万级以上,且应用程序和论坛间的用户数据是同步的,因此要求应用程序能够稳定的负载大量用户的访问。

Discuz! Passport 原理与流程

假设已设置如下变量或参数

挂接 Discuz! Passport 的应用程序假设为一套 PHP 语言编写的 CMS 系统

Discuz! 的 URL 为 http://www.myforums.com

应用程序的 URL 为 http://www.mywebsite.com

应用程序的注册页面为 http://www.mywebsite.com/register.php

应用程序的登录页面为 http://www.mywebsite.com/login.php?action=login

应用程序的退出页面为 http://www.mywebsite.com/login.php?action=logout

开启通行证后的用户登录流程

如果用户在论坛点击“登录”,则转向到事先设置好的应用程序登录页面(http://www.mywebsite.com/login.php?action=login),并在登录页面的 URL 中加入参数 forward(加入 forward 后的链接例如 http://www.mywebsite.com/login.p ... orums.com/index.php),用于在登录后将用户导向到指定的 URL。

应用程序收到此请求后,按照惯例生成表单,并增加一个表单变量 ,将 GET 方式传递过来的 forward 参数通过表单进行传递。

用户在应用程序的表单中填写登录信息,并提交到应用程序的登录验证程序。应用程序验证用户提交的用户名和密码的合法性:

如果不通过:提示用户名密码错误,要求其返回上一页重新填写。

如果通过,需要进行如下操作:

设置自身 Cookie 或 Session,使得应用程序自身处于登录状态。

检查表单中是否提交了 forward 变量,如有,则意味着登录请求可能是由论坛而来,将此变量传递到后面的请求中;如没有,自行生成 forward 变量,使得论坛登录后能够跳转回到应用程序中。

通过 header('Location: http://www.myforums.com/api/pass ... /yyy&verify=zzz') 的方式,将登录请求传递到论坛进行处理。其中 auth 用来将用户信息与资料以特定的格式,加密传递给论坛,forward 用于告知论坛 Passport API 完成自身操作后转向到的 URL 地址,verify 用于验证前面两个变量的有效性。auth、forward、verify 格式与结构将在后面进行说明。

Discuz! Passport API 在接收到由应用程序通过 header() 提交过来的请求后,进行如下操作:

根据 verify 判断 auth 和 forward 变量是否合法,如合法则继续,否则终止。

将 auth 根据既定算法解密,并还原成数组,数组的内容与格式将在后面进行说明。根据数组中的内容,检查此用户是否存在。如存在,则根据上述数组中的内容 UPDATE 论坛中相应的用户资料;如不存在,则使用数组中的信息 INSERT 到论坛用户资料表中。

论坛设置 Cookie 或 Session,使得论坛自身处于登录状态。

根据应用程序反馈的 forward 值,通过 header('Location: http://xxx') 的形式将页面跳转到 forward 变量指定的 URL。

至此,登录流程结束

开启通行证后的用户退出流程

如果用户在论坛点击“退出”,则转向到事先设置好的应用程序退出页面(http://www.mywebsite.com/login.php?action=logout),并在登录页面的 URL 中加入参数 forward(例如 http://www.mywebsite.com/login.p ... orums.com/index.php),用于在退出后将用户导向到指定的 URL。

应用程序收到此请求后,清除自身 Cookie 或 Session,使得应用程序自身处于非登录状态。

检查是否提交了 forward 变量,如有,则意味着登录请求可能是由论坛而来,将此变量传递到后面的请求中;如没有,自行生成 forward 变量,使得论坛登录后能够跳转回到应用程序中。

通过 header('Location: http://www.myforums.com/api/pass ... /yyy&verify=zzz') 的方式,将退出请求传递到论坛进行处理。其中 forward 用于告知论坛 Passport API 完成自身操作后转向到的 URL 地址,verify 用于验证 forward 变量的有效性。forward、verify 格式与结构将在后面进行说明。

Discuz! Passport API 在接收到由应用程序通过 header() 提交过来的请求后,进行如下操作:

根据 verify 判断 forward 变量是否合法,如合法则继续,否则终止。

清楚论坛的 Cookie 或 Session,使得论坛自身处于非登录状态。

根据应用程序反馈的 forward 值,通过 header('Location: http://xxx') 的形式将页面跳转到 forward 变量指定的 URL。

至此,退出流程结束。

开启通行证后的用户注册流程

如果用户在论坛点击“注册”,则转向到事先设置好的应用程序注册页面(http://www.mywebsite.com/register.php),并在注册页面的 URL 中加入参数 forward(例如 http://www.mywebsite.com/registe ... orums.com/index.php),用于在注册后将用户导向到指定的 URL

应用程序收到此请求后,按照惯例生成表单,并增加一个表单变量 ,将 GET 方式传递过来的 forward 参数通过表单进行传递

用户在应用程序的表单中填写注册信息,并提交到应用程序的注册验证程序。应用程序验证用户提交信息的完整性和合法性:

如果不通过:提示其问题所在,要求其返回上一页重新填写

如果通过,需要进行如下操作:

将用户资料插入到应用程序自身用户数据库中

设置自身 Cookie 或 Session,使得应用程序自身处于登录状态

检查表单中是否提交了 forward 变量,如有,则意味着注册请求可能是由论坛而来,将此变量传递到后面的请求中;如没有,自行生成 forward 变量,使得论坛注册后能够跳转回到应用程序中

通过 header('Location: http://www.myforums.com/api/pass ... /yyy&verify=zzz') 的方式,将注册请求传递到论坛进行处理。其中 auth 用来将用户信息与资料以特定的格式,加密传递给论坛,forward 用于告知论坛 Passport API 完成自身操作后转向到的 URL 地址,verify 用于验证前面两个变量的有效性。auth、forward、verify 格式与结构将在后面进行说明

Discuz! Passport API 在接收到由应用程序通过 header() 提交过来的请求后,进行如下操作:

根据 verify 判断 auth 和 forward 变量是否合法,如合法则继续,否则终止

将 auth 根据既定算法解密,并还原成数组,数组的内容与格式将在后面进行说明。根据数组中的内容,检查此用户是否存在。如存在,则根据上述数组中的内容 UPDATE 论坛中相应的用户资料;如不存在,则使用数组中的信息 INSERT 到论坛用户资料表中

论坛设置 Cookie 或 Session,使得论坛自身处于登录状态

根据应用程序反馈的 forward 值,通过 header('Location: http://xxx') 的形式将页面跳转到 forward 变量指定的 URL

至此,注册流程结束

本部分中,加下划线显示的部分,是需要对您的应用程序进行更改的部分,事实上,这部分更改会非常容易和方便。

Discuz! Passport 参数规格与加密方式

私有密匙(passport_key)

由于一些关键参数采用了 GET 方式进行传递,即便两次 header 跳转并不会直接将链接显示在外面,但我们仍然对关键的参数进行了加密,私有密匙共有两个作用:其一是供下面提到的可逆加密算法(AzDGCrypt)进行数据的加解密;其二是生成不可逆验证字串(verify),以防止关键信息被伪造。

在启用 Discuz! Passort 后,您需要在应用程序和 Discuz! 后台配置两处私有密匙,这两处的内容必须完全相同,这样应用程序和论坛之间才能正常通信。私有密匙决定了加密算法的强度,因此密匙长度请不要小于 10 个字节,并包含字母、数字和符号,以保证系统的安全。

加密算法

Discuz! Passport 采用 Azerbaijan Development Group(AzDG)开发的可逆加密算法 AzDGCrypt 对用户资料进行加密。如提供正确的私有密匙,可通过本加密算法对数据进行加密及解密,因此只要保证私有密匙的保密性,即可确保数据传递过程中的安全。以下为 Discuz! Passport 中应用到的可逆加密算法,为了生成可以被 Discuz! Passport 正确解密的 auth 字串,需要将如下函数放置于应用程序中,并可在登录及注册时调用。

passport_encrypt()是加密函数,用法为 passport_encrypt($txt, $key),其中 $txt 是待加密的字串,$key 是私有密匙。

passport_decrypt()是解密函数,用法为 passport_decrypt($txt, $key),其中 $txt 是加密后的字串,$key 是私有密匙。

/**

* Passport 加密函数

*

* @param string 等待加密的原字串

* @param string 私有密匙(用于解密和加密)

*

* @return string 原字串经过私有密匙加密后的结果

*/

function passport_encrypt($txt, $key) {

// 使用随机数发生器产生 0~32000 的值并 MD5()

srand((double)microtime() * 1000000);

$encrypt_key = md5(rand(0, 32000));

// 变量初始化

$ctr = 0;

$tmp = '';

// for 循环,$i 为从 0 开始,到小于 $txt 字串长度的整数

for($i = 0; $i < strlen($txt); $i++) {

// 如果 $ctr = $encrypt_key 的长度,则 $ctr 清零

$ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;

// $tmp 字串在末尾增加两位,其第一位内容为 $encrypt_key 的第 $ctr 位,

// 第二位内容为 $txt 的第 $i 位与 $encrypt_key 的 $ctr 位取异或。然后 $ctr = $ctr + 1

$tmp .= $encrypt_key[$ctr].($txt[$i] ^ $encrypt_key[$ctr++]);

}

// 返回结果,结果为 passport_key() 函数返回值的 base65 编码结果

return base64_encode(passport_key($tmp, $key));

}

/**

* Passport 解密函数

*

* @param string 加密后的字串

* @param string 私有密匙(用于解密和加密)

*

* @return string 字串经过私有密匙解密后的结果

*/

function passport_decrypt($txt, $key) {

// $txt 的结果为加密后的字串经过 base64 解码,然后与私有密匙一起,

// 经过 passport_key() 函数处理后的返回值

$txt = passport_key(base64_decode($txt), $key);

// 变量初始化

$tmp = '';

// for 循环,$i 为从 0 开始,到小于 $txt 字串长度的整数

for ($i = 0; $i < strlen($txt); $i++) {

// $tmp 字串在末尾增加一位,其内容为 $txt 的第 $i 位,

// 与 $txt 的第 $i + 1 位取异或。然后 $i = $i + 1

$tmp .= $txt[$i] ^ $txt[++$i];

}

// 返回 $tmp 的值作为结果

return $tmp;

}

/**

* Passport 密匙处理函数

*

* @param string 待加密或待解密的字串

* @param string 私有密匙(用于解密和加密)

*

* @return string 处理后的密匙

*/

function passport_key($txt, $encrypt_key) {

// 将 $encrypt_key 赋为 $encrypt_key 经 md5() 后的值

$encrypt_key = md5($encrypt_key);

// 变量初始化

$ctr = 0;

$tmp = '';

// for 循环,$i 为从 0 开始,到小于 $txt 字串长度的整数

for($i = 0; $i < strlen($txt); $i++) {

// 如果 $ctr = $encrypt_key 的长度,则 $ctr 清零

$ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;

// $tmp 字串在末尾增加一位,其内容为 $txt 的第 $i 位,

// 与 $encrypt_key 的第 $ctr + 1 位取异或。然后 $ctr = $ctr + 1

$tmp .= $txt[$i] ^ $encrypt_key[$ctr++];

}

// 返回 $tmp 的值作为结果

return $tmp;

}

/**

* Passport 信息(数组)编码函数

*

* @param array 待编码的数组

*

* @return string 数组经编码后的字串

*/

function passport_encode($array) {

// 数组变量初始化

$arrayenc = array();

// 遍历数组 $array,其中 $key 为当前元素的下标,$val 为其对应的值

foreach($array as $key => $val) {

// $arrayenc 数组增加一个元素,其内容为 "$key=经过 urlencode() 后的 $val 值"

$arrayenc[] = $key.'='.urlencode($val);

}

// 返回以 "&" 连接的 $arrayenc 的值(implode),例如 $arrayenc = array('aa', 'bb', 'cc', 'dd'),

// 则 implode('&', $arrayenc) 后的结果为 ”aa&bb&cc&dd"

return implode('&', $arrayenc);

}

passport_encode()是将数组转换合成为字串形式存储的函数:变量名和数值之间用等号连接,如果数值包含特殊字符,使用 urlencode() 将其转码;多个变量间使用 & 分割。例如原始数组内容为 array('username' => 'abc', 'email' => 'my+discuz@gmail.com'),经过 passport_encode() 编码后结果为 username=abc&email=my%2Bdiscuz%40gmail.com。

信息字串(auth)

应用程序在收到登录或注册请求,并读取到用户资料后,请按如下的要求将用户资料及部分其他信息存放于一个数组之中。数组各键值的含义为:

cookietime

应用程序保存该用户登录记录的时间,可为非负整数,单位秒,Discuz! Passport 收到此参数后,会设置同样的 Cookie 过期时间,这样应用程序和论坛可以保证同样的登录有效性。如不传递此参数,或参数数值不正确,则 Discuz! Passport 按照 0 设置 Cookie 有效期。

time

应用程序所在服务器当前时间(9 或 10 位数字 Unix Timestamp),此参数用于 Discuz! 所在服务器当前时间进行比对,如果早于当前时间超过若干秒(取决于 Discuz! Passport 中的“验证字串有效期”设定),则视为本 auth 内容无效,避免此URL被人得知后可能的安全问题。

username

用户登录或注册的用户名。Discuz! 的注册用户名规则为:

长度 1~15 个字符,不得为空

不得为 c:concon、游客(gb2312 或 big5 内码)、Guest

不得包含 (,)、(*)、(")、([TAB])、([SPACE])、([r])、([n])、(<)、(>)、(&)其中之一

如果应用程序提交过来的用户名不符合上述规则,Passport 将自动去处其中的特殊字符,将过滤后的结果写入数据库中。

password

用户密码经 MD5 不可逆加密后的值。如果此密码使用非 MD5 加密,则应用程序和 Passport 不能正常关联和使用。

email

用户 Email 地址(50 个字节以内)。

isadmin

当前用户是否是应用程序的最高管理员,1=是,0=否。最高管理员的权限,将同步到论坛中去,其他下级管理员的身份将不进行同步,而由最高管理员分别在不同的系统中进行设置。

credits

当前用户在应用程序中的积分值,范围 -2147483648 到 2147483647,如果 Discuz! Passport 中设置了目标积分项,则用户登录时,Passport 会把应用程序传递过来的 credits 值同步到指定的论坛的指定积分项目中

gender

当前用户的性别,1=男,2=女,0=未知。

bday

当前用户的生日,格式 yyyy-mm-dd。

regip

当前用户注册时的 IP 地址。

regdate

当前用户注册的时间(9 或 10 位数字 Unix Timestamp)。

nickname

当前用户的昵称(30 个字节以内,如传递此参数,必须打开相应用户组的昵称权限,否则用户在控制面板中提交个人资料时,会导致昵称失效)。

site

当前用户的主页地址(包含http://)。

qq

当前用户的 QQ 号码。

ICQ

当前用户的 ICQ 账号。

msn

当前用户的 MSN Messenger 账号。

yahoo

当前用户的 Yahoo! Messanger 账号。

以上参数中,以黑体下划线显示的 time、username、password、email 是必须传递的参数,缺少上述参数 Passport 将无法正常工作;其他的参数是可选的,如果不传递某些参数,则 Passport 会进行识别,自动不更新没有传递的参数所在的字段;所有数值,请提供原始值,而非经过反斜线转义(addslashes)后的结果。

把上述信息存放于数组中,假定为如下的形式:

$member = array

(

'cookietime' => 31536000,

'time' => 1117415922,

'username' => 'Abcd',

'password' => 'e2fc714c4727ee9395f324cd2e7f331f',

'email' => 'abcd@efgh.com',

'credits' => 123,

'regip' => '210.120.222.111',

'regdate' => '1012752000',

'msn' => 'email@hotmail.com'

);

将其经过如下的加密变换,即可得到 auth 的值:

$auth = passport_encrypt(passport_encode($member), $passport_key);

其中,passport_encode() 在前文已做了说明,用于将数组内容存放于特定的格式,$passport_key 是私有密匙。

切记:由于 $auth 中可能含有等号、加号等特殊字符,请将 $auth 经过 rawurlencode() 编码后再在 URL 中传递,否则可能会产生问题。

导向字串(forward)

导向字串用于通知 Discuz! Passport 在完成自身操作后,返回到哪一个 URL 地址,例如 http://www.myforums.com/forumdisplay.php?fid=2。如果 forward 为空,则默认导向到应用程序的首页

切记:由于 $forward 中可能含有冒号、问号、等号等特殊字符,请将 $forward 经过 rawurlencode() 编码后再在 URL 中传递,否则可能会产生问题。

验证字串(verify)

验证字串用户检验 auth 和 forward 两个参数的合法性,避免非法构造参数进行破坏的可能。无论 auth 和 forward 变量是否存在,验证字串(verify)的值均为:

$verify = md5($action.$auth.$forward.$passport_key);

其中,$action 是当前执行的 Passport 操作,如 login 等等;$auth 是用户信息加密后,并经 rawurlencode() 之前的内容;$forward 是经 rawurlencode() 前的导向字串;$passport_key 是私有密匙。如果 verify 的值不匹配,则 Passport 拒绝进行下一步操作。

Discuz! Passport 设置与启用

内置关联

Discuz! 以战略合作的方式,与业内知名的产品实现了 Passport 关联,目前内置了 SiteEngine 建站引擎(http://www.siteengine.net)和 Shopex 通用型网上商店系统(http://www.shopex.cn)的相关接口,这样用户只须透过在两套软件中简单的设置,即可开启这些关联。

其他应用程序

由于 Discuz! Passport 的高可扩展性和平台无关性,使得您可以参照前文的说明,稍稍改动小部分的代码,便将任何 B/S 模式的应用程序与 Discuz! 进行关联。

参数设置

您可以在 Discuz! 系统设置中,看到相应的通行证设置功能,在 Discuz! 合作伙伴的软件中,也可以找到这些设置入口。相关的操作已比较简单,在此不再详细叙述。

特别说明

如果您先运营了论坛,后与其他应用程序启用了 Passport 关联,由于之前论坛中的用户数据没有同步,您需要先写一个导入程序,将论坛的用户数据导入到应用程序的用户表中,否则以往在论坛注册的用户将无法通过 Passport 登录。已成功关联后新注册的用户无此问题。

在开启了 Discuz! 通行证后,您仍然可以通过 logging.php?action=login 这个链接来登录论坛,以备调试之用,但页面上显示的链接将改为应用程序的登录 URL。注意:开启通行证后,建议您通过 Discuz! 选项关闭论坛本身的注册功能,以免用户通过论坛注册而产生无法同步的问题。

您可以在 Discuz! 的 api/passport.php 找到 Discuz! Passport 的全部源程序,您也许通过他更好的理解 Passport 的原理,更快的完成应用程序与 Discuz! 之间的整合。

典型错误提示

Illegal request

非法请求,当验证字串 verify 不匹配时会产生此提示。可能是应用程序与 Discuz! 配置的私有密匙不同,或是通过 URL 传递前,未将必要的参数(如 auth、forward 等)进行 URL 编码,也有可能是使用了经过 URL 编码的参数值用来计算 verify 的 md5 值造成。以 PHP 语言为例,正确的代码应当是类似于的如下的格式:

$action = 'login';

$auth = passport_encrypt(passport_encode($autharray), $passport_key);

$forward = 'http://www.discuz.net/index.php';

$verify = md5($action.$auth.$forward.$passport_key);

header("Location: http://www.discuz.net/api/passport.php".

"?action=$action".

"&auth=".rawurlencode($auth).

"&forward=".rawurlencode($forward).

"&verify=$verify");

Lack of required parameters

auth 内容解密后,缺少必要的信息 time、username、password、email。

Request expired

请求过期。当前服务器时间与应用程序提交过来的 time 之差大于 Discuz! Passport 中设置的请求有效期。可能是使用以往的代码非法尝试,也可能是由于应用程序和 Discuz! 论坛所在的两台服务器,时间设置有误造成。

Invalid action

没有指定 Passport 所执行的 action。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有