当前位置: 首页 > news >正文

Python学习之路-Tornado基础:安全应用

Python学习之路-Tornado基础:安全应用

Cookie

对于RequestHandler,除了在初始Tornado中讲到的之外,还提供了操作cookie的方法。

设置

set_cookie(name, value, domain=None, expires=None, path=‘/’, expires_days=None)

参数说明:

参数名说明
namecookie名
valuecookie值
domain提交cookie时匹配的域名
path提交cookie时匹配的路径
expirescookie的有效期,可以是时间戳整数、时间元组或者datetime类型,为UTC时间
expires_dayscookie的有效期,天数,优先级低于expires
import datetimeclass IndexHandler(RequestHandler):def get(self):self.set_cookie("n1", "v1")self.set_cookie("n2", "v2", path="/new", expires=time.strptime("2016-11-11 23:59:59","%Y-%m-%d %H:%M:%S"))self.set_cookie("n3", "v3", expires_days=20)# 利用time.mktime将本地时间转换为UTC标准时间self.set_cookie("n4", "v4", expires=time.mktime(time.strptime("2016-11-11 23:59:59","%Y-%m-%d %H:%M:%S")))self.write("OK")

原理

设置cookie实际就是通过设置header的Set-Cookie来实现的。

class IndexHandler(RequestHandler):def get(self):self.set_header("Set-Cookie", "n5=v5; expires=Fri, 11 Nov 2016 15:59:59 GMT; Path=/") self.write("OK")

获取

get_cookie(name, default=None)

获取名为name的cookie,可以设置默认值。

class IndexHandler(RequestHandler):def get(self):n3 = self.get_cookie("n3")self.write(n3)

清除

clear_cookie(name, path=‘/’, domain=None)

删除名为name,并同时匹配domain和path的cookie。

clear_all_cookies(path=‘/’, domain=None)

删除同时匹配domain和path的所有cookie。

class ClearOneCookieHandler(RequestHandler):def get(self):self.clear_cookie("n3")self.write("OK")class ClearAllCookieHandler(RequestHandler):def get(self):self.clear_all_cookies()self.write("OK")

{{< admonition warning “注意” true >}}

执行清除cookie操作后,并不是立即删除了浏览器中的cookie,而是给cookie值置空,并改变其有效期使其失效。真正的删除cookie是由浏览器去清理的。

{{< /admonition >}}

安全Cookie

Cookie是存储在客户端浏览器中的,很容易被篡改。Tornado提供了一种对Cookie进行简易加密签名的方法来防止Cookie被恶意篡改。

使用安全Cookie需要为应用配置一个用来给Cookie进行混淆的秘钥cookie_secret,将其传递给Application的构造函数。我们可以使用如下方法来生成一个随机字符串作为cookie_secret的值。

>>> import base64, uuid
>>> base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes)
'2hcicVu+TqShDpfsjMWQLZ0Mkq5NPEWSk9fi0zsSt3A='

{{< admonition tip “提示” true >}}

Base64是一种基于64个可打印字符来表示二进制数据的表示方法。由于2的6次方等于64,所以每6个比特为一个单元,对应某个可打印字符。三个字节有24个比特,对应于4个Base64单元,即3个字节需要用4个可打印字符来表示。

uuid, 通用唯一识别码(英语:Universally Unique Identifier,简称UUID),是由一组32个16进制数字所构成(两个16进制数是一个字节,总共16字节),因此UUID理论上的总数为1632=2128,约等于3.4 x 10^38。也就是说若每纳秒产生1兆个UUID,要花100亿年才会将所有UUID用完。

uuid模块的uuid4()函数可以随机产生一个uuid码,bytes属性将此uuid码作为16字节字符串。

{{< /admonition >}}

将生成的cookie_secret传入Application构造函数:

app = tornado.web.Application([(r"/", IndexHandler),],cookie_secret = "2hcicVu+TqShDpfsjMWQLZ0Mkq5NPEWSk9fi0zsSt3A="
)

获取和设置

set_secure_cookie(name, value, expires_days=30)

设置一个带签名和时间戳的cookie,防止cookie被伪造。

get_secure_cookie(name, value=None, max_age_days=31)

如果cookie存在且验证通过,返回cookie的值,否则返回None。max_age_day不同于expires_days,expires_days是设置浏览器中cookie的有效期,而max_age_day是过滤安全cookie的时间戳。

class IndexHandler(RequestHandler):def get(self):cookie = self.get_secure_cookie("count")count = int(cookie) + 1 if cookie else 1self.set_secure_cookie("count", str(count))self.write('<html><head><title>Cookie计数器</title></head>''<body><h1>您已访问本页%d次。</h1>' % count + '</body></html>')

我们看签名后的cookie值:

"2|1:0|10:1476412069|5:count|4:NQ==|cb5fc1d4434971de6abf87270ac33381c686e4ec8c6f7e62130a0f8cbe5b7609"

字段说明:

  1. 安全cookie的版本,默认使用版本2,不带长度说明前缀
  2. 默认为0
  3. 时间戳
  4. cookie名
  5. base64编码的cookie值
  6. 签名值,不带长度说明前缀

{{< admonition warning “注意” true >}}

Tornado的安全cookie只是一定程度的安全,仅仅是增加了恶意修改的难度。Tornado的安全cookies仍然容易被窃听,而cookie值是签名不是加密,攻击者能够读取已存储的cookie值,并且可以传输他们的数据到任意服务器,或者通过发送没有修改的数据给应用伪造请求。因此,避免在浏览器cookie中存储敏感的用户数据是非常重要的。

{{< /admonition >}}

XSRF

跨站请求伪造

先建立一个网站127.0.0.1:8000,使用上一节中的Cookie计数器:

class IndexHandler(RequestHandler):def get(self):cookie = self.get_secure_cookie("count")count = int(cookie) + 1 if cookie else 1self.set_secure_cookie("count", str(count))self.write('<html><head><title>Cookie计数器</title></head>''<body><h1>您已访问本页%d次。</h1>' % count +'</body></html>')

再建立一个网站127.0.0.1:9000,

class IndexHandler(RequestHandler):def get(self):self.write('<html><head><title>被攻击的网站</title></head>''<body><h1>此网站的图片链接被修改了</h1>''<img alt="这应该是图片" src="http://127.0.0.1:8000/?f=9000/">''</body></html>')

在9000网站我们模拟攻击者修改了我们的图片源地址为8000网站的Cookie计数器页面网址。当我们访问9000网站的时候,在我们不知道、未授权的情况下8000网站的Cookie被使用了,以至于让8000网址认为是我们自己调用了8000网站的逻辑。这就是CSRF(Cross-site request forgery)跨站请求伪造(跨站攻击或跨域攻击的一种),通常缩写为CSRF或者XSRF。

我们刚刚使用的是GET方式模拟的攻击,为了防范这种方式的攻击,任何会产生副作用的HTTP请求,比如点击购买按钮、编辑账户设置、改变密码或删除文档,都应该使用HTTP POST方法(或PUT、DELETE)。但是,这并不足够:一个恶意站点可能会通过其他手段来模拟发送POST请求,保护POST请求需要额外的策略。

XSRF保护

{{< admonition tip “提示” true >}}

浏览器有一个很重要的概念——同源策略(Same-Origin Policy)。 所谓同源是指,域名,协议,端口相同。 不同源的客户端脚本(javascript、ActionScript)在没明确授权的情况下,不能读写对方的资源。

{{< /admonition >}}

由于第三方站点没有访问cookie数据的权限(同源策略),所以我们可以要求每个请求包括一个特定的参数值作为令牌来匹配存储在cookie中的对应值,如果两者匹配,我们的应用认定请求有效。而第三方站点无法在请求中包含令牌cookie值,这就有效地防止了不可信网站发送未授权的请求。

开启XSRF保护

要开启XSRF保护,需要在Application的构造函数中添加xsrf_cookies参数:

app = tornado.web.Application([(r"/", IndexHandler),],cookie_secret = "2hcicVu+TqShDpfsjMWQLZ0Mkq5NPEWSk9fi0zsSt3A=",xsrf_cookies = True
)

当这个参数被设置时,Tornado将拒绝请求参数中不包含正确的_xsrf值的POST、PUT和DELETE请求。

class IndexHandler(RequestHandler):def post(self):self.write("hello itcast")

用不带_xsrf的post请求时,报出了HTTP 403: Forbidden ('_xsrf' argument missing from POST)的错误。

模板应用

在模板中使用XSRF保护,只需在模板中添加

module xsrf_form_html()

如新建一个模板index.html

<!DOCTYPE html>
<html>
<head><title>测试XSRF</title>
</head>
<body><form method="post"><input type="text" name="message"/><input type="submit" value="Post"/></form>
</body>
</html>

后端

class IndexHandler(RequestHandler):def get(self):self.render("index.html")def post(self):self.write("hello itcast")

模板中添加的语句帮我们做了两件事:

  • 为浏览器设置了_xsrf的Cookie(注意此Cookie浏览器关闭时就会失效)
  • 为模板的表单中添加了一个隐藏的输入名为_xsrf,其值为_xsrf的Cookie值

渲染后的页面原码如下:

<!DOCTYPE html>
<html><head><title>测试XSRF</title></head><body><form method="post"><input type="hidden" name="_xsrf" value="2|543c2206|a056ff9e49df23eaffde0a694cde2b02|1476443353"/><input type="text" name="message"/><input type="submit" value="Post"/></form></body>
</html>

非模板应用

对于不使用模板的应用来说,首先要设置_xsrf的Cookie值,可以在任意的Handler中通过获取self.xsrf_token的值来生成_xsrf并设置Cookie。

下面两种方式都可以起到设置_xsrf Cookie的作用。

class XSRFTokenHandler(RequestHandler):"""专门用来设置_xsrf Cookie的接口"""def get(self):self.xsrf_tokenself.write("Ok")class StaticFileHandler(tornado.web.StaticFileHandler):"""重写StaticFileHandler,构造时触发设置_xsrf Cookie"""def __init__(self, *args, **kwargs):super(StaticFileHandler, self).__init__(*args, **kwargs)self.xsrf_token

对于请求携带_xsrf参数,有两种方式:

  • 若请求体是表单编码格式的,可以在请求体中添加_xsrf参数
  • 若请求体是其他格式的(如json或xml等),可以通过设置HTTP头X-XSRFToken来传递_xsrf值
请求体携带_xsrf参数

新建一个页面xsrf.html:

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>测试XSRF</title>
</head>
<body><a href="javascript:;" onclick="xsrfPost()">发送POST请求</a><script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script><script type="text/javascript">//获取指定Cookie的函数function getCookie(name) {var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");return r ? r[1] : undefined;}//AJAX发送post请求,表单格式数据function xsrfPost() {var xsrf = getCookie("_xsrf");$.post("/new", "_xsrf="+xsrf+"&key1=value1", function(data) {alert("OK");});}</script>
</body>
</html>
HTTP头X-XSRFToken

新建一个页面json.html:

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>测试XSRF</title>
</head>
<body><a href="javascript:;" onclick="xsrfPost()">发送POST请求</a><script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script><script type="text/javascript">//获取指定Cookie的函数function getCookie(name) {var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");return r ? r[1] : undefined;}//AJAX发送post请求,json格式数据function xsrfPost() {var xsrf = getCookie("_xsrf");var data = {key1:1,key1:2};var json_data = JSON.stringify(data);$.ajax({url: "/new",method: "POST",headers: {"X-XSRFToken":xsrf,},data:json_data,success:function(data) {alert("OK");}})}</script>
</body>
</html>

用户验证

简介

用户验证是指在收到用户请求后进行处理前先判断用户的认证状态(如登陆状态),若通过验证则正常处理,否则强制用户跳转至认证页面(如登陆页面)。

authenticated装饰器

为了使用Tornado的认证功能,我们需要对登录用户标记具体的处理函数。我们可以使用@tornado.web.authenticated装饰器完成它。当我们使用这个装饰器包裹一个处理方法时,Tornado将确保这个方法的主体只有在合法的用户被发现时才会调用。

class ProfileHandler(RequestHandler):@tornado.web.authenticateddef get(self):self.write("这是我的个人主页。")

get_current_user()方法

装饰器@tornado.web.authenticated的判断执行依赖于请求处理类中的self.current_user属性,如果current_user值为假(None、False、0、""等),任何GET或HEAD请求都将把访客重定向到应用设置中login_url指定的URL,而非法用户的POST请求将返回一个带有403(Forbidden)状态的HTTP响应。

在获取self.current_user属性的时候,tornado会调用get_current_user()方法来返回current_user的值。也就是说,我们验证用户的逻辑应写在get_current_user()方法中,若该方法返回非假值则验证通过,否则验证失败。

class ProfileHandler(RequestHandler):def get_current_user(self):"""在此完成用户的认证逻辑"""user_name = self.get_argument("name", None)return user_name @tornado.web.authenticateddef get(self):self.write("这是我的个人主页。")

login_url设置

当用户验证失败时,将用户重定向到login_url上,所以我们还需要在Application中配置login_url。

class LoginHandler(RequestHandler):def get(self):"""在此返回登陆页面"""self.write("登陆页面")app = tornado.web.Application([(r"/", IndexHandler),(r"/profile", ProfileHandler),(r"/login", LoginHandler),],"login_url":"/login"
)

在login_url后面补充的next参数就是记录的跳转至登录页面前的所在位置,所以我们可以使用next参数来完成登陆后的跳转。

修改登陆逻辑:

class LoginHandler(RequestHandler):def get(self):"""登陆处理,完成登陆后跳转回前一页面"""next = self.get_argument("next", "/")self.redirect(next+"?name=logined")

相关文章:

Python学习之路-Tornado基础:安全应用

Python学习之路-Tornado基础:安全应用 Cookie 对于RequestHandler&#xff0c;除了在初始Tornado中讲到的之外&#xff0c;还提供了操作cookie的方法。 设置 set_cookie(name, value, domainNone, expiresNone, path‘/’, expires_daysNone) 参数说明&#xff1a; 参数名…...

6.0 Zookeeper session 基本原理详解教程

客户端与服务端之间的连接是基于 TCP 长连接&#xff0c;client 端连接 server 端默认的 2181 端口&#xff0c;也就 是 session 会话。 从第一次连接建立开始&#xff0c;客户端开始会话的生命周期&#xff0c;客户端向服务端的ping包请求&#xff0c;每个会话都可以设置一个…...

生成式人工智能攻击的一年:2024

趋势科技最近公布了其关于预期最危险威胁的年度研究数据。生成人工智能的广泛可用性和质量将是网络钓鱼攻击和策略发生巨大变化的主要原因。 趋势科技宣布推出“关键可扩展性”&#xff0c;这是著名年度研究的新版本&#xff0c;该研究分析了安全形势并提出了全年将肆虐的网络…...

K8S之Namespace的介绍和使用

Namespace的理论和实操 Namespace理论说明Namespace实操创建、查看命名空间使用ResouceQuota 对Namespace做资源限额更多ResouceQuota 的使用 Namespace理论说明 命名空间定义 K8s支持多个虚拟集群&#xff0c;它们底层依赖于同一个物理集群。 这些虚拟集群被称为命名空间&…...

封装sku组件

1. 准备模板渲染规格数据 使用Vite快速创建一个Vue项目&#xff0c;在项目中添加请求插件axios&#xff0c;然后新增一个SKU组件&#xff0c;在根组件中把它渲染出来&#xff0c;下面是规格内容的基础模板 <script setup> import { onMounted, ref } from vue import axi…...

Unity笔记:相机移动

基础知识 鼠标输入 在Unity中&#xff0c;开发者在“Edit” > “Project Settings” > “Input Manager”中设置输入&#xff0c;如下图所示&#xff1a; 在设置了Mouse X后&#xff0c;Input.GetAxis("Mouse X")返回的是鼠标在X轴上的增量值。这意味着它会…...

Java项目管理01-Maven基础

一、Maven的常用命令和生命周期 1.Maven的常用命令使用方式 complie&#xff1a;编译&#xff0c;将java文件编译为class字节码文件 clean&#xff1a;清理&#xff0c;删除字节码文件 test&#xff1a;测试&#xff0c;运行项目中的test类 package&#xff1a;打包&#x…...

计算机网络(第六版)复习提纲30

B HTTP 名词解释&#xff1a;协议HTTP定义了浏览器怎样向万维网服务器请求万维网文档&#xff0c;以及服务器怎样把文档传给浏览器。从层次的角度看&#xff0c;HTTP是面向事务的应用层协议&#xff0c;它是万维网上可靠地交换文件的重要基础&#xff0c;不仅能够传送完成超文本…...

基于SSM的图书管理系统

点击以下链接获取资源&#xff1a; https://download.csdn.net/download/qq_64505944/88820548?spm1001.2014.3001.5503 Java项目-6 librarySystem 开发完毕 万一你要作为课程设计或者毕设&#xff0c;不太会配&#xff0c;可以到下面我博客中私信&#xff0c;我帮你远程部…...

【GAMES101】Lecture 19 相机

目录 相机 视场 Field of View (FOV) 曝光&#xff08;Exposure&#xff09; 感光度&#xff08;ISO&#xff09; 光圈 快门 相机 成像可以通过我们之前学过的光栅化成像和光线追踪成像来渲染合成&#xff0c;也可以用相机拍摄成像 今天就来学习一下相机是如何成像的…...

《走进科学》灵异事件:Nginx配置改了之后一直报错

想要安装WoWSimpleRegistration&#xff0c;就定下来要用nginxphp8 &#xff0c;结果nginx那里加上php的支持之后一直报错&#xff1a; $ sudo service nginx restart Job for nginx.service failed because the control process exited with error code. See "systemctl…...

Select 选择器 el-option 回显错误 value

离谱 回显的内容不是 label 而是 value 的值 返回官方看说明&#xff1a; v-model的值为当前被选中的el-option的 value 属性值 value / v-model 绑定值有3种类型 boolean / string / number 根据自身代码猜测是&#xff1a;tableData.bookId 与 item.id 类型不一致导致 &…...

【51单片机Keil+Proteus8.9】门锁控制电路

门锁控制电路 二、设计思路 电路设计 1.电源部分&#xff1a;使用BATTERY为整个电路提供电源&#xff0c;可以在电路中加入一个电 源开关&#xff0c;以便控制电源的开启和关闭。 2.处理器部分&#xff1a;使用AT89C51芯片作为主处理器&#xff0c;通过编写程序实现门锁的 …...

比较Kamailio和OpenSIPS的重写contact函数

Kamailio&#xff1a;调用set_contact_alias()之后&#xff0c;在原有的contact的后面增加参数&#xff0c;具体地说&#xff0c;就是网络地址&#xff0c;网络端口和transport&#xff0c;好处是收到后续请求之时可以恢复原有contact的内容&#xff08;当然也有坏处&#xff0…...

【ETOJ P1046】斐波那契数列 题解(数学+动态规划)

题目描述 给定一个整数 T T T&#xff0c;表示样例数。 对于每个样例&#xff0c;给定一个整数 n n n&#xff0c;求斐波那契数列的第 n n n 项。 斐波那契数列定义为 f ( 1 ) f ( 2 ) 1 f(1) f(2) 1 f(1)f(2)1&#xff0c; f ( n ) f ( n − 1 ) f ( n − 2 ) f(…...

编码技巧——基于RedisTemplate的RedisClient实现、操作Lua脚本

1. 背景 在新公司的脚手架中开发&#xff0c;需要用到redis&#xff0c;发现没有封装好一套能集成各种常用命令、包括Lua脚本的方便使用的RedisTemplateClient&#xff0c;于是自己来实现下&#xff1b; springboot整合redis之后&#xff0c;提供了操作redis的简便方式&#…...

Asp .Net Core 系列:Asp .Net Core 集成 Panda.DynamicWebApi

文章目录 简介Asp .Net Core 集成 Panda.DynamicWebApi配置原理什么是POCO Controller&#xff1f;POCO控制器原理ControllerFeatureProvider实现自定义判断规则IApplicationModelConventionPanda.DynamicWebApi中的实现ConfigureApiExplorer()ConfigureSelector()ConfigurePar…...

【PTA浙大版《C语言程序设计(第4版)》|编程题】习题7-3 判断上三角矩阵(附测试点)

目录 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 代码呈现 测试点 上三角矩阵指主对角线以下的元素都为0的矩阵&#xff1b;主对角线为从矩阵的左上角至右下角的连线。 本题要求编写程序&#xff0c;判断一个给定的方阵是否…...

JVM 性能调优 - 参数调优(3)

查看 JVM 内存的占用情况 编写代码 package com.test;public class PrintMemoryDemo {public static void main(String[] args) {// 堆内存总量long totalMemory Runtime.getRuntime().totalMemory();// jvm 试图使用的最大堆内存long maxMemory Runtime.getRuntime().maxM…...

Django(十)

1. Ajax请求 浏览器向网站发送请求时&#xff1a;URL 和 表单的形式提交。 GETPOST 特点&#xff1a;页面刷新。 除此之外&#xff0c;也可以基于Ajax向后台发送请求&#xff08;偷偷的发送请求&#xff09;。 依赖jQuery编写ajax代码 $.ajax({url:"发送的地址"…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

云计算——弹性云计算器(ECS)

弹性云服务器&#xff1a;ECS 概述 云计算重构了ICT系统&#xff0c;云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台&#xff0c;包含如下主要概念。 ECS&#xff08;Elastic Cloud Server&#xff09;&#xff1a;即弹性云服务器&#xff0c;是云计算…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

Golang——6、指针和结构体

指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

AI语音助手的Python实现

引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...

阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)

cd /home 进入home盘 安装虚拟环境&#xff1a; 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境&#xff1a; virtualenv myenv 3、激活虚拟环境&#xff08;激活环境可以在当前环境下安装包&#xff09; source myenv/bin/activate 此时&#xff0c;终端…...