国际化安全课件

上传人:夏** 文档编号:592167161 上传时间:2024-09-19 格式:PPT 页数:36 大小:343KB
返回 下载 相关 举报
国际化安全课件_第1页
第1页 / 共36页
国际化安全课件_第2页
第2页 / 共36页
国际化安全课件_第3页
第3页 / 共36页
国际化安全课件_第4页
第4页 / 共36页
国际化安全课件_第5页
第5页 / 共36页
点击查看更多>>
资源描述

《国际化安全课件》由会员分享,可在线阅读,更多相关《国际化安全课件(36页珍藏版)》请在金锄头文库上搜索。

1、第六章第六章国际化安全国际化安全 国际化(internationalization),是为了保证软件产品适应不同区域语言要求的一种方式。由于英文单词 internationalization的首末字符i和n之间的字符数为18,因此业界内常把I18N作为“国际化”的简称。以WEB应用为例,随着经济的发展,全球经济一体化已经慢慢成为一种主流趋势,WEB应用要求必须能够支持多国语言。对于同一个WEB应用,在不同的语言环境下需要显示不同的效果,来方便用户。我们经常看到,一些网站都有各个不同的语言版本,在运行时,能够根据客户浏览器所在的国家和语言的不同,显示不同的用户界面。软件支持多种不同的语言,绝不是

2、开发了软件的多个版本。业界具有一定的规则让信息进行复用,即对同样信息进行各种代码的转换。这样可以使得当需要在应用程序中添加对一种新的语言的支持时,不需要重新再开发一个软件,造成重复劳动。而安全问题就存在于代码转换的过程之中。本章主要针对国际化过程中的安全问题进行讲述,首先讲解常见的国际化过程,然后讲解国际化转码中需要注意的安全问题。6.1 国际化的基本机制随着经济全球化的发展,软件也应该具有支持各种语言和地区的能力。国际化的主要目的,是调整软件,使之能适用于不同的语言及地区。如图所示是一个表单在中国和美国的两个显示效果。从以上界面上看出,两个页面功能相同,但是在不同的地区,为了照顾不同的用户,

3、显示界面不同,这就需要在开发的过程中充分考虑国际化问题。与国际化类似的另一个概念是本地化(localization)。在业界内,两个概念一般一起讲,有时候甚至被等同起来。不过,从概念上说,本地化是实现国际化的一些手段的集合。国际化的概念,比较偏向表达软件的设计思想,要求当软件被移植到不同的语言及地区时,软件的业务逻辑和程序源代码不用作改变或修正,但是软件又必须让该地区和语言的用户方便地使用;本地化的概念偏向对软件进行加工,使之满足特定地区和特定语言的用户对语言和功能的特殊要求,实际上是一指一系列工作的过程。软件本地化工作,可能涉及文字的翻译、用户界面布局调整、本地特性开发、联机文档和印刷手册的

4、制作,以及保证本地化版本能正常工作等,实际上也算是软件质量保证活动的一部分。国际化简称为I18N,本地化由于其单词localization的L和N之间有10个字母,因此也被简称为L10N。国际化和本地化这两个工作,一个是设计思想,一个是工作的手段,相辅相成,互为补充。在有些企业中,也使用全球化(globalization)来表示国际化和本地化的合称,使用 G11N作为简称。从具体的工作内容上说,国际化与本地化工作,实际上包括的细节很多,也很繁杂。以下列举一些常见的工作:不同语言表达方式;不同语言表达方式; 电子文件的编码;电子文件的编码; 数字命名系统的不同;数字命名系统的不同; 文字书写方向

5、文字书写方向(如英语是从左到右,阿拉伯语从右到左如英语是从左到右,阿拉伯语从右到左); 语言细微差别(如英国英语中的语言细微差别(如英国英语中的Colour和美国英语中的和美国英语中的Color);); 货币;货币; 日期格式;日期格式; 数字格式;等等。数字格式;等等。 6.1.2 国际化过程开发软件时,国际化和本地化对开发者是一个有挑战性的任务。很多软件,在件刚开始设计时,并没有考虑到需要在不同的语言和地区使用,于是就没有按照国际化的思想去设计,但是一段时间之后,软件突然出现要在其他地区使用的任务,国际化和本地化的工作将会十分艰难。怎样让程序从一开始就为国际化和本地化提供开发基础呢?我们知

6、道,软件的难度在于程序的业务逻辑,一般情况下不应该随便对业务逻辑进行改动。程序在不同地区运行的过程中,实际上,程序的逻辑只有一份,只是界面的表示有所不同,而应该避免的是程序逻辑的修改。因此,通常作法是:将文本和其他与环境相关的资源单独编写,和程序代码相分离。这样,在理想的情况下,应对变化的环境时,无需修改代码,只需要修改资源,从而显著简化了工作。以下是国际化(本地化)的基本过程。 从上面的图中可以看出,国际化过程包括3个部分:1:资源文件。是一个文件,能够保存各种不同:资源文件。是一个文件,能够保存各种不同语言所对应的资源。语言所对应的资源。2:读取工具。能够根据语言来读取资源文件。:读取工具

7、。能够根据语言来读取资源文件。3:应用程序。调用读取工具,读取资源文件。:应用程序。调用读取工具,读取资源文件。在很多软件里面实现了国际化,以Java框架为例,要开发一个支持英文和中文的欢迎界面,该界面标题根据系统语言的不同而自动变化,可以利用接下来的一些代码来实现。以下是支持英文显示的资源文件:messageResource_en_US.properties以下是支持中文显示的资源文件(在Java中实现了转码,将“欢迎您来到本系统”转化成了ASCII码表示,用native2ascii来实现。这是Java的语言特点,其他语言不一定相同):messageResource_zh_CN.proper

8、tiesP06_01.java为界面类。welcomeMessage=Welcome to visit our system! welcomeMessage=u6B22u8FCEu60A8u6765u5230u672Cu7CFBu7EDF 首先,将系统语言变为中文(中国)。如果使用的是windows系统,你可以通过控制面板中的“区域和语言选项”来修改: 运行,得到如下界面;然后将系统语言变为“英语”,运行,得到如下界面。 6.2 国际化中的安全问题6.2 国际化中的安全问题国际化过程中,遇到的最重要的问题是不同的语言文字,在不同的系统中具有不同的表达方式,也就是通常所说的编码。编码是不同国家的

9、语言在计算机中的一种存储和解释规范,在各个不同规范中,存储了相应能够表达一定内容的的若干字符,称为字符集。最原始的字符集是美国国家标准学会(American National Standards Institute, ANSI)的美国信息交换标准码(American Standard Code for Information Interchange,ASCII字符集),它使用7个比特来表示一个字符,总共表示128个字符;不过,由于一个字节一般占用8个比特,为了充分利用一个字节所能表达的最大信息,IBM公司对ASCII字符集进行了扩展,用一个字节来表示一个字符,这样,让ASCII码字符集总共可以

10、表示256个字符。不过,我们常说的ASCII码字符集表达的还是128个字符,常见的ASCII码字符集表也是基于128字符的ASCII码字符集编写的。由于英文和大部分的西方语言都是以字母拼写为基础的,需要的字母数量不多。因此,以上ASCII码字符集对这些语言的表达,基本能够胜任。但是,世界上的语言种类多种多样,如中文、日文、韩文等,语言中含有的文字就几千个,ASCII字符集就无法胜任其表达。因此,在ASCII字符集的基础之上,又派生出了一些新的字符集,如:GB2312、UTF-8、UTF-16等,称为MBCS(Multi-Byte Chactacter System,多字节字符系统。提示:提示:

11、注意,同一个字,在不同的系统中可能有不同的表达方式。如“中国人”,在GB2312字符集和UTF-8字符集中,其表达方式完全不一样,读者可以编写相应的程序进行验证。有人可能会问,为什么不将这些字符集统一成为一个呢?这是因为各种字符集都有其发布的历史,一个字符集发布一段时间,功能需要扩充,于是出来了新的字符集,但是原有的项目要改成新的字符集,需要花费一些成本。久而久之,就造成了很多字符集并存的局面。6.2.2 字符集转换在实际的项目中,源数据可能来自于不同的字符集(如支持不同字符集的数据库),而源数据需要以某种方式被目标程序使用,如显示在界面上、或者被目标程序进行加工等等。这时候遇到的最大问题就是

12、:目标程序所支持的字符集和源数据属于的字符集可能不一致。此种情况下,可能造成系统显示出错。根据情况的不容,这里进行一个分类:1:当目标程序所支持的字符集和源数据属于的字符集完全不兼容时,数:当目标程序所支持的字符集和源数据属于的字符集完全不兼容时,数据无法显示据无法显示(或者以乱码形式显示或者以乱码形式显示)。例如,对于中文来说,如果如源数据。例如,对于中文来说,如果如源数据库使用字符集库使用字符集GBK,从数据库中查出来的中文内容,想要在目标程序上使,从数据库中查出来的中文内容,想要在目标程序上使用,而目标程序默认支持用,而目标程序默认支持ASCII,由于,由于GBK是是16位字符集,而位字

13、符集,而ASCII是是7位位字符集,两者完全不兼容,或者说没有任何关系,每一个中文字符在字符集,两者完全不兼容,或者说没有任何关系,每一个中文字符在ASCII中,都不能够找到对等的字符,所以所有中文字符都会丢失而变成中,都不能够找到对等的字符,所以所有中文字符都会丢失而变成乱码形式。乱码形式。2: 当目标程序所支持的字符集是源数据属于的字符集的子集时,当目标程序所支持的字符集是源数据属于的字符集的子集时,信息会部分丢失。例如,如果源数据库使用信息会部分丢失。例如,如果源数据库使用GBK,而目标程序字,而目标程序字符集使用符集使用GB2312,这个过程中绝大部分字符都能够正确转换,但,这个过程中

14、绝大部分字符都能够正确转换,但是由于是由于GB2312字符集小于字符集小于GBK,因此一些超出,因此一些超出GB2312字符集的字符集的字符变为乱码。字符变为乱码。在这些情况下,就必须进行字符集转换,俗称转码。各种语言中都有不同的转码支持。本节以Visual C+为例来讲解转码问题。在Visual C+中,有如下两个函数对转码进行支持: MultiByteToWideChar; WideCharToMultiByte。在这两个函数中,“MultiByte”称为短字符,一般为8bit或8bit以内来表示的字符,如ASCII码。“WideChar”称为宽字符,一般是指用16bit或以上(如果有的话

15、)表示的字符,如UNICODE。MultiByteToWideChar函数的功能是:将一个由短字符组成的字符串转换为一个宽字符组成的字符串。函数原型是: int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cchMultiByte, LPWSTR lpWideCharStr, int cchWideChar) 由于本书内容所限,不对该函数进行详细的讲解,有关各参数,在此进行简单的阐述:w1:CodePage:指定执行转换的代码页(实际上就是字符集或编码方式),这个参数可以为系统已安装或

16、有效的任何代码页所给定的值。你也可以指定其为下面的任意一值:CP_ACP:ANSI代码页;代码页; CP_MACCP:Macintosh代码页;代码页; CP_OEMCP:OEM代码页;代码页; CP_SYMBOL:符号代码页;:符号代码页; CP_THREAD_ACP:当前线索:当前线索ANSI代码页;代码页; CP_UTF7:使用:使用UTF-7转换;转换; CP_UTF8:使用:使用UTF-8转换。转换。 w2:dwFlags:一组标记,用以指出字符的处理方式。可以是以下标记常量的组合,含义如下: MB_PRECOMPOSED:由一个基本字符和一个非空字符组成的字符只有:由一个基本字符和

17、一个非空字符组成的字符只有一个单一的字符值。这是缺省的转换选择。不能与一个单一的字符值。这是缺省的转换选择。不能与MB_COMPOSITE值一值一起使用。注意,推荐使用该标志,因为这会在某种程度上消除产生组合字起使用。注意,推荐使用该标志,因为这会在某种程度上消除产生组合字符的可能并加速规范化。符的可能并加速规范化。MB_COMPOSITE:由一个基本字符和一个非空字符组成的字符分别有不:由一个基本字符和一个非空字符组成的字符分别有不同的字符值。这是缺省的转换选择。该选项不能与同的字符值。这是缺省的转换选择。该选项不能与MB_PRECOMPOSED值一起使用。值一起使用。MB_ERR_INVA

18、LID_CHARS:如果函数遇到无效的输入字符,它将运行:如果函数遇到无效的输入字符,它将运行失败。该标记能够捕获未定义的字符,因此,在不大于失败。该标记能够捕获未定义的字符,因此,在不大于50000的的CodePage中,推荐使用该标记。中,推荐使用该标记。MB_USEGLYPHCHARS:使用象形文字替代控制字符。:使用象形文字替代控制字符。w3:lpMultiByteStr:将被转换字符串的字符。w4:cchMultiByte:指定由参数lpMultiByteStr指向的字符串中字节的个数。如果这个值为-1,字符串将被设定为以NULL为结束符的字符串,并且自动计算长度。w5:lpWide

19、CharStr:指向接收被转换字符串的缓冲区。w6:cchWideChar:指定由参数lpWideCharStr指向的缓冲区的字节个数。若此值为零,函数返回缓冲区所必需的宽字符数。w该函数返回值含义为: 如果函数运行成功,并且如果函数运行成功,并且cchWideChar不为零,返回不为零,返回值是由值是由lpWideCharStr指向的缓冲区中写入的宽字符数;指向的缓冲区中写入的宽字符数; 如果函数运行成功,并且如果函数运行成功,并且cchMultiByte为零,返回值是为零,返回值是接收到待转换字符串的缓冲区所需求的宽字符数大小;接收到待转换字符串的缓冲区所需求的宽字符数大小; 如果函数运行

20、失败,返回值为零。如果函数运行失败,返回值为零。 WideCharToMultiByte函数功能是:将一个由宽字符组成的字符串转换为一个由短字符组成的字符串。函数原型是: int WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPWSTR lpWideCharStr, int cchWideChar, LPCSTR lpMultiByteStr, int cchMultiByte, LPCSTR lpDefaultChar, PBOOL pfUsedDefaultChar ) w1:CodePage:指定执行转换的代码页(字符集或编码方式)

21、,意义和MultiByteToWideChar中类似。w2:dwFlags:一组位标记用以指出字符的处理方式,意义和MultiByteToWideChar中类似;不过,该参数还有一个WC_NO_BEST_FIT_CHARS标记推荐使用,该标记可以防止函数将字符映射到相似但语义完全不同的字符上。w3:lpWideCharStr:指向将被转换的unicode字符串。w4:cchWideChar:指定由参数lpWideCharStr指向的缓冲区的字符个数。如果这个值为-1,字符串将被设定为以NULL为结束符的字符串,并且自动计算长度。w5:lpMultiByteStr:指向接收被转换字符串的缓冲区。

22、w6:cchMultiByte:指定由参数lpMultiByteStr指向的缓冲区最大值(用字节来计量)。若此值为零,函数返回lpMultiByteStr指向的目标缓冲区所必需的字节数,在这种情况下,lpMultiByteStr参数通常为NULL。w7:lpDefaultChar和pfUsedDefaultChar:只有当WideCharToMultiByte函数遇到一个宽字节字符,而该字符在CodePage参数标识的代码页中并没有它的表示法时,WideCharToMultiByte函数才使用这两个参数。lpDefaultChar意义如下: 如果宽字节字符不能被转换,该函数便使用如果宽字节字符

23、不能被转换,该函数便使用lpDefaultChar参数指向的字符;参数指向的字符; 如果该参数是如果该参数是NULL(这是大多数情况下的参数值),那么该函数使用系统的默认(这是大多数情况下的参数值),那么该函数使用系统的默认字符。字符。pfUsedDefaultChar参数指向一个布尔变量:参数指向一个布尔变量: 如果如果Unicode字符串中至少有一个字符不能转换成等价多字节字符,那么函数就将字符串中至少有一个字符不能转换成等价多字节字符,那么函数就将该变量置为该变量置为TRUE; 如果所有字符均被成功地转换,那么该函数就将该变量置为如果所有字符均被成功地转换,那么该函数就将该变量置为FAL

24、SE; 当函数返回以便检查宽字节字符串是否被成功地转换后,可以测试该变量。当函数返回以便检查宽字节字符串是否被成功地转换后,可以测试该变量。 w该函数的返回值意义为: 如果函数运行成功,并且如果函数运行成功,并且cchMultiByte不为零,返回值是由不为零,返回值是由 lpMultiByteStr指向的缓冲区中写指向的缓冲区中写入的字节数;入的字节数; 如果函数运行成功,并且如果函数运行成功,并且cchMultiByte为零,返回值是接收到待转换字符串的缓冲区所必需的为零,返回值是接收到待转换字符串的缓冲区所必需的字节数;字节数;如果函数运行失败,返回值为零。如果函数运行失败,返回值为零。

25、 在Java语言中,字符集的转换也具有一定的支持,列举如下:1:编译阶段:一般javac根据当前操作系统区域设置,自动决定源文件的编码,可以通过-encoding强制指定。如:2:资源文件:资源文件一般为.properties文件,可由Properties用ISO-8859-1编码读取,需要使用JDK 的native2ascii工具转换汉字为uXXXX格式,才能正确读取里面的汉字。如: 如例P06_01.java中,中文资源文件messageResource_zh_CN.properties的内容如下(可用文本编辑器打开阅读): 等号右边的内容实际上就是“欢迎您来到本系统”经过native2a

26、scii命令转码之后的结果。native2ascii -encoding GBK source native2ascii -encoding GBK source javac -encoding gb2312 Hello.java 3:字节数组:可使用new String(byteArray,encoding) 和 String.getBytes(encoding) 在字节数组和字符串之间进行转换。如下例子,如果我们得到的字符串string是由ISO-8859-1转码方式产生的,要在支持GB2312中文的界面上显示,可以以如下方式转为正确的中文: 4:JSP:如果要支持汉字,可在头部加上: 这

27、样的标签。JSP表单的过程中,经常会出现乱码问题,如表单中的中文汉字无法被获取之后成为乱码,此时可以采用上面字节数组的处理方法,也可以使用: 来处理,当然,也可以编写过滤器,将该内容放在过滤器中。string = new String(string.getBytes(ISO-8859-1), GB2312); string = new String(string.getBytes(ISO-8859-1), GB2312); request.setCharacterEncoding(gb2312); 6: Servlet:如果要输出中文,确定设置:另外,在标记语言中,也可以设置其编码方式:1:X

28、ML文件:XML文件读写同于文件读写,如果有汉字,应注意确保XML头中声明如:与文件编码保持一致。2:HTML:在head中加上:让浏览器正确确定HTML编码。 response.setContentType(text/html; charset=GB2312); 6.2.3 I18N缓冲区溢出问题在转码时,如果使用不当,就会出现缓冲区溢出问题。这种情况在使用MultiByteToWideChar和WideCharToMultiByte函数时更容易出现,在此进行讲解。如下函数: 函数的参数5中,定义了目标字符串的指针。怎样定义目标字符串呢?一种方法是,事先定义一个足够大的宽字符数组,但是可能有

29、如下的缺陷:当当lpMultiByteStr占用空间较小时,可能会造成空间浪费;占用空间较小时,可能会造成空间浪费;如果为了避免空间浪费,分配的数组空间较小,当如果为了避免空间浪费,分配的数组空间较小,当lpMultiByteStr占用的空间超过占用的空间超过了预分配空间时,又可能造成缓冲区溢出。了预分配空间时,又可能造成缓冲区溢出。因此,该方法不是一个最好的办法。int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cchMultiByte, LPWSTR lpWideCharStr,

30、 int cchWideChar) 此种情况下,我们需要通过一些手段来获知转码的目标数组lpMultiByteStr所需要的数组空间。方法是:将MultiByteToWideChar函数的第四个形参设为-1,即可返回所需的短字符数组空间的个数。下面就是一个例子:nLen中得到的就是所需的短字符数组空间的个数。因此,完整的安全的代码结构如下: int nLen = MultiByteToWideChar (CP_UTF8, 0, lpMultiByteStr, -1, NULL, 0); /获得需要分配的内存大小,存入nLen int nLen = MultiByteToWideChar(CP_

31、UTF8,MB_ERR_INVALID_CHARS, lpMultiByteStr,-1, NULL,0); if(nLen=0) /函数调用异常,作异常处理 /跳出 /为新的字符串分配内存LPWSTR lpWideCharStr = (LPWSTR)GlobalAlloc(0,sizeof(WCHAR)*nLen); if(lpWideCharStr=NULL) /内存分配失败,作相应处理 /跳出 /正式转换nLen = MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS, lpMultiByteStr,-1, lpWideCharStr,nLe

32、n); if(nLen=0) /函数调用异常,作异常处理 /跳出 /其它收尾操作6.3 推荐使用Unicode 实际上,理想的情况是,在软件开发的过程中,全程使用某一种编码。并且不在这种编码和别的字符集之间进行转换,自然不会出现字符集转换中信息丢失的问题。在我们要选择的编码中,Unicode(Universal Multiple-Octet Coded Character Set)是一种值得推荐的字符集。随着信息化交流的迅速发展,个体之间进行的数据交换越来越频繁,不同的编码体系渐渐成为信息交换的障碍;虽然我们可以进行编码转换,但是由于多种语言的文档共存的现象不断增多,编码转换也成了一件麻烦的事

33、情。因此,设计一种统一的编码显得非常重要,此种情况下,Unicode应运而生。Unicode又称统一码、万国码或单一码,是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。Unicode于1990年开始研发,在1994年正式公布。随着网络的发展和信息交流的增强, Unicode也在面世以来的十多年里得到普及。作为一种在计算机上使用的字符编码,针对每种语言中的每个字符,Unicode为都设定了统一并且唯一的二进制编码,这样,不管在什么语言,什么平台下,Unicode不需要转码,满足了跨语言、跨平台进行文本转换、处理的要求。在Unicode公布之前,对于同一个字符,可能出现多种不同的编码,

34、由于无法知道信息的来源,任何一台特定的计算机(特别是服务器)都必须安装多个字符集,需要支持许多不同的编码。但是有了Unicode,每个字符的编码就唯一了。Unicode是采用16位编码体系,一律使用两个字节表示一个字符,特别值得强调的是,对于ASCII字符它也使用两字节表示。它覆盖了美国、欧洲、中东、非洲、印度、亚洲和太平洋的语言,以及古文和专业符号。由于支持Unicode的成员,一般都是全球主要系统及软件制造商,因此Unicode很快就成为事实上的工业标准。Unicode制定了三套编码方式。分别是UTF-8,UTF-16和UTF-32(使用较少)。因此,如果在开发软件的过程中使用Unicod

35、e,并且不将Unicode和其他字符集进行转换,就基本上不会遇到转码过程中的安全问题。不过,以上情况只是理想状态,实际操作的过程中,由于软件产品的生产商繁多,本软件使用了Unicode编码,无法保证其它软件(如数据库软件)使用的也是Unicode编码,因此要保证自己不遇到转码的问题,基本很难。但是,Unicode的推荐使用,也为软件国际化难度的降低提供了一个较好的解决方案。小结本章对国际化(internationalization),进行了介绍,首先阐述了国际化和本地化的需求和基本原理,然后主要基于Visual C+语言,讲解了国际化转码中需要注意的安全问题,最后对Unicode的使用进行了推

36、荐。练习1:一个软件能够有多种语言版本,并不是开发了多个版本。(1) 用用JSP完成一个页面,包含一个登录表单完成一个页面,包含一个登录表单(参考参考6-1-1),要求能够在英文、简体中文、繁体中文,要求能够在英文、简体中文、繁体中文之间切换。之间切换。(2) 资源文件的命名有何讲究?资源文件的命名有何讲究? 2:在用Visual C+进行转码的过程中,会出现缓冲区溢出的问题,请用Visual C+对转码中缓冲区溢出的问题进行测试。3:同一个字符在不同的字符集中可能有不同的表达方式。编写代码: (1) 查看查看“安全编程安全编程”的的Unicode表示方法;表示方法; (2) 查看查看“安全编程安全编程”在在gb2312字符集中的表示方法。字符集中的表示方法。4:书中提到,如果使用Unicode就可以避免转码问题。(1) 为什么统一使用为什么统一使用Unicode就没有转码问题?就没有转码问题? (2) 实际上实现起来有何难度?实际上实现起来有何难度? 5:很多语言都支持国际化资源文件。请用.Net系列编写若干个不同的资源文件,并且可以用资源文件改变网页上内容,以不同的语言显示。

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 资格认证/考试 > 自考

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号