转载:通过 Firefox 扩展为 Google Calendar 加密
通过 Firefox 扩展为 Google Calendar 加密
在 Google Calendar 中存储表示事件名称和描述的加密数据
文档选项
未显示需要 JavaScript 的文档选项
打印本页
将此页作为电子邮件发送
样例代码
英文原文
级别: 初级
Nathan Harrington, I/T 专家, IBM
2008 年 8 月 04 日
当今的 Web 应用程序为在线存储、访问和协作提供了许多便利。虽然一些应用程序为用户数据提供了加密,但为数不多。本文提供了添加基本加密支持所需的工具和代码,使您可以在一个最流行的在线日程表上加密用户数据。通过运用 Firefox 扩展和 Gnu Privacy Guard 的巨大灵活性,本文展示如何将加密的事件描述存储到 Google Calendar 应用程序,并且只向拥有解密密匙的用户显示纯文本。
本文从 Elias Torres 的出色的 “Google Calendar Quick Add” 扩展开始,逐步向您介绍如何提取、更改和插入各种组件,为事件进行加密,而不仅仅是使用加密的 TLS 数据通道。按照本文的说明进行操作之后,将会得到下面 图 1 所示的示例,在这里,服务器操作人员看到的是左边的内容,而 Web 浏览者看到的是右边的内容。
图 1. 加密的 Google Calendar
需求
硬件
任何能够提供 2002 年以后的浏览体验的硬件设备,应该能够运行本文所使用的代码。加密算法属于处理器密集型作业,因此,如果要在一个页面上对十几个,甚至上百个日程表事件进行加密,更快的硬件支持能够提供更好的浏览体验。
软件
需要 Firefox 1.5(或更新的版本)和 GnuPG(Gnu Privacy Guard)。具备各种 Firefox 扩展开发工具也是个不错的主意。查看下面的 参考资料 小节获得这些软件包的链接。
虽然本文的开发基于 Ubuntu 7.10 系统,但所用到的概念适用于各种操作环境。着手开发之前,确保您的系统支持 Perl、GnuPG 和 Firefox。
回页首
主要的编程方法
本文要求您基本熟悉 Firefox 扩展的开发过程。本文不需要特定的编译器和环境配置,但您必须熟悉软件开发,因为可能需要诊断与设置有关的问题或配置错误。
出于开发的需要,建议创建一个新的 Firefox 配置文件。下面的 参考资料 小节提供了如何创建新的配置文件的信息,同时也提供了有用的关于扩展开发站点的链接。
跨平台兼容性
本文的开发基于 Ubuntu 7.10 Linux® 发行版。如果要严格地遵循本文的说明进行操作,必须使用较新的 Linux 发行版。不过这里提到的所有软件(Firefox、GnuPG 和 Perl)都有对应的 Microsoft® Windows® 版本。只要稍微修改路径名和外部程序,这里所用的代码将可以在其他各种平台上使用。
GnuPG 将用于处理这个扩展的所有加密函数。这个方法提高了 JavaScript 的算法实现效率,同时也提供了健壮的跨平台密匙管理。您需要一个功能齐全的 GnuPG 安装,并且能够访问您选择的加密密匙。此外,还需要 gpg-agent
程序(通常是 GnuPG 包的一部分),用于处理已记住的 passphrase 的临时存储和过期。
有了处理加密的函数性扩展开发环境和 GnuPG 之后,可以通过更改前面开发的扩展的一些代码来修改接口。Elias Torres 开发的 Google Quick Add 扩展为插入加密修改提供了一个很好的开端。在加密阶段,当前输入的事件文本将存储在磁盘上,然后一个外部程序对它进行加密,加密后的文件将被读入并发送到 Google 服务器。在解密阶段,每个事件都被写到磁盘上,然后进行解密,最后读回纯文本并显示给用户。另一种代替方法涉及到编写新的协议,该协议由 Firefox 程序管理。尽管这个过程能够提供更加健壮的数据管道,输出、处理和读取则是更加简单的进程间通信方法,适合于这类扩展。
在继续使用本文提供的代码之前,确保您设置了函数性 Firefox GnuPG 和 gpg-agent。
回页首
构建 Google Calendar Quick Add 扩展
Google Calendar Quick Add 扩展利用 Google Calendar SOAP API 从任意页面上获得事件并添加日程表中。本文通过负载截取和过程加密的方式向日程表添加加密事件。一个代替方法是简单地向 Google Calendar 添加经 ASCII 封装的条目,但这里讨论的方法将自动完成这个过程。
首先,创建一个保存扩展目录和代码的目录,比如 mkdir ~/calendarEncrypt
。转到这个目录,然后从 参考资料 小节中指定的链接将 Quick Google Calendar Quick Add 扩展 xpi 下载到这个目录。
用以下命令解压缩 xpi:unzip quickgooglecal.xpi
。转到刚才创建的 chrome 目录,然后运行命令 unzip quickgooglecal.jar
。现在您会看到一个目录树,类似于 清单 1:
清单 1. Google Calendar Quick Add 目录结构
calendarEncrypt/chrome.manifest calendarEncrypt/readme.txt calendarEncrypt/chrome calendarEncrypt/chrome/content calendarEncrypt/chrome/content/hello.xul calendarEncrypt/chrome/content/overlay.xul calendarEncrypt/chrome/content/overlay.js calendarEncrypt/chrome/skin calendarEncrypt/chrome/skin/overlay.css calendarEncrypt/chrome/quickgooglecal.jar calendarEncrypt/chrome/locale calendarEncrypt/chrome/locale/en-US calendarEncrypt/chrome/locale/en-US/hello.dtd calendarEncrypt/chrome/locale/en-US/overlay.dtd calendarEncrypt/install.rdf calendarEncrypt/quickgooglecal.xpi
转到 ~/calendarEncrypt 目录并编辑 install.rdf 文件。更改标识符和创建者,如 清单 2 所示:
清单 2. 更改 install.rdf 的标识符和创建者
Change the identifier: {E31AE5B1-3E5B-4927-9B48-76C0A701F105}to: calendarEncrypt@devWorks_IBM.comAlso, change the creator: Elias Torresto: Elias Torres with modifications from devWorks
编辑 chrome.manifest 文件,将基于 jar 的扩展打包方式更改为更适合开发人员的目录结构打包方式。清单 3 给出了必要的更改。
清单 3. 将 chrome.manifest jar 更改为目录结构
Original chrome.manifest content quickgooglecal jar:chrome/quickgooglecal.jar!/content/ overlay chrome://browser/content/browser.xul chrome://quickgooglecal/content/overlay.xul locale quickgooglecal en-US jar:chrome/quickgooglecal.jar!/locale/en-US/ skin quickgooglecal classic/1.0 jar:chrome/quickgooglecal.jar!/skin/ style chrome://global/content/customizeToolbar.xul chrome://quickgooglecal/skin/overlay.css Change to directory based chrome.manifest: content quickgooglecal chrome/content/ overlay chrome://browser/content/browser.xul chrome://quickgooglecal/content/overlay.xul locale quickgooglecal en-US chrome/locale/en-US/ skin quickgooglecal classic/1.0 chrome/skin/ style chrome://global/content/customizeToolbar.xul chrome://quickgooglecal/skin/overlay.css
现在,在 Firefox 开发配置文件中为当前的扩展目录设置一个链接。例如,如果您的配置文件是 ~/.mozilla/firefox/b2w2sglj.development
,就在 ~/.mozilla/firefox/b2w2sglj.development/extensions/
创建一个称为 calendarEncrypt@devWorks_IBM.com
的链接。将当前的 Google Quick Add 开发目录路径放置到 calendarEncrypt@devWorks_IBM.com
文件中,在这个示例中是:/home/username/calendarEncrypt
。
登录 Google Calendar 然后按 ctrl+;激活 Google Calendar Quick Add 扩展。通过输入 Test unencrypted event tomorrow 15:30
验证可以正确添加事件。验证事件可以正确地显示在日程表上。
Google Calendar Quick Add 扩展就绪之后,现在就可以向扩展插入支持加密和解密的修改了。
回页首
修改 Quick Add 扩展,使其支持透明加密
对发送的事件进行加密
本文通过拦截和加密 Quick Add Event 负载的方式,帮助实现自动向日程表添加加密事件。修改现有的扩展使其支持拦截,见下面的 清单 4。编辑 chrome/content/hello.xul
文件并删除第 69 行:var quickAddText = number_html(document.getElementById("quickText").value);
。将下面的代码插入到第 69 行:
清单 4. 加密变量,提取日期和时间
var words = document.getElementById("quickText").value.split(' '); var dayVal = words[words.length-2]; var timeVal = words[words.length-1]; var elementData = ""; for( var n=0; n < words.length-2; n++ ){ elementData = elementData + " " + words[n]; }
以上的代码假设日期和时间通常是快速事件文本中的最后两个单词。为了确保能够在日程表中准确放置,所有后来快速添加的事件的格式必须是 “消息 文本 日期 时间”,日期的格式为 “friday/monday/tomorrow/etc”,时间的格式为 “05:30/10:30/etc”。
将事件描述文本从事件日期和时间分离出来后,接下来应该将事件文本写到磁盘,然后对它进行加密。添加 清单 5 中第 77 行的代码:
清单 5. 将加密文本写到磁盘,然后调用程序
// Write the quick event text to disk var fileOut = Components.classes["@mozilla.org/file/local;1"] .createInstance(Components.interfaces.nsILocalFile); fileOut.initWithPath("/tmp/calendarEvent"); var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"] .createInstance(Components.interfaces.nsIFileOutputStream); foStream.init(fileOut, 0x02 | 0x08 | 0x20, 0666, 0); foStream.write(elementData, elementData.length); foStream.close(); // Run the external encryption process var fileExe = Components.classes["@mozilla.org/file/local;1"] .createInstance(Components.interfaces.nsILocalFile); fileExe.initWithPath("/tmp/CalendarCrypt.pl"); var process = Components.classes["@mozilla.org/process/util;1"] .createInstance(Components.interfaces.nsIProcess); process.init(fileExe); var args = ["encrypt"]; process.run(true, args, args.length);
清单 5 的第一部分将配置文件 "/tmp/calendarEvent"
,将拦截到的事件文本写到磁盘。这个文件包含纯文本数据,但这些数据将在加密完成后被分解并删除。清单 5 的第二部分配置了一个文件,使它能够通过推荐的 nsIProcess 组件执行。/tmp/CalendarCrypt.pl
是一个 Perl 程序(将在后面介绍),它处理加密、解密和安全删除函数。对数据进行加密之后,清单 6 中的代码读回加密后的事件文本。将下面的代码置于 chrome/content/hello.xul
的第 98 行。
清单 6. 读回加密文件,然后构建事件文本
// Read the encrypted text back in var fileIn = Components.classes["@mozilla.org/file/local;1"] .createInstance(Components.interfaces.nsILocalFile); fileIn.initWithPath("/tmp/calendarEvent.asc"); var istream = Components.classes["@mozilla.org/network/file-input-stream;1"] .createInstance(Components.interfaces.nsIFileInputStream); istream.init(fileIn, 0x01, 0444, 0); istream.QueryInterface(Components.interfaces.nsILineInputStream); var data = ""; var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"] .createInstance(Components.interfaces.nsIFileInputStream); var sstream = Components.classes["@mozilla.org/scriptableinputstream;1"] .createInstance(Components./ interfaces.nsIScriptableInputStream); // above line split on / for formatting, do not include the line break fstream.init(fileIn, -1, 0, 0); sstream.init(fstream); var str = sstream.read(4096); while (str.length > 0) { data += str; str = sstream.read(4096); } sstream.close(); fstream.close(); quickAddText = data + " " + dayVal + " " + timeVal;
清单 5 的代码表示将文本事件写到一个文件中并运行加密程序。清单 6 中的代码表示读入经 ASCII 封装的加密文本文件(附带了存储日期和时间的信息),然后重新启动快速添加文本的过程。
每个通过 Google Calendar Quick Add 程序添加的事件都被拦截、加密,然后以加密的形式发送到 Google 服务器。
为读取的加密事件修改 overlay.js
完成对 chrome/content/hello.xul
中的事件进行加密的代码后,插入这些代码,解密 chrome/content/overlay.js
中的事件。插入 清单 7 中的行,从文件的末尾开始。
清单 7. 解密事件侦听器,组件定义
window.addEventListener("load", function() { calendarDecryptExtension.init(); }, false); var calendarDecryptExtension = { init: function() { var appcontent = document.getElementById("appcontent"); // browser if(appcontent) appcontent.addEventListener("DOMContentLoaded", this.onPageLoad, true); var messagepane = document.getElementById("messagepane"); // mail if(messagepane) messagepane.addEventListener("load", function () / { calendarDecryptExtension.onPageLoad(); }, false); // above line split on / for formatting, do not include the line break },
事件显示页面每次加载时,都会通过 addEventListener
调用 calendarDecryptExtension
函数。calendarDecryptionExtension
函数开始是各种定义和挂钩,保证代码在页面装载时开始运行以及函数可以访问正确的数据。将 清单 8 中的代码置于清单 7 中的代码之下,继续文档解密过程:
清单 8. 写入事件的 onPageLoad 函数
onPageLoad: function(aEvent) { dump("pre elemenet /n"); var elementData = "NODATA"; var allSpans = content.document.getElementsByTagName("span"); for (var n = 0; n < allSpans.length; n++){ if( allSpans[n].innerHTML.indexOf("BEGIN PGP") > -1 ){ // file output for encrypted event text elementData = allSpans[n].innerHTML; var fileOut = Components.classes["@mozilla.org/file/local;1"] .createInstance(Components.interfaces.nsILocalFile); fileOut.initWithPath("/tmp/calendarEvent.temp"); var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"] .createInstance(Components. / interfaces.nsIFileOutputStream); // above line split on / for formatting, do not include the line break foStream.init(fileOut, 0x02 | 0x08 | 0x20, 0666, 0); foStream.write(elementData, elementData.length); foStream.close();
每个日程表条目都存在 HTML 的 span
元素中。临时变量设置完成后,一个 for
循环将处理每个 span
元素,并在文本加密后删除这些内容。下面的 清单 9 调用 CalendarCrypt.pl
程序,使用 "decrypt"
选项,提取事件文本。
清单 9. 运行加密脚本
// create an nsILocalFile for the executable var fileExe = Components.classes["@mozilla.org/file/local;1"] .createInstance(Components.interfaces.nsILocalFile); fileExe.initWithPath("/tmp/CalendarCrypt.pl"); var process = Components.classes["@mozilla.org/process/util;1"] .createInstance(Components.interfaces.nsIProcess); process.init(fileExe); var args = ["decrypt"]; process.run(true, args, args.length);
将加密后的事件文本写到磁盘,然后运行解密程序,这将创建一个不加密的事件文本文件。添加 清单 10 中的代码将数据读回并更改呈现的信息。
清单 10. 读取解密后的文本
// file input for decrypted event text var data = ""; var fileIn = Components.classes["@mozilla.org/file/local;1"] .createInstance(Components.interfaces.nsILocalFile); fileIn.initWithPath("/tmp/calendarEvent.decrypted"); var istream = Components.classes["@mozilla.org/network/file-input-stream;1"] .createInstance(Components.interfaces.nsIFileInputStream); istream.init(fileIn, 0x01, 0444, 0); istream.QueryInterface(Components.interfaces.nsILineInputStream); var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"] .createInstance(Components.interfaces.nsIFileInputStream); var sstream = Components.classes["@mozilla.org/scriptableinputstream;1"] .createInstance(Components. / interfaces.nsIScriptableInputStream); // above line split on / for formatting, do not include the line break fstream.init(fileIn, -1, 0, 0); sstream.init(fstream); var str = sstream.read(4096); while (str.length > 0) { data += str; str = sstream.read(4096); } sstream.close(); fstream.close(); allSpans[n].innerHTML = data;
从文件读取文本类似于加密阶段中执行的过程。注意,清单 10 中的最后一行,它将当前的 span
数据设置为未加密文本,而不是 “BEGIN PGP...” 原始文本。 添加 清单 11 中的代码,完成解密过程。
清单 11. 分解磁盘上的文本,结束循环
// sanitize the data stored on disk args = ["shred"]; process.run(true, args, args.length); }//if the span item is encrypted }//for each span }//on page load }//calendarDecryptExtension
将选项更改为 "shred"
并重用 nsiProcess
组件,确保加密后的事件文本安全地从文件系统的临时位置删除。下一部分给出了前面部分调用的 CalendarCrypt.pl
程序。
CalendarCrypt.pl 程序
为了完成加密支持修改,需要根据下面的清单创建 CalendarCrypt.pl
程序。注意,对于典型 GnuPG 用户,给出的实现假设了一些最可能用到的加密/解密设置。如果愿意,可以考虑用 GnuPG::Encrypt
提供的其他选项替换外部程序调用和设置。例如,如果您希望使用多个键,或者 gpg-agent
与您的配置不兼容,GnuPG::Encrypt
Perl 模块提供了许多帮助实现环境兼容的选项。首先,通过 清单 12 中的文本将 Perl 程序 calendarEncrypt.pl
置于 /tmp
中。
清单 12. calendarEncrypt.pl header 和加密
#!/usr/bin/perl -w # calendarEncrypt.pl - encrypt/decrypt/shred files use strict; die "specify a mode " unless @ARGV == 1; my $mode = $ARGV[0]; chomp(my $userName = `whoami`); if( $mode eq "encrypt" ) { my $res = `gpg --yes --armor --encrypt -r $userName /tmp/calendarEvent`; $res = `shred /tmp/calendarEvent; rm /tmp/calendarEvent`;
在检查选项并设置默认用户名之后,将加密 /tmp/calendarEvent
文件。分解并删除原始的纯文本事件文件,确保磁盘上没有遗留敏感的数据。 下面的 清单 13 描述了解密模式:
清单 13. 文件处理,解密
}elsif( $mode eq "decrypt" ) { open(INFILE,"/tmp/calendarEvent.temp") or die "no in file"; open(OUTFILE,"> /tmp/calendarEvent.encrypted" ) or die "no file out"; while(my $line =) { my $begin = substr( $line, 0, 23 ); print OUTFILE "-----$begin/n"; my $version = substr( $line, 23, 34 ); print OUTFILE "$version/n"; print OUTFILE "/n"; my $body = substr( $line, 57 ); $body = substr($body, 0, length($body)-26); my @parts = split " ", $body; for my $piece( @parts ) { print OUTFILE "$piece/n"; } print OUTFILE "-----END PGP MESSAGE-----/n"; }#while each line close(OUTFILE); close(INFILE); my $cmd = qq{ gpg --yes --decrypt /tmp/calendarEvent.encrypted }; $cmd .= qq{ > /tmp/calendarEvent.decrypted }; my $res = `$cmd`;
在上传、处理或显示日程表事件的某个时刻,会丢失一些原始格式。尤其是 “BEGIN PGP” 消息的前缀 “-----” 以及换行指示,将被去除。在调用解密命令之前,清单 13 中的字符串操作函数将替换丢失的格式。最后,清单 14 中的代码负责安全删除解密文本文件,从而去除任何存储在磁盘上的纯文本信息。
清单 14. 分解纯文本文件
}elsif( $mode eq "shred" ) { my $res = `shred /tmp/calendarEvent.decrypted`; $res = `rm /tmp/calendarEvent.decrypted`; } # EOF
在将文件保存为 /tmp/CalendarCrypt.pl
之后,要确保该文件在发出以下命令时执行:chmod a+x /tmp/CalendarCrypt.pl
。
回页首
用例
现在将拦截、加密使用 Google Calendar Quick Add 程序添加的事件,然后以加密的形式将其发布到 Google 服务器。使用 Extension Developer 的 Extension 重新加载所有的 chrome 事件,或重启 Firefox。使用 Ctrl+; 组合键,然后添加一个事件,比如 “Private doctor appointment tomorrow 16:30”。以 “regular” 模式打开您的 Google 日程表,您将看到一个事件描述,类似于 图 1 左边所示的内容。
要显示解密后的事件,请转到链接:http://www.google.com/calendar/htmlembed?src=%40gmail.com
,在这里,是您的帐户名,比如 “developer.works” 或 “bob_smith”。页面装载开始时,您会看到将弹出一个 gpg-agent,请求您的 passphrase。为 CalendarCrypt.pl
程序的第 1 小节中 “uname” 命令识别出的用户输入 passphrase,您的事件将被解密并显示在日程表上。
回页首
结束语
使用本文给出的工具和代码,您现在就可以在 Google 日程表中存储加密事件文本。使用 Elias Torres 的 Google Calendar Quick Add Firefox 扩展的修改,对每个添加和查看的事件进行无缝的加密和解密。在重新掌控数据的同时,也获得了 Web 2.0 应用程序带来的好处。
考虑创建一个模糊层(obfuscation layer)来更改事件存储的时间,降低网络流量分析的效率。编写您自己的程序,借助 Google Calendar SOAP API 提取、加密和存储以前和将来的日程表事件。尝试为默认的 Google Calendar 接口创建一个具有 Ajax 风格的透明加密扩展。
from http://www.ibm.com/developerworks/cn/web/wa-googlecal/index.html
转载:通过 Firefox 扩展为 Google Calendar 加密相关推荐
- firefox 扩展
firefox 扩展 <table border="0" cellpadding="4"> <tbody><tr> < ...
- 你了解Google Calendar吗?
Google Calendar Google日历(Google Calendar)Google公司推出的免费在线可共享的日历服务.使用Google Calendar,可以容易地跟踪生活中的重要时间:生 ...
- 50个最好的firefox扩展让你尽情冲浪
50个最好的firefox扩展让你尽情冲浪 Submitted by xyz黑板 on 星期四, 一月 19, 2006 - 19:04 资源 | 开发 xyz黑板翻译 原文:http://pcher ...
- Google Calendar 跨平台同步方案(随时同步手机与电脑的日程安排)
Google Calendar跨平台同步方案 Google 随着搜索引擎的大获成功,不断推出很多受欢迎的服务产品,包括 Gmail,Calendar,Documents,Reader等等.其中的 Go ...
- Firefox 扩展开发 install.rdf和chrome.manifest
现在我们以一个hello world扩展为例来说明Firefox 扩展的基本运行方式.先下载 Hello World extension,解压缩,下面假定路径是c:\helloworld. 设置fir ...
- Firefox扩展开发 Hello World!
今天尝试开发一个Firefox的扩展.虽然比较简单,网上也有很多教程,但是感觉一些教程写的比较麻烦,在初步的开发过程中并没有用到那些东西,于是自己把开发过程记录下来.我是根据Mozilla官方教程开发 ...
- firefox扩展开发(二):用XUL创建窗口控件
firefox扩展开发(二):用XUL创建窗口控件 2008-06-11 16:57 1.创建一个简单的窗口 <?xml version="1.0"?> <?xm ...
- Google Calendar API练习
今天看一篇关于介绍如何在.NET下对Google Calendar API进行操作的文章. Link: http://www.cnblogs.com/SkyD/archive/2009/07/23/1 ...
- firefox扩展开发(四) : 更多的窗口控件
firefox扩展开发(四) : 更多的窗口控件 2008-06-11 17:00 标签盒子 标签盒子是啥?大家都见过,就是分页标签: 对应的代码: <?xml version="1. ...
- PHP 文件加密Zend Guard Loader 学习和使用(如何安装ioncube扩展对PHP代码加密)
一.大体流程图 二.PHP 项目文件加密 下表列出了Zend产品中的PHP版本及其内部API版本和Zend产品版本. 如何加密请往后看 三.如何使用 第一步:确认当前环境 Amai Phalcon 前 ...
最新文章
- 体验决定销量,真假4K争论仅仅是忽悠人而已
- python 设置x轴_python matplotlib坐标轴设置的方法
- GM6 frontend Shell._resolveHashFragment when tile is clicked
- 运算符的优先级总能起作用吗?
- vs2010上opencv配置
- c语言字母等腰三角形边框,用C语言编写一个四行*等腰三角形
- endnotex9安装后使用方法_endnotex9使用教程
- 基于springboot助学贷款管理毕业设计源码061528
- 开发工具总结(8)之图文并茂全面总结上百个AS好用的插件(下)
- 爬取去哪儿酒店信息及评论
- 王牌特工:黄金圈 经典例句
- Sweet 简洁是美
- 经常使用网页播放器代码
- Android开发之仿360手机卫士悬浮窗效果
- 2K分辨率显示器调整缩放125%部分软件模糊的解决办法
- 区块链项目ICO注意事项
- imagemagick安装问题
- Flash鼠绘入门第五课:绘制可口的樱桃-Flash鼠绘可口的樱桃(1)
- Unity3D 动态加载本地/网络GLB模型
- python中幂运算_python的幂运算
热门文章
- 听刘万祥老师讲“模拟范围滑尺的动态图表”
- 数据库中的数据模型 概念数据模型、逻辑数据模型、物理数据模型
- [NSSCTF][羊城杯2020]WEB复现
- Django 4.x Caches 缓存使用示例和配置方法
- 傻子的代码怎么写_傻瓜不写单元测试
- CAJViewer安装报错卸载报错打开报错解决流程
- HTTP请求报文和响应报文、http协议状态码分类和常用状态码、Cookie、curl工具、httpd自带的工具、压力测试工具
- 集成AGC崩溃服务如何生成iOS符号文件表并上传
- matlab 对信号抽样,matlab信号抽样与恢复
- 2022年下半年(软考高级)信息系统项目管理师报名条件