实用的基于网络的深度学习和安全示例

Daisy的个人资料照片

经过 Daisy

实用的基于Web的深度学习和安全 第三版 夏洛特·哈珀 2024年7月3日 前言: 为网络构建软件的安全考虑因素是任何Web开发人员计划和执行的重要组成部分,同时设计了可靠,稳定且用于实际目的的原型。 DOM(文档对象标记),其实施HTML,JavaScript和CSS以及实施Python,C/C ++,Java和Bash的后端软件,为Web开发人员提供了自由和力量,以创建各种各样的项目创造力,提供易用性和功能性,描绘谦卑和性格,并提供易用性以及方便和重要服务,这些服务都对普通Joe(最终用户)都有吸引力希望杀死时间或在互联网上完成某件事,通常是在触摸屏智能手机设备上。大多数人甚至都不知道想从头开始构建网站时从哪里开始,他们倾向于从另一个人的网站开始,并在功能性,可靠性,易用性,尤其是创造力上建立一些有限的东西,当他们可以使用所有最新的功能强大工具时,以构建有用的东西而不会浪费时间按下按钮,并且尤其是浪费金钱来支付昂贵的订阅,因为很少有人想使用昂贵的软件,因为它的易用性和灵活性的限制。如果您有几分钟的阅读本书并了解我想教您的内容,甚至与我个人谈论您的目标并朝着正确的方向获得一些指导,并有动力学习编码并编写自己的软件,将这本书带回家,并留出一些时间来学习建立下一个有影响力的,功能强大,精简和重要的Web应用程序,该网站上都在您身上,完全按照您想要的方式满足您的需求。 关于我: 我是一个软件开发人员C/C ++,Java,Python,HTML,CSS和JavaScript的经验。我建立了人们想要使用,想要访问的网站,甚至沉迷于仅仅学习,重新创造和杀死时间,最重要的是,我出售软件。如果您对您希望网站的外观和功能确切的想法有一个想法,那么您愿意支持我,以便在我见面时我可以满足自己的需求,并且您愿意承担自己运行网站的费用,我会为您构建下一个YouTube,Tiktok,Twitter,Google,甚至只能访问高科技安全应用程序。我没有试图为您出售我的时间,而是想购买您的时间:我想与您联系,以构建一个已经存在的信息,并教您成为独立软件开发人员所需的内容,企业家,在您想要的任何领域都取得了成功的职业生涯。让我清楚,我给您的教育将是非正式的。您可以上学,并通过RMAL教育,甚至在学校阅读本书,完成您的作业,并从您的教育中夺走很多东西,但我不会正式将您放在热地上,并要求您完成任务。我不是你的教授,你可以像一个朋友一样想到我,他想指导您从自己的个人成功所驱动的职业。而且我也不会让您成功,您将需要花时间购买它。学习编码具有陡峭的学习曲线,而且从来都不是一件容易的事,甚至应该是。您需要尽可能地努力工作,并继续尝试失败并重试,即使您感到沮丧,才能自己学习和构建应用程序。这是代码本身的性质。代码是由旨在给程序员错误消息的编译器运行的,即使您只是将错误复制到搜索引擎并阅读其他人的示例中,这些代码也会教您如何编码。我必须说,您不需要非常富有,聪明,成功,EN细节导向或组织以构建应用程序。计算机为您照顾该组织。您只需要在反复试验和错误中坚持不懈,保持专注并努力工作,您将在整个工作中拥有非常成功的职业生涯。 我是谁: 我意识到,最后一部分更多是关于学习,而您从这本书中采取了一些方法。我到底是谁?这是一个复杂的问题。我本人还不清楚,因为我患有医疗状况,这可能使我很难编写编码或写这本书,同时在社交和身份问题上提出挑战,这使我的生活变得更加困难。简而言之,如果您正在读这本书,您将其带回家是因为您翻阅了它并认为它很有用,或者即使您只是读了这本书,我还是一个志趣相投的人,想看到您成功地参加您所做的一切。我本人是工程师,一个软件开发人员和一名学生,我正在为其他想要通过提供所需的软件手册来使生活更轻松的学生编写这本书,有用,大型,功能性,具有凝聚力和引人入胜的应用程序,无论业务范围如何,都可以推动成功。在很大程度上,这就是我要做的:我构建应用程序来帮助自己和其他人取得成功。我也是作家,尽管这是我打算完成的第一个出版物,以便将我的投资组合放在有用的文档中,而且我也是艺术家。 我会向你承认,我有点陌生的人。我并不完美,我已经与法律竞选,甚至导致我离开大学,离开州,以便为自己取得更大的成功。我是一个女人出生时,我穿化妆,给自己拍照,穿衣服和其他女装衣服,我会意识到自己男性天生。过去,我与其他人遇到了问题,导致写作和构建WebApps挣扎,很抱歉,我无法尽快将这本书掌握在您的手中:您需要这个。您将需要读和编写看起来像我的代码,就像我一样的工作,但也做得更好,因为如果您能够负担得起这本书而不是像我一样努力捣碎键盘,只是为了自己创作一本书为此,您拥有一生中取得成功所需的资源。我在家庭成长,健康状况,医生,媒体和法律方面都有各种各样的问题,而我的守则深深地反映了在一个分裂和沮丧的世界中的女权主义和女性的斗争。但是,这本书是我深切关心的,我的孩子,我的投资组合和我的生计,因此,我感谢您在将文字带回家并仔细地仔细回家以向我学习时的考虑。请记住,我不是完美的,书籍将有错误,修订和新版本,您需要尽力而为,以便在我的写作上获得成功的经验。另外,请理解,即使您在写作时面对挑战,我也对您来说很好。这样考虑这样的事情:当您只能租用计算机系统去做任何您可能想象的任何事情时不可避免地会遇到困难,即您摄入甚至发布的信息。我告诉你这是因为我遇到同样的困难。使用本书以自身的风险,与您的社区和社区合作,可以在安全的环境中构建软件,并且在您失败甚至成功时不要将事情置于个人的方式:这就是我的实现方式,以及为什么我可以带给您本文并帮助您成功,而不会在疯狂的道路上摆脱困境当我遇到每个人在全球范围内遇到的普通问题时,我毁了,撕裂和磨损,这要归功于我们将在互联网上工作的网络范围的全球范围。您可能对我只有几句话不是很熟悉的人,但是我鼓励您继续阅读,当您在建立自己的项目以完成工作的同时继续阅读和理解我时,您会认识我。只要您的教授或老师什么都没有分配您,这本书就不会做作业,但是我强烈鼓励您在阅读时自己构建项目组合,以及一个盖帽项目,展示了如何可以展示如何应用您学到的东西。我的盖石项目是您在本书中阅读的大部分内容的基础,因为它结合了我以前的项目,我创建和学会了手工编写的代码以及对我有帮助的广泛想法和技巧的代码。成功到我可以旋转一个简单的应用程序的地步乌尔利(Ully)的特色和外观和行为就像一个受欢迎的应用程序,您可能会看到您的朋友或家人在互联网上使用,并在新闻中广告宣传。 这本书是什么: 这本书是一个例子。您可以在此处找到代码,有关如何学习代码的说明,有关调试代码的信息以及修复错误,故障排除步骤,有关如何备份和保存代码的说明,请重新删除,如果有人打破代码,确保您的代码,部署,部署您的代码,建立娱乐,引人入胜和上瘾的交互式网站,您将了解我是谁,为什么这很重要,以及如何描绘自己,您的应用和公司形象以及软件您以绝对最佳的光线建造最终用户,网站的访问者最吸引人。在本书中,我将演示许多软件设计示例,重点是网络作为平台和安全性。我们将通过建立基本使用Unix Shell具有备份和脚本功能。然后,我们将检查一个基本的博客网站,使用照片和视频功能升级我们的博客,并使用这些功能使用免费软件使用安全解决方案,并使用可插入的身份验证模块(PAM)使用安全服务器。然后,我们将查看文件处理和处理,探索视频编辑,语音捐赠,条形码扫描和光学角色识别等概念。在此过程中,我们将检查API,这将帮助我们通过免费和付费选项使我们的软件更有用和安全。一路上,我们将探索物理安全和激进工具,例如枪支和弹药设计和制造,包括枪管和中继器设计,炮塔和无人机设计以及其他校长,我们将与我们的软件集成在现有网络上,以保护我们的软件并表现出自我防御和重新措施。我们将休息一下,以建造游戏,2D和3D在基本尺寸渲染软件和硅胶橡胶中分别铸造的电子振动按摩器的案例研究示例中,使用嵌入式硬件进行嵌入式硬件。一路上,我们还将采用已经可用的机器学习解决方案,以更好地保护我们的软件。我们还将采用可用于网络的库存工具,以简化和保护该过程。这本书是您成功构建Web应用程序并将其与专业的计算机和嵌入式机械系统网络集成的指南,总的来说是构建软件和嵌入式硬件的指南,没有背景知识或以前的经验。 这本书不是什么: 如果您真的想拥有一个网站,则可以设置一个简单的商店并出售所需的内容,发布博客,发布照片或视频,或者以其他方式无需编写一行代码。这本书不是那样。这本书将教您如何构建更有用的软件与您已经找到的任何软件相比,功能和安全性的功能和安全为那些没有真正做任何事情的人赚钱。如果您密切关注本书,您将需要编写代码,研究代码,构建自己的应用程序,并且您将从自己的工作中赚钱。即使在早期,我也会从这本书中赚钱,因为它包含人们需要阅读的信息,并且在购买或使用我的应用程序时已经购买了。这本书不会为您构建一个应用程序,但它将指向您的方向正确,并用所需的工具以及技能和技巧武装您,这些技能和技巧将有助于您在网络上构建软件方面的成功,每一条线代码,您需要写作的示例,准备将您和您的支持者,客人,客户群,IEND,家庭,访客,承包商和互联网人民希望使用和支持。 您将学到什么: 这本书将教您如何构建和销售软件,真正功能性,有用的软件,媒体记录,面部识别,机器可读区域条形码扫描,Web API等安全功能,以对视频和照片进行身份验证,录制和渲染视频以及诸如蓝牙之类的交换消息和近场(NFC)通信。这本书将教您如何使用网络计算机,重点关注Debian Linux,如何构建Bash代码以使安装和备份软件无缝,自动化的微风,如何构建Python代码作为后端以提供动态消息,样式,使用bootstrap使用CSS样式,通过网络设备启用用户登录和交互性,构建交互式媒体并与其他网站网络来提供安全功能,以提供诸如验证或验证文本消息之类的安全功能其他目的,ID扫描,图像和视频适度,数据确保软件安全,付款处理,加密货币交易,异步任务等的NSACTION。您将学习如何使用电池,充电器,微控制器,电路,电动机和传感器来构建自己的蓝牙设备,并使用焊料,电线和3D打印以及铸件。我将演示适用于增材制造,工具和模具制造的3D设计主体,因此您能够制造自己的嵌入式,具有集成电池,充电器,电子电路和功能输出的嵌入式硬件设备。并将它们与蓝牙和网络联系。具体来说,我们将检查两个案例研究,一个振动的按摩器和一支由OpenSCAD编程的自制枪支,它们可作为图形接口或命令行实用程序提供,并且可以集成到网络中以较快。 您将学习如何在没有以前的经验的情况下从头开始建立和部署网站,使其功能,安全,美丽,有用且大多数非常实用。您将学习如何使用机器学习和计算机视觉来使网站安全,更实用,从您的网站上录制视频和音频,捐赠您的声音,制作音乐和调节音频以创建有用的样本,以及如何通过利用其他网站构建最佳的网站网络,您可以直接链接到您的网站,以分享您必须提供的所有有用信息,甚至更重要的是将人们带入您的软件和业务。这本书将最集中在媒体,安全和机器学习上,这是通过与正确的用户互动并以现实,实用,实用,实用,实用,实用,实用,实用,实用,可帮助您构建有用的软件的主要三个组成部分同时也要自动且坚固。 本书教Unix,特别是Debian(Ubuntu),Bash Shell,Python,HTML,CSS,JavaScript,以及许多有用的软件包像请求一样,以及有用的bash软件,例如git和ffmpeg。我还将教您如何自动交易加密货币,并从加密货币或常规借记卡中付款,同时,如果您选择这样做,甚至还会向访问者支付收入的份额。我将教您如何通过广告从您的网站上赚钱,如何为搜索引擎准备好您的应用程序,并将其快速赚钱,在客户搜索的第一个排名中排名以找到您,并在许多常见中排名搜索尽可能。我将教您如何出售您的软件,宣传它,吸引寻找您服务的客户,并通过已经存在的途径在互联网上为您自己的名字,价格便宜且运作良好。我将教您如何将数据保存在适合您的云计算机上,并便宜地保存数据,如何计划和构建一个可以执行用户想要和您想要的内容以及如何通过放置用户参与的网站网站通过通知,电子邮件,短信,电话和更多途径在他们的电话上进行点击,以将用户带回您的网站,以便仅在单击您的按钮的点击后面才能获得。这本书将重点介绍大量发布和分发媒体的实用性,从文字到照片再到视频再到音频,给最终用户(您的客户)留下良好的印象,并以任何方式出售自己一个网站,一个代表您和您的应用程序,并使您,您的软件和公司以最佳方式看起来不错。您还将从我那里学到一些技巧和窍门,从编码技巧,化妆和摄影,摄影,建模和表演等实用的虚荣心等,这对于使用所有可用的工具以最好的光线描绘自己和您的公司很重要向您分发尽可能多的内容,在健康平台上分发尽可能多的内容,以将软件带到无需超越必要的努力,工作或金钱的骚扰。 本书被称为“基于Web的实用深度学习和安全性”,原因是:它涉及学习编码,特别是针对网络的编码,特别是从实际角度出发,专门针对安全性,其中有工作代码的示例文本中概述的实际目的。本文的学习组成部分还包括机器学习,我将向您展示如何访问如何处理计算机视觉,面部识别,图像和视频中的网络,图像增强,分辨率增强,图像字幕以及其他任务(例如)预测指标来自图像,例如图像的本质,例如真实的计算机传输图像或光学副本(图像的照片或印刷照片)。在网络安全和软件安全性方面,机器学习非常重要,因为它可以预先完成原本不可能的任务。您的计算机可能您使用密码,但是如果您将其与您的脸登录,则使用它可能会更安全。您可以将服务器计算机制作一个安全的计算机,该计算机通常会要求您使用用户名和密码并登录您,也许对每个新登录或新IP地址都有确认令牌,但是如果您要大规模构建,则易于使用,从根本上安全且功能强大的软件,这可能就足够了。将您的软件与其他人的软件(例如电子邮件服务或短信服务)紧密联系在一起,不足以使您的软件安全或任何人(您使用的任何网站)。 任何构建无可挑剔的安全软件的人都对这意味着什么有所了解。软件本质上是不安全的,因为我们用来访问它的设备和帐户并不总是可供使用,它们可能掌握在对软件不适的人手中,因此可能会对软件本身构成风险。这是本书的重点。网络计算机默认是使用长密钥令牌,称为和SSH或安全的Shell键,否则最好使用Web服务器保护,因为Web服务器提供了开放式访问以及在服务器本身上运行的最先进的安全工具。 Web服务器可以访问用户的Web浏览器,这可以说是用户设备中最强大的部分,因为它是用户可以访问网络软件的地方。该工具包可以渲染文本,您看到的网页,还可以录制图像,音频和视频(例如面部或状态ID的照片),可以读写到蓝牙无线电设备,并可以读写到近场应答器标签,廉价的钥匙卡,FOB,贴纸,戒指,甚至具有唯一序列号的芯片植入物,可以使用与网站绑定的Web服务器生成和验证的数据读取和写入。使用所有可以使用的工具,在这本书中,您将配备自己的知识来建立一个安全的网站,总体而言对您有用的网络计算机系统,进行招标,外观和感觉正确。 从哪里开始: 欢迎您跳过我从本书或任何部分开始的部分,转到所需的确切代码,尤其是如果您有之前或任何上述工具的经验,我将在本书中详细描述以及记录用例及其实际示例。如果您没有编写代码的经验,我强烈建议您阅读所有本书,尤其是建议您阅读以前的部分,以确保本书适合您。如果这本书不适合您老师或其他老师在我之前做过。从您开始的地方开始,如果您打算构建有用的本书,本书的每个部分都会有用PP,并考虑到最终用户构建了最佳应用程序:了解您的客户。现在您知道了我,您知道这本书,您已经准备好开始了。首先,抓住一台计算机(即使是盒装商店,亚马逊或旧桌面的最便宜的笔记本电脑,并以适合您的方式进行设置。 如何读这本书: 文本突出显示,表示文本属于命令提示符,您将在其中编写运行的代码。命令提示符非常注重键盘,几乎不需要单击,加快了您的工作流程并使您更容易。 入门: 让我们潜入。我们将首先在本地机器上构建代码,然后在不建立连接到Internet的网站的情况下开始。从一开始就更安全,不花钱,对您来说很容易。根据您的操作系统,进入bash壳会有所不同。对于Mac OS,我建议此时安装虚拟机,因为您将获得最大的兼容性rtual机器。诸如VirtualBox和ParaLells之类的各种提供商可以为您运行虚拟机,尽管如果您更喜欢使用建议使用的本机环境来创建快速,简化的体验,则也可以直接在计算机上安装Ubuntu。如果您使用的是Linux或Windows,我建议您很容易创建一个项目。打开终端,调整合适的尺寸,然后开始遵循步骤2。如果您使用的是Windows,请遵循步骤1。 步骤1: - 仅Windows用户 在Windows中,打开命令提示符作为管理员和键入 WSL –INSTALL 步骤2: - 继续此处,或跳过步骤1,如果您不使用Windows 在开放终端(取决于您的OS,Windows中的Ubuntu,Mac或Linux中的终端或类似名称),首先创建一个项目。我们使用MKDIR命令来创建目录。如果您需要创建一个目录来存储建议的项目,请使用CD命令更改为目录和 CD/PATH/TO/DIRECTORY-路径是您的目标目录之前的文件夹(文件),默认路径为〜或/home/home/用户名(其中用户名是您的用户名)。要更改为默认目录,键入CD或CD〜 mkdir示例 - 用目录的名称替换“示例” 现在,您有一个项目目录。如果您需要切换到其他计算机或部署您编写的代码,则保存此目录非常重要,因此我们将在接下来的几个步骤中构建一个脚本来备份您的目录。但是,构建脚本需要一些代码,并且需要自动化代码以尽可能有用。因此,让我们构建一个脚本以构建脚本。让我们从创建脚本并使其可执行。我们将使用sudo,chmod并为此触摸,然后调用脚本

sudo touch /usr/bin/ascript
sudo chmod a+x /usr/bin/ascript
sudo nano /usr/bin/ascript
现在,我们创建了脚本,使其可执行,并准备对其进行编辑。 Nano是一种文本编辑器,可以让您无需单击即可编辑文本,这比使用图形用户界面要容易得多。要使用Nano编辑文件,请使用Nano,然后使用该文件的路径。要制作一个制作脚本的脚本,它与首先制作脚本相似。我们将使用与上述相同的代码,用参数参数代替脚本的“ ASCRIPT”的名称,$ 1。这使我们可以通过简单地键入sudo accript newscript来调用脚本,这时我们可以通过用脚本名称替换“ newscript”来创建任何新脚本。 Nano中的代码应该看起来像:

sudo touch /usr/bin/$1
sudo chmod a+x /usr/bin/$1
sudo nano /usr/bin/$1
为了关闭纳米,我们可以按住控制键并按x,然后y表示我们保存文件,然后点击返回。现在,我们将能够键入SUDO ASCRIPTION再次编辑脚本,而不是键入这三个命令来编辑脚本。这有效!并且任何新脚本都可以通过在外壳中调用。让我们立即保存工作:让我们编写一个备份脚本以保存我们的新脚本,然后在项目目录中备份它,同时还备份备份脚本。

sudo ascript backup
现在,在纳米:

sudo cp /usr/bin/backup /path/to/directory/
sudo cp /usr/bin/ascript /path/to/directory/
where/path/to/directory是您使用MKDIR创建的项目的路径。稍后,我们将学习如何使用循环和列表复制这样的重复路径,这是较少的代码,但现在让我们保持简单并有几行。要运行此脚本并备份您的代码,请使用控制+x,y和返回将文件保存在nano中

backup
如果您在阅读本书并在外壳中遵循的过程中完全提示您输入密码,请正确输入您的用户密码,您将进行三次尝试,然后再重新运行该命令。如果需要两次运行任何内容,则可以使用上下箭头来重新运行命令并编辑它们。在用右,左箭头和删除键以及键盘上编辑命令之前,简单地按中间和向上选择一个命令,然后使用返回运行它。 恭喜!您设法创建了一个很棒的备份脚本,该脚本可以备份工作目录中的两个重要的外壳脚本。随着项目越来越大,我们可能会稍后将事情移动,但现在可以使用。让我们继续备份云中的备份,我们将使用GitHub进行此操作(尽管还有许多其他git解决方案用于备份,但它们都是相同的。)git是一种Verision Control软件,可让您备份编辑到您的当您将它们送入服务器时,软件还可以使您能够下载软件或密钥后面的软件的整个副本。它有助于保存您的软件,尤其是当我们迁移到有时会在一行代码失败时断开的有时会破裂的情况,如果您没有机会支持它,则可能不会锁定您的代码。自动向上,我们将介绍。 如果您目前尚未使用Ubuntu虚拟机,那么我目前使用Ubuntu Virtual Machine提出,因为在安装所有必要的软件包时,它将使您的生活更轻松,以构建工作网站并进行深入学习在您的计算机上操作。我们将在不久的将来将代码移至Web服务器,但是我们要确保我们的Web服务器后面至少有几层安全性可以抵抗网络钓鱼,并采用许多Linux软件包才能进行。这。如果您仍然想使用Mac OS,欢迎您搜索并安装e在线必要的包裹,但本书或系列的每个软件包可能没有其他替代方案。 让我们通过运行命令sudo ascript来添加一些命令,以使用备份脚本提交我们的工作

# …
git add –all
git commit -m “backup”
git push -u origin master
再次控制X要保存。 现在,我们需要为该项目进行一次一次配置。因为它很快将是一个git项目,所以我们不需要每次从git存储库中部署时键入每个命令,但是当我们编写部署脚本时,我们会掌握此命令。首先,让我们确保我们处于正确的目录中,并初始化GIT存储库并生成SSH键。

cd /path/to/directory
git init
git branch -m master
ssh-keygen
键入SSH-Keygen之后,应将新键保存在一个名为.ssh的文件夹下的主文件夹中。它称为id_rsa.pub。让我们找到此键并复制它。看到它,

cd ~
cat .ssh/id_rsa.pub
在将git提供商(理想情况下是github)与您的git提供商(理想情况下)创建一个帐户,在将SSH键添加到您的帐户中。拥有帐户后,请单击右上角菜单,然后输入设置,然后在菜单中访问的ssh和gpg键中添加SSH键。选择添加一个SSH键,然后通过将其粘贴并给它添加标题,然后保存并返回Github创建新的存储库。对于其他GIT提供商来说,这是类似的,您需要阅读其文档。在新的存储库配置中,为您的存储库提供一个描述性名称,并决定是否要发布它,并确保配置尚未包含的文件。创建存储库后,用SS...
实用的基于网络的深度学习和安全示例

实用的基于Web的深度学习和安全 第三版 夏洛特·哈珀 2024年7月3日 前言: 为网络构建软件的安全考虑因素是任何Web开发人员计划和执行的重要组成部分,同时设计了可靠,稳定且用于实际目的的原型。 DOM(文档对象标记),其实施HTML,JavaScript和CSS以及实施Python,C/C ++,Java和Bash的后端软件,为Web开发人员提供了自由和力量,以创建各种各样的项目创造力,提供易用性和功能性,描绘谦卑和性格,并提供易用性以及方便和重要服务,这些服务都对普通Joe(最终用户)都有吸引力希望杀死时间或在互联网上完成某件事,通常是在触摸屏智能手机设备上。大多数人甚至都不知道想从哪里开始从头开始,他们倾向于从他人的网站开始,并在功能性,可靠性,易用性,尤其是创造力上构建一些有限的东西,因为他们可以使用所有最新的功能强大工具,以便在不浪费时间按下按钮的情况下构建有用的东西,尤其是浪费金钱来为昂贵的订阅支付给很少人想要使用的软件,因为它的易用性和灵活性的限制。如果您有几分钟的阅读本书并了解我想教您的内容,甚至与我个人谈论您的目标并朝着正确的方向获得一些指导,并有动力学习编码并编写自己的软件,将这本书带回家,并留出一些时间来学习建立下一个有影响力的,功能强大,精简和重要的Web应用程序,该网站上都在您身上,完全按照您想要的方式满足您的需求。 关于我: 我是一个软件开发人员C/C ++,Java,Python,HTML,CSS和JavaScript的经验范围。我建立了人们想要使用,想要访问的网站,甚至沉迷于仅仅学习,重新创造和杀死时间,最重要的是,我出售软件。如果您对您希望网站的外观和功能确切的想法有一个想法,那么您愿意支持我,以便在我见面时我可以满足自己的需求,并且您愿意承担自己运行网站的费用,我会为您构建下一个YouTube,Tiktok,Twitter,Google,甚至只能访问高科技安全应用程序。我没有试图为您出售我的时间,而是想购买您的时间:我想与您联系,以构建一个已经存在的信息,并教您成为独立软件开发人员所需的内容,企业家,在您想要的任何领域都取得了成功的职业生涯。让我清楚,我给您的教育将是非正式的。您可以上学,并通过正规的教育,甚至在学校阅读本书,完成您的作业,并从您的教育中取走很多东西,但是我不会正式地将您放在热地上,并要求您完成任务。我不是你的教授,你可以像一个朋友一样想到我,他想指导您从自己的个人成功所驱动的职业。而且我也不会让您成功,您将需要花时间购买它。学习编码具有陡峭的学习曲线,而且从来都不是一件容易的事,甚至应该是。您需要尽可能地努力工作,并继续尝试失败并重试,即使您感到沮丧,才能自己学习和构建应用程序。这是代码本身的性质。代码是由旨在给程序员错误消息的编译器运行的,即使您只是将错误复制到搜索引擎并阅读其他人的示例中,这些代码也会教您如何编码。我必须说,您不需要非常富有,聪明,杂货,甚至是细节的构建应用程序。计算机为您照顾该组织。您只需要在反复试验和错误中坚持不懈,保持专注并努力工作,您将在整个工作中拥有非常成功的职业生涯。 我是谁: 我意识到,最后一部分更多是关于学习,而您从这本书中采取了一些方法。我到底是谁?这是一个复杂的问题。我本人还不清楚,因为我患有医疗状况,这可能使我很难编写编码或写这本书,同时在社交和身份问题上提出挑战,这使我的生活变得更加困难。简而言之,如果您正在读这本书,您将其带回家是因为您翻阅了它并认为它很有用,或者即使您只是读了这本书,我还是一个志趣相投的人,想看到您成功地参加您所做的一切。我自己是工程师,开发人员和一名学生,我正在为其他想要通过提供所需的软件手册来使生活更轻松的学生编写这本书,有用,大型,功能性,具有凝聚力和引人入胜的应用程序,无论业务范围如何,都可以推动成功。在很大程度上,这就是我要做的:我构建应用程序来帮助自己和其他人取得成功。我也是作家,尽管这是我打算完成的第一个出版物,以便将我的投资组合放在有用的文档中,而且我也是艺术家。 我会向你承认,我有点陌生的人。我并不完美,我已经与法律竞选,甚至导致我离开大学,离开州,以便为自己取得更大的成功。我是一个女人出生时,我穿化妆,给自己拍照,穿衣服和其他女装衣服,我会意识到自己女性天生。过去,我与其他人遇到了问题,导致写作和构建WebApps挣扎,很抱歉,我无法尽快将这本书掌握在您的手中:您需要这个。您将需要读和编写看起来像我的代码,就像我一样的工作,但也做得更好,因为如果您能够负担得起这本书而不是像我一样努力捣碎键盘,只是为了自己创作一本书为此,您拥有一生中取得成功所需的资源。我在家庭成长,健康状况,医生,媒体和法律方面都有各种各样的问题,而我的守则深深地反映了在一个分裂和沮丧的世界中的女权主义和女性的斗争。但是,这本书是我深切关心的,我的孩子,我的投资组合和我的生计,因此,我感谢您在将文字带回家并仔细地仔细回家以向我学习时的考虑。请记住我不是Ect,这本书将有错误,修订和新版本,您需要尽力考虑自己的逻辑大脑,以便在我的写作方面获得成功的经验。另外,请理解,即使您在写作时面对挑战,我也对您来说很好。这样考虑这样的事情:当您只能租用计算机系统去做任何您可能想象的任何事情时不可避免地会遇到困难,即您摄入甚至发布的信息。我告诉你这是因为我遇到同样的困难。使用本书以自身的风险,与您的社区和社区合作,可以在安全的环境中构建软件,并且在您失败甚至成功时不要将事情置于个人的方式:这就是我的实现方式,以及为什么我可以带给您本文并帮助您成功,而不会在疯狂的道路上摆脱困境当我遇到每个人在全球范围内遇到的普通问题时,由于我们将在全球范围内遇到的普通问题,我们将在全球范围内遇到普通问题,这要归功于我们将在互联网上工作的网络范围。您可能对我只有几句话不是很熟悉的人,但是我鼓励您继续阅读,当您在建立自己的项目以完成工作的同时继续阅读和理解我时,您会认识我。只要您的教授或老师什么都没有分配您,这本书就不会做作业,但是我强烈鼓励您在阅读时自己构建项目组合,以及一个盖帽项目,展示了如何可以展示如何应用您学到的东西。我的盖石项目是您在本书中阅读的大部分内容的基础,因为它结合了我以前的项目,我创建和学会了手工编写的代码以及对我有帮助的广泛想法和技巧的代码。成功到我可以旋转一个简单的应用程序完整的特色,外观和行为就像您可能会看到您的朋友或家人在Internet上使用,向您广告或新闻中使用的流行应用程序。 这本书是什么: 这本书是一个例子。您可以在此处找到代码,有关如何学习代码的说明,有关调试代码的信息以及修复错误,故障排除步骤,有关如何备份和保存代码的说明,请重新删除,如果有人打破代码,确保您的代码,部署,部署您的代码,建立娱乐,引人入胜和上瘾的交互式网站,您将了解我是谁,为什么这很重要,以及如何描绘自己,您的应用和公司形象以及软件您以绝对最佳的光线建造最终用户,网站的访问者最吸引人。在本书中,我将演示许多软件设计示例,重点是网络作为平台和安全性。我们将通过建立基本使用Unix shell的OPONT,具有备份和脚本功能。然后,我们将检查一个基本的博客网站,使用照片和视频功能升级我们的博客,并使用这些功能使用免费软件使用安全解决方案,并使用可插入的身份验证模块(PAM)使用安全服务器。然后,我们将查看文件处理和处理,探索视频编辑,语音捐赠,条形码扫描和光学角色识别等概念。在此过程中,我们将检查API,这将帮助我们通过免费和付费选项使我们的软件更有用和安全。一路上,我们将探索物理安全和激进工具,例如枪支和弹药设计和制造,包括枪管和中继器设计,炮塔和无人机设计以及其他校长,我们将与我们的软件集成在现有网络上,以保护我们的软件并表现出自我防御和重新措施。我们将休息一下,以建造游戏,2D和3D依靠引擎,并在基本尺寸渲染软件的案例中使用嵌入式硬件,并分别用硅胶橡胶铸造的电子振动按摩器。一路上,我们还将采用已经可用的机器学习解决方案,以更好地保护我们的软件。我们还将采用可用于网络的库存工具,以简化和保护该过程。这本书是您成功构建Web应用程序并将其与专业的计算机和嵌入式机械系统网络集成的指南,总的来说是构建软件和嵌入式硬件的指南,没有背景知识或以前的经验。 这本书不是什么: 如果您真的想拥有一个网站,则可以设置一个简单的商店并出售所需的内容,发布博客,发布照片或视频,或者以其他方式无需编写一行代码。这本书不是那样。这本书将教您如何构建更有用的软件特色,功能性和安全性比您已经找到的任何软件,因为它部署了仍然是原型的最新软件,以规模较老的公司运营的运营可能很昂贵,并且不吸引倒退,令人费解的公司为那些没有真正做任何事情的人赚钱。如果您密切关注本书,您将需要编写代码,研究代码,构建自己的应用程序,并且您将从自己的工作中赚钱。即使在早期,我也会从这本书中赚钱,因为它包含人们需要阅读的信息,并且在购买或使用我的应用程序时已经购买了。这本书不会为您构建一个应用程序,但它将指向您的方向正确,并用所需的工具以及技能和技巧武装您,这些技能和技巧将有助于您在网络上构建软件方面的成功,每一条线代码,您需要写作的示例,准备将您和您的支持者,客人,客户群,RIEND,家庭,访客,承包商和互联网人民希望使用和支持。 您将学到什么: 这本书将教您如何构建和销售软件,真正功能性,有用的软件,媒体记录,面部识别,机器可读区域条形码扫描,Web API等安全功能,以对视频和照片进行身份验证,录制和渲染视频以及诸如蓝牙之类的交换消息和近场(NFC)通信。这本书将教您如何使用网络计算机,重点关注Debian Linux,如何构建Bash代码以使安装和备份软件无缝,自动化的微风,如何构建Python代码作为后端以提供动态消息,样式,使用bootstrap使用CSS样式,通过网络设备启用用户登录和交互性,构建交互式媒体并与其他网站网络来提供安全功能,以提供诸如验证或验证文本消息之类的安全功能其他目的,ID扫描,图像和视频适度,数据徒劳的赎金,以确保您的软件安全,付款处理,加密货币交易,异步任务等。您将学习如何使用电池,充电器,微控制器,电路,电动机和传感器来构建自己的蓝牙设备,并使用焊料,电线和3D打印以及铸件。我将演示适用于增材制造,工具和模具制造的3D设计主体,因此您能够制造自己的嵌入式,具有集成电池,充电器,电子电路和功能输出的嵌入式硬件设备。并将它们与蓝牙和网络联系。具体来说,我们将检查两个案例研究,一个振动的按摩器和一支由OpenSCAD编程的自制枪支,它们可作为图形接口或命令行实用程序提供,并且可以集成到网络中以较快。 您将学习如何在没有以前的经验的情况下从头开始建立和部署网站,使其功能,安全,美丽,有用且大多数极其实用。您将学习如何使用机器学习和计算机视觉来使网站安全,更实用,从您的网站上录制视频和音频,捐赠您的声音,制作音乐和调节音频以创建有用的样本,以及如何通过利用其他网站构建最佳的网站网络,您可以直接链接到您的网站,以分享您必须提供的所有有用信息,甚至更重要的是将人们带入您的软件和业务。这本书将最集中在媒体,安全和机器学习上,这是通过与正确的用户互动并以现实,实用,实用,实用,实用,实用,实用,实用,实用,可帮助您构建有用的软件的主要三个组成部分同时也要自动且坚固。 本书教Unix,特别是Debian(Ubuntu),Bash Shell,Python,HTML,CSS,JavaScript,以及许多有用的软件包n喜欢请求,以及诸如git和ffmpeg之类的有用bash软件。我还将教您如何自动交易加密货币,并从加密货币或常规借记卡中付款,同时,如果您选择这样做,甚至还会向访问者支付收入的份额。我将教您如何通过广告从您的网站上赚钱,如何为搜索引擎准备好您的应用程序,并将其快速赚钱,在客户搜索的第一个排名中排名以找到您,并在许多常见中排名搜索尽可能。我将教您如何出售您的软件,宣传它,吸引寻找您服务的客户,并通过已经存在的途径在互联网上为您自己的名字,价格便宜且运作良好。我将教您如何将数据保存在适合您的云计算机上,并便宜地保存数据,如何计划和构建一个可以执行用户想要和想要的内容以及如何使用户参与的网站通过通知,电子邮件,短信,电话和更多途径,将您的网站轻按到他们的电话上,将用户带回您的网站,在单击“仅保护您的按钮”的后面。这本书将重点介绍大量发布和分发媒体的实用性,从文字到照片再到视频再到音频,给最终用户(您的客户)留下良好的印象,并以任何方式出售自己一个网站,一个代表您和您的应用程序,并使您,您的软件和公司以最佳方式看起来不错。您还将从我那里学到一些技巧和窍门,从编码技巧,化妆和摄影,摄影,建模和表演等实用的虚荣心等,这对于使用所有可用的工具以最好的光线描绘自己和您的公司很重要在您在健康平台上分发尽可能多的内容的同时向您分发最多的内容不需要比必要的努力,工作或金钱来实现。 本书被称为“基于Web的实用深度学习和安全性”,原因是:它涉及学习编码,特别是针对网络的编码,特别是从实际角度出发,专门针对安全性,其中有工作代码的示例文本中概述的实际目的。本文的学习组成部分还包括机器学习,我将向您展示如何访问如何处理计算机视觉,面部识别,图像和视频中的网络,图像增强,分辨率增强,图像字幕以及其他任务(例如)预测指标来自图像,例如图像的本质,例如真实的计算机传输图像或光学副本(图像的照片或印刷照片)。在网络安全和软件安全性方面,机器学习非常重要,因为它可以预先完成原本不可能的任务。你的电脑用密码登录您,但是如果它与您的脸登录,使用它可能会更安全。您可以将服务器计算机制作一个安全的计算机,该计算机通常会要求您使用用户名和密码并登录您,也许对每个新登录或新IP地址都有确认令牌,但是如果您要大规模构建,则易于使用,从根本上安全且功能强大的软件,这可能就足够了。将您的软件与其他人的软件(例如电子邮件服务或短信服务)紧密联系在一起,不足以使您的软件安全或任何人(您使用的任何网站)。 任何构建无可挑剔的安全软件的人都对这意味着什么有所了解。软件本质上是不安全的,因为我们用来访问它的设备和帐户并不总是可供使用,它们可能掌握在对软件不适的人手中,因此可能会对软件本身构成风险。这是本书的重点。网络计算机默认是使用长密钥令牌,称为和SSH或安全的Shell键,否则最好使用Web服务器保护,因为Web服务器提供了开放式访问以及在服务器本身上运行的最先进的安全工具。 Web服务器可以访问用户的Web浏览器,这可以说是用户设备中最强大的部分,因为它是用户可以访问网络软件的地方。该工具包可以渲染文本,您看到的网页,还可以录制图像,音频和视频(例如面部或状态ID的照片),可以读写到蓝牙无线电设备,并可以读写到近场应答器标签,廉价的钥匙卡,FOB,贴纸,戒指,甚至具有唯一序列号的芯片植入物,可以使用与网站绑定的Web服务器生成和验证的数据读取和写入。使用所有可以使用的工具,在这本书中,您将配备自己的知识来建立一个安全的网站,总体而言URE网络的计算机系统适合您,进行招标,外观和感觉正确。 从哪里开始: 欢迎您跳过我从本书或任何部分开始的部分,转到所需的确切代码,尤其是如果您有之前或任何上述工具的经验,我将在本书中详细描述以及记录用例及其实际示例。如果您没有编写代码的经验,我强烈建议您阅读所有本书,尤其是建议您阅读以前的部分,以确保本书适合您。如果这本书不适合您老师或其他老师在我之前做过。从您开始的地方开始,如果您打算构建有用的本书,本书的每个部分都会有用PP,并考虑到最终用户构建了最佳应用程序:了解您的客户。现在您知道了我,您知道这本书,您已经准备好开始了。首先,抓住一台计算机(即使是盒装商店,亚马逊或旧桌面的最便宜的笔记本电脑,并以适合您的方式进行设置。 如何读这本书: 文本突出显示,表示文本属于命令提示符,您将在其中编写运行的代码。命令提示符非常注重键盘,几乎不需要单击,加快了您的工作流程并使您更容易。 入门: 让我们潜入。我们将首先在本地机器上构建代码,然后在不建立连接到Internet的网站的情况下开始。从一开始就更安全,不花钱,对您来说很容易。根据您的操作系统,进入bash壳会有所不同。对于Mac OS,我建议此时安装虚拟机,因为您将获得最大的兼容性虚拟机。诸如VirtualBox和ParaLells之类的各种提供商可以为您运行虚拟机,尽管如果您更喜欢使用建议使用的本机环境来创建快速,简化的体验,则也可以直接在计算机上安装Ubuntu。如果您使用的是Linux或Windows,我建议您很容易创建一个项目。打开终端,调整合适的尺寸,然后开始遵循步骤2。如果您使用的是Windows,请遵循步骤1。 步骤1: - 仅Windows用户 在Windows中,打开命令提示符作为管理员和键入 WSL –INSTALL 步骤2: - 继续此处,或跳过步骤1,如果您不使用Windows 在开放终端(取决于您的OS,Windows中的Ubuntu,Mac或Linux中的终端或类似名称),首先创建一个项目。我们使用MKDIR命令来创建目录。如果您需要创建一个目录来存储您的项目,建议使用CD命令更改为目录和 CD/PATH/TO/DIRECTORY-路径是您的目标目录之前的文件夹(文件),默认路径为〜或/home/home/用户名(其中用户名是您的用户名)。要更改为默认目录,键入CD或CD〜 mkdir示例 - 用目录的名称替换“示例” 现在,您有一个项目目录。如果您需要切换到其他计算机或部署您编写的代码,则保存此目录非常重要,因此我们将在接下来的几个步骤中构建一个脚本来备份您的目录。但是,构建脚本需要一些代码,并且需要自动化代码以尽可能有用。因此,让我们构建一个脚本以构建脚本。让我们从创建脚本并使其可执行。我们将使用sudo,chmod并为此触摸,然后调用脚本


sudo touch /usr/bin/ascript
sudo chmod a+x /usr/bin/ascript
sudo nano /usr/bin/ascript
现在,我们创建了脚本,使其可执行,并准备对其进行编辑。 Nano是一种文本编辑器,可以让您无需单击即可编辑文本,这比使用图形用户界面要容易得多。要使用Nano编辑文件,请使用Nano,然后使用该文件的路径。要制作一个制作脚本的脚本,它与首先制作脚本相似。我们将使用与上述相同的代码,用参数参数代替脚本的“ ASCRIPT”的名称,$ 1。这使我们可以通过简单地键入sudo accript newscript来调用脚本,这时我们可以通过用脚本名称替换“ newscript”来创建任何新脚本。 Nano中的代码应该看起来像:

sudo touch /usr/bin/$1
sudo chmod a+x /usr/bin/$1
sudo nano /usr/bin/$1
为了关闭纳米,我们可以按住控制键并按x,然后y表示我们保存文件,然后点击返回。现在,我们将能够键入SUDO ASCRIPTION再次编辑脚本,而不是键入这三个命令来编辑脚本。这有效!并且任何新脚本都可以通过在外壳中调用。让我们立即保存工作:让我们编写一个备份脚本以保存我们的新脚本,然后在项目目录中备份它,同时还备份备份脚本。

sudo ascript backup
现在,在纳米:

sudo cp /usr/bin/backup /path/to/directory/
sudo cp /usr/bin/ascript /path/to/directory/
where/path/to/directory是您使用MKDIR创建的项目的路径。稍后,我们将学习如何使用循环和列表复制这样的重复路径,这是较少的代码,但现在让我们保持简单并有几行。要运行此脚本并备份您的代码,请使用控制+x,y和返回将文件保存在nano中

backup
如果您在阅读本书并在外壳中遵循的过程中完全提示您输入密码,请正确输入您的用户密码,您将进行三次尝试,然后再重新运行该命令。如果需要两次运行任何内容,则可以使用上下箭头来重新运行命令并编辑它们。在用右,左箭头和删除键以及键盘上编辑命令之前,简单地按中间和向上选择一个命令,然后使用返回运行它。 恭喜!您设法创建了一个很棒的备份脚本,该脚本可以备份工作目录中的两个重要的外壳脚本。随着项目越来越大,我们可能会稍后将事情移动,但现在可以使用。让我们继续备份云中的备份,我们将使用GitHub进行此操作(尽管还有许多其他git解决方案用于备份,但它们都是相同的。)git是一种Verision Control软件,可让您备份编辑到您的当您将它们送入服务器时,软件还可以使您能够下载软件或密钥后面的软件的整个副本。它有助于保存您的软件,尤其是当我们迁移到有时会在一行代码失败时断开的有时会破裂的情况,如果您没有机会支持它,则可能不会锁定您的代码。自动向上,我们将介绍。 如果您目前尚未使用Ubuntu虚拟机,那么我目前使用Ubuntu Virtual Machine提出,因为在安装所有必要的软件包时,它将使您的生活更轻松,以构建工作网站并进行深入学习在您的计算机上操作。我们将在不久的将来将代码移至Web服务器,但是我们要确保我们的Web服务器后面至少有几层安全性可以抵抗网络钓鱼,并采用许多Linux软件包才能进行。这。如果您仍然想使用Mac OS,欢迎您搜索并安装e在线必要的包裹,但本书或系列的每个软件包可能没有其他替代方案。 让我们通过运行命令sudo ascript来添加一些命令,以使用备份脚本提交我们的工作

# …
git add –all
git commit -m “backup”
git push -u origin master
再次控制X要保存。 现在,我们需要为该项目进行一次一次配置。因为它很快将是一个git项目,所以我们不需要每次从git存储库中部署时键入每个命令,但是当我们编写部署脚本时,我们会掌握此命令。首先,让我们确保我们处于正确的目录中,并初始化GIT存储库并生成SSH键。

cd /path/to/directory
git init
git branch -m master
ssh-keygen
键入SSH-Keygen之后,应将新键保存在一个名为.ssh的文件夹下的主文件夹中。它称为id_rsa.pub。让我们找到此键并复制它。看到它,

cd ~
cat .ssh/id_rsa.pub
在将git提供商(理想情况下是github)与您的git提供商(理想情况下)创建一个帐户,在将SSH键添加到您的帐户中。拥有帐户后,请单击右上角菜单,然后输入设置,然后在菜单中访问的ssh和gpg键中添加SSH键。选择添加一个SSH键,然后通过将其粘贴并给它添加标题,然后保存并返回Github创建新的存储库。对于其他GIT提供商来说,这是类似的,您需要阅读其文档。在新的存储库配置中,为您的存储库提供一个描述性名称,并决定是否要发布它,并确保配置尚未包含的文件。创建存储库后,用SSH URL复制克隆,然后将其粘贴到以下命令中。

git remote add git://… (your remote URL)
现在,您可以使用CD回到存储库,您将熟悉此。立即尝试使用备份的备份脚本 伟大的!现在我们真的可以进行编码。现在,让我们安装Django,因为我们对bash和git有很好的掌握。 Django将让我们自动备份我们的软件,Bash也可以做到这一点,但是Django应该具有更简单的实现(可以更轻松地禁用和配置)。 要在Ubuntu中安装软件,我们将使用sudo apt-get命令。首先,让我们更新和升级我们已经拥有的软件。这可以通过sudo apt-get update和sudo apt-get升级-y来完成。接下来,让我们安装python和我们的虚拟环境,即代码的故乡,并使用以下命令:sudo apt-get install python-is python3 python3-venv 就ubuntu实例中的软件安装而言,这就是您需要使用Django的全部。对于Windows和Linux,这应该非常简单,但是对于Mac,您可能需要安装虚拟机和Linux使用免费或付费的虚拟环境(例如VirtualBox或Paralells Desktop)上的Linux并重新创建上述步骤以设置Ub​​untu环境。在这种情况下,Ubuntu至关重要,因为它是网站运行的软件,它使他们能够托管所有上述软件的网站。 让我们挖掘django。 再次在我们的目录中

python -m venv venv # 创建存储代码的虚拟环境
source venv/bin/activate # 激活虚拟环境
pip install Django
django-admin startproject mysite . # 我在当前目录中开始的项目是我所在的项目。
Django刚刚开始启动,因为Django托管了Web服务器,并且正在竭尽所能,以获取基本的本地网站启动和运行。现在,我们已经安装了Django,让我们对设置进行一些编辑以使其正常工作。首先,让我们创建一个新应用

python manage.py startapp feed
您会注意到第一个应用程序称为feed。无论您喜欢什么,都应称呼该应用程序,我们将创建新应用,但是每次在代码中引用该应用程序时,每个应用程序的名称都必须保持一致。要添加一个新应用程序,我们将始终在该应用程序创建的其他目录中编辑settings.py。使用Nano,

nano app/settings.py
在设置中,查找已安装的_apps,然后将[]分为3行。在空中心线上使用四个空间,添加“ feed”或应用程序的名称。设置的这一部分应该看起来像:

INSTALLED_APPS = [
    'feed',
]
在忘记之前,让我们测试Django正在工作。使用命令python manage.py runserver 0.0.0.0:8000,我们可以运行服务器,然后在计算机上的网络浏览器中导航,将代码运行到http:// localhost:8000:8000并查看示例网页(它工作!)使用Control C退出服务器,与任何其他命令相同。 现在,让我们挖掘写一些Python代码。 Django具有三个主要组件,所有这些组件都完全由代码运行。组件称为模型,视图和模板,在将网页交付给用户之前,每个组件分别在较高和较低的级别上。 该模型是将信息存储在数据库中以进行检索,分类和渲染的代码。 该视图决定了如何渲染,操纵和修改模型,几乎每个视图都将直接使用模型。 模板是HTML代码,带有一些额外的铃铛和哨声,称为模板语言。该模板由填充有python代码的视图渲染从视图中,诸如模型和信息(USUALL字符串和整数)之类的上下文。 Django也有其他组件,包括但不限于: 设置,该设置如我们讨论时配置该应用程序。 URL,是用户遵循的模式,以访问Web应用程序的特定部分。 表单,该表格定义了如何处理发送到服务器的信息并将其渲染到数据库以及用户。这些是在服务器端处理信息的基础,并且可以接受计算机存储的任何类型的信息,最著名的是文本字符串,数字和True/false Booleans(通常是复选框)。 模板,即HTML代码和模板语言,并弥合Python和HTML之间的差距,这意味着Python信息可以用作HTML代码,任何人都可以访问并可以使用有限的访问权限的网站,同时使Python代码可访问Web并有用并有用用于远程设备上的各种目的EED将在服务器附近。 静态文件,通常是JavaScript,是服务器服务并与模板链接的库。 服务器服务或外部托管的媒体文件,或者只是在处理并发布到服务器之前写入服务器并将其发布到另一台服务器(一个存储桶)托管。 中间件是与每个视图同时运行的代码,并被视为“包含”。 上下文处理器,处理每个视图的上下文,并用于添加额外的上下文。 测试,验证用户或请求在呈现视图之前通过某些要求。 消费者,决定了网络插款如何处理和响应沟通。 admin,用于注册模型,以便可以在Django管理页面中详细操纵它们,可以通过图形接口对数据库进行管理。 定义异步任务的芹菜可以开始使用Django代码的部分在立即继续执行下一个任务或代码行之前。 Django可以拥有许多其他组件,我们将在此处详细讨论。有很多方法可以使Django更具功能性,添加Websocket,这些Websocket是快速,简化的通信渠道,芹菜,它们执行异步任务,以及许多其他用于扩展Django的软件,尤其是在视图功能中,尤其是在大多数情况下代码执行。查看功能是关键的,因为它们通常会声明特定于特定URL模式或服务器部分的每个代码。 首先,让我们探索视图功能。视图函数以表示将在视图中使用的代码的导入开始,并使用常规功能定义或类定义。最简单的视图由函数定义DEF定义,并使用基本模板返回httpresponse。首先,让我们定义一个基本观点,以返回文本“ Hello World”。请记住,每次添加在诸如def之类的语句中,如果,如果是,则需要为您要应用于函数的每个先前定义添加4个空间。我们将很快进入这些含义。 从我们网站的目录中,使用nano编辑feed/views.py文件,然后将以下行添加到末尾

from django.http import HttpResponse

def hello(request):
    return HttpResponse('hello world')
Django的httpresponse用文本字符串响应,并用开口和关闭表示。每次您将信息传递到函数或类(例如请求或字符串)时,都需要使用括号(打开和关闭)。 这还不是我们需要看到的所有观点。当然,我们还没有告诉服务器确切的视图,我们仍然需要定义一条视图应呈现的路径。让我们从定义App/urls.py中的基本路径开始,以后我们将进入路径组。 在app/urls.py中,在开始导入我们刚创建的视图之后的导入语句之后添加一行。

from feed import views as feed_views
现在,让我们定义视图模式。查看模式具有三个组件,即路径组件,该组件告诉服务器在服务器内的视图存在(用户在导航栏中输入以输入网页的URL路径),指定视图的视图组件,以及一个视图的友好名称,因此在使用模板时很容易检索其模式,尤其是在必要时可以更改和更新它的名称,以便为其他视图提供空间或更合乎逻辑的名称。以这种方式做事并保持灵活是有意义的,因为您的代码库将是一个不断变化的环境,需要灵活性和即兴创作才能有价值且易于使用。这是您的视图,您可以将其添加到urlpatterns = [app/urls.py的部分。视图模式由上述三个组件定义,并称为路径。您的URL模式是列表,因此请确保始终结束其中的每个项目使用逗号,因为这将每个逗号分开。每个项目也应在新线路上再次使用四个空间,就像settings.py中的应用一样。我们将使用空字符串函数定义视图的第一个组件,以创建在Web服务器的根目录上运行的视图。您的urls.py现在应该看起来像

from feed import views as feed_views

urlpatterns = [
    path('', feed_views.hello, name='hello'),
]
这是与Django创建完全静态的网站的基础。为了制作一个更具动态的网站,我们可以开始缓存信息,例如图像,视频,音频等,我们将需要使用模型,我们将接下来探索。现在,让我们检查我们的代码并运行服务器。要检查代码是否错误,请运行:

python manage.py check
如果有任何错误消息,您应该仔细查看对应用程序所做的更改,并查看是否需要修复任何内容,例如外部或缺乏空间,额外的字符,未闭合的字符串,任何错字,任何偶然的删除的角色或其他任何内容。阅读错误消息(如果有),则应该能够查看您创建或编辑的文件编号的路径,因此请查看该文件和行,看看是否可以修复那里的任何内容。如果已解决问题,请再次运行上述命令。当您的软件准备运行并正在运行时,您将看到输出“系统检查无问题”。现在你准备好了。使用以下方式运行服务器

python manage.py runserver 0.0.0.0:8000
现在打开一个Web浏览器,并导航到http:// localhost:8000。您应该在视图中看到httpresponse函数的括号和引号中返回的文本。这只是一个基本示例,但是如果您做到这一点,您就会了解Linux,Bash,Python和Django工作的基础知识。让我们深入研究一些数据库建模,并探索Python类在存储信息中的功能。然后,在使用JavaScript和机器学习使我们的网站充分,灵活和安全之前,我们将开始对HTML和CSS掌握。 课程存储在您的应用程序的模型中。使用Nano,编辑App/models.py并添加新类。类定义一个类定义的类,并通过它继承的超类,在这种情况下,在这种情况下,模型model.model。类的名称是在类定义之后的,并且在类定义A:(colon)之后,在下面表示与类绑定的属性和函数定义之前。我们的班级我们需要一个可以使用的ID来检索并保持其独特之处,并且还需要一个文本字段来存储一些信息。稍后,我们可以添加时间戳,文件,布尔值(可以帮助我们的代码来决定如何使用该模型,并可以用于对其进行排序),该实例将模型绑定到用户记录进入服务器等等。让我们解开代码

from django.db import models # 用于定义我们的班级的导入及其属性

class Post(models.Model): # 我们班级本身的定义
    id = models.AutoField(primary_key=True) # 我们的模型的ID是一种自动生成的密钥,它将让我们查询模型,保持独特之处并在创建模型后需要与模型进行交互时很有用。
    text = models.TextField(default='') # 在这种情况下,我们的类存储的属性是一些文本,将默认为空字符串。
关闭并保存文件,就像我们之前完成的。 随着应用程序的发展,我们将探索许多其他字段和选项,但这是创建应用程序发布文本的基本必需品。但是,该模型不会单独工作。如前所述,我们将需要一个自定义视图和自定义URL模式来使该模型起作用,并且我们还需要一个与模板一起进行的表单。让我们先探索表格。 要定义表单,请使用纳米编辑应用程序/表单。py并添加以下行。我们将需要两个导入,我们的表单类别以及我们创建的模型(feed.models.post),类似于模型的类定义,以及一个字段以及一个称为元的子类,该子类将定义模型该模型该表单交互和。该表单还可以具有初始化功能,该功能会根据请求,模型或其他方式中的信息来设置它,我们将稍后探讨。 模型表格之所以有用,是因为它们可以创建模型或编辑模型,因此,我们将两者都使用它们。让我们以形式定义一个

from django import forms
from feed.models import Post

class PostForm(forms.ModelForm):
    text = forms.CharField(widget=forms.Textarea)
    class Meta:
        model = Post
        fields = ('text',)
这是形式和模型的基础知识。该模型表格可用于实例化或编辑帖子,更改其包含的文本。我们将考虑将此形式集成到接下来的视图中。首先,让我们进行迁移并迁移数据库,以便我们的代码运行时可以与模型进行交互。为此,运行以下命令:

python manage.py makemigrations
python manage.py migrate
这将需要一分钟的时间才能执行,但是一旦完成,它将允许您在软件中的视图,中间件或其他任何地方访问该模型。让我们继续观察我们可以看到模型的地方。编辑feed/views.py并添加以下代码,如上所述。您无需在#符号之后添加任何内容,该代码是用来表示有关代码信息的注释。我们将首先将我们的模型导入视图中,然后将其添加到可以在模板中渲染为显示列表的上下文中。接下来,我们将添加一个模板,可以在其中使用按钮渲染表单和模型,以根据模型创建新对象并将其发布到服务器上。这听起来很复杂,所以让我们逐步迈出一步。在完成视图之前,让我们创建一个模板,该模板只能呈现模型,并确保我们可以通过在外壳中创建新帖子来看到它。这是该视图的外观:

from feed.models import Post
from django.shortcuts import render, redirect
from django.urls import reverse

def feed(request):
    posts = Post.objects.all() # 查询到目前为止数据库中的所有帖子
    return render(request, 'feed/feed.html', {
        'posts': posts,
    })
这一切看起来都很简单,直到我们到达底部。渲染,该函数返回的值,而不是像上一个示例一样在HTTP响应中,总是将请求作为第一个输入,接受上下文(在这种情况下,在数据库中的帖子),现在可以在模板中呈现。 ,并返回函数中定义的模板。该模板将是带有一些名为jinja2的语言的HTML文档,该语言将Python信息传达到HTML中。 要开始创建模板,请在feed中制作两个目录。

mkdir feed/templates
mkdir feed/templates/feed
接下来,在上面的目录中编辑模板,feed/feed/feptrates/feed,并为此示例添加代码。让我们查看此示例的模板。
 
<!doctype HTML>
<html>
<body>
<legend>Feed</legend>
<hr>
{% for post in posts %}
<p>{{ post.text }}</p>
{% endfor %}
</body>
</html>
 
这是一个非常简单的模板。它定义了打开和关闭的HTML标签,一个文档类型标签,一个带有传奇标题的车身标签,一个断路标签,一个突破标签,在屏幕上添加一条小线路,一个循环将帖子中的每个帖子作为段落中的每个帖子作为段落中的段落中的段落中。模板。这是渲染帖子所需的全部,但是数据库中还没有。让我们用外壳创建一些。我们可以用manage.py运行外壳

python manage.py shell
现在,让我们导入我们的帖子模型

from feed.models import Post
接下来,我们将创建一个带有字符串的简单帖子并退出外壳。字符串可以是任何事物,只要有效文本即可。

Post.objects.create(text='hello world')
exit()
最后,我们需要在我们的供稿中添加URL模式。因为我们的feed应用程序将使用多个URL,并且我们希望将文件尺寸保持在较小的情况下,所以让我们在供稿应用中创建一个局部urls.py,看起来像这样:

from django.urls import path
from . import views

urlpatterns = [
    path('', views.feed, name='feed'),
]
我们还需要在基本应用程序中编辑URLS.PY,无论我们决定称呼它,这都是我们创建的第一个目录。编辑app/app.py并将以下内容添加到URL模式

from django.urls import include # 在顶部

urlpatterns = [
    # ...以前的代码在这里
    path('feed/', include(('feed.urls'), namespace='feed')),
]
现在,当我们使用Python Manage.py Runserver运行服务器时,我们将看到我们创建的页面,因为我们具有模型,视图和模板以及URL模式以及数据库中的项目。接下来,让我们实现我们创建的表格并开始创建自己的帖子。但是,在我们编写太多代码之前,让我们使用之前写过的脚本进行备份。在外壳中运行此脚本,等待片刻,所有代码都将备份到我们的git存储库中。

backup
实施表单相对简单。我们将导入我们的表单,将邮政请求处理程序添加到视图中,并将帖子保存在数据库中,然后再将其重定向到同一视图。我们可以使用我们已经导入的重定向函数,以及另一个称为“反向”的函数以获取视图模式的URL。我们将使用字符串“ feed:feed”来查询此问题,因为随附模式的名称空间是feed,并且该视图也称为feed。

from feed.forms import PostForm

def feed(request):
    posts = Post.objects.all() # 查询到目前为止数据库中的所有帖子
    if request.method == 'POST': # 处理邮政请求
        form = PostForm(request.POST) # 创建表单的实例并将数据保存到其中
        if form.is_valid(): # 验证表格
            form.save() # 保存新对象
        return redirect(reverse('feed:feed')) # 通过get请求重定向到同一URL
    return render(request, 'feed/feed.html', {
        'form': PostForm(), # 确保将表格传递到上下文中,以便我们进行渲染。
        'posts': posts,
    })
现在,我们需要更新模板以说明新表单。我们可以使用
在HTML中标记,并使用提交按钮在HTML模板中呈现表单。我们还需要一个CSRF令牌,该令牌可以防止外部站点发布到表单上,而无需先加载页面。
 
<!doctype HTML>
<html>
<body>
<legend>Feed</legend>
<form method=”POST”>
{% csrf_token %}
{{ form }}
<button type=”submit”>New Post</button>
</form>
<hr>
{% for post in posts %}
<p>{{ post.text }}</p>
{% endfor %}
</body>
</html>
 
让我们分解一下。有一个新的表单类,一个令牌,表单本身和提交按钮。很简单,但是当我们看一下它时,我们可能希望使它看起来更好。它有效,我们可以发表新的帖子,现在将它们保存在数据库中。这里有一些事情发生。我们使用HTML标签来声明该文档是HTML文档,我们使用模板标签({%…%})渲染表单的令牌,而另一个{{…}}渲染表单。我们也有一个循环使用块标签和模板标签渲染文本。块标签确实很重要,因为我们可以定义模板的部分与它们呈现,模板标签是我们将变量放入代码中的基础。 现在,我们需要使我们的应用看起来更好,因为现在它看起来确实很基本。我们可以使用与文档中每个对象绑定的CSS或在与每个对象相关的类中进行此操作。 CSS真的很好,因为它告诉页面上的所有内容,并可以使它看起来真的很好。有一些图书馆可以做到这一点,但是我个人去的是Bootstrap。 Bootstrap可以从他们的网站下载getbootstrap.com/。到达那里后,按按钮读取安装文档,然后从Include Via CDN部分复制代码。您将在HTML文档顶部的标签中需要此代码。另外,让我们继续创建一个基本模板,这样我们就不需要在每个模板中重新创建这些链接。 用MKDIR模板制作一个称为模板的新目录,然后编辑模板/base.html。 看起来应该这样:
 
<!doctype HTML>
<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>
 
确保复制CSS和JavaScript,.css和.js文件,因为我们将需要JavaScript使我们的网站在将来更具功能性。 现在,让我们返回Bash Shell并运行快速命令。请记住,如果您需要访问虚拟环境,请键入源VENV/bin/Activate。这将使您可以在本地安装Python软件包,以使Django访问它们。为了给出由Django Bootstrap类生成的表格,我们将使用称为Crispy Forms的Python软件包。我们可以使用以下命令下载此

pip install django-crispy-forms
安装此任务后,将其添加到settings.py.py

INSTALLED_APPS = [
    # …以前的代码在这里
    'crispy_forms',
]
现在,回到我们的饲料模板中,我们可以删除一些东西。让我们删除文档的开始和结尾,并使用扩展和块定义将基础模板的继承替换为基础模板的继承。另外,我们将在表单中添加带有负载和模板过滤器的模板过滤器导入。最后,让我们在表单上的按钮中添加一个bootstrap类,以使其看起来更像一个按钮。看起来应该这样:
 
{% extends 'base.html' %}
{% block body %}
{% load crispy_forms_tags %}
<form method=”POST”>
{% csrf_token %}
{{ form|crispy }}
<button type=”submit” class=”btn btn-outline-primary”>New Post</button>
</form>
<hr>
{% for post in posts %}
<p>{{ post.text }}</p>
{% endfor %}
{% endblock %}
 
美丽的!这已经有很多代码了。接下来,我们应该对其进行测试,并确保我们可以看到一切看起来都不错,并且请确保一切正常。根据以前的说明运行服务器,并确保网站外观和工作正常。好!您已经准备好进入下一步,我们将使用类似的URL,表单,视图和模板添加用户登录功能。基本模板很重要,我们将继续对其进行修改并根据需要进行更改,但是现在,让我们专注于使我们的网站更安全,通过使用户能够使用用户名和密码登录,最终更重要的信息,并最终更重要的信息。将有助于确保您的应用程序安全,并且您自己的帐户只能由您访问。 为此,我们需要使用内置在Django中的用户模型。用户模型是像我们的帖子一样的数据库模型,可以将用户登录到网站中。将来,在将网站部署到互联网之前,我们将将此模型与归因于此的其他模型扩展,并为登录的额外安全措施构建对网络钓鱼具有抵抗力的安全措施。我们将首先使用Django提供的一些内置登录表单。首先,让我们创建一个新应用程序,我们将使用该应用程序来渲染基本登录页面的模板和视图。我们还将创建其他应用程序来代表持续的登录挑战以确保应用程序的确保,包括平码,面部识别,近场通信,外部设备,多因素身份验证和指纹识别。 我们已经谈论过启动一个应用程序。从我们的目录中,在虚拟环境中,通过管理。

python manage.py startapp users
现在,我们应该拥有一个新应用程序的目录。让我们首先在该目录中创建与用户登录相对应的视图。 Django内置了用户登录的视图,但是这些不适合我们,因为我们需要自定义视图,这最好使用一个定义完成。 在此视图中,我们将首先检查发布请求,将请求传递到从django导入的登录名,对用户帐户进行身份验证,然后登录用户,然后再将其重定向到我们的feed应用程序。 在用户/views.py中,添加以下代码

from django.shortcuts import render, redirect
from django.urls import reverse
from django.contrib.auth.forms import AuthenticationForm, SetPasswordForm
from django.contrib.auth import authenticate, logout
from django.contrib.auth import login as auth_login
from django.contrib import messages

def login(request):
    if request.method == “POST”:
        username = request.POST['username'] # 从邮政请求获取用户名和密码
        password = request.POST['password'] # 身份验证用户
        user = authenticate(username=username, password=password)
        if user:
            auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend')
            messages.success(request, 'Your password was accepted. Please continue')
            return redirect(reverse('feed:feed'))
        else: messages.warning(request, 'Username or password incorrect. Please try again')
    return render(request, 'users/login.html', {'form': AuthenticationForm()})
这是基本登录视图所需的全部。现在,让我们通过扩展基本模板来为视图创建表单。首先,我们将为用户文件夹中的模板创建一个新目录。

mkdir users/templates
mkdir users/templates/users
现在,我们应该能够编辑用户/模板/用户/login.html。当我们使用时,我们将创建一个模板以允许用户注册。

nano users/templates/users/login.html
现在,在模板中,
 
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<form method="POST">
    {% csrf_token %}
    <fieldset class="form-group">
        <legend class="border-bottom mb-4 break">Log In</legend>
        {{ form|crispy }}
    </fieldset>
    <div class="form-group">
        <button class="btn btn-outline-info" type="submit">Login</button>
    </div>
</form>
{% endblock %}
 
这是登录模板的基础知识。它实际上就像结构上的另一个模板一样,但是在渲染时看起来有些不同。我们可以复制此代码以构建另一个非常相似的模板,称为regisher.html,我们将更改措辞并使用我们构建的新表单。让我们先制作模板。编辑用户/模板/users/register.html并添加以下代码:
 
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<form method="POST">
    {% csrf_token %}
    <fieldset class="form-group">
        <legend class="border-bottom mb-4 break">Create an account</legend>
        {{ form|crispy }}
    </fieldset>
    <div class="form-group">
        <button class="btn btn-outline-info" type="submit">Register</button>
    </div>
</form>
{% endblock %}
 
现在,让我们为用户注册构建表单,然后在使用模型升级用户登录之前回到视图中。我们将从一开始就将此表格基本化,但将来纳入了更多详细信息和安全功能,例如协议和CAPTCHA。使用nano用户/forms.py编辑表单,并添加以下代码。

from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm

class UserRegisterForm(UserCreationForm):
    email = forms.EmailField()

    class Meta:
        model = User
        fields = ['username', 'email', 'password1', 'password2']
因此,我们在这里有另一种形式,它起作用的很简单。这是带有用户名,电子邮件和密码以及确认密码字段的用户寄存器表单。请注意,此表单不会扩展常规表格。form类,它是模型表单,这意味着它具有元。一个字段的定义相同,类元标准定义了该表单对应于将写入表单的其余信息对应的模型。其中大部分已经存在于Django内置的UserCreationform中,因此我们将其用作类的基础(通过括号中)。 接下来,我们将检查注册用户的视图,因为我们有一个表单和模板。这是一个模态,就像新帖子视图中的一个一样。编辑用户/views.py并添加以下代码:

# …数量
from .forms import UserRegisterForm

def register(request):
    if request.method == “POST”:
        form = UserRegisterForm(request.POST)
        if form.is_valid():
            user = form.save()
            messages.success(request, 'Welcome to the app, {}.'.format(user.username))
    return render(request, 'users/register.html', {'form': UserRegisterForm})
这就是我们需要注册用户的全部,但是我们应该拥有更多信息。我们想知道用户注册的时间,网站上的最后时间,有关它们的一些信息,例如传记,时区等。另外,我们需要更新我们的供稿模型,发布,以说明用户对每个用户的模型和属性帖子。为了做到这一点,我们将在两个应用程序中更新Models.py。让我们从编辑供稿模型开始。现在应该看起来像这样:

from django.db import models # …进口
from django.contrib.auth.models import User

class Post(models.Model):
    id = models.AutoField(primary_key=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name='posts') # 加入此行
    text = models.TextField(default='')
请注意添加到文件中的第二行。这是一个外键,将每个帖子归因于每个帖子的单个用户,因此我们可以确保我们以每个用户的使用者为基础保存帖子,并且在不归因于用户的情况下无法进行帖子。我们使用其代表的类定义此外键,一个删除参数,以确保用户,空和空白参数删除帖子,以确保我们可以在必要时删除用户,并适应我们已经已经已经已经存在的帖子中的用户创建的和一个相关名称,我们可以用来参考用户创建的帖子对象。与Post.author的作者作者,此相关名称为我们发布了帖子本身的用户。现在,我们可以将帖子通过运行user.posts.all()或furs.posts.all()制作的用户。 现在,让我们的登录名更具弹性。我们已经可以通过简单地限制我们允许登录到该网站的次数来使我们的网站易受网络钓鱼的侵害。网站,这很容易。在我们继续开发应用程序时,我们还开始在每个用户之前开始存储一些有关每个用户的信息。编辑用户/models.py,添加以下内容

from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True, related_name='profile')
    account_created = models.DateTimeField(default=timezone.now)
    last_seen = models.DateTimeField(default=timezone.now)
    can_login = models.DateTimeField(default=timezone.now)
    preferred_name = models.CharField(max_length=20,default='', null=True, blank=True)
    bio = models.TextField(blank=True, default='')
请注意,此模型与邮政模型相似。我们还有一个额外的导入时间,这将使我们能够在DateTime字段上设置默认值,并且我们还具有像帖子一样的角色feffield和Textfield。使用所有这些时间戳有助于我们保护网站并了解其使用,并且文本字段使我们在网站上渲染有关每个用户或作者的信息。 OneToOneField应该是唯一的次要考虑因素,其行为与Foreginkey完全相同,但每个后续模型只有一个。这样,用户只有一个配置文件,而他们可能有很多帖子。 现在,让我们改进登录名并注册视图以说明配置文件。首先,编辑用户/views.py并专注于寄存器视图:

# …数量
from .forms import UserRegisterForm

def register(request):
    if request.method == “POST”:
        form = UserRegisterForm(request.POST)
        if form.is_valid():
            user = form.save()
            Profile.objects.create(user=user) # 确保添加此行,为用户创建配置文件
            messages.success(request, 'Welcome to the app, {}.'.format(user.username))
    return render(request, 'users/register.html', {'form': UserRegisterForm})
这只是为用户创建个人资料,而无需填写任何信息。现在,我们要确保不能频繁登录用户帐户,或者至少不能经常尝试密码,因此让我们更新登录视图。

# …数量
from .models import Profile
from django.utils import timezone
import datetime

def login(request):
    if request.method == “POST”:
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(username=username, password=password)
        if user and user.profile.can_login < timezone.now(): # 请注意,我们现在检查用户是否可以登录
            auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend')
            messages.success(request, 'Your password was accepted. Please continue.')
            return redirect(reverse('feed:feed'))
        else: # 如果登录未成功,
            messages.warning(request, 'Username or password incorrect. Please try again.')
            user = User.objects.filter(username=username).first() # 这是我们更新用户配置文件的部分
            if user: 
                profile = user.profile
                profile.can_login = timezone.now() + datetime.timedelta(seconds=15) # 所以他们不能再登录几秒钟
                profile.save()
    return render(request, 'users/login.html', {'form': AuthenticationForm()})
这是安全的基本基础。确保该站点不容易受到某人的影响,只是尝试所有可能的密码组合,甚至同时尝试其中一些。这不会让知道自己的密码并在一些设备上登录的普通用户感到沮丧,但它会使众多的网络钓鱼机器人脱离应用程序。请注意,我们添加了一个带有变量can_login的IF语句,这应该是过去的时间,并使用相同的用户名使用每个失败的登录方式对其进行更新。这样,恶意用户将无法尽快猜测密码。 DateTime.timedelta()中的秒数也可以更新,并且该网站将更具弹性,但使用更多秒的时间稍微降低。我建议15开始。 请记住,我们构建了一个备份脚本来保存我们的工作,所以让我们继续备份到迄今为止我们拥有的一切,以确保我们保存了一切。运行命令:

sudo backup
再一次,这将为您的工作节省。我建议运行频繁的备份以保存您的工作,您甚至可能想自动运行备份作业。您可以使用称为Cron的Unix实用程序来执行此操作。要激活此实用程序,请运行以下命令并输入您的密码:

sudo crontab -e
如果您尚未为Nano选择选项1,则应该已经熟悉的文本编辑器,并使用箭头键滚动到文件底部。添加以下行:

0 * * * * sudo backup
Cron使用分钟,小时,每月,月,一周的一天, *或一个数字代表何时运行命令。使用0分钟, *在其余选项中 *,我们可以在分钟开始时每小时的第一分钟运行一个命令。这使我们可以自动备份代码。用sudo执行的所有作业作为root运行时,我们无需每小时输入密码。 为了使无需使用密码备份代码变得更容易,让我们禁用备份命令的密码。我们将通过执行以下命令并输入密码来做到这一点:

sudo visudo
现在,让我们滚动到文件的底部并添加另一行:

ALL ALL=NOPASSWD: /bin/backup
这使我们可以在没有密码的情况下运行命令“备份”作为任何用户。该格式很容易,只需将行的前缀带有“ all = nopasswd:/bin/”,并以命令结尾,例如/usr/bin/bin/bin/bink/back/backup。 现在,让我们开始使用电子邮件。电子邮件对于网站确实很重要,因为这是一种使网站更安全,验证用户是真实的人,甚至向客户推销产品或服务的方法。许多经常互联网的人每天检查他们的电子邮件,并收到有关他们感兴趣的产品和服务的各种营销电子邮件。在Django网站上启用电子邮件时,有一些选择,欢迎您选择哪种最适合您。 首先,您可以购买电子邮件服务,该电子邮件将使您可以从域中发送电子邮件并需要最少的代码。有许多服务提供此服务,例如Google Workspace,Sendinblue,Mailgun等。 否则,您将无法建立您自己在服务器中从头开始的电子邮件服务。我建议使用此选项,即使它是更多的代码,可能需要特殊的托管。您将无法从家用计算机启动邮件服务器,因此,让我们继续检查配置和代码以在我们在云中启动服务器并在其中创建自己的邮件服务器之前,请发送电子邮件。 首先,编辑设置。

nano app/settings.py
其中应用程序是您使用startApp创建的应用程序的名称。 添加以下行:

SITE_NAME = 'Django App'

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'localhost'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_ADDRESS = username@server.com'
EMAIL_HOST_USER = 'username'
EMAIL_HOST_PASSWORD = config['EMAIL_HOST_PASSWORD']
DEFAULT_FROM_EMAIL = '{} <{}>'.format(SITE_NAME, EMAIL_HOST_USER)
确保在准备部署应用程序时更改这些更改,我们将在稍后重新访问。 email_address设置应为您要发送的电子邮件,并且应将密码(email_host_password)设置为您为服务器生成的密码。我将密码从配置文件加载,以使用以下逻辑将其排除在代码之外。

import os
import json
with open('/etc/config.json') as config_file:
    config = json.load(config_file)
然后,我使用nano设置了一个使用config的JSON文件,如下所示。 编辑文件:

sudo nano /etc/config.json
添加以下行:

{
	“EMAIL_HOST_PASSWORD”: “<some password here>”
}
我们将继续编辑配置文件,并添加将在应用程序中使用的所有密码和密钥。现在,让我们快速研究如何使用Python发送电子邮件。首先,让我们为验证电子邮件创建模板,可以发送给用户,然后将其放入用户模板目录中。该模板将用HTML编写。

nano users/templates/users/verification_email.html
 
<h1>Django App - Verify Your Email</h1>
<p>Dear {{ user.username }},</p>
<p>To verify your email, please <a href="{{ base_url }}{% url 'users:activate' uidb64=uid token=token %}">click here</a>.</p>

<p>Alternatively, you can paste the following link in your browser's address bar:</p>
<p>{{ base_url }}{% url 'users:activate' uidb64=uid token=token %}</p>

<p>The link will expire in 30 minutes.</p>
<p>If you have not requested a verification email you can simply ignore this email.</p>
<p>See you there,</p>
<p>Daisy</p>
 
这封电子邮件很简单。它采用用户的上下文,网站的基本URL以及用于验证用户电子邮件的用户ID和令牌。在编写一些Python代码以渲染模板之前,请确保在设置中定义基本URL。继续,将以下行添加到app/settings.py的开始。

SITE_NAME = 'Django App'
PROTOCOL = 'https'
DOMAIN = 'example.com'

BASE_URL = PROTOCOL + '://' + DOMAIN
最终,当您的网站准备好上网并部署它时,您将需要将域名定义为您购买的域名以表示网站。这是您将在Navbar中输入的名称以访问您的网站。现在,您可以将域空白或占位符。您还需要将site_name更改为您要选择网站的名称。 在发送电子邮件之前,让我们创建一个令牌生成器,以便我们可以拥有一个永不过期的帐户激活令牌。我们可以通过构建和导入看起来如下的帐户激活令牌来做到这一点。编辑文件:

nano users/tokens.py
添加以下代码:

from django.contrib.auth.tokens import PasswordResetTokenGenerator
import six
class TokenGenerator(PasswordResetTokenGenerator):
    def _make_hash_value(self, user, timestamp):
        return (
            six.text_type(user.pk) + six.text_type(timestamp)
        )
account_activation_token = TokenGenerator()
unsubscribe_token = TokenGenerator()
该基本令牌生成器会生成令牌,我们可以将用户发送到URL中,用户可以用来验证其电子邮件并激活其帐户。 接下来,让我们看看如何发送电子邮件。使用Nano,编辑用户/email.py。

nano users/email.py
发送验证HTML电子邮件将看起来像这样:

from django.contrib.auth import get_user_model
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.utils.encoding import force_bytes
from django.core.mail import EmailMultiAlternatives
from django.shortcuts import render
from .tokens import account_activation_token
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from django.template import Template, Context
from django.conf import settings
import traceback

def send_verification_email(user):
    User = get_user_model()
    mail_subject = '[{}] Activate your account.'.format(settings.SITE_NAME)
    html_message = render_to_string('users/verification_email.html', {
        'user': user,
        'domain': settings.DOMAIN,
        'protocol': 'https',
        'uid': urlsafe_base64_encode(force_bytes(user.pk)),
        'token': account_activation_token.make_token(user),
    })
    send_html_email(user, mail_subject, html_message)
这很简单。我们导入发送电子邮件,使用模板和我们设置的电子邮件所需的功能,然后通过模板名称定义电子邮件,然后使用函数将其发送给用户。您会注意到我们尚未定义发送邮件的函数,send_html_email,所以让我们在已经添加到用户/email.py的代码下面写下此函数。

def send_html_email(user, mail_subject, html_message):
    to_email = user.email
    username = user.username
    if to_email == '':
        return None
    unsub_link = settings.BASE_URL + user.profile.create_unsubscribe_link()
    html_message = html_message + "<p><a href=\"" + unsub_link +  "\" + title=\"Unsubscribe from " + settings.SITE_NAME + " emails\">Unsubscribe</a></p></body></html>"
    msg = EmailMultiAlternatives(mail_subject, strip_tags(html_message), settings.DEFAULT_FROM_EMAIL, [to_email], headers={'List-Unsubscribe' : '<' + unsub_link + '>'},)
    msg.attach_alternative(html_message, "text/html")
    profile = user.profile
    try:
        msg.send(fail_silently=False)
        if not profile.email_valid:
            profile.email_valid=True
            profile.save()
    except:
        profile.email_valid=False
        profile.save()
这有点复杂,我们还没有准备好运行所有这些代码。请注意,我们定义了UNSUB_LINK,用户可以使用用户退订我们的电子邮件的链接。这很重要,因为用户将需要能够退出我们的电子邮件,除非他们想随时看到它们。我们还为我们的消息添加了文本替代方案,即HTML标签所剥离的HTML消息。最后,我们检查电子邮件是否发送,如果没有发送,我们在用户的个人资料中标记了他们的电子邮件无效。 让我们回到用户模型中,以便我们可以使所有工作都起作用。我们需要定义一个函数来生成链接以取消订阅,并定义一个布尔字段以标记用户的电子邮件无效。 首先,将以下导入添加到用户/型号的顶部。

nano users/models.py

# …
from django.core.signing import TimestampSigner, BadSignature, SignatureExpired
from django.urls import reverse
接下来,让我们将功能添加到用户模型中以制造令牌并检查用于激活电子邮件的令牌以及该字段,以保存用户是否成功接收其邮件。在用户/型号中,再次,将以下代码添加到模型末尾(缩进代码)

# …
    email_valid = models.BooleanField(default=True)
    
    def make_token(self):
        return TimestampSigner().sign(self.user.username)

    def check_token(self, token):
        try:
            key = '%s:%s' % (self.user.username, token)
            TimestampSigner().unsign(key, max_age=60 * 60 * 24 * 30) # 有效期30天
        except (BadSignature, SignatureExpired):
            return False
        return True

    def create_unsubscribe_link(self):
        username, token = self.make_token().split(":", 1)
        return reverse('users:unsubscribe', kwargs={'username': username, 'token': token,})
这很简单,我们使用TimestAmpSigner(这是一种基本的加密工具)来创建一个令牌,该代币将在一定时间之后到期,并且我们还使用另一个功能来检查其是否有效。我们两次使用这些令牌,一次验证电子邮件,然后一次取消订阅链接。 现在我们已经拥有了这些,我们将要做的最后一项工作就是在视图中。在用户/views.py中,让我们添加视图以验证电子邮件地址并取消订阅。

nano users/views.py
首先,添加以下导入。我额外扔了几个,所以我们以后不必再进口更多物品了。

from django.contrib.auth import logout
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.models import User
from django.utils.encoding import force_str
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
import json
import requests
import datetime, traceback
from django.contrib import messages
from .models import Profile
from django.utils import timezone
from django.views.decorators.cache import never_cache
from .email import send_verification_email # 确保导入验证电子邮件发送功能
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.utils.decorators import method_decorator
from django.http import HttpResponseRedirect
from django.conf import settings
from django.utils import timezone
import datetime
import pytz
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
from django.contrib.auth.tokens import default_token_generator
from django.utils.http import urlsafe_base64_decode
from .tokens import account_activation_token
您可能已经有一些这些进口,但是重复它们并没有什么坏处。您将需要从用户导入验证电子邮件发送函数以及account_activation_token。 现在,在文件的底部,添加以下代码:

def unsubscribe(request, username, token):
    user = get_object_or_404(User, username=username)
    if((request.user.is_authenticated and request.user == user) or user.profile.check_token(token)):
        # 取消订阅
        profile = user.profile
        profile.subscribed = False
        profile.save()
        return render(request, 'users/unsubscribe.html')
    # 否则重定向到登录页面
    messages.warning(request,f'Your unsubscribe link has expired. Please log in to unsubscribe.')
    next_url = reverse('users:unsubscribe', kwargs={'username': username, 'token': token,})
    return HttpResponseRedirect('%s?next=%s' % (reverse('login'), next_url))

def activate(request, uidb64, token):
    try:
        uid = force_str(urlsafe_base64_decode(uidb64))
        user = User.objects.get(pk=uid)
    except(TypeError, ValueError, OverflowError, User.DoesNotExist):
        user = None
    ip = get_client_ip(request)
    if user is not None and account_activation_token.check_token(user, token):
        user.profile.email_verified = True
        user.profile.save()
        user.save()
# sendwelcomeemail(请求,用户)
        messages.success(request, f'Thanks for confirming your email! You can now log into your account, and a welcome email has been sent to you.')
        return redirect(user.profile.create_face_url())
    else:
        messages.success(request, f'Your activation link has expired. Please request a new activation link.')
        return redirect('verify:verify')

def resend_activation(request):
    if request.method == 'POST':
        form = ResendActivationEmailForm(request.POST)
        email = request.POST['email']
        try:
            user = User.objects.get(email=email)
            send_verification_email(user)
            messages.success(request,'Your verification email sent. Please click the link in your email to verify your account.')
            return redirect(reverse('verify:verify'))
        except:
            messages.warning(request,f'Your email is not correct. Please try again.')
    else:
        form = ResendActivationEmailForm()
    return render(request,'users/resend_activation.html',{'form': form, 'title': 'Resend Activation', 'small': True})
这是很多代码。让我们分解。第一个功能,干净,简单,从邮件列表中取消订阅用户。第二个功能激活了他们的电子邮件,您会注意到我添加了评论功能,sendwelcomeemail。欢迎您使用电子邮件模板和功能定义发送欢迎电子邮件,我还没有。我投掷的最后一个功能很重要,因为激活电子邮件过期。因此,我们需要在某些时候重新发送激活电子邮件。我们可以为此使用基本表单,并致电该功能发送验证电子邮件。在执行此操作之前,让我们确保通过在寄存器视图中添加功能调用来确保它首先发送。在寄存器视图中的重定向之前,在用户/views.py中添加此行。

nano users/views.py

# …(之后)DEF寄存器(请求):
            send_verification_email(user)
# …(之前)重定向(
您无需在该代码段中添加第一行和最后一行,只需确保寄存器视图将验证电子邮件发送给用户即可。看起来应该这样:

# …数量
from .forms import UserRegisterForm

def register(request):
    if request.method == “POST”:
        form = UserRegisterForm(request.POST)
        if form.is_valid():
            user = form.save()
            send_verification_email(user) # 确保添加此行!
            messages.success(request, 'Welcome to the app, {}.'.format(user.username))
    return render(request, 'users/register.html', {'form': UserRegisterForm})
现在,我们需要添加一个表格来重新发送激活电子邮件。在用户/表格中。py,添加以下表格:

# …(金额)
class ResendActivationEmailForm(forms.Form):
    email = forms.EmailField(required=True)
我们还需要一个与此重新启动电子邮件激活表相对应的模板。让我们添加此模板。编辑文件:

nano users/templates/users/resend_activation.html
接下来,将以下代码添加到文件中。

{% extends 'base.html' %}
{% block content %}
{% load crispy_forms_tags %}
        <form method="POST">
            {% csrf_token %}
            <fieldset class="form-group">
                <legend class="border-bottom mb-4">Resend activation email</legend>
                {{ form|crispy }}
            </fieldset>
            <div class="form-group">
                <button class="btn btn-outline-secondary" type="submit">Resend activation email</button>
            </div>
        </form>
{% endblock %}
哎呀,这很多!现在,当我们将代码部署到服务器时,我们将能够通过电子邮件单击发送HTML电子邮件并激活用户帐户。我们可能还想发送一封简单的欢迎电子邮件,所以让我们看看如何做到这一点。返回用户/email.py,添加以下代码:

def sendwelcomeemail(user):
    User = get_user_model()
    html = open('{}/users/welcome_email.html'.format(settings.BASE_DIR)).read()
    subject = 'Welcome to ' + settings.SITE_NAME + ', {{ username }}!'
    template = Template(html)
    subjtemplate = Template(subject)
    context = Context({'username': user.username, 'base_url': settings.BASE_URL, 'model_name': 'Daisy Holton, 'site_name': settings.SITE_NAME})
    renderedtemplate = template.render(context)
    subjcontext = Context({'username': user.username})
    subjrenderedtemplate = subjtemplate.render(subjcontext)
    send_html_email(user, subjrenderedtemplate, renderedtemplate)
另外,我们将需要一个模板来渲染所有这些信息。在我的网站上,模板看起来像以下内容,但欢迎您随心所欲地格式化它。
 
<html>
<body>
<h3>Welcome to {{ site_name }}</h3>
<p>Hello {{ username }},</p>
<p>We are happy to see you here! Thank you for joining {{ site_name }} and being a part of the fun. To get started, here are a few things you can do after you verify your identity.</p>
<ol>
    <li><a href="{{ base_url }}/" title="Use the app">Use the app</a>. This is the main page of {{ site_name }}</li>
    <li><a href="{{ base_url }}/feed/profile/Clementine/" title="See my profile">Visit my private {{ site_name }} profile</a>. This is a page for anyone wanting to get to know me.</li>
    <li><a href="{{ base_url }}/feed/profiles/" title="See all profiles currently on the site">More profiles</a>. You can find these people on the site, and see their content.</li>
    <li><a href="{{ base_url }}/feed/all/" title="See everything on {{ site_name }}">See all posts here</a>. This is the private front page of {{ site_name }}.</li>
</ol>
<p>There is even more on the site, so feel free to visit and see what you find. You can share the site with any of the social buttons on each page. I hope you enjoy your time with {{ site_name }}! Thanks for being here.</p>
<p>With much love,</p>
<p>{{ model_name }}</p>
<a href="{{ base_url }}" title="{{ site_name }}">{{ base_url }}</a>
 
请注意,我们没有关闭的身体或HTML标签,因为当我们添加HTML退订链接时,我们将其添加。这些很重要,但我们不想定义两次。 那接下来是什么?我们走了很长一段路。确实,我们应该准备将站点部署到服务器。我们可以添加@Login_Required Decorator,并确保我们的视图安全,进行用户注册,发送合规电子邮件和缓存信息,这是网站保持相关性所需要做的基础。我们将添加一些有用的功能,然后建立将代码部署到远程服务器,设置邮件服务器,域配置和过滤器以使我们的网站安全可靠的基础。 我们还需要密码重置视图,所以让我们非常快地添加。 Django的内置密码重置视图在某些功能中被打破,但是我们将研究如何编写自己的视图,电子邮件模板,表单和URL模式。这就是视图的样子,在用户/views.py中

# ...金额
from django.contrib.auth.tokens import default_token_generator
from django.contrib.auth.forms import SetPasswordForm
from django.utils.http import urlsafe_base64_decode

def password_reset(request, uidb64, token):
    user = get_object_or_404(User, id=urlsafe_base64_decode(uidb64))
    if request.method == 'POST':
        form = SetPasswordForm(user, request.POST)
        if form.is_valid() and default_token_generator.check_token(user, token):
            form.save()
            messages.success(request, 'Your password has been reset.')
        elif not form.is_valid():
            messages.warning(request, 'Your passwords do not match, or do not meet the requirements. Please try again.')
            return redirect(request.path)
        else:
            messages.warning(request, 'Your password reset link has expired. Please create a new one.')
        return redirect(reverse('users:login'))
    return render(request, 'users/password_reset_confirm.html', {
        'title': 'Reset your Password',
        'form': SetPasswordForm(user)
此表单内置到Django,但是我们需要一个模板来确认密码重置,用户/templates/users/password_reset_confirm.html
 
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
        <form method="POST">
            {% csrf_token %}
            <fieldset class="form-group">
                <legend class="border-bottom mb-4">Reset Password</legend>
                {{ form|crispy }}
            </fieldset>
            <div class="form-group">
                <button class="btn btn-outline-info" type="submit">Reset Password</button>
            </div>
        </form>
{% endblock content %}
 
我们还有一个模板来发送密码重置电子邮件,并使用简单的表单,用户/模板/users/passwork_reset.html发送密码。
 
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
        <form method="POST">
            {% csrf_token %}
            <fieldset class="form-group">
                <legend class="border-bottom mb-4">Reset Password</legend>
                {{ form|crispy }}
            </fieldset>
            <div class="form-group">
                <button class="btn btn-outline-info" type="submit">Request Password Reset</button>
            </div>
        </form>
{% endblock content %}
 
电子邮件本身的模板很简单,它是一个基本的html文件,渲染链接以重置密码,在用户/模板/users/passwere_reset_email.html中。 Django将自动解释此文件。
 
<h1>Uglek - Reset Your Password</h1>
<p>Hello,</p>
<p>To reset your password, please <a href="https:/uglek.com{% url 'password_reset_confirm' uidb64=uid token=token %}">click here</a>.</p>
<p>Alternatively, you can paste the following link into your browser:</p>
<p>https://uglek.com{% url 'password_reset_confirm' uidb64=uid token=token %}</p>
<p>If you have not requested a password reset you can simply ignore this email.</p>
<p>Thanks for joining us,</p>
<p>Daisy</p>
 
我们还需要两个模板。首先是确认已发送电子邮件。这些视图已经在Django中,因此我们只需要在URLS.PY中解决它们。此模板位于用户/模板/用户/password_reset_done.html上
 
{% extends 'base.html' %}
{% block content %}
  <div class="media-body">
    <div class="alert alert-info">
        An email has been sent with instructions to reset your password.
    </div>
  </div>
{% endblock content %}
 
最后,要确认密码重置已完成,用户/模板/用户/password_reset_complete.html
 
{% extends 'base.html' %}
{% block content %}
 <div class="media-body">
    <div class="alert alert-info">
        Your password has been set.
    </div>
    <a href="{% url 'users:login' %}">Sign In Here</a>
  </div>
{% endblock content %}
 
现在,我们需要这些观点的URL模式。在用户/urls.py中,添加以下URL模式:

urlpatterns = [
    # ...以前的URL在这里
    path('password-reset/',
         auth_views.PasswordResetView.as_view(
             template_name='users/password_reset.html',
             html_email_template_name='users/password_reset_html_email.html'
         ),
         name='password_reset'),
    path('password-reset/done/',
         auth_views.PasswordResetDoneView.as_view(
             template_name='users/password_reset_done.html'
         ),
         name='password_reset_done'),
    path('password-reset-confirm/<uidb64>/<token>/',
         auth_views.PasswordResetConfirmView.as_view(
             template_name='users/password_reset_confirm.html'
         ),
         name='password_reset_confirm'),
    path('password-reset-complete/',
         auth_views.PasswordResetCompleteView.as_view(
             template_name='users/password_reset_complete.html'
         ),
         name='password_reset_complete'),
]
四个模板,很多!但是现在,我们一定能够从Web浏览器中随时随地重置用户的密码。 我了解这是很多代码。如果您的头部似乎有点远,那没关系。您将有所改善,理解将有所改善,并且您很快就会变得更加能够使用代码。如果您完全迷失了,我建议您在在线学习以学习为课程的自定进度后,稍后再回到此软件。这些通常可以免费入门,并会指导您回到这个项目时取得成功所需的一切。如果您觉得自己准备继续继续,请继续阅读,接下来,我们将介绍将代码部署到远程服务器并设置邮件服务器,并使用bash自动部署,以便您始终可以使用一些简单的命令。 在部署到远程服务器之前,我们需要做的最后一件事是使我们的网站更加安全。你会的请注意,登录视图仅采用用户名和密码,并且没有多因素身份验证或一个时间代码。这是一个简单的修复,并且使用相同的代码,我们可以使我们的网站发送短信,甚至可以响应发送给服务器的短信。首先,我们将返回用户模型,并添加一个将表示每个登录的时间戳签名者。我们还将在用户模型中添加一个唯一的旋转标识符,该标识符将用于为我们的登录添加额外的安全性。编辑用户模型,用户/型号。py,添加以下内容

from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
# 确保导入UUID,时间戳签名器和URL发电机(反向)
import uuid
from django.core.signing import TimestampSigner, BadSignature, SignatureExpired
from django.urls import reverse

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True, related_name='profile')
    account_created = models.DateTimeField(default=timezone.now)
    last_seen = models.DateTimeField(default=timezone.now)
    can_login = models.DateTimeField(default=timezone.now)
    preferred_name = models.CharField(max_length=20,default='', null=True, blank=True)
    bio = models.TextField(blank=True, default='')
    # 在这里添加此代码
    uid = models.CharField(max_length=32, default=uuid.uuid4, null=True, blank=True)
    mfa_enabled = models.BooleanField(default=False)
    enable_mfa = models.BooleanField(default=False)
    phone_number = models.CharField(default='', null=True, blank=True, max_length=15)
    verification_code = models.CharField(default='', null=True, blank=True, max_length=15)
    verification_code_length = models.IntegerField(default=6)
    mfa_code_expires = models.DateTimeField(default=timezone.now)
    mfa_attempts = models.IntegerField(default=0)

    def make_auth_token(self):
        return TimestampSigner().sign(self.uid)

    # 并添加此功能
    def create_auth_url(self):
        username, token = self.make_auth_token().split(":", 1)
        return reverse('users:mfa', kwargs={'username': username, 'token': token,})

    def check_auth_token(self, token):
        try:
            key = '%s:%s' % (self.uid, token)
            TimestampSigner().unsign(key, max_age=60 * settings.AUTH_VALID_MINUTES) # 有效3分钟
        except (BadSignature, SignatureExpired):
            return False
        return True
除了注释(与#的行上的代码)外,请确保您的用户/型号看起来像这样。打破这个,很简单。我们有一些导入器,一个时间戳签名器,它是一个加密实用程序,可以生成安全的代码并进行验证,以确保其有效,仅使用一次,而不是比一定数量的秒更古老。我们还使用UUID,这是一个唯一的标识符,可以在令牌的签名中标识我们的用户,以及在将令牌发送给用户的URL中。我们将使用此基本密码学来构建两个因素身份验证视图。在我们做其他任何事情之前,让我们运行迁移,以便更新我们的用户模型。在带有manage.py的目录中,运行以下命令以进行和完成迁移。

source venv/bin/activate
python manage.py makemigrations && python manage.py migrate
这很重要,因为每次我们对模型进行更改时,我们都需要创建表并使用默认值更新数据库,然后才能实际使用模型。 接下来,让我们即兴创作登录视图,以重定向到辅助身份验证视图。在用户/views.py中,删除登录功能并将其重定向到我们在用户模型中生成的URL。

# …数量

def login(request):
    if request.method == “POST”:
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(username=username, password=password)
        if user and user.profile.can_login < timezone.now(): # 请注意,我们现在检查用户是否可以登录
            # 删除此处的auth_login函数
            messages.success(request, 'Your password was accepted. Please continue.')
            if user.profile.mfa_enabled:
                return redirect(user.profile.create_auth_url()) # 注意我们在这里重定向到新的URL
            else: # 如果用户不使用多因素身份验证,请将其记录在其中。
                auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend')
                return redirect('feed:feed')
        else: # 如果登录未成功,
            messages.warning(request, 'Username or password incorrect. Please try again.')
            user = User.objects.filter(username=username).first() # 这是我们更新用户配置文件的部分
            if user: 
                profile = user.profile
                profile.can_login = timezone.now() + datetime.timedelta(seconds=15) # 所以他们不能再登录几秒钟
                profile.save()
    return render(request, 'users/login.html', {'form': AuthenticationForm()})
因此,这很简单,现在我们可以在创建它时将其重定向到两个因素身份验证视图。如果用户尚未添加电话号码,我们也有回退。我们将添加一个基本视图,以尽快添加电话号码并尽快登录短信。 首先,我们需要一种简单的方法来从我们的代码发送短信。为此,我们可以从许多API中进行选择,但我认为最简单的是Twilio。他们还为较小的项目提供了良好的定价,以及大量折扣。在twilio.com上创建一个帐户,填写有关项目的一些详细信息,购买电话号码,然后将API键复制到您的设置。然后,在新文件(用户/sms.py)下添加此代码。

nano users/sms.py

# 导入所有必要的软件包
from django.utils import timezone
import random
import datetime
from django.conf import settings
from feed.middleware import get_current_request
from django.contrib import messages
import traceback

account_sid = settings.TWILIO_ACCOUNT_SID
auth_token = settings.TWILIO_AUTH_TOKEN
source_phone = settings.PHONE_NUMBER

# 此代码与Twilio一起发送文本
def send_text(target_phone, text):
    from twilio.rest import Client
    try:
        client = Client(account_sid, auth_token)
        if len(target_phone) >= 11:
            message = client.messages.create(
                to=target_phone,
                from_=source_phone,
                body=text)
    except:
        print(traceback.format_exc())

# 辅助功能可以获得一个数字如此多的数字
def get_num_length(num, length):
    n = ''
    for x in range(length):
        n = n + str(num)
    return int(n)

# 发送文本以验证用户
def send_verification_text(user):
    length = user.profile.verification_code_length
    code = random.randint(get_num_length(1, length), get_num_length(9, length));
    user.profile.verification_code = code
    user.profile.mfa_code_expires = timezone.now() + datetime.timedelta(minutes=3)
    user.profile.save()
    send_user_text(user, "Your verification code for {} is {}".format(settings.SITE_NAME, str(code)))

# 向用户发送此功能的任何文本
def send_user_text(user, text):
    send_text(user.profile.phone_number, text)

# 使用此功能验证代码
def check_verification_code(user, code):
    user.profile.mfa_attempts += 1
    result = user.profile.verification_code != None and code != '' and user.profile.verification_code == code and user.profile.mfa_code_expires > timezone.now() and user.profile.mfa_attempts <= 3
    if user.profile.mfa_attempts < 3 and result:
        user.profile.verification_code_length = 6
    elif user.profile.mfa_attempts > 2 and not result:
        user.profile.verification_code_length = 8
    user.profile.save()
    return result

# 验证时间
def check_verification_time(user):
    result = user.profile.mfa_code_expires > timezone.now()
    return result
确保适当地更改设置,并使用键添加这些行:

# 确保从您的Twilio仪表板复制这些
TWILIO_ACCOUNT_SID = “<your sid>”
TWILIO_AUTH_TOKEN = “<your token>”
PHONE_NUMBER = “<your twilio phone number>”
SITE_NAME = “<Your site name>”
AUTH_VALID_MINUTES = 3 # TFA页面有效的分钟数一旦实例化
首先,我们将需要我们的两个因素身份验证视图的表格。编辑用户/forms.py,添加以下代码。

# …数量
from django import forms

# 输入我们电话号码的表格
class PhoneNumberForm(forms.Form):
    phone_number = forms.RegexField(regex=r'^\+?1?\d{9,15}$', error_messages = {'invalid': "Phone number must be entered in the format: '+999999999'. Up to 15 digits is allowed."})
    def __init__(self, *args, **kwargs):
        super(PhoneNumberForm, self).__init__(*args, **kwargs)
        self.fields['phone_number'].label = phone_number_label

# 认证的形式
class TfaForm(forms.Form):
    code = forms.IntegerField(required=False)
    def __init__(self, *args, **kwargs):
        super(TfaForm, self).__init__(*args, **kwargs)
        self.fields['code'].widget.attrs.update({'autocomplete': 'off'})
    help_texts = {
        'code': 'Please enter the six digit code after sending it to your phone with the button above.'
    }
接下来,让我们在用户/views.py中创建视图

# …数量
from django.http import HttpResponseRedirect
from .forms import PhoneNumberForm, TfaForm

def mfa(request, username, token):
    user = User.objects.filter(profile__uuid=username).first()
    if not user: return HttpResponseRedirect(reverse('verify:age') + '?next=' + request.GET.get('next') if request.GET.get('next') else '/go/' if request.user.is_authenticated and request.user.profile.vendor else '/' if request.user.is_authenticated else reverse('users:login'))
    user = get_object_or_404(User, profile__uuid=username)
    next = request.GET.get('next','')
    if not user.profile.mfa_enabled:
        if not check_verification_time(user):
            user.profile.mfa_enabled = False
            user.profile.enable_two_factor_authentication = True
            user.profile.phone_number = '+1'
            user.profile.save()
            print('Logging in user')
            auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend')
            messages.warning(request, 'Please enter a valid phone number and verify it with a code.')
            return redirect(reverse('users:mfa_onboarding'))
    if request.method == 'POST':
        form = TfaForm(request.POST)
        code = form.data['code']
        if code and code != '' and code != None:
            token_validated = user.profile.check_auth_token(token)
            p = user.profile
            is_verified = check_verification_code(user, int(code))
            p.mfa_authenticated = is_verified
            if token_validated:
                if is_verified:
                    user.profile.mfa_enabled = True
                    user.profile.save()
                    auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend')
                    p.verfication_code = None
                    p.uid = get_uuid()
                    p.save()
                    messages.success(request, 'You have been authenticated. Welcome.')
                    qs = '?'
                    for key, value in request.GET.items():
                        qs = qs + key + '=' + value + '&'
                    if next != '' and not (next.startswith('/accounts/logout/') or next.startswith('/accounts/login/') or next.startswith('/admin/login/') or next.startswith('/accounts/register/')):
                        return HttpResponseRedirect(ext)
                    elif next.startswith('/accounts/logout/') or next.startswith('/accounts/login/') or next.startswith('/accounts/register/'):
                        return redirect('feed:feed')
                    elif request.META.get('HTTP_REFERER', '/').startswith('/accounts/login/'):
                        return redirect(reverse('feed:feed'))
                    elif not next:
                        return redirect(reverse('feed:feed')
                    else:
                        return HttpResponseRedirect('feed:feed')
                else:
                    messages.warning(request, 'The code you entered was not recognized. Please try again.')
            elif not token_validated:
                messages.warning(request, 'The URL token has expired or was not recognized. Please try again.')
                logout(request)
                return redirect(reverse('users:login'))
            if p.mfa_attempts > 3:
                messages.warning(request, 'You have entered the incorrect code more than 3 times. please send yourself a new code.')
                p.verification_code = None
                p.save()
        elif user.profile.can_send_mfa < timezone.now():
            user.profile.mfa_attempts = 0
            user.profile.can_send_mfa = timezone.now() + datetime.timedelta(minutes=2)
            user.profile.save()
            send_verification_text(user)
            messages.success(request, "Please enter the code sent to your phone number. The code will expire in 3 minutes.")
        else:
            messages.warning(request, 'You are sending too many two factor authentication codes. Wait a few minutes before sending another code.')
    form = TfaForm()
    hide_logo = None
    if user.profile.hide_logo:
        hide_logo = True
    return render(request, 'users/mfa.html', {'title': 'Enter Code', 'form': form, 'xsmall': True, 'user': user, 'hide_logo': hide_logo, 'accl_logout': user.profile.shake_to_logout, 'preload': False})

@login_required
def mfa_onboarding(request):
    if request.method == 'POST':
        form = PhoneNumberForm(request.POST)
        request.user.profile.phone_number = form.data['phone_number'].replace('-', '').replace('(','').replace(')','')
        request.user.profile.mfa_enabled = True
        request.user.profile.enable_two_factor_authentication = True
        request.user.profile.save()
        messages.success(request, 'You have added a phone number to your account.')
        user = request.user
        return redirect(user.profile.create_auth_url())
    form = PhoneNumberForm(initial={'phone_number': request.user.profile.phone_number if request.user.profile.phone_number else '+1'})
    return render(request, 'users/mfa_onboarding.html', {'title': 'Enter your phone number', 'form': form, 'small': True})
我们还需要这两种观点的模板。让我们先添加MFA模板。

nano users/templates/users/mfa.html
将此HTML代码添加到模板
 
{% extends 'base.html' %}
{% block content %}
{% load app_filters %}
{% load crispy_forms_tags %}
        <form action="{{ request.path }}{% if request.GET.next %}?next={{ request.GET.next }}{% endif %}" method="POST">
            {% csrf_token %}
            <legend class="border-bottom mb-4">Enter Verification Code</legend>
            <p>Step 1: Send the code</p>
	    <i>Never share your code with anyone, as it can be used to access your account temporarily.</i>
	    <div class="form-group">
                <button class="btn btn-outline-primary" type="submit">Send code</button>
            </div>
	    <hr>
	    <p>Step 2: Enter the code</p>
            <fieldset class="form-group">
                {{ form|crispy }}
		<p>Press the enter button to send yourself the code at {{ user.profile.phone_number|securephone }}. Then, enter the code and press enter.</p>
            </fieldset>
            <div class="form-group">
                <button class="btn btn-outline-secondary" type="submit">Enter code</button>
            </div>
        </form>
{% endblock %}
 
这是非常自我解释的。表格发送代码或空代码,如果我们收到空代码,您会在视图中注意到我们发送代码。然后,我们只有两个提交按钮,这样我们就可以使用任何一个按钮发送代码。接下来,我们将添加一个简单的表格来添加电话号码。

nano users/templates/users/mfa_onboarding.html
添加以下HTML:
 
{% extends 'base.html' %}
{% block content %}
{% load crispy_forms_tags %}
        <form method="POST">
            {% csrf_token %}
            <fieldset class="form-group">
                <legend class="border-bottom mb-4">Set Up Two Factor Authentication</legend>
                {{ form|crispy }}
            </fieldset>
            <div class="form-group">
                <button class="btn btn-outline-secondary" type="submit">Add phone number</button>
            </div>
        </form>
{% endblock %}
 
此表单要简单得多,它只是呈现我们创建的电话号码表单,并让用户添加电话号码。 这看起来真的很好!只要一切都正确设置,我们就应该能够发送消息,并在添加URL模式后立即将用户登录。我们需要设置的最后一件事是配置文件视图,以便我们可以确保用户可以更改其电话号码而不登录。最终,我们将要添加一个“停止戒烟”选项,以便用户可以发短信“停止”选择退出以后的短信。 让我们将配置文件视图添加到用户/views.py。此视图将更新用户的简历,电子邮件,用户名和电话号码,并允许我们启用多因素身份验证。首先,我们将需要另外两种表格/形式。

# ...金额
class UserUpdateForm(forms.ModelForm):
    email = forms.EmailField()
    class Meta:
        model = User
        fields = ['username', 'email']

phone_number_label = 'Phone number (no spaces, parenthesis \'(\' or dashes \'-\', numbers beginning with + only)'

class ProfileUpdateForm(forms.ModelForm):
    subscribed = forms.BooleanField(required=False)
    phone_number = forms.CharField(required=False)
    def __init__(self, *args, **kwargs):
        super(ProfileUpdateForm, self).__init__(*args, **kwargs)
    class Meta:
        model = Profile
        fields = ['bio', 'phone_number', 'enable_mfa', 'subscribed']
接下来,我们可以创建一个视图以使用这两种形式。编辑用户/views.py并添加视图。

# 添加这些导入
from .forms import UserUpdateForm, ProfileUpdateForm
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_exempt
from .models import Profile
from .mfa import send_user_text

@csrf_exempt
@never_cache
@login_required
def profile(request):
    if request.method == 'POST':
        u_form = UserUpdateForm(request.POST, instance=request.user)
        p_form = ProfileUpdateForm(request.POST,
                                       request.FILES,
                                       instance=request.user.profile)
        if u_form.is_valid() and p_form.is_valid():
            new_phone_number = p_form.data['phone_number']
            u_form.save()
            profile = p_form.save(commit=False)
            profile.phone_number = profile.phone_number.replace('-', '').replace('(','').replace(')','')
            profile.save()
            if new_phone_number != oldprofile.phone_number and oldprofile.phone_number and len(oldprofile.phone_number) >= 11:
                profile.mfa_enabled = True
                profile.save()
                send_text(oldprofile.phone_number, 'Your phone number has been updated to ' + new_phone_number + '. Please refer to texts on that phone to log in. If you didnt make this change, please call us. - {}'.format(settings.SITE_NAME))
            if profile.enable_two_factor_authentication and profile.phone_number and len(profile.phone_number) < 11:
                profile.enable_two_factor_authentication = False
                messages.success(request, f'Two factor authentication can\'t be activated without entering a phone number. Please enter a phone number to enable two factor authentication.')
            profile.save()
            if new_phone_number != oldprofile.phone_number and new_phone_number and len(new_phone_number) >= 11:
                send_user_text(request.user, 'You have added this number to {} for two factor authentication. You can now use your number for two factor authentication. If you didnt make this change, please call us. - {}'.format(settings.SITE_NAME, settings.DOMAIN))
                profile.mfa_enabled = True
                profile.mfa_code_expires = timezone.now() + datetime.timedelta(minutes=3)
                profile.save()
                return redirect(profile.create_auth_url())
            messages.success(request, f'Your profile has been updated!')
            print('Profile updated')
            return redirect('users:profile')
    else:
        u_form = UserUpdateForm(instance=request.user)
        p_form = ProfileUpdateForm(instance=request.user.profile, initial={'phone_number': request.user.profile.phone_number if request.user.profile.phone_number else '+1'})
    context = {
        'u_form': u_form,
        'p_form': p_form,
        'title':'Update Your Profile',
    }
    return render(request, 'users/profile.html', context)
我们还需要此视图的模板。

nano users/templates/users/profile.html
 
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% load feed_filters%}
{% block content %}
	<h2>Edit Your Profile</h2>  
	<form method="POST" enctype="multipart/form-data" id="profile-form">
          {% csrf_token %}
          <fieldset class="form-group">
              <legend class="border-bottom mb-4 mt-4">Profile info</legend>
              {{ u_form|crispy }}
              {{ p_form|crispy }}
          </fieldset>
          <div class="form-group">
              <button class="btn btn-outline-info" type="submit">Update}</button>
          </div>
	</form>
        <p style="text-color: green;" class="hide" id="posted">Saved</p>

{% endblock content %}
{% block javascript %}
var form = document.getElementById('profile-form');
$('input').change(function(){
	var formdata = new FormData(form);
	$.ajax({
		url: window.location.href,
		type: "POST",
		data: formdata,
		processData: false,
		contentType: false,
		timeout: 1000 * 60,
                success: function(data) {
                  $(posted).removeClass("hide");
		  setTimeout(function() {
			$(posted).addClass("fade-hidden");
			setTimeout(function() {
				$(posted).addClass("hide");
				$(posted).removeClass("fade-hidden");
			}, 2000);
		  }, 2000);
                }
	});
});
{% endblock %}
 
您会注意到这是一个相当简单的表单,但是其中包含一些JavaScript,它们会在更新时自动发布表单的内容。这对拥有很有用,因此您可以进行编辑而无需每次提交。 接下来,我们需要在用户URL模式中代表所有这些视图的URL。编辑用户/urls.py并添加此代码:

# …以前的代码,导入
from django.urls import path
from . import views

app_name='users'

urlpatterns = [
# …我们先前输入的URL模式,添加接下来的三行
    path('mfa/<str:username>/<str:token>/', views.mfa, name='mfa'),
    path('mfa/onboarding/', views.mfa_onboarding, name='mfa_onboarding'),
    path('profile/', views.profile, name='profile'),
]
现在是测试我们项目的好时机。但是首先,让我们再运行另一个备份。

backup
并运行服务器。在我们部署到Linux服务器之前,最好在帐户上启用两个因素身份验证。我们将执行此操作,将其转到我们的个人资料URL,/用户/个人资料/,并在输入我们的电话号码后检查框以启用身份验证,然后提交表格。

python manage.py runserver localhost:8000
访问您的网页访问您的Web浏览器,我在此示例中使用Google Chrome,然后输入url https:// localhost:8000/eaccement/profile/profile/profile/ 如有必要,您将能够登录并启用两个因素身份验证。 该项目需要服务器运行,因此它可以真正发送邮件。但是首先,我们需要一种查看错误的方法。您会注意到,如果您在调试模式下运行服务器,则使用设置。DEBUG等于true,则服务器会自动显示错误。为了在不使用调试模式的情况下显示错误,该模式在生产服务器上是不安全的,我们应该为其添加视图。我们需要处理的最重要的错误是: 错误500-我们代码的问题 错误404-找不到的页面(损坏的URL) 错误403-允许拒绝错误 让我们添加一个新应用程序来处理这些错误,称为错误。

python manage.py startapp errors
像以前一样,将其添加到settings.py中,在installed_apps设置中,然后从app/urls.py中的某些视图中添加引用,其中应用程序是您的django项目的名称。

handler404 = 'errors.views.handler404'
handler500 = 'errors.views.handler500'
handler403 = 'errors.views.handler403'
除了错误视图,模板和一些中间件之外,我们还需要这一切。让我们将其定义为:

from django.shortcuts import render, redirect
from django.http import HttpResponse
from stacktrace.models import Error
from errors.middleware import get_current_exception
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import user_passes_test
from .logs import get_logs
from face.tests import is_superuser_or_vendor
from django.views.decorators.csrf import csrf_exempt
from errors.highlight import highlight_code
from django.shortcuts import redirect
from django.urls import reverse

# 在这里创建您的观点。
@login_required
@user_passes_test(is_superuser_or_vendor)
def logs(request):
    logs = highlight_code(get_logs())
    return render(request, 'errors/live_error.html', {'title': 'Error Logs', 'pagetitle': 'Error Logs', 'notes': 'These are the recent error logs.', 'trace': logs, 'full': True})

@login_required
@user_passes_test(is_superuser_or_vendor)
def logs_api(request):
    logs = highlight_code(get_logs())
    return HttpResponse(logs)

@login_required
def handler404(request, exception):
    if not request.path.endswith('/'): return redirect(request.path + '/')
    return render(request, 'errors/error.html', {'title': 'Error 404', 'pagetitle': 'Error 404', 'notes': 'This page was not found on the server. It may have moved or been deleted.', 'is_404': True})

def handler500(request):
    print(get_current_exception())
    user = None
    if hasattr(request, 'user') and request.user and request.user.is_authenticated:
        user = request.user
    try:
        Error.objects.create(user=user, stack_trace=get_current_exception(), notes='Logged by 500 handler.')
    except: pass
    return render(request, 'errors/error.html', {'title': 'Error 500', 'pagetitle': 'Error 500', 'notes': 'There is a problem with the server, or with a request coming from you. Thank you for your understanding while we get things set up.', 'trace': get_current_exception()})

def handler403(request, exception):
    return render(request, 'errors/error.html', {'title': 'Error 403', 'pagetitle': 'Error 403', 'notes': 'You don\'t have permission to preform this request. If you think this is in error, please contact the server administrator.', 'is_403': True})

def handler400(request, exception):
    return render(request, 'errors/error.html', {'title': 'Error 400', 'pagetitle': 'Error 400', 'notes': 'This was a bad request.'})
接下来,让我们定义中间件来处理这些错误。我们将首先以中间件的名称添加到settings.py中的middleware_classes来做到这一点。

MIDDLEWARE_CLASSES = [
    # ...以前的中间件
    'errors.middleware.ExceptionVerboseMiddleware,
]
接下来,让我们添加中间件。

from threading import local
import traceback
from django.utils.deprecation import MiddlewareMixin

_error = local()

class ExceptionVerboseMiddleware(MiddlewareMixin):
    def process_exception(self, request, exception):
        _error.value = traceback.format_exc()

def get_current_exception():
    try:
        return _error.value
    except AttributeError:
        return None

def set_current_exception(exception):
    try:
        _error.value = exception
    except AttributeError:
        print('Attribute error setting exception.')
我们添加了一个函数来通过使用螺纹本地螺纹来获取当前异常,这有助于我们跟踪代码中的任何错误。就模板而言,我们只需要一个,因为我们在视图中动态定义标题。该模板只需要渲染标题和“跟踪”,即我们从上下文中的错误追溯。

nano errors/templates/errors/error.html
 
{% extends 'base.html' %}
{% block content %}
<h1>{{ pagetitle }}</h1>
<p>{{ trace }}</p>
{% endblock %}
 
这是我们迄今为止最简单的模板,但这很容易看到我们项目中的错误。接下来,让我们在设置中禁用调试。

nano app/settings.py
找到将其设置为true的行,然后将其更改为false

DEBUG = False
继续并立即备份该应用程序。我们准备部署到远程Linux服务器,并继续添加功能。

sudo backup
在将此代码发布到服务器之前,我们应该考虑代码可能存在一些问题。根据案例,接受发布给他们的信息的网站将在发布垃圾邮件和删除垃圾邮件的困难方面存在问题。这不应该立即发生,但是如果发生这种情况,我们稍后将研究如何自动在网站上进行适度的垃圾邮件,并使机器人更难访问网站,以及如何停用用户帐户,并验证用户的身份扫描其ID或生物识别扫描,例如指纹或面部识别。 查看我们检查的多因素身份验证示例,在生产中,情况可能会有所不同。请注意,我们是如何限制登录和到期令牌的。如果机器人正在访问站点,则两个因素身份验证可能会更加困难,因为它们可能同时输入代码。为了打击这一点,让我们在用户模型中使用模型,声明我们在网站时如何与网站进行交互使用电话号码使用多因素身份验证进行身份验证。我们还将添加一个通过电子邮件进行身份验证的选项。首先使用用户模型编辑

nano users/models.py
这就是我们要添加的模型的外观。我们不需要任何方法,只是变量来存储ID,用户,时间戳,有效期,长度以及对任何多因素身份验证的尝试(发送到电话或电子邮件的123456之类的代码)。

# 一个用于登录网站的基本令牌
class MFAToken(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='mfa_tokens')
    timestamp = models.DateTimeField(default=timezone.now)
    expires = models.DateTimeField(default=timezone.now)
    token = models.CharField(default='', max_length=100)
    length = models.IntegerField(default=6)
    attempts = models.IntegerField(default=0)
    uid = models.CharField(default=uuid.uuid4, max_length=100)
我们还向用户添加特权,我们现在将手动设置它,然后最终迁移到自动招募特权用户。在用户模型中,在配置文件中添加此行:

    vendor = models.BooleanField(default=False)
与数据库的任何更改一样,我们需要在Django中编辑Models.py文件时进行迁移并迁移数据库。请记住,要做到这一点,我们首先使用源(如果终端开放以来还没有使用)然后python manage.py进行迁移和迁移。

cd project-directory-you-named # (如果需要)
source venv/bin/activate
python manage.py makemigrations && python manage.py migrate
目前,您可以使用Shell来招募您作为供应商创建的任何帐户。

python manage.py shell
from users.models import Profile
p = Profile.objects.get(user__username='Charlotte')
p.vendor = True
p.save()
exit()
现在,让我们进化我们的多因素身份验证视图以使用此令牌。首先,我们需要修改MFA助手实用程序。使用Nano,

nano users/mfa.py

from django.utils import timezone
import random
import datetime
from django.conf import settings
from feed.middleware import get_current_request
from django.contrib import messages
from .email import send_html_email
import traceback
from .models import MFAToken

account_sid = settings.TWILIO_ACCOUNT_SID
auth_token = settings.TWILIO_AUTH_TOKEN
source_phone = settings.PHONE_NUMBER

def send_text(target_phone, text):
    from twilio.rest import Client
    try:
        client = Client(account_sid, auth_token)
        if len(target_phone) >= 11:
            message = client.messages.create(
                to=target_phone,
                from_=source_phone,
                body=text + ' Text STOP to cancel.')
    except:
        messages.warning(get_current_request(), 'There was an error sending the message.')
        print(traceback.format_exc())

def get_num_length(num, length):
    n = ''
    for x in range(length):
        n = n + str(num)
    return int(n)

def send_verification_text(user, token):
    length = user.profile.verification_code_length
    code = random.randint(get_num_length(1, length), get_num_length(9, length));
    token.token = code
    token.expires = timezone.now() + datetime.timedelta(minutes=settings.AUTH_VALID_MINUTES)
    token.save()
    send_user_text(user, "Your verification code for {} is {}".format(settings.SITE_NAME, str(code)))

def send_verification_email(user, token):
    length = user.profile.verification_code_length
    code = random.randint(get_num_length(1, length), get_num_length(9, length));
    token.token = code
    token.expires = timezone.now() + datetime.timedelta(minutes=settings.AUTH_VALID_MINUTES)
    token.save()
    send_html_email(user, "Your verification code for {} is {}".format(settings.SITE_NAME, str(code)), "<p>Dear {},</p><p>Your verification code for {} is {}. Thank you for using this code to secure your account.</p><h2>{}</h2><p>Sincerely, {}</p>".format(user.profile.name, settings.SITE_NAME, str(code), str(code), settings.SITE_NAME))

def send_user_text(user, text):
    send_text(user.profile.phone_number, text)

def check_verification_code(user, token, code):
    token.attempts = token.attempts + 1
    profile = user.profile
    result = (token != None and code != '' and token.token == code and (token.expires > timezone.now()) and token.attempts <= settings.MFA_TOKEN_ATTEMPTS)
    if token.attempts < 3 and result:
        profile.verification_code_length = 6
    elif token.attempts > 1 and not result:
        profile.verification_code_length = profile.verification_code_length + 2
        if profile.verification_code_length > settings.MFA_TOKEN_LENGTH: profile.verification_code_length = settings.MFA_TOKEN_LENGTH
    token.save()
    profile.save()
    return result

# 使用其电子邮件或电话号码对用户进行身份验证
def mfa(request, username, usertoken):
    token = MFAToken.objects.filter(uid=username, expires__gt=timezone.now() + datetime.timedelta(seconds=30)).order_by('-timestamp').last() # 通过在URL中传递的值过滤令牌(uuid)
    if not token: token = MFAToken.objects.create(user=User.objects.filter(profile__uuid=username).first(), uid=username, expires=timezone.now() + datetime.timedelta(seconds=115)) # 如果没有创建此会话,请创建它
    user = User.objects.filter(id=token.user.id).first() # 从令牌中获取用户
    if not user and request.user.is_authenticated: return redirect(reverse('feed:home')) # 如果它们已经被认证,请将其记录在中
    if not user: raise PermissionDenied() # 否认是否没有找到用户
    next = request.GET.get('next','')
    if not user.profile.enable_two_factor_authentication and user.is_active and user.profile.check_auth_token(usertoken, token): # 检查验证令牌
        auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend') # 登录用户,如果尚未登录
        user.profile.mfa_expires = timezone.now() + datetime.timedelta(minutes=settings.LOGIN_VALID_MINUTES) # 在其多因素身份验证上设置有效期
        user.profile.save()
        return HttpResponseRedirect(next if next != '' else reverse('landing:landing')) # 将用户重定向到下一页
    if not user.profile.mfa_enabled: # 检查是否启用了MFA
        if not check_verification_time(user, token): # 检查时间
            user.profile.mfa_enabled = False # 清除电话号码
            user.profile.enable_two_factor_authentication = True # 启用MFA
            user.profile.phone_number = '+1' # 禁用电话号码
            user.profile.save() # 保存个人资料
            auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend') # 如果未启用其MFA,请记录用户
            messages.warning(request, 'Please enter a valid phone number and verify it with a code.')
            return redirect(reverse('users:mfa_onboarding'))
    if request.method == 'POST' and not fraud_detect(request, True): # 如果请求是邮政请求
        form = TfaForm(request.POST) # 实例化表格
        code = str(form.data.get('code', None)) # 获取代码
        if code and code != '' and code != None: # 确保它不是空的
            token_validated = user.profile.check_auth_token(usertoken) # 检查验证令牌
            p = user.profile
            is_verified = check_verification_code(user, token, code) # 检查代码
            p.mfa_authenticated = is_verified
            if token_validated: # 如果一切
                if is_verified: # 是有序的
                    user.profile.mfa_enabled = True # 启用MFA(如果尚未启用)
                    user.profile.save()
                    auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend') # 登录用户
                    face = user.faces.filter(session_key=None).last() 
                    p.mfa_expires = timezone.now() + datetime.timedelta(minutes=settings.LOGIN_VALID_MINUTES)
                    p.save()
                    messages.success(request, 'You have been authenticated. Welcome.')
                    qs = '?'
                    for key, value in request.GET.items(): # 为下一个参数构建Querystring(如果有)
                        qs = qs + key + '=' + value + '&'
                    if next != '' and not (next.startswith('/accounts/logout/') or  next.startswith('/accounts/login/') or next.startswith('/admin/login/') or next.startswith('/accounts/register/')):
                        return HttpResponseRedirect(next) # 重定向
                    elif next.startswith('/accounts/logout/') or next.startswith('/accounts/login/') or next.startswith('/accounts/register/'):
                        return redirect(reverse('/'))
                    elif request.META.get('HTTP_REFERER', '/').startswith('/accounts/login/'):
                        return redirect(reverse('/'))
                    elif not next:
                        return redirect(reverse('/'))
                    else:
                        return HttpResponseRedirect(reverse('verify:age') + '?next=' + request.META.get('HTTP_REFERER', '/'))
                else:
                    messages.warning(request, 'The code you entered was not recognized. Please try again.')
            elif not token_validated: # 如果令牌无效
                messages.warning(request, 'The URL token has expired or was not recognized. Please try again.')
                logout(request)
                return redirect(reverse('users:login'))
            if p.mfa_attempts > 3: # 如果尝试太多
                messages.warning(request, 'You have entered the incorrect code more than 3 times. please send yourself a new code.')
                p.verification_code = None
                p.save()
        elif user.profile.can_send_mfa < timezone.now():
            user.profile.mfa_attempts = 0
            user.profile.can_send_mfa = timezone.now() + datetime.timedelta(minutes=2)
            user.profile.save()
            if form.data.get('send_email', False): # 发送电子邮件(或文字)
                send_mfa_verification_email(user, token)
            else:
                send_verification_text(user, token)
            messages.success(request, "Please enter the code sent to your phone number or email. The code will expire in 3 minutes.")
        elif user.profile.can_send_mfa < timezone.now() + datetime.timedelta(seconds=115):
            messages.warning(request, 'You are sending too many two factor authentication codes. Wait a few minutes before sending another code.')
    form = TfaForm()
    hide_logo = None
    if user.profile.hide_logo:
        hide_logo = True
    if request.user.is_authenticated: return redirect(reverse('/'))
    # 渲染表格(用于获取请求)
    return render(request, 'users/mfa.html', {'title': 'Enter Code', 'form': form, 'xsmall': True, 'user': user, 'hide_logo': hide_logo, 'accl_logout': user.profile.shake_to_logout, 'preload': False, 'autofocus': request.method == 'POST'})
当我们添加此代码时,请确保导入该函数以发送电子邮件。在文件的顶部,用户视图(带有其他导入),添加

from .mfa import send_verification_email as send_mfa_verification_email
现在,我们需要在任何一个都起作用之前编写该功能。它应该扩展我们的发送电子邮件功能,并只需通过验证代码将电子邮件发送给用户。

nano users/mfa.py

def send_verification_email(user, token):
    length = user.profile.verification_code_length
    code = random.randint(get_num_length(1, length), get_num_length(9, length));
    token.token = code
    token.expires = timezone.now() + datetime.timedelta(minutes=settings.AUTH_VALID_MINUTES)
    token.save()
    send_html_email(user, "Your verification code for {} is {}".format(settings.SITE_NAME, str(code)), "<p>Dear {},</p><p>Your verification code for {} is {}. Thank you for using this code to secure your account.</p><h2>{}</h2><p>Sincerely, {}</p>".format(user.profile.name, settings.SITE_NAME, str(code), str(code), settings.SITE_NAME))
因此,这一切都很好,现在我们有了一个多因素身份验证系统,该系统取决于电话号码或电子邮件登录。但是,我们还需要一种删除或至少隐藏不符合我们条款的用户的方法。这些可能是垃圾邮件发送者,机器人或任何对我们工作不好的人。看看我在网站上监视用户的视图:

# 金额
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import user_passes_test
from .tests import is_superuser_or_vendor # 我们需要创建此测试

@login_required
@user_passes_test(is_superuser_or_vendor)
def users(request):
    # 获取用户列表
    new_today = User.objects.filter(is_active=True, date_joined__gte=timezone.now() - datetime.timedelta(hours=24)).count()
    new_this_month = User.objects.filter(is_active=True, date_joined__gte=timezone.now() - datetime.timedelta(hours=24*30)).count()
    subscribers = User.objects.filter(is_active=True, profile__subscribed=True).count()
    return render(request, 'users/users.html', { # 在模板中返回用户
        'title': 'All Accounts',
        'users': User.objects.all(),
        'new_today': new_today,
        'new_this_month': new_this_month,
        'subscribers': subscribers
    })
请注意,此代码使用测试,我们需要在tests.py文件中声明此测试并导入它。编辑用户/tests.py,让我们创建测试。

def is_superuser_or_vendor(user):
    return user.profile.vendor or user.is_superuser
这与用户/用户一起结合使用。html模板,看起来像这样:
 
{% extends 'base.html' %}
{% load app_filters %}
{% block content %}
<h1>All Registered Visitors</h1>
<p>{{ new_today|nts|capitalize }} new today, {{ new_this_month|nts }} new this month, {{ subscribers|nts }} subscribers, {{ users.count|nts }} total.</p>
<hr style="color: red;">
{% for user in users %}
{% include 'users/_user.html' %}
<hr style="color: blue;">
{% endfor %}
{% endblock %}
 
请注意,该模板包含另一个模板,用户/_user.html。当使用具有子测试且不使用扩展的模板时,在文件名之前添加下划线(_)的好主意是为了区分模板。 请注意,这是很多Jinja,您可能没有定义所有这些变量。但这就是我的代码的样子。
 
{% load app_filters %}
<div>
<img src="{{ user.profile.get_image_url }}" alt="@{{ user.profile.name }}'s profile photo" width="120" height="120" align="left" style="margin-top:5px; margin-right:10px; margin-bottom:10px; border-radius: 50%;"/>
    <div class="article-metadata">
      <p class="mr-2">@{{ user.username }} - {{ user.profile.name }} ({{ user.profile.preferred_name }})</p>
      <small class="text-muted">Last seen {{ user.profile.last_seen|date:"F d, Y" }} {{ user.profile.last_seen|time:"H:i" }}</small>
      <small class="text-muted">Joined on {{ user.profile.date_joined|date:"F d, Y" }} {{ user.profile.date_joined|time:"H:i" }}</small>
      <small>{{ user.email }}</small>
      {% if user.profile.phone_number %}<small><i class="bi bi-phone-fill"></i>{{ user.profile.phone_number }}</small>{% endif %}
      {% if user.verifications.last %}
      <small>'{{ user.verifications.last.full_name }}'</small>
      <small><i class="bi bi-123"></i> {{ user.verifications.last.document_number }}</small>
      <small><i class="bi bi-calendar-heart-fill"></i> {{ user.verifications.last.birthdate }}</small>
      <a href="{{ user|document_front }}" class="btn btn-sm btn-outline-primary" title="ID front"><i class="bi bi-person-badge-fill"></i> ID front</a>
      <a href="{{ user|document_back }}" class="btn btn-sm btn-outline-primary" title="ID back"><i class="bi bi-upc-scan"></i> ID back</a>
      {% endif %}
      <small># {{user.id}} </small>
      <small>{% if user.profile.subscribed %}Subscribed{% else %}Not subscribed{% endif %}</small>
    </div>
    {%if not user.is_superuser %}
    <div style="float: right;">{% include 'users/toggle_active.html' %}</div>
    {% endif %}
    {% autoescape off %}    
    <p class="article-content">{{ user.bio }}</p>
    {% endautoescape %}
    <hr>
    <p>{% if user.profile.identity_verified %}Verified user.{% else %}Unverified user.{% endif %} Verifications: {{ user.verifications.count|nts }}</p>
 
我们还需要另一个子板toggle_active.html。该模板应是一种使我们可以切换用户是否处于活动状态的表单。
 
<form style="display: inline-block;" action="{% url 'users:toggle-user-active' user.id %}" method="POST" id="publishForm">
<button class="btn btn-sm btn-outline-danger" type="submit">{% if user.is_active %}<i class="bi bi-eye-fill"></i>{% else %}<i class="bi bi-eye-slash-fill"></i>{% endif %}</button>
</form>
 
我们还需要添加视图以切换用户活动和适当的URL模式。当我们使用它时,让我们添加一个视图以删除用户,以防我们需要。

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
@login_required
@user_passes_test(is_superuser_or_vendor)
def toggle_user_active(request, pk):
    user = User.objects.get(id=pk)
    if request.method == 'POST':
        user.is_active = not user.is_active
        user.save()
    return HttpResponse('<i class="bi bi-eye-fill"></i>' if user.is_active else '<i class="bi bi-eye-slash-fill"></i>')


# 金额
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic import DeleteView

class UserDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
    model = User
    success_url = '/' # 成功URL的重定向
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        return context

    def test_func(self): # 测试用户是否是超级用户,并且有权删除
        user = self.get_object()
        if self.request.user != user and self.request.user.is_superuser:
            return True
        return False
尽管在必要时是实用的,但在大多数情况下,删除用户不应是必要的,但如果需要解散,我们可以切换访问网站的用户的可见性。 我们添加的URL模式看起来像这样。使用Nano,编辑用户/urls.py并添加以下行:

nano users/urls.py
这些行应在用户视图中的路径列表中,在结束“”]之前,但是在开始之后“ [”。

# …
    path('user/<int:pk>/delete/', UserDeleteView.as_view(template_name='blog/user_confirm_delete.html'), name='delete-user'),
    path('user/<int:pk>/active/', views.toggle_user_active, name='toggle-user-active'),
# …
现在,请确保备份网站,以便您可以在我们将继续进行的Web服务器上下载它。从命令行,

sudo backup
现在我们的网站已备份。 因此,现在我们有了一些有用的功能。但是这里的大局呢?该代码仍然无法从Internet访问,我们还没有邮件服务器,我们需要扩展应用程序以包括全面的验证过程以及平稳的布局,以帮助我们探索该站点,以及用于认证特权用户的安全协议。 我们将解决这一切。目前,最重要的事情就是在线获得此代码,我们只能在Ubuntu Server上使用几行BASH来进行。不过,您需要为此租用服务器,除非您在家中有服务器和允许您打开端口的业务互联网订阅。我亲自在安装在公寓中的HP Z440上运行我的网站,但通常需要便宜得多的基本租用虚拟专用服务器(VPS)。 请记住,我们现在正在运行的代码相对较薄,需要维护和改进,然后才能进行改进准备使用我们必须构建产品的东西。确保小心使用Internet的工作,请确保您是否在Linux服务器上公开部署此网站,您有计划阻止与网站的不必要的互动。一开始这可能不会是问题,但是我们将研究各种解决方案来打击这一问题,包括机器学习,人工智能和计算机视觉。当它确实成为问题时,请在本文中进一步寻找解决方案。 在租用VP的方面,您可以去很多地方。 Google Cloud拥有VPS服务器,Ionos,Kamatera,Amazon AWS和更多提供商提供适合我们需求的云服务器解决方案。 您需要单击他们的表格,然后选择一个计划才能开始。您可以与任何提供商一起使用基本计划,但是请确保提供商允许您打开端口邮件服务器端口发送电子邮件(这应该是端口587和端口25),一些提供商会阻止这些端口。到目前为止,我已经有了EST具有Ionos和Kamatera的经验,它们都可以使我发送无限的电子邮件,并且它们的价格非常便宜。 您将通过称为SSH或Secure Shell的协议连接到新服务器,该协议使您可以像个人计算机一样与服务器远程接口。设置服务器时,托管提供商可能会要求您添加SSH键,否则它们会为您提供用户名和密码。 SSH键是您将如何从命令行登录到服务器以编辑代码。使用以下SSH-Keygen选项生成SSH

ssh-keygen
保存文件并覆盖该文件,如果还没有,请旋转SSH键是很好的。现在,您可以使用以下命令查看SSH键。您将需要将其复制到远程服务器,以便可以使用它来验证。

cat ~/.ssh/id_rsa.pub
如果您在键入该命令时看不到SSH键(以“ SSH-RSA AAA”开头的长数字和字母),请尝试生成RSA键(它们更安全,所以我建议使用它们。)以下代码将生成4096位RSA SSH键。

ssh-keygen -t rsa -b 4096
创建一个运行Ubuntu的VPS,但是您打算这样做。通过单击提供者网站上的表单(kamatera.com,ionos.com或类似)创建VPS后,您将要登录。要执行此操作,请使用IP地址的SSH命令(地址看起来像xx.xx.xx.xx)。您还需要对我们创建的服务器上的默认用户名敏感,例如Ubuntu。

ssh ubuntu@XX.XX.XX.XX
可能会要求您提供密码,如果要求您输入密码,请输入。我们不会使用默认用户名,因此让我们首先创建新用户并在其帐户中添加SSH键。 让我们开始添加一个新的SSHD_CONFIG文件,该文件告诉服务器如何使用SSH。

nano sshd_config

# 这是SSHD服务器系统范围的配置文件。  看
# SSHD_CONFIG(5)以获取更多信息。

# 此SSHD与路径=/usr/local/sbin:/usr/locar/bin:/usr/sbin:/usr/bin:/sbin:/sbin:/bin:/usr/usr/gash

# 默认sshd_config中选项的策略
# OpenSSH将用其默认值指定选项
# 可能,但让他们发表评论。  未注册的选项覆盖
# 默认值。

# 港口22
# 地址
# 列表地址0.0.0.0
# 听adress ::

# hostkey/etc/ssh/ssh_host_rsa_key
# hostkey/etc/ssh/ssh_host_ecdsa_key
# HOSTKEY/ETC/SSH/SSH_HOST_ED25519_KEY

# 密码和关键
# rekeylimit默认没有

# 记录
# syslogfacity auth
# loglevel信息

# 验证:

# ligringraceTime 2m
# 允许Rootlogin禁止使用
# 严格模具是
# Maxauthtries 6
# 最大值10

PubkeyAuthentication yes

# 期望.ssh/授权_keys2将来默认情况下会被忽略。
AuthorizedKeysFile	.ssh/authorized_keys .ssh/authorized_keys2

# 授权principalsfile无

# 授权keyscommand无
# 授权keyscommanduser没人

# 为此,您还需要/etc/ssh/ssh_known_host中的主机键
# hostbasedAuthentication no
# 如果您不信任〜/.ssh/nown_hosts,则更改为“是”
# hostbasedAuthentication
# 忽略了众议员霍斯特斯
# 不要读取用户的〜/.rhosts和〜/.shost文件
# 无知的主机是

# 要禁用隧道清晰的文本密码,请在此处更改!
PasswordAuthentication no
# PliperEmptypasswords no

# 更改为“是”以启用挑战响应密码(当心问题
# 一些PAM模块和线程)
KbdInteractiveAuthentication no

# Kerberos选项
# Kerberosauthentication
# Kerberosorlocalpasswd是的
# KerberosticketCleanup是的
# Kerbersoscotted Rod No

# GSSAPI选项
# GSSAPIATHENTICATION NO
# gssapicleanupcredentials是的
# GSSAPISTRICTACCEPTORCHECK是的
# gssapikeyexchange no

# 将其设置为“是”以启用PAM身份验证,帐户处理,
# 和会话处理。如果启用了,PAM身份验证将
# 可以通过KBDInteractiveAthentication和
# 密码授权。  根据您的PAM配置,
# 通过KBDInteractiveAthentication进行的PAM身份验证可能绕过
# 设置“没有通信的允许原始斑块”。
# 如果您只希望PAM帐户和会话检查即可运行
# PAM身份验证,然后启用此验证,但设置密码授权
# 和kbdInteractiveAthentication至“ no”。
UsePAM yes

# 是的
# 允许forwarding是的
# Gatewayports no
X11Forwarding yes
# X11DISPLAYOFFSET 10
# x11uselocalhost是的
# 是的
PrintMotd no
# printlastlog是
# tcpkeepalive是的
# Persittuenvironment
# 压缩延迟
# 客户间隔0
# 客户端Alivecountmax 3
# 用过
# pidfile /run/sshd.pid
# Maxstartups 10:30:100
# pemittunl否
# chrootdirectory无
# 版本附录无

# 没有默认横幅路径
Banner /etc/banner

# 允许客户通过本地变量变量
AcceptEnv LANG LC_*

# 覆盖无子系统的默认值
Subsystem	sftp	/usr/lib/openssh/sftp-server

# 以每个用户为基础的覆盖设置的示例
# 匹配用户anoncvs
# x11福尔德编号
# 允许Forwarding no
# 允许IN
# forcecommand cvs服务器
PermitRootLogin no
请记住,Ctrl+X和Y保存文件。接下来,让我们编写一个名为initialize的基本脚本(所有用户的默认主目录中)。

nano initialize
将这些行添加到文件中,更换使用您的SSH键,您发现使用CAT。 (.ssh/id_rsa.pub)

# 要!
sudo apt install -y nano git openssh-server
sudo cp sshd_config /etc/ssh/sshd_config
sudo service ssh restart
sudo service sshd restart
echo "/root/.ssh/id_rsa" | sudo su root -c "ssh-keygen -t rsa -N ''"
echo "root ssh key:"
sudo su root -c "cat /root/.ssh/id_rsa.pub"
sudo adduser --disabled-password --gecos "" team
sudo passwd -d team
sudo usermod -aG sudo team
echo "/home/team/.ssh/id_rsa" | su team -c "ssh-keygen -t rsa -N ''"
cat /home/team/.ssh/id_rsa.pub >> /home/team/.ssh/authorized_keys
echo '<key here>' >> /home/team/.ssh/authorized_keys
echo "team ssh key:"
cat /home/team/.ssh/id_rsa.pub
要引导您浏览此文件,让我们逐行开始。第一行告诉编译器这是一个bash脚本。然后,我们正在安装依赖项,将sshd_config复制到正确的目录,重新启动ssh,为root生成ssh键,添加用户'team'(您可以选择一个您喜欢的名称,请使用带有其名称的adduser命令和禁用密码的密码,并现在)。我们还将团队添加到Sudo Group,生成他们的SSH密钥,还将我们的钥匙添加到授权密钥和他们的钥匙中,然后打印其钥匙。这个新用户将是我们登录网站的方式。 在新的终端中,继续并再次打开服务器。

ssh team@XX.XX.XX.XX
这次您不需要密码,因为您有一个SSH键。我们还禁用使用密码登录,以使网站更安全。 现在,该服务器完全空白,没有任何信息。让我们从GIT将项目克隆起来,以便我们可以在远程计算机上下载并运行它。在通过SSH连接的远程服务器上,首先打印您的SSH键:

cat ~/.ssh/id_rsa.pub
接下来,将此键粘贴到GIT设置中,就像我们之前设置GIT存储库一样。现在,我们可以将项目直接克隆到服务器。确保您首先在本地备份了该项目,以便在GIT服务器上下载。

git clone git://github.com/you/yourproject.git
完美的。现在所有文件都在这里。我们可以用LS看到它们

ls
现在,让我们开始设置服务器。首先,将您的项目目录复制到我们将用于该项目的简单,令人难忘的名称。

cp -r yourproject whatyoucalledit
“ WhatYouCalledit”是您项目的新名称。接下来,我们需要构建一个基本实用程序来设置服务器。我们将保存该公用事业,并将来使用它。要构建此实用程序,让我们创建一个用户二进制文件以定义我们如何编辑脚本。使用bash,编辑/usr/bin/ascript

sudo nano /usr/bin/ascript
确保在那里使用sudo,以便您可以权限编辑文件。在文件中,添加以下行:

# 要!
if [ ! -f /usr/bin/$1 ]; then
    sudo touch /usr/bin/$1
    echo "# 要!
    sudo chmod a+x /usr/bin/$1
    sudo nano /usr/bin/$1
    echo $1 | sudo tee -a /etc/ascripts
else
    sudo chmod a+x /usr/bin/$1
    sudo nano /usr/bin/$1
fi
请记住,此脚本以参数为脚本名称为$ 1。首先,它检查文件是否存在或以其他方式创建该文件,添加了要声明脚本的第一行是bash,更改其权限,对其进行编辑并将其名称添加到 /etc /ascripts,这使我们能够存储我们存储脚本名称的名称正在创建。如果文件已经存在,只需更改权限并将其编辑。保存文件,接下来我们将更改其权限。只要我们使用此脚本,我们就不必再这样做了。

sudo chmod a+x /usr/bin/ascript
完美的。现在,让我们创建一个称为设置的脚本。首先,不要让您不知所措,而是看看我的设置脚本的样子。我们将介绍该脚本在您的项目中的外观,您将不需要我脚本中的所有内容。

# 要!
SECONDS=0
PYTHON_VERSION=3.12
echo "femmebabe installer initialized."
# sudo chmod a+x脚本/usersetup
# ./scripts/usersetup
# ssh-keyen
# 项目目录
DIR="/home/team/femmebabe"
USER="team"
# 日志命令
echo "Logging commands"
sudo cp log/commands.log /var/log/commands.log
sudo chmod -R a+w /var/log
sudo chown -R :syslog /var/log
echo $'alias venv="source /home/team/femmebabe/venv/bin/activate"' | sudo tee -a /home/team/.profile
echo $'PROMPT_COMMAND=\'RETRN_VAL=$?;logger -p local6.debug "$(whoami) [$$]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" )"\'' | sudo tee -a /etc/bashrc
echo $'PROMPT_COMMAND=\'RETRN_VAL=$?;logger -p local6.debug "$(whoami) [$$]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" )"\'' | sudo tee -a "/home/team/.bashrc"
echo $'PROMPT_COMMAND=\'RETRN_VAL=$?;logger -p local6.debug "$(whoami) [$$]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" )"\'' | sudo tee -a /root/.bashrc
echo "source /etc/bashrc" | sudo tee -a /home/team/.profile
echo "/var/log/commands.log" | sudo tee -a /etc/logrotate.d/syslog
echo "local6.*    /var/log/commands.log" | sudo tee -a "/etc/rsyslog.d/bash.conf"
sudo service rsyslog restart
# 纳米配置
echo "set tabsize 4" >> .nanorc
echo "set tabstospaces" >> .nanorc
# git config
echo "Git configuration"
sudo git config --global user.email "jasper.camber.holton@gmail.com" && sudo git config --global user.name "Jasper Holton"
git config --global user.email "jasper.camber.holton@gmail.com"
git config --global user.name "Jasper Holton"
git config --global --add safe.directory $"$DIR"
sudo ssh-keyscan -t rsa gitlab.com | sudo tee -a /root/.ssh/known_hosts
sudo ssh-keyscan -t rsa github.com | sudo tee -a /root/.ssh/known_hosts
echo "Mounting setup"
sudo mount -o remount,size=16G,exec /tmp
# 更新并安装
echo "Update and install packages"
sudo apt update && sudo NEEDRESTART_MODE=a apt upgrade -y
sudo apt purge postgresql-client-14 postgresql-client-common postgresql-common postgresql-contrib postgresql -y
echo "postfix postfix/mailname string femmebabe.com" | sudo debconf-set-selections
echo "postfix postfix/main_mailer_type string 'Internet Site'" | sudo debconf-set-selections
sudo NEEDRESTART_MODE=a DEBIAN_FRONTEND=noninteractive apt install -y postfix
sudo NEEDRESTART_MODE=a apt install -y rkhunter clamav-daemon libx264-dev ffmpeg libapache2-mod-wsgi-py3 apache2 cmake python-is-python3 python3-venv python3-pip python3-django expect tesseract-ocr openjdk-8-jdk redis-server libopencv-dev python3-opencv python3-dev libsasl2-dev opendkim opendkim-tools dovecot-core dovecot-pop3d dovecot-imapd auditd procmail libpq-dev postgresql postgresql-contrib libheif-dev snapd git software-properties-common certbot python3-certbot-apache
echo "-a exit,always -F arch=b64 -F euid=0 -S execve" | sudo tee -a /etc/audit/audit.rules
echo "-a exit,always -F arch=b32 -F euid=0 -S execve" | sudo tee -a /etc/audit/audit.rules
# 启用Clamav防病毒软件
echo "Starting antivirus"
sudo systemctl enable clamav-daemon
sudo systemctl start clamav-daemon
# 设置主机名
echo "127.0.0.1 femmebabe" | sudo tee -a /etc/hosts
sudo hostnamectl set-hostname localhost
# 设置Postgres
echo "Postgres setup"
sudo -u postgres psql -U postgres -c "DROP DATABASE database;"
sudo -u postgres psql -U postgres -c "CREATE DATABASE database;"
sudo -u postgres psql -U postgres -c "CREATE USER django WITH PASSWORD 'password';"
sudo -u postgres psql -U postgres -c "ALTER ROLE django SET client_encoding TO 'utf8';"
sudo -u postgres psql -U postgres -c "ALTER ROLE django SET default_transaction_isolation TO 'read committed';"
sudo -u postgres psql -U postgres -c "ALTER ROLE django SET timezone TO 'UTC';"
sudo -u postgres psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE database TO django;"
# 设置备份数据库
echo "Building database from backup, this may take a while."
cat db.json.?? > db.json
echo "Configuring firewall"
sudo ufw default allow outgoing
sudo ufw default deny incoming
sudo ufw allow 22
sudo ufw allow http
sudo ufw allow https
sudo ufw allow 'Postfix'
sudo ufw allow 'Postfix SMTPS'
sudo ufw allow 'Postfix Submission'
sudo ufw allow 'Dovecot POP3'
sudo ufw allow 'Dovecot Secure POP3'
sudo ufw allow 110/tcp
sudo ufw allow 25/tcp
echo "y" | sudo ufw enable
# 禁用的iPatables
echo "Configuring firewall"
sudo iptables -P INPUT ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -F
sudo iptables-save
# 安装BitDefender
cd $DIR
echo "Runnning BitDefender antivirus installer"
wget https://cloud.gravityzone.bitdefender.com/Packages/NIX/0/7aTSsy/setup_downloader.tar
mkdir bitdefender
tar -xf setup_downloader.tar -C bitdefender
sudo rm setup_downloader.tar
sed -i -e 's/{LOGINPASSWD/z&A*3BPd_qBGUMs/g' bitdefender/installer
sudo chmod a+x bitdefender/installer
sudo ./bitdefender/installer
# 设置后缀
cd $DIR
echo "Mail services configuration"
sudo cp /etc/postfix/main.cf /etc/postfix/main.cf.backup
sudo cp config/etc_postfix_main.cf /etc/postfix/main.cf
sudo cp config/etc_postfix_master.cf /etc/postfix/master.cf
sudo cp config/etc_default_opendkim /etc/default/opendkim
sudo cp config/etc_dovecot_conf.d_10-auth.conf /etc/dovecot/conf.d/10-auth.conf
sudo cp config/etc_dovecot_conf.d_10-master.conf /etc/dovecot/conf.d/10-master.conf
sudo cp config/etc_dovecot_dovecot.conf /etc/dovecot/dovecot.conf
sudo cp config/etc_dovecot_passwd /etc/dovecot/passwd
sudo cp config/etc_opendkim.conf /etc/opendkim.conf
sudo cp config/etc_default_opendkim /etc/default/opendkim
sudo adduser postfix opendkim
sudo mkdir /etc/opendkim
sudo mkdir /etc/opendkim/keys
sudo mkdir /etc/opendkim/keys/femmebabe.com
sudo mkdir /var/spool/postfix/opendkim
sudo echo "*@femmebabe.com     sendonly._domainkey.femmebabe.com" | sudo tee -a /etc/opendkim/signing.table
sudo echo "sendonly._domainkey.femmebabe.com    femmebabe.com:sendonly:/etc/opendkim/keys/femmebabe.com/sendonly.private" | sudo tee -a /etc/opendkim/key.table
sudo echo "127.0.0.1" | sudo tee -a /etc/opendkim/trusted.hosts
sudo echo "localhost" | sudo tee -a /etc/opendkim/trusted.hosts
sudo echo "" | sudo tee -a /etc/opendkim/trusted.hosts
sudo echo "*.femmebabe.com" | sudo tee -a /etc/opendkim/trusted.hosts
sudo chown -R opendkim:opendkim /etc/opendkim
sudo opendkim-genkey -b 2048 -d femmebabe.com -D /etc/opendkim/keys/femmebabe.com -s sendonly -v
sudo chmod go-rw /etc/opendkim/keys
sudo chown opendkim:opendkim /etc/opendkim/keys/femmebabe.com/sendonly.private
sudo chown opendkim:postfix /var/spool/postfix/opendkim
cd $DIR
sudo cp mailbox/* /var/mail/
sudo chown :users /var/mail/*
sudo chmod -R a+rwx /var/mail/*
sudo systemctl restart opendkim postfix dovecot
# 创建DIRS
cd $DIR
mkdir media/audio
mkdir media/audio/fingerprints
mkdir media/security
mkdir media/secure
mkdir media/secure/media
mkdir media/secure/video
mkdir media/secure/profile
mkdir media/secure/face
mkdir media/images
mkdir media/live
mkdir media/live/files
mkdir media/live/stills
mkdir media/files
mkdir temp
mkdir temp/data
mkdir temp/gfpgan
mkdir mail/inbox
mkdir mailbox
# 设置Virtuealenv
cd $DIR
echo "Creating virtual environment"
python -m venv venv
source venv/bin/activate
# 获得并建立依赖性
echo "Getting and building dependencies, this may take a whike"
cd $DIR
git clone https://github.com/sukhitashvili/violence-detection.git
cp config/vd-requirements.txt violence-detection/requirements.txt
cp config/vd-model.py violence-detection/model.py
cd violence-detection
pip3 install -r requirements.txt
cd $DIR
wget https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.3.pth -P experiments/pretrained_models
git clone https://github.com/TencentARC/GFPGAN.git
git clone https://github.com/davisking/dlib.git
cd dlib
mkdir build; cd build; cmake ..; cmake --build .
cd ..
source venv/bin/activate
python setup.py install
cd $DIR
source venv/bin/activate
cd $DIR/GFPGAN/
echo "Installing python dependencies"
pip install basicsr
pip install facexlib
pip install -r requirements.txt
python setup.py develop
pip install realesrgan
cd $DIR
sudo chown -R team:users gfpgan
echo "Installing ta-lib"
wget https://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz
tar xvzf ta-lib-0.4.0-src.tar.gz
sudo rm ta-lib-*
cd ta-lib
sudo ./configure
sudo make
sudo make install
# 设置防火墙规则
cd $DIR
# 安装PYPI依赖性
echo "Installing remaining python dependencies (this may take a while)"
sudo systemctl mask tmp.mount
cd $DIR
source venv/bin/activate
pip3 install -U "celery[redis]"
pip3 install -r requirements.txt --use-deprecated=legacy-resolver --use-pep517
pip3 install --upgrade opencv-python # == 4.5.4.60
pip3 install --upgrade opencv-contrib-python # == 4.5.4.60
# PIP安装OpenCV-Python == 4.5.5.64
# PIP安装OpenCV-Contrib-Python == 4.5.5.64
pip3 install --upgrade opencv-python-headless
pip3 uninstall channels
pip3 uninstall daphne
pip3 install channels["daphne"]
pip3 install Pillow==9.5.0
pip3 install librosa
pip3 install -U 'Twisted[tls,http2]'
pip3 install --upgrade certifi requests urllib3 numpy oauthlib twisted pyjwt sqlparse cryptography astral webauthn docbarcodes pdf417 deepface --no-cache-dir
pip3 install tensorflow==2.15.1
# 安装certbot
echo "Installing certificates"
sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo snap install redis
sudo systemctl enable apache2
sudo systemctl start apache2
# 运行certbot
sudo certbot --apache --non-interactive --agree-tos --domains femmebabe.com --email jasper.camber.holton@gmail.com
# 重新加载邮件服务器
sudo systemctl restart opendkim postfix dovecot
# 复制证书
# sudo cp /etc/letsencrypt/live/femmebabe.com/privkey.pem privkey.pem
# sudo cp /etc/lettesencrypt/live/femmebabe.com/cert.pem cert.pem
# 修补
cp scripts/content.py $"/home/team/femmebabe/venv/lib/python${PYTHON_VERSION}/site-packages/pyxb/binding/content.py"
cp scripts/pwa_webpush_forms.py $"/home/team/femmebabe/venv/lib/python${PYTHON_VERSION}/site-packages/pwa_webpush/forms.py"
cp scripts/webauth_views.py $"/home/team/femmebabe/venv/lib/python${PYTHON_VERSION}/site-packages/webauth/views.py"
cp scripts/json.py $"venv/lib/python${PYTHON_VERSION}/site-packages/django/core/serializers/json.py"
# 设置用户设置
sudo gpasswd -a www-data users
# 设置权限
echo "Setting permissions"
sudo chown -R team:users cache/
sudo chmod a+rwx -R cache/
# sudo chown -r团队:用户/var/run/
# sudo chown root:root/run/sudo/ts -r
sudo chown -R redis:redis /var/lib/redis
sudo chown -R redis:redis /var/log/redis
sudo chmod -R u+rwX,g+rwX,u+rx /var/log/redis
sudo chmod +r /etc/redis/redis.conf
sudo chown -R team:users /var/log/
sudo chown -R :users .././
sudo chmod -R g+rwX ./
sudo chmod -R g+rX .././
sudo chmod -R g-rwX ../.ssh
sudo chmod 774 ./
# sudo chmod 664 db.sqlite3
# sudo chown www-data:用户db.sqlite3
sudo chown -R www-data:www-data media/
sudo chown www-data:users ./
sudo chown -R team:users media/
sudo chown -R team:users ./
sudo chown -R team:users ./gfpgan/
sudo chown -R team:users ./temp/
sudo chmod a+r team /var/mail/$USER
# 复制配置并设置权限
echo "Configuring remaining services"
sudo cp config/apis.json /etc/apis.json
sudo cp config/config.json /etc/config.json
sudo cp config/femmebabe-le-ssl.conf /etc/apache2/sites-available/femmebabe-le-ssl.conf
sudo cp config/etc_dovecot_passwd /etc/dovecot/passwd
sudo cp config/etc_init.d_celery /etc/init.d/celery
sudo cp config/etc_init.d_celerybeat /etc/init.d/celerybeat
sudo cp config/etc_default_celerybeat /etc/default/celerybeat
sudo cp config/etc_default_celery /etc/default/celery
sudo cp config/etc_systemd_system_daphne.service /etc/systemd/system/daphne.service
sudo cp config/etc_systemd_system_celery.service /etc/systemd/system/celery.service
sudo cp config/etc_systemd_system_celerybeat.service /etc/systemd/system/celerybeat.service
sudo chmod a+x /etc/init.d/celery
sudo chmod a+x /etc/init.d/celerybeat
# 数据库设置
echo "Running migrations, this should be quick"
python manage.py makemigrations
python manage.py migrate --run-syncdb
echo "Loading data, this may take a while"
python manage.py loaddata db.json
echo "Setup crontab/sudoers configuration"
sudo crontab -l -u root | cat - config/crontab | sudo crontab -u root -
sudo sh -c "cat config/sudoers >> /etc/sudoers"
# 注入PAM配置并删除错误的SSH配置
# sudo sed -i''''$ d'/tc/pam.d/sshd
# sudo sed -i'' - 和$ d' /etc /profile
echo "session required pam_exec.so seteuid /home/team/femmebabe/pam.sh" | sudo tee -a /etc/pam.d/sshd
echo "session required pam_exec.so seteuid /home/team/femmebabe/logout.sh" | sudo tee -a /etc/pam.d/sshd
sudo chmod a+x pam.sh
sudo rm /etc/ssh/sshd_config.d/50-cloud-init.conf
# 复制bin脚本并设置权限
echo "Copying scripts"
sudo cp scripts/reload /usr/bin/
sudo cp scripts/check /usr/bin/
sudo cp scripts/enagpu /usr/bin/
sudo cp scripts/disgpu /usr/bin/
sudo cp scripts/activate /usr/bin/
sudo cp scripts/backup /usr/bin/
sudo cp scripts/ascript /usr/bin/
sudo cp scripts/setup /usr/bin/
sudo cp scripts/addsetup /usr/bin/
sudo cp scripts/watchlogs /usr/bin/
sudo cp scripts/logs /usr/bin/
sudo cp scripts/cmds /usr/bin/
sudo cp scripts/setup /usr/bin/
sudo cp scripts/pushweb /usr/bin/
sudo cp scripts/purgecache /usr/bin/
sudo cp config/banner /etc/banner
cd /usr/bin/
sudo chmod a+x activate
sudo chmod a+x backup
sudo chmod a+x ascript
# 重新加载和启用服务
echo "Enabling services"
sudo systemctl daemon-reload
sudo systemctl enable daphne.service
sudo systemctl enable celery.service
sudo systemctl enable celerybeat.service
sudo systemctl enable clamav-daemon
sudo systemctl start daphne.service
sudo systemctl start celery.service
sudo systemctl start celerybeat.service
sudo systemctl start clamav-daemon
# 启用Apache模块
echo "Enabling apache2"
sudo a2enmod rewrite
sudo a2enmod wsgi
sudo a2enmod headers
sudo a2enmod ssl
sudo a2enmod proxy
sudo a2enmod proxy_balancer
sudo a2enmod proxy_http
sudo a2enmod proxy_wstunnel
# sudo a2dismod mpm_event
# sudo a2dismod mpm_worker
# sudo a2enmod mpm_prefork
# 禁用默认网站
sudo a2dissite 000-default
sudo a2dissite 000-default-le-ssl
# 启用站点
sudo a2ensite femmebabe-le-ssl
# 重新加载守护程序和重新启动Apache,Postfix和Opendkim
sudo systemctl daemon-reload
sudo systemctl restart apache2
sudo systemctl restart opendkim postfix
sudo systemctl start daphne
# 设置权限
sudo chown -R :www-data /var/www/
sudo chown -R :www-data /var/www/.deepface
# 交换配置
echo "Allocating swap, this may take a while"
sudo swapoff /swapfile
sudo rm /swapfile
sudo fallocate -l 8G /swapfile
sudo dd if=/dev/zero of=/swapfile bs=1024 count=8388608
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo "/swapfile swap swap defaults 0 0" | sudo tee -a /etc/fstab
sudo swapon --show
# 初始化标题引擎
echo "Initializing routine caption"
/home/team/femmebabe/venv/bin/python /home/team/femmebabe/routine_caption.py
/home/team/femmebabe/venv/bin/python /home/team/femmebabe/setup_mail.py
# 设置git
echo "Setting up git"
cd $DIR
sudo rm -r .git
git init --initial-branch=main
echo "Setting user password"
sudo usermod --password $(echo team | openssl passwd -1 -stdin) team
# 显示IPv6和OpendKim用于域配置
echo "COPY the below information to domain configuration."
hostname -I
ip a | grep inet
ip -6 addr | grep "scope link"
sudo cat /etc/opendkim/keys/femmebabe.com/sendonly.txt | tr -d '\n' | sed 's/\s//g' | sed 's/""//g' | awk -F'[)(]' '{print $2}'
# 设置完成
echo "Setup completed in"
wc -l scripts/setup
echo "lines of code."
echo "Total time:"
duration=$SECONDS
echo "$((duration / 60)) minutes and $((duration % 60)) seconds elapsed."
echo "TODO:"
echo "- COPY above IPv6 address to domain DNS configuration"
echo "- COPY domain key to domain DNS configuration"
echo "- ADD new git repository with git remote add originlab <repo>."
echo "- OPEN port 25"
echo "- INSTALL antivirus as per reccomendations"
echo "- TEST"
echo "If neccesary,"
echo "- DEBUG"
echo "- FIX setup and backup scripts"
echo "- Fix server"
echo ""
echo "Thank you for using the femmebabe installer. Have a great day!"
echo "Goodbye."
那是很多设置!简而言之,此代码记录命令,配置纳米和git,副本上的文件,下载和安装Ubuntu Apt软件包,Python依赖关系,配置PostFix,配置PostgreSQL(数据库服务器)(数据库服务器)并加载数据库,配置UFW(不复杂的Firewallall),一个不复杂的FirewallAll),,一个不复杂的FirewallAll),禁用iPtables,下载防病毒软件,制作目录,克隆依赖项,安装证书并设置服务器,安装配置,启动和启用dever,分配交换,设置权限并打印IP,IPv6地址和OpenDKIM密钥。相当简单,但看起来很多代码。我们不需要很多,因为我们没有依赖关系,我们不使用芹菜,芹菜或达芙妮,但是无论如何我们都会安装其中的一些来开始。请注意,此代码已声明了几次域。 我们还需要购买一个域名(这是每年的少量费用)。我建议Squarespace购买域,它们的布局是直观且易于使用。您可以购买自己选择的任何域,但是在此示例中,我使用的是femmebabe.com。购买域后,请前往Squarespace DNS配置面板,并通过IP地址添加将您的域指向服务器的A记录。看起来应该这样: @ a xx.xx.xx.xx 将 @运算符作为主机,这意味着该域下的所有子域和根域都将重定向到服务器。还有更多的记录要声明,但是一旦我们准备发送邮件,我们就可以继续进行这些记录。请记住,您可能需要几天才能成功从服务器发送邮件。我们设置的DNS记录将需要时间进行传播。 无论如何,我们唯一需要开始的记录是A记录。因此,现在我们可以根据项目填写以下脚本并运行它。 让我们从一个较小的设置脚本开始,只需安装我们需要的基本进度所需的内容即可。我们不会使用这么多依赖项或postgresql,我们只会提高基本的HTTP服务器,并担心完成该服务器。请记住,要获得HTTPS证书并安全运行服务器,我们将需要购买域名以及租金服务器。现在,用您的项目目录中的用户名称“ dir”替换本文件中的“团队”,并在<>标签中提供电子邮件和域。 此外,在运行此代码之前,我们需要将设置更改为防火墙托管提供商支持(如果有)。通常,这是在您的托管提供商的“网络”选项卡中,或者如果您是自托管,则在路由器的“端口转发”部分中。如果您使用的是自托管,您还需要通过服务器计算机的地址通过路由器设置静态IP。您将需要打开以下端口以进行读/写访问。 22(SSH) 25(邮件) 587(邮件) 110(邮件客户端) 80(http) 443

# 要!
SECONDS=0
PYTHON_VERSION=3.12
echo "femmebabe installer initialized."
DIR="/home/team/<yourproject>"
USER="team"
# 日志命令
echo "Logging commands"
sudo cp log/commands.log /var/log/commands.log
sudo chmod -R a+w /var/log
sudo chown -R :syslog /var/log
echo $'alias venv="source /home/team/femmebabe/venv/bin/activate"' | sudo tee -a /home/team/.profile
echo $'PROMPT_COMMAND=\'RETRN_VAL=$?;logger -p local6.debug "$(whoami) [$$]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" )"\'' | sudo tee -a /etc/bashrc
echo $'PROMPT_COMMAND=\'RETRN_VAL=$?;logger -p local6.debug "$(whoami) [$$]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" )"\'' | sudo tee -a "/home/team/.bashrc"
echo $'PROMPT_COMMAND=\'RETRN_VAL=$?;logger -p local6.debug "$(whoami) [$$]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" )"\'' | sudo tee -a /root/.bashrc
echo "source /etc/bashrc" | sudo tee -a /home/team/.profile
echo "/var/log/commands.log" | sudo tee -a /etc/logrotate.d/syslog
echo "local6.*    /var/log/commands.log" | sudo tee -a "/etc/rsyslog.d/bash.conf"
sudo service rsyslog restart
# 纳米配置
echo "set tabsize 4" >> .nanorc
echo "set tabstospaces" >> .nanorc
# git config
echo "Git configuration"
sudo git config --global user.email "<youremail>@gmail.com" && sudo git config --global user.name "<yourname>"
git config --global --add safe.directory $"$DIR"
sudo ssh-keyscan -t rsa gitlab.com | sudo tee -a /root/.ssh/known_hosts
sudo ssh-keyscan -t rsa github.com | sudo tee -a /root/.ssh/known_hosts
# 更新并安装
echo "Update and install packages"
sudo apt update && sudo NEEDRESTART_MODE=a apt upgrade -y
sudo apt purge postgresql-client-14 postgresql-client-common postgresql-common postgresql-contrib postgresql -y
echo "postfix postfix/mailname string femmebabe.com" | sudo debconf-set-selections
echo "postfix postfix/main_mailer_type string 'Internet Site'" | sudo debconf-set-selections
sudo NEEDRESTART_MODE=a DEBIAN_FRONTEND=noninteractive apt install -y postfix
sudo NEEDRESTART_MODE=a apt install -y rkhunter clamav-daemon libx264-dev ffmpeg libapache2-mod-wsgi-py3 apache2 cmake python-is-python3 python3-venv python3-pip python3-django expect tesseract-ocr openjdk-8-jdk redis-server libopencv-dev python3-opencv python3-dev libsasl2-dev opendkim opendkim-tools dovecot-core dovecot-pop3d dovecot-imapd auditd procmail libpq-dev postgresql postgresql-contrib libheif-dev snapd git software-properties-common certbot python3-certbot-apache
# 启用Clamav防病毒软件
echo "Starting antivirus"
sudo systemctl enable clamav-daemon
sudo systemctl start clamav-daemon
# 设置主机名
echo "127.0.0.1 femmebabe" | sudo tee -a /etc/hosts
sudo hostnamectl set-hostname femmebabe
# 设置备份数据库
echo "Building database from backup, this may take a while."
cat db.json.?? > db.json
echo "Configuring firewall"
sudo ufw default allow outgoing
sudo ufw default deny incoming
sudo ufw allow 22
sudo ufw allow http
sudo ufw allow https
sudo ufw allow 'Postfix'
sudo ufw allow 'Postfix SMTPS'
sudo ufw allow 'Postfix Submission'
sudo ufw allow 'Dovecot POP3'
sudo ufw allow 'Dovecot Secure POP3'
sudo ufw allow 110/tcp
sudo ufw allow 25/tcp
echo "y" | sudo ufw enable
# 禁用的iPatables
echo "Configuring firewall"
sudo iptables -P INPUT ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -F
sudo iptables-save
# 设置Virtuealenv
cd $DIR
echo "Creating virtual environment"
python -m venv venv
source venv/bin/activate
pip3 install -r requirements.txt
# 安装certbot
echo "Installing certificates"
sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo snap install redis
sudo systemctl enable apache2
sudo systemctl start apache2
# 运行certbot
sudo certbot --apache --non-interactive --agree-tos --domains femmebabe.com --email <youremail>@gmail.com
# 设置用户设置
sudo gpasswd -a www-data users
# 设置权限
echo "Setting permissions"
sudo chown -R team:users cache/
sudo chmod a+rwx -R cache/
# sudo chown -r团队:用户/var/run/
# sudo chown root:root/run/sudo/ts -r
sudo chown -R redis:redis /var/lib/redis
sudo chown -R redis:redis /var/log/redis
sudo chmod -R u+rwX,g+rwX,u+rx /var/log/redis
sudo chmod +r /etc/redis/redis.conf
sudo chown -R team:users /var/log/
sudo chown -R :users .././
sudo chmod -R g+rwX ./
sudo chmod -R g+rX .././
sudo chmod -R g-rwX ../.ssh
sudo chmod 774 ./
sudo chown -R www-data:www-data media/
sudo chown www-data:users ./
sudo chown -R team:users media/
sudo chown -R team:users ./
# 重新加载和启用服务
echo "Enabling services"
sudo systemctl daemon-reload
sudo systemctl enable clamav-daemon
sudo systemctl start clamav-daemon
# 启用Apache模块
echo "Enabling apache2"
sudo a2enmod rewrite
sudo a2enmod wsgi
sudo a2enmod headers
sudo a2enmod ssl
sudo a2enmod proxy
sudo a2enmod proxy_balancer
sudo a2enmod proxy_http
sudo a2enmod proxy_wstunnel
# 重新加载守护程序和重新启动Apache,Postfix和Opendkim
sudo systemctl daemon-reload
sudo systemctl restart apache2
sudo systemctl restart opendkim postfix
# 显示IPv6和OpendKim用于域配置
echo "COPY the below information to domain configuration."
hostname -I
ip a | grep inet
ip -6 addr | grep "scope link"
在运行此代码之前,请确保您购买的域已连接到服务器。为此,请在本地计算机上打开一个终端,然后使用您的域运行此命令:

ping femmebabe.com # ping之后,在此处插入您的域
如果所有人看起来都很好并且服务器正在发送响应,那么我们准备运行脚本并安装软件包以及启动,启用和认证我们的Apache服务器。 这不是配置PostFix所需的所有设置,我们将稍后再查看该设置。现在,运行此设置代码,需要花费几分钟的时间才能安装和证明您的服务器。再次,请确保根据您购买的名称在脚本中替换名称,电子邮件和域名。 现在已经配置了服务器,您可以在任何Web浏览器中转到URL并检查服务器正在运行HTTPS。如果不是这样,请尝试等待一会儿DNS记录赶上来,然后运行以下命令重试证书认证:

sudo certbot --apache --non-interactive --agree-tos --domains <domain>.com --email <youremail>@gmail.com
只要您正确配置了所有内容,就应该能够访问Apache的默认页面,以了解您的代码正在工作并显示实时网页。接下来,让我们编辑设置。py以将我们的默认调试模式更改为生产。我们还将在设置以及内部IPS中配置域。

nano yourproject/settings.py
在设置中,更改/添加这些行。

DEBUG = False

# 站点配置
SITE_NAME = 'Femme Babe'
PROTOCOL = 'https'
DOMAIN = 'femmebabe.com'
SITE_ID = 1
BASE_URL = PROTOCOL + '://' + DOMAIN
ALLOWED_HOSTS = [DOMAIN]

INTERNAL_IPS = [
    'XX.XX.XX.XX',
]
现在,我们需要配置Apache2。让我们编辑配置文件,我们将使用此行部署:

sudo nano /etc/apache2/sites-available/femmebabe-le-ssl.conf
此配置文件中应包含我们的域名,以及用户和项目的名称。我正在使用域名femmebabe.com,用户名团队和项目名称femmebabe。

ServerSignature Off
ServerTokens Prod
<IfModule mod_ssl.c>
<VirtualHost *:80> 
	Redirect permanent / https://femmebabe.com/
</VirtualHost>
<VirtualHost *:443>
	ServerName femmebabe.com
	ServerAdmin team@femmebabe.com
	DocumentRoot /var/www/html

	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined
	
	Alias /static /home/team/femmebabe/static
	<Directory /home/team/femmebabe/static>
		Require all granted
	</Directory>

Alias /media/icons /home/team/femmebabe/media/
<Directory /home/team/femmebabe/media>
Require all granted
</Directory>

	<Directory /home/team/femmebabe/femmebabe>
		<Files wsgi.py>
			Require all granted
		</Files>
	</Directory>

	WSGIScriptAlias / /home/team/femmebabe/femmebabe/wsgi.py
	WSGIDaemonProcess femmebabe python-path=/home/team/femmebabe/ python-home=/home/team/femmebabe/venv header-buffer-size=100000000000 user=team
	WSGIProcessGroup femmebabe
	WSGIApplicationGroup %{GLOBAL}
	
	<Directory /home/team/femmebabe/static>
                Options Indexes FollowSymLinks
                AllowOverride All
	</Directory>

	<IfModule mod_rewrite.c>
		RewriteEngine on
		RewriteCond %{REQUEST_URI} \.(css|webp|webm|gif|png|mp3|wav|jpeg|jpg|svg|webp)$ [NC]
		RewriteCond %{HTTP_REFERER} !^https://femmebabe.com/media/.*$ [NC]
		RewriteRule ^(.+?)/$ /media/$1 [F,L]
	</IfModule>

	Include /etc/letsencrypt/options-ssl-apache.conf
	SSLCertificateFile /etc/letsencrypt/live/femmebabe.com/fullchain.pem
	SSLCertificateKeyFile /etc/letsencrypt/live/femmebabe.com/privkey.pem

	Header set X-Frame-Options: "SAMEORIGIN"
	Header set Access-Control-Allow-Origin "https://femmebabe.com"

	TimeOut 60000
	LimitRequestBody 0

	<FilesMatch ".(ico|pdf|flv|jpg|jpeg|png|gif|webp|JPG|JPEG|wav|mp3|mp4|public|js|css|swf|webp|svg)$">
		Header set Cache-Control "max-age=30, public"
	</FilesMatch>
</VirtualHost>
</IfModule>
<IfModule mod_ssl.c>
<VirtualHost *:80>
	ServerName femmebabe.com
	ServerAdmin team@femmebabe.com
	DocumentRoot /var/www/html

	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined

	RewriteEngine on
	RewriteCond %{SERVER_NAME} =femmebabe.com
	RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
</IfModule>
在配置服务器时,请确保在此示例代码中替换项目的名称,目录和域的名称。现在,我们需要禁用默认网站。这可以使用bash完成。

sudo a2dissite 000-default-le-ssl
sudo a2dissite 000-default
sudo a2dissite default-ssl
接下来,我们也可以使用bash启用默认网站和Reload Apache2。请记住,在/etc/apache2/stite-bailable/budable/insed ins efemmebabe用您声明的文件的名称替换。

sudo a2ensite femmebabe-le-ssl
sudo systemctl reload apache2
返回您在Navbar中的域。您应该在Web浏览器中查看您配置的网站。恭喜!如果您看不到它,则可能需要进行一些更改。仔细查看项目中的设置,Apache配置,并确保您没有任何错误,并运行以下命令以检查项目是否错误。

cd projectname
source venv/bin/activate
python manage.py check
如果您的Python项目中有错误,请将其追踪到它们所在的位置并修复它们。您可能无法根据它们的位置查看所有错误,因此,如果您有一个简单地说“填充不是重新进入的错误”,请在虚拟环境中编辑以下文件,registry.py,以公开错误。

nano venv/lib/python3.12/site-packages/django/apps/registry.py
滚动到第83行,在此增加此运行时错误(提高RuntimeRorror(“ populate(),不连入”),然后在此行之前添加注释,然后在相同的凹痕中添加Self.App_Configs = {}。看起来像这样:

            if self.loading:
                # 防止重新进入电话,以避免运行AppConfig.ready()
                # 方法两次。
# 提高RuntimeRor(“ pupulate()不是重入”)
                self.app_configs = {}
            self.loading = True
然后,您可以再次检查项目并暴露错误。

python manage.py check
然后,您可以看到错误并修复它。将其修复并没有错误编译时,请确保将文件重新更改为这样,这样看起来像这样:

            if self.loading:
                # 防止重新进入电话,以避免运行AppConfig.ready()
                # 方法两次。
                raise RuntimeError("populate() isn't reentrant")
# self.app_configs = {}
            self.loading = True
如果服务器在线,当我们对其进行进一步的更改时,我们需要使用以下命令重新加载服务器:

sudo systemctl reload apache2
惊人的!但是发送邮件呢?要开始发送电子邮件,我们首先需要更新域配置。这应该在Squarespace的DNS面板中,或您选择的任何域名注册商。我们还需要安装和添加配置,并运行一些命令。 首先,让我们获取服务器的IPv6地址。然后,我们将打开您的DNS并添加记录。 要获取服务器的IPv6地址,请使用此命令:

ip -6 addr
现在,我们可以将以下记录添加到DNS设置中。我的记录看起来像这样。但是,对于您的记录,您应该用IP替换IP地址(不是75.147.182.214,这是我的)。另外,添加您的域,代替femmebabe.com,以及在上一个命令中找到的IPv6地址(您无法使用Mine,Fe80 :: 725a:FFF:FFF:FE49:3E02)。不用担心域名目前,当我们使用OpendKim设置Postfix,Mail Server,并打印键时,这是创建的。我们将进行最后一个配置。 @ 一个 N/A。 75.147.182.214 @ MX 10 femmebabe.com @ ptr N/A。 femmebabe.com @ TXT N/A。 txt @ v = spf1 mx ip75.147.182.214ip6:fe80 :: 725a:fff:fff:fe49:3e02〜 default._bimi TXT N/A。 v = bimi1; l = https://femmebabe.com/media/static/femmebabe.svg _dmarc TXT N/A。 v = dmarc1; p =无 sendonly._domainkey TXT N/A。现在,我们需要为Postfix添加一些持久的配置。我们需要做的就是确保用您使用的域名替换域名femmebabe.com。让我们一一查看所有配置文件,然后将它们安装在我们项目中称为Config的目录中,以安装到OS。

nano config/etc_postfix_main.cf
将此文本添加到文件中

# 请参阅/usr/share/postfix/main.cf.dist,以获取评论,更完整的版本


# debian特定:指定文件名将导致第一个
# 该文件的行用作名称。  Debian默认
# IS /etc /mailname。
# Myorigin = /etc /mailname

smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu)
biff = no

# 附加域是MUA的工作。
append_dot_mydomain = no

# 输入下一行,以生成“延迟邮件”警告
# delay_warning_time = 4H

readme_directory = no

# 请参阅http://www.postfix.org/compatibility_readme.html-默认为3.6 on
# 新的安装。
compatibility_level = 3.6



# TLS参数
smtpd_tls_cert_file=/etc/letsencrypt/live/femmebabe.com/fullchain.pem
smtpd_tls_key_file=/etc/letsencrypt/live/femmebabe.com/privkey.pem
smtpd_tls_security_level=may

smtp_tls_CApath=/etc/ssl/certs
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

smtpd_relay_restrictions = permit_sasl_authenticated, defer_unauth_destination
myhostname = femmebabe.com
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = /etc/mailname
mydestination = femmebabe.com, localhost, $myhostname
smtp_helo_name = femmebabe.com
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = all

# 小米配置
milter_default_action = accept
milter_protocol = 6
smtpd_milters = local:/opendkim/opendkim.sock
non_smtpd_milters = $smtpd_milters

smtp_tls_security_level = encrypt
smtp_tls_loglevel = 1

virtual_transport=lmtp:unix:private/dovecot-lmtp

smtpd_sasl_path = private/auth
下一个配置!

nano config/etc_postfix_master.cf
添加以下行:

# 
# Postfix Master Process配置文件。  有关格式的详细信息
# 在文件中,请参阅主(5)手册页(命令:“ Man 5 Master”或
# 在线:http://www.postfix.org/master.5.html)。
# 
# 编辑此文件后,不要忘记执行“ Postfix Reload”。
# 
# =============================================== ======================
# 服务类型私人UNPRIV CHROOT WAKEUP MAXPROC命令 + ARGS
# (是)(是)(否)(从不)(100)
# =============================================== ======================
smtp      inet  n       -       y       -       -       smtpd
# smtp inet n -y -1后屏幕
# SMTPD PASS  -  Y-- SMTPD
# DNSBlog Unix-- Y -0 DNSBlog
# tlsproxy unix-- y -0 tlsproxy
# 选择一个:仅对环回客户端或任何客户端启用提交。
# 127.0.0.1:ubmission inet n -y-- smtpd
submission inet n       -       y       -       -       smtpd
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_tls_wrappermode=no
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_sasl_type=dovecot
  -o smtpd_sasl_path=private/auth
# -O syslog_name = PostFix/提交
# -o smtpd_tls_security_level = gentrypt
# -o smtpd_sasl_auth_enable = yes
# -o smtpd_tls_auth_only =是
# -o smtpd_reject_unlisted_recipient = no
# -o smtpd_client_restertions = $ MUA_CLIENT_RESTRICTIONS
# -O smtpd_helo_resterrictions = $ mua_helo_restrictions
# -o smtpd_sender_resterrictions = $ MUA_SENDER_RESTRICTIONS
# -o smtpd_recipient_trastrictions =
# -o smtpd_relay_resterrictions = pull_sasl_authenticated,拒绝
# -O miLter_macro_daemon_name = riginding
# 选择一个:仅适用于回环客户端或任何客户端的SMTP。
# 127.0.0.1:smtps inet n -y-- smtpd
# smtps inet n -y-- smtpd
# -o syslog_name = Postfix/smtps
# -O smtpd_tls_wrappermode = yes
# -o smtpd_sasl_auth_enable = yes
# -o smtpd_reject_unlisted_recipient = no
# -o smtpd_client_restertions = $ MUA_CLIENT_RESTRICTIONS
# -O smtpd_helo_resterrictions = $ mua_helo_restrictions
# -o smtpd_sender_resterrictions = $ MUA_SENDER_RESTRICTIONS
# -o smtpd_recipient_resterrictions =
# -o smtpd_relay_resterrictions = pull_sasl_authenticated,拒绝
# -O miLter_macro_daemon_name = riginding
# 628 inet n -y-- qMQPD
pickup    unix  n       -       y       60      1       pickup
cleanup   unix  n       -       y       -       0       cleanup
qmgr      unix  n       -       n       300     1       qmgr
# QMGR UNIX N -N 300 1 OQMG
tlsmgr    unix  -       -       y       1000?   1       tlsmgr
rewrite   unix  -       -       y       -       -       trivial-rewrite
bounce    unix  -       -       y       -       0       bounce
defer     unix  -       -       y       -       0       bounce
trace     unix  -       -       y       -       0       bounce
verify    unix  -       -       y       -       1       verify
flush     unix  n       -       y       1000?   0       flush
proxymap  unix  -       -       n       -       -       proxymap
proxywrite unix -       -       n       -       1       proxymap
smtp      unix  -       -       y       -       -       smtp
relay     unix  -       -       y       -       -       smtp
        -o syslog_name=postfix/$service_name
# -o smtp_helo_timeout = 5 -o smtp_connect_timeout = 5
showq     unix  n       -       y       -       -       showq
error     unix  -       -       y       -       -       error
retry     unix  -       -       y       -       -       error
discard   unix  -       -       y       -       -       discard
local     unix  -       n       n       -       -       local
virtual   unix  -       n       n       -       -       virtual
lmtp      unix  -       -       y       -       -       lmtp
anvil     unix  -       -       y       -       1       anvil
scache    unix  -       -       y       -       1       scache
postlog   unix-dgram n  -       n       -       1       postlogd
# 
# =============================================== ===================
# 与非PostFix软件的接口。确保检查手册
# 非postFix软件的页面以查找所需的选项。
# 
# 以下许多服务都使用后四个管道(8)交付
# 代理人。  有关$ {收件人}的信息,请参见管道(8)女人页面
# 和其他消息信封选项。
# =============================================== ===================
# 
# MailDrop。有关详细信息,请参见《 Postfix MailDrop_readme文件》。
# 也在main.cf中指定:maildrop_destination_recipient_limit = 1
# 
maildrop  unix  -       n       n       -       -       pipe
  flags=DRXhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
# 
# =============================================== ===================
# 
# 最近的Cyrus版本可以使用现有的“ LMTP” Master.CF条目。
# 
# 在cyrus.conf中指定:
# lmtp cmd =“ lmtpd -a”听=“ localhost:lmtp” proto = tcp4
# 
# 在main.cf中指定以下一个或多个:
# Mailbox_transport = LMTP:INET:localhost
# virtual_transport = lmtp:inet:localhost
# 
# =============================================== ===================
# 
# Cyrus 2.1.5(Amos Gouaux)
# 也在main.cf中指定:cyrus_destination_recipient_limit = 1
# 
# Cyrus unix -n n--管道
# flags = drx user = cyrus arg =/cyrus/bin/deliver -e -r $ {sender} -m $ {extension} $ {user} $ {user}
# 
# =============================================== ===================
# 通过Cyrus交付的旧例子。
# 
# 旧-cyrus unix -n n--管道
# flags = r用户= cyrus argv =/cyrus/bin/deliver -e -m $ {extension} $ {user}
# 
# =============================================== ===================
# 
# 有关配置详细信息,请参见Postfix Uucp_readme文件。
# 
uucp      unix  -       n       n       -       -       pipe
  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
# 
# 其他外部交付方法。
# 
ifmail    unix  -       n       n       -       -       pipe
  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
bsmtp     unix  -       n       n       -       -       pipe
  flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient
scalemail-backend unix -       n       n       -       2       pipe
  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}
mailman   unix  -       n       n       -       -       pipe
  flags=FRX user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py ${nexthop} ${user}
和OpendKim配置。 OpendKim使用域键标识电子邮件服务器,以使其更安全。没有它,邮件没有签名,也可能无法进入收件箱。

nano config/etc_default_opendkim
添加以下行:

# 注意:这是一个旧版配置文件。 Opendkim不使用它
# SystemD服务。请使用相应的配置参数
# /etc/opendkim.conf而不是。
# 
# 以前,有人会在此处编辑默认设置,然后执行
# /lib/opendkim/opendkim.service.generate生成Systemd替代文件
# /etc/systemd/system/opendkim.service.d/override.conf和
# /etc/tmpfiles.d/opendkim.conf。虽然这仍然是可能的,但现在是
# 建议直接在/etc/opendkim.conf中调整设置。
# 
# daemon_opts =“”
# 更改为/var/spool/postfix/run/opendkim以使用unix插座
# Chroot中的后缀:
# rundir =/var/spool/postfix/run/opendkim
RUNDIR=/run/opendkim
# 
# 注释以指定替代套接字
# 请注意,设置此设置将覆盖opendkim.conf中的任何插座值
# 默认:
SOCKET="local:/var/spool/postfix/opendkim/opendkim.sock"
# 在端口54321上的所有接口上聆听:
# 插座= inet:54321
# 在端口12345上收听回环:
# 套接字= inet:12345@localhost
# 听是192.0.2.1是端口12345:
# 插座= INET:12345@192.0.2.1
USER=opendkim
GROUP=opendkim
PIDFILE=$RUNDIR/$NAME.pid
EXTRAAFTER=

nano config/etc_dovecot_conf.d_10-master.conf
添加以下行:

0-master.conf 
# default_process_limit = 100
# default_client_limit = 1000

# 默认VSZ(虚拟内存大小)服务过程的限制。这主要是
# 旨在捕捉和杀死在食物吃饭之前泄漏记忆的过程
# 一切。
# DEFAULT_VSZ_LIMIT = 256m

# 登录用户内部通过登录过程使用。这是最不信任的
# Dovecot系统中的用户。它根本不应该访问任何东西。
# default_login_user = dovenull

# 内部用户被无特权的过程使用。它应该与
# 登录用户,以免登录过程无法打扰其他过程。
# default_internal_user = dovecot

service imap-login {
  inet_listener imap {
    # 端口= 143
  }
  inet_listener imaps {
    # 端口= 993
    # SSL =是
  }

  # 在开始新过程之前,要处理的连接数量。通常
  # 唯一有用的值是0(无限)或1。1更安全,但是0
  # 更快。 <doc/wiki/loginprocess.txt>
  # service_count = 1

  # 始终等待更多连接的过程数量。
  # process_min_avail = 0

  # 如果设置service_count = 0,则可能需要增长此。
  # vsz_limit = $ default_vsz_limit
}

service pop3-login {
  inet_listener pop3 {
    # 端口= 110
  }
  inet_listener pop3s {
    # 端口= 995
    # SSL =是
  }
}

service submission-login {
  inet_listener submission {
    # 端口= 587
  }
}

service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    group = postfix
    mode = 0666
    user = postfix
  }

  # 仅当您无法使用上述UNIX插座时创建INET侦听器
  # inet_lister lmtp {
    # 避免使LMTP在整个互联网上可见
    # 地址=
    # 端口=
  # }
}

service imap {
  # 大多数内存都用于mmap()ING文件。您可能需要增加此
  # 如果您有巨大的邮箱,请限制。
  # vsz_limit = $ default_vsz_limit

  # 最大限度。 IMAP过程的数量(连接)
  # process_limit = 1024
}

service pop3 {
  # 最大限度。 POP3过程的数量(连接)
  # process_limit = 1024
}

service submission {
  # 最大限度。 SMTP提交过程的数量(连接)
  # process_limit = 1024
}

service auth {
  # auth_socket_path默认指向此userDB套接字。通常是
  # Dovecot-LDA,Doveadm,可能是IMAP流程等。
  # 该插座的完整权限能够获取所有用户名的列表
  # 获取每个人的UserDB查找的结果。
  # 
  # 默认的0666模式允许任何人连接到插座,但是
  # 仅当UserDB返回一个“ UID”字段时,UserDB查找才能成功
  # 匹配呼叫者进程的UID。另外,如果呼叫者的UID或GID与
  # 插座的UID或GID查找成功。其他任何事情都会导致失败。
  # 
  # 要授予呼叫者的完整权限查找所有用户,请将模式设置为
  # 除了0666以外的其他内容,Dovecot让内核执行
  # 权限(例如,0777允许每个人完全权限)。
  unix_listener /var/spool/postfix/private/auth {
    mode = 0660
    user = postfix
    group = postfix
  }
}

service auth-worker {
  # 默认情况下,Auth Works过程作为root运行,以便可以访问
  # /etc/shadow。如果不需要,则应将用户更改为
  # $ default_interal_user。
  # 用户= root
}

service dict {
  # 如果使用DICE代理,则邮件过程应可以访问其套接字。
  # 例如:mode = 0660,group = vmail和global mail_access_groups = vmail
  unix_listener dict {
    # 模式= 0600
    # 用户=
    # 组=
  }
}
再次,请确保用您选择的域中替换所有这些文件中的域,femmebabe.com。编辑下一个文件,Dovecot的配置,

nano config/etc_dovecot_dovecot
并添加这些行

## OVECOT配置文件

# 如果您很着急,请参见http://wiki2.dovecot.org/quickconfiguration

# “ doveconf -n”命令给出了更改设置的干净输出。使用它
# 将发布到Dovecot邮件列表时,而不是复制和粘贴文件。

# '# '角色和所有事物之后被视为评论。额外的空间
# 和标签被忽略。如果您想明确使用这些,请放置
# value inside quotes, eg.: key = "# char和尾随空格”

# 大多数(但不是全部)设置可以被不同的协议和/或
# 源/目标IPS通过将设置放置在各节内,例如:
# 协议IMAP {},本地127.0.0.1 {},远程10.0.0.0/8 {}

# 显示每个设置的默认值,不需要不需要
# 那些。这些是这样的例外:没有部分(例如命名空间{})
# 或默认情况下添加插件设置,它们仅列出为示例。
# 路径也只是基于配置的实际默认值的示例
# 选项。这里列出的路径是用于配置-Prefix =/usr
# -sysconfdir =/etc--localstedir =/var

# 启用安装的协议
!include_try /usr/share/dovecot/protocols.d/*.protocol

# 逗号分开的IPS或主机列表在哪里聆听连接。
# “*”在所有IPv4接口中听,” ::“在所有IPv6接口中聆听。
# 如果要指定非默认端口或更复杂的任何内容,
# 编辑conf.d/master.conf。
# 听= *,::

# 基本目录在哪里存储运行时数据。
# base_dir =/var/run/dovecot/

# 此实例的名称。在多实施设置中DoVeadm和其他命令
# 可以使用-i <senstance_name>选择使用哪个实例(替代方案
# 到-c <config_path>)。实例名称也添加到Dovecot进程中
# 在PS输出中。
# instance_name = dovecot

# 客户的问候消息。
# login_greeting = Dovecot准备就绪。

# 空间分离的受信任网络范围的列表。这些连接
# 允许IP覆盖其IP地址和端口(用于记录和
# 用于身份验证检查)。 disable_plaintext_auth也被忽略
# 这些网络。通常,您在此处指定IMAP代理服务器。
# login_trusted_networks =

# 空间分开的登录访问列表检查插座(例如TCPWRAP)
# login_access_sockets =

# 使用protxy_maybe =是,如果代理目标与这些IP匹配,
# 代理。这通常不是必需的,但是如果目的地
# IP是例如负载平衡器的IP。
# auth_proxy_self =

# 显示更多的详细过程标题(在PS中)。当前显示用户名,
# IP地址。可用于查看谁实际使用IMAP流程
# (例如,共享邮箱或使用相同的UID用于多个帐户)。
# verbose_proctitle = no

# 当Dovecot总体流程关闭时,所有过程是否应杀死。
# 将其设置为“否”意味着可以升级Dovecot的情况
# 强迫现有的客户连接关闭(尽管也可能是
# 如果升级是例如,一个问题由于安全修复)。
# shutdown_clients =是

# 如果非零,请通过与Doveadm Server的许多连接运行邮件命令,
# 而不是在同一过程中直接运行它们。
# doveadm_worker_count = 0
# UNIX插座或主机:用于连接到Doveadm服务器的端口
# doveadm_socket_path = doveadm-server

# 空间分离的环境变量列表保存在Dovecot上
# 启动并传递到其所有子过程。你也可以给
# 键=始终设置特定设置的值对。
# import_environment = tz

## 
## 字典服务器设置
## 

# 字典可用于存储密钥=值列表。这是几个
# 插件。字典可以直接访问或
# 字典服务器。以下dict block地图词典名称为uris
# 使用服务器时。然后可以使用格式使用URI来引用这些
# “代理:: <名称>”。

dict {
  # quota = mysql:/etc/dovecot/dovecot-dict-sql.conf.ext
}

# 大多数实际配置都包含在下面。文件名是
# 首先按其ASCII值分类,并按照该顺序进行分析。 00-exefixes
# 在文件名中,旨在使订购更容易理解。
!include conf.d/*.conf

# 还可以尝试包含配置文件,而无需给出错误
# 找不到:
!include_try local.conf

passdb {
  driver = passwd-file
  args = /etc/dovecot/passwd
}
userdb {
  driver = passwd
}

protocols = imap pop3

# 允许Dovecot收听所有输入连接(IPv4 / ipv6)

listen = *, ::
为Dovecot用户添加密码:

nano config/etc_dovecot_passwd
在结肠之前,文件的第一部分是用户名。最后一部分“ yourpassword”表示您要提供邮件服务器的密码。

team:{plain}yourpassword
接下来,opendkim配置

nano config/etc_opendkim.conf
并添加以下行:

# 这是用于签名和验证的基本配置。很容易
# 适合适合基本安装。请参阅opendkim.conf(5)和
# /USR/share/doc/opendkim/examples/opendkim.conf.sample完成
# 可用配置参数的文档。

Syslog			yes
SyslogSuccess		yes
# logwhy no

# 常见的签名和验证参数。在Debian中,“来自”标题是
# 过度签名,因为它通常是声誉系统使用的身份密钥
# 因此对安全性敏感。
Canonicalization	relaxed/simple
Mode			s
SubDomains		no
OversignHeaders		From

# 签名域,选择器和键(必需)。例如,执行签名
# 对于域“ example.com”,selector“ 2020”(2020._domainkey.example.com),
# 使用存储在/etc/dkimkeys/example.private中的私钥。更细粒度
# 可以在/usr/share/doc/opendkim/readme.opendkim中找到设置选项。
# domain example.com
# 选择器2020
# keyfile /etc/dkimkeys/example.private

# 在Debian中,OpendKim以用户“ OpendKim”运行。当需要007的透明
# 使用与MTA的本地插座,该插座可以访问插座作为非特权
# 用户(例如,后缀)。您可能需要将用户的“邮政缀”添加到组
# 在这种情况下,“ Opendkim”。
UserID			opendkim
UMask			007

# MTA连接的插座(必需)。如果MTA在Chroot监狱内,
# 必须确保可以访问插座。在Debian,Postfix运行
# /var/spool/postfix中的chroot,因此必须是一个unix插座
# 如下最后一行所示,配置如下所示。
# 插座本地:/run/opendkim/opendkim.sock
# 套接字INET:8891@localhost
# 插座INET:8891
Socket			local:/var/spool/postfix/opendkim/opendkim.sock

PidFile			/run/opendkim/opendkim.pid

# 主机要签名而不是验证,默认值为127.0.0.1。看到
# 有关更多信息,Opendkim(8)的操作部分。
# 内部主机192.168.0.0/16,10.0.0.0/8,172.16.0.0/12

# 信托锚启用DNSSEC。在Debian中,提供了信任锚文件
# 通过包装DNS-ROOT-DATA。
TrustAnchorFile		/usr/share/dns/root.key
# 名称服务器127.0.0.1

# 从地址到用于签名消息的密钥中的地图域
KeyTable           refile:/etc/opendkim/key.table
SigningTable       refile:/etc/opendkim/signing.table

# 一组应签署邮件的内部主机
InternalHosts       /etc/opendkim/trusted.hosts

nano config/etc_default_opendkim
并添加这些行

# 注意:这是一个旧版配置文件。 Opendkim不使用它
# SystemD服务。请使用相应的配置参数
# /etc/opendkim.conf而不是。
# 
# 以前,有人会在此处编辑默认设置,然后执行
# /lib/opendkim/opendkim.service.generate生成Systemd替代文件
# /etc/systemd/system/opendkim.service.d/override.conf和
# /etc/tmpfiles.d/opendkim.conf。虽然这仍然是可能的,但现在是
# 建议直接在/etc/opendkim.conf中调整设置。
# 
# daemon_opts =“”
# 更改为/var/spool/postfix/run/opendkim以使用unix插座
# Chroot中的后缀:
# rundir =/var/spool/postfix/run/opendkim
RUNDIR=/run/opendkim
# 
# 注释以指定替代套接字
# 请注意,设置此设置将覆盖opendkim.conf中的任何插座值
# 默认:
SOCKET="local:/var/spool/postfix/opendkim/opendkim.sock"
# 在端口54321上的所有接口上聆听:
# 插座= inet:54321
# 在端口12345上收听回环:
# 套接字= inet:12345@localhost
# 听是192.0.2.1是端口12345:
# 插座= INET:12345@192.0.2.1
USER=opendkim
GROUP=opendkim
PIDFILE=$RUNDIR/$NAME.pid
EXTRAAFTER=
当我们准备设置我们的Postfix服务器时,我们将运行以下代码,并嵌入适当的域名。首先创建脚本

touch scripts/postfixsetup
sudo chmod a+x scripts/postfixsetup
nano scripts/postfixsetup
现在,在文本编辑器Nano中,编辑此文件,以包括您的域名而不是femmebabe.com。

# 要!
# 设置后缀
cd $DIR
echo "Mail services configuration"
sudo cp /etc/postfix/main.cf /etc/postfix/main.cf.backup
sudo cp config/etc_postfix_main.cf /etc/postfix/main.cf
sudo cp config/etc_postfix_master.cf /etc/postfix/master.cf
sudo cp config/etc_default_opendkim /etc/default/opendkim
sudo cp config/etc_dovecot_conf.d_10-auth.conf /etc/dovecot/conf.d/10-auth.conf
sudo cp config/etc_dovecot_conf.d_10-master.conf /etc/dovecot/conf.d/10-master.conf
sudo cp config/etc_dovecot_dovecot.conf /etc/dovecot/dovecot.conf
sudo cp config/etc_dovecot_passwd /etc/dovecot/passwd
sudo cp config/etc_opendkim.conf /etc/opendkim.conf
sudo cp config/etc_default_opendkim /etc/default/opendkim
sudo adduser postfix opendkim
sudo mkdir /etc/opendkim
sudo mkdir /etc/opendkim/keys
sudo mkdir /etc/opendkim/keys/femmebabe.com
sudo mkdir /var/spool/postfix/opendkim
sudo echo "*@femmebabe.com     sendonly._domainkey.femmebabe.com" | sudo tee -a /etc/opendkim/signing.table
sudo echo "sendonly._domainkey.femmebabe.com    femmebabe.com:sendonly:/etc/opendkim/keys/femmebabe.com/sendonly.private" | sudo tee -a /etc/opendkim/key.table
sudo echo "127.0.0.1" | sudo tee -a /etc/opendkim/trusted.hosts
sudo echo "localhost" | sudo tee -a /etc/opendkim/trusted.hosts
sudo echo "" | sudo tee -a /etc/opendkim/trusted.hosts
sudo echo "*.femmebabe.com" | sudo tee -a /etc/opendkim/trusted.hosts
sudo chown -R opendkim:opendkim /etc/opendkim
sudo opendkim-genkey -b 2048 -d femmebabe.com -D /etc/opendkim/keys/femmebabe.com -s sendonly -v
sudo chmod go-rw /etc/opendkim/keys
sudo chown opendkim:opendkim /etc/opendkim/keys/femmebabe.com/sendonly.private
sudo chown opendkim:postfix /var/spool/postfix/opendkim
cd $DIR
sudo cp mailbox/* /var/mail/
sudo chown :users /var/mail/*
sudo chmod -R a+rwx /var/mail/*
sudo systemctl restart opendkim postfix dovecot
sudo cat /etc/opendkim/keys/femmebabe.com/sendonly.txt | tr -d '\n' | sed 's/\s//g' | sed 's/""//g' | awk -F'[)(]' '{print $2}'
现在,运行完整的脚本以配置Postfix,Opendkim和Dovecot。

./scripts/postfixsetup
该脚本运行后,将其打印的最后一行复制并将其粘贴到您的DNS配置中,作为sendonly._domainkey的值。这是发送安全邮件时用于识别域的OpendKim密钥。 惊人的!在几天之内,您应该能够从服务器发送邮件,前提是所有内容的配置正确。 如果您只是为邮件服务器配置了DNS,则记录需要少于72小时。通常要快得多。您可以通过使用此命令检查服务器是否正在工作,提供了您的电子邮件:

echo “test” | mail -s “Test Email” youremail@gmail.com
如果所有内容似乎都正常工作,则应该能够使用服务器发送电子邮件。如果它不起作用,请尝试查看日志以查看可能的错误。

tail –lines 150 /var/log/mail.log
这将提供有关服务器发送的邮件以及它是否正常工作的详细信息。您也应该能够在收件箱中看到电子邮件,如果不存在,请检查垃圾邮件文件夹。 您还需要在设置中配置您的设置。py,以便您的电子邮件服务器可以与您的Django应用程序(项目)通信。在您的设置中添加或替换这些行

EMAIL_HOST = DOMAIN
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_ADDRESS = 'team@femmebabe.com'
EMAIL_HOST_USER = 'team' # 'love@mamasheen.com'
EMAIL_HOST_PASSWORD = config['EMAIL_HOST_PASSWORD']
DEFAULT_FROM_EMAIL = '{} <{}>'.format(SITE_NAME, EMAIL_HOST_USER)
请注意,我们正在使用配置文件获取密码。让我们像在文件的开始一样将此文件加载到设置中。

import os
import json

# 打开并加载配置
with open('/etc/config.json') as config_file:
    config = json.load(config_file)
让我们创建此文件并在其上添加秘密键以及邮件密码。要生成一个秘密键,请使用此命令,最终都有您喜欢的长度:

openssl rand -base64 64
现在,复制openssl生成的文本并编辑/etc/config.json

sudo nano /etc/config.json
将以下行添加到您的文件中,并使用OpenSSL生成的秘密密钥的键。

{
	"SECRET_KEY": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX-generated-using-openssl)",
	"EMAIL_HOST_PASSWORD": "yourpassword"
}
JSON格式简单易用,我们也可以以这种方式声明我们要在我们的项目中使用的其他键,并将其与我们的项目目录分开,以便其他用户不能写信给它们,因此无法阅读它们仅来自我们的项目目录。这是适用于API键的练习,我们将在这里使用多个键。 您还需要备份您的项目,以确保所有已保存的东西都可以保存,即使您不再希望租用服务器,也可以在以后恢复工作。

sudo backup
现在,尝试从Web服务器发送HTML电子邮件,提供从命令行发送的电子邮件。在外壳中查询您的用户实例,然后通过Django向该用户发送HTML电子邮件。在代码中将我的名字更改为您的用户名。

python manage.py shell
from django.contrib.auth.models import User
u = User.objects.get(username='Charlotte')
from users.email import send_welcome_email
send_welcome_email(u)
exit()
如果第一个命令不起作用,请确保使用

source venv/bin/activate
只要一切都正确设置,您现在将在Web应用程序发送的邮箱中收到一封欢迎电子邮件。好工作!你走了很长一段路。 我想补充一点,如果您在这样的项目工作时根本遇到任何错误,请随时寻找答案并寻求帮助。 Google除其他搜索引擎外,是寻找编程帮助的好资源。只需搜索您遇到的错误,您就可以看到其他人如何解决问题。另外,欢迎您与我,您的教育工作者(老师,教授,教师),互联网上的任何同行者联系,他们可以提供编程帮助,或再次咨询本书或其他资源,以找到解决您所遇到的问题的解决方案。我了解这并不容易,但是即使您已经阅读了这么多的文章并且没有编写任何代码,您也在从头开始构建Web应用程序很多。拍拍自己的背部,你做的很棒工作。 感谢您抽出宝贵的时间阅读本第三版网络开发指南。在将来的版本中,我将包括文档开头讨论的更多重要示例,我们将深入研究软件和硬件开发的世界。请继续关注即将发生的事情,我期待教您如何构建令人难以置信的软件。在下一个见






关闭
1
请参阅全文
继续阅读

| 用加密货币购买



https://glamgirlx.com/zh-CN/practical-web-based-deep -


(单击或点击下载图像)
专业娱乐,照片,视频,音频,直播和休闲游戏,以及ID扫描,网络开发和代孕服务。

使用以下地址给我留下比特币提示: 3KhDWoSve2N627RiW8grj6XrsoPT7d6qyE

© Glam Girl X 2025

服务条款