RESTful规范的API接口设计

在项目中,需要为 APP 撰写 API。刚开始接触的时候,并没有考虑太多,就想提供 URL,APP 端通过该 URL 进行查询、创建、更新等操作即可。但再对相关规范进行了解后,才发现,API 的设计并没有那么简单,远远不是 URL 的问题,而是一个通信协议的整体架构。 使用 SSL(https)来提供 URL 首先,使用 https 可以在数据包被抓取时多一层加密。 我们现在的 APP 使用环境大部分都是在路由器 WIFI 环境下,一旦路由器被入侵,那么黑客可以非常容易的抓取到用户通过路由器传输的数据,如果使用 http 未经加密,那么黑客可以很轻松的获取用户的信息,甚至是账户信息。 其次,即使使用 https,也要在 API 数据传输设计时,正确的采用加密。 例如直接将 token 信息放在 URL 中的做法,即使你使用了 https,黑客仅能抓到域名字符部分,不能抓到请求的数据,但是 URL 可以在浏览器或特殊客户端工具中直接看到。因此,使用 https 进行请求时,要采用 POST、PUT 或者 HEAD 的方式传输必要的数据。 使用 GET、POST、PUT、DELETE 这几种请求模式 请求模式也可以说是动作、数据传输方式,通常我们在 web 中的 form 有 GET、POST 两种。 而在 HTTP 中,存在以下这几种。 请求方式 功能 GET(选择) 从服务器上获取一个具体的资源或者一个资源列表。 POST(创建) 在服务器上创建一个新的资源。 PUT(更新) 以整体的方式更新服务器上的一个资源。 PATCH (更新) 只更新服务器上一个资源的一个属性。 DELETE(删除) <td ...
Introduce 2023年10月04日

JWT浅析

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准((RFC 7519). token 被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。 JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 token 也可直接被用于认证,也可被加密。 Jwt起源 说起 JWT,我们应该来谈一谈基于 token 的认证和传统的 session 认证的区别。 传统的 session 认证 我们知道,http 协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据 http 协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为 cookie, 以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了, 这就是传统的基于 session 认证。 但是这种基于 session 的认证使应用本身很难得到扩展,随着不同客户端用户的增加,独立的服务器已无法承载更多的用户,而这时候基于 session 认证应用的问题就会暴露出来. 基于 session 认证所显露的问题 Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言 session 都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。 扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上, 这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。 CSRF: 因为是基于 cookie 来进行用户识别的, cookie 如果被截获,用户就会很容易受到跨站请求伪造的攻击。 基于 token 的鉴权机制 基于 token 的鉴权机制类似于 http 协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于 token 认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。 流程上是这样的: 用户使用用户名密码来请求服务器 服务器进行验证用户的信息 服务器通过验证发送给用户一个 token 客户端存储 token,并在每次请求时附送上这个 token 值 服务端验证 token 值,并返回数据 这个 token 必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了Access-Control-Allow-Origin: *。 那么我们现在回到 JWT 的主题上。 JWT解析 网上大多数介绍JWT的文章实际介绍的都是JWS(JSON Web Signature),也往往导致了人们对于JWT的误解,但是JWT并不等于JWS,JWS只是JWT的一种实现,除了JWS外,JWE(JSON Web Encryption)也是JWT的一种实现。 下面就来详细介绍一下JWT与JWE的两种实现方式: ...
JWT Auth Introduce 2023年10月04日

HTTP请求方法

根据 HTTP 标准,HTTP 请求可以使用多种请求方法。 HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD 方法。 HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。 方法 描述 GET 请求指定的页面信息,并返回实体主体。 HEAD 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头 POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。 PUT 从客户端向服务器传送的数据取代指定的文档的内容。 DELETE 请求服务器删除指定的页面。 CONNECT HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。 OPTIONS 允许客户端查看服务器的性能。 TRACE 回显服务器收到的请求,主要用于测试或诊断。 PATCH 是对 PUT 方法的补充,用来对已知资源进行局部更新 。
HTTP Introduce 2023年10月04日

HTTP简介

网络基础 TCP/IP 为了理解 HTTP,我们有必要事先了解一下 TCP/IP 协议族。 通常使用的网络(包括互联网)是在 TCP/IP 协议族的基础上运作 的。而 HTTP 属于它内部的一个子集。 TCP/IP 协议族 TCP/IP 协议族是互联网相关的各类协议族的总称 像这样把与互联网相关联的协议集合起来总称为 TCP/IP。也有说法 认为,TCP/IP 是指 TCP 和 IP 这两种协议。还有一种说法认为,TCP/ IP 是在 IP 协议的通信过程中,使用到的协议族的统称。 TCP/IP 分层管理 TCP/IP 协议族里重要的一点就是分层。TCP/IP 协议族按层次分别分为以下 4 层: 应用层:决定向用户提供应用服务时通信的活动, FTP(File Transfer Protocol) 文件传输协议、DNS(Domain Name System) 域名系统、HTTP(HyperText Transfer Protocol) 传输层:提供处于网络连接中两台计算机之间的数据传输。 TCP(Transmission Control Protocol) 传输控制协议、UDP(User Data Protocol) 用户数据报协议 网络层(网络互联层):处理在网络上流动的数据包(数据包是网络传输的最小数据单位)。该层规定了通过怎样的路径(传输路线)到达对方计算机,并传递数据。作用就是在与对方计算机之间通过多台计算机或网络设备进行传输时,选择一条传输路线。 链路层(数据链路层、网络接口层):用来处理网络的硬件部分。包括控制操作系统、硬件设备驱动、NIC(Network Interface Card,网络适配器)、光纤等物理课件部分(连接器等一切传输媒介)。 TCP/IP 通信传输流 应用发请求通过 TCP 处理报文进行分隔发送给网络层网络层增加 MAC 地址给链路, 之后反向操作, 请求数据的时候每一层处理完成后会给这个数据加上这个层的首部信息, 相反, 处理请求的时候, 每处理一层就删除一个首部, 这叫做数据信息的封装 (encapsulate)。 与 HTTP 关系密切的协议 : IP、TCP 和DNS IP ...
HTTP Introduce 2023年10月04日

HTTP状态码

HTTP状态码(HTTP Status Code)是用以表示网页服务器HTTP响应状态的3位数字代码。借助状态码,用户可以知道服务器端是正常处理了请求,还是出现了错误。 它由 RFC 2616 规范定义的,并得到RFC 2518、RFC 2817、RFC 2295、RFC 2774、RFC 4918等规范扩展。 HTTP 状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型。 响应分为五类:信息响应(100–199),成功响应(200–299),重定向(300–399),客户端错误(400–499)和服务器错误 (500–599): 类别 原因短语 1XX Informational(信息性状态码) 信息,服务器收到请求,需要请求者继续执行操作 2XX Success(成功状态码) 成功,操作被成功接收并处理 3XX Redirection(重定向状态码) 重定向,需要进一步的操作以完成请求 4XX Client Error(客户端错误状态码) 客户端错误,请求包含语法错误或无法完成请求 5XX Server Error(服务器错误状态码) 服务器错误,服务器在处理请求的过程中发生了错误 1xx(临时响应) 表示临时响应并需要请求者继续执行操作的状态代码。 状态码 状态码英文名称 中文描述 100 Continue (继续) 请求者应当继续提出请求。 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。 101 Switching Protocols (切换协议) 请求者已要求服务器切换协议,服务器已确认并准备切换。 2xx (成功) 表示成功处理了请求的状态代码。 ...
HTTP StatusCode Introduce 2023年10月04日

HTTP消息结构

HTTP 报文 HTTP是基于客户端/服务端(C/S)的架构模型,通过一个可靠的链接来交换信息,是一个无状态的请求/响应协议。 一个HTTP&quot;客户端&quot;是一个应用程序(Web浏览器或其他任何客户端),通过连接到服务器达到向服务器发送一个或多个HTTP的请求的目的。 一个HTTP&quot;服务器&quot;同样也是一个应用程序(通常是一个Web服务,如Apache Web服务器或IIS服务器等),通过接收客户端的请求并向客户端发送HTTP响应数据。 HTTP使用统一资源标识符(Uniform Resource Identifiers, URI)来传输数据和建立连接。 用于 HTTP 协议交互的信息被称为 HTTP 报文。 请求端(客户端)的 HTTP 报文叫做请求报文,响应端(服务器端)的叫做响应报文。 HTTP 报文大致可分为报文首部和报文主体两块。两者由最初出现的 空行(CR+LF)来划分。通常,并不一定要有报文主体。 请求报文及响应报文的结构 我们来看一下请求报文和响应报文的结构。 客户端请求消息 客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(request line)、可选的请求头部(header)、空行和请求数据四个部分组成,下图给出了请求报文的一般格式。 服务器响应消息 HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。 编码提升传输速率 HTTP 在传输数据时可以按照数据原貌直接传输,但也可以在传输过 程中通过编码提升传输速率。通过在传输时编码,能有效地处理大量 的访问请求。但是,编码的操作需要计算机来完成,因此会消耗更多 的 CPU 等资源。 报文主体和实体主体的差异 报文(message)是 HTTP 通信中的基本单位,由 8 位组字节流(octet sequence, 其中 octet 为 8 个比特)组成,通过 HTTP 通信传输。 实体(entity) 作为请求或响应的有效载荷数据(补充项)被传输,其内容由实 体首部和实体主体组成。 HTTP 报文的主体用于传输请求或响应的实体主体。 通常,报文主体等于实体主体。只有当传输中进行编码操作时,实体 主体的内容发生变化,才导致它和报文主体产生差异。 压缩传输的内容编码 HTTP 协议中的内容编码功能就像用压缩软件压缩东西一样,对实体内容进行编码压缩,内容编码后的实体由客户端接受并解码。这可以压缩传输内容,加快传输速度。 常用的内容编码有以下几种。 gzip(GNU zip) compress(UNIX 系统的标准压缩) deflate(zlib) identity(不进行编码) <h3 ...
HTTP Introduce 2023年10月04日

GIL全局解释器锁

GIL全局解释器锁 GIL:又叫全局解释器锁,每个线程在执行的过程中都需要先获取GIL,保证同一时刻只有一个线程在运行,目的是解决多线程同时竞争解释器资源而出现的线程安全问题。它并不是python语言的特性,仅仅是由于历史的原因在CPython解释器中难以移除,因为python语言运行环境大部分默认在CPython解释器中。 多线程下每个线程在执行的过程中都需要先获取GIL,保证同一时刻只有一个线程在运行。 由于GIL的存在,即使是多线程,事实上同一时刻只能保证一个线程在运行,既然这样多线程的运行效率不就和单线程一样了吗,那为什么还要使用多线程呢? 由于以前的电脑基本都是单核CPU,多线程和单线程几乎看不出差别,可是由于计算机的迅速发展,现在的电脑几乎都是多核CPU了,这时差别就出来了: 即使在多核CPU中,多线程同一时刻也只有一个线程在运行,这样不仅不能利用多核CPU的优势,反而由于每个线程在多个CPU上是交替执行的,导致在不同CPU上切换时造成资源的浪费,反而会更慢。 即原因是一个进程只存在一把gil锁,当在执行多个线程时,内部会争抢gil锁,这会造成当某一个线程没有抢到锁的时候会让cpu等待,进而不能合理利用多核cpu资源。 要去除GIL,主要要考虑四个主要技术点。 引用计数。引用计数在 Python 里是 GCC 的主要方式, C-API 里Py_INCREF 和 Py_DECREF 太好用了,所有C库都用。而这些操作都用到了GIL,只要引用计数活着一天,去掉 GIL 就不容易。 全局和静态变量。这个不用说。 C扩展的并行和重入。 原子性。很多 Python 对象都保证原子性,比如基本类型 list、dict 啥的。 说到在这里要先介绍两个概念:计算密集型和IO密集型 计算密集型:要进行大量的数值计算,例如进行上亿的数字计算、计算圆周率、对视频进行高清解码等等。这种计算密集型任务虽然也可以用多任务完成,但是花费的主要时间在任务切换的时间。这类情况使用多进程实现多任务,可以充分利用多核cpu。 IO密集型:涉及到网络请求(time.sleep())、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。这类情况使用多线程实现多任务,速度要超过单线程的。 解决GIL问题的方案: 使用其他语言的解释器,如java的解释器jython 使用其它语言实现多线程,例如C,Java 使用多进程,是可以利用多核的CPU资源的优势
CPython Introduce 2023年10月04日

CSRF浅析

CSRF跨站点请求伪造(Cross—Site Request Forgery),存在巨大的危害性,你可以这样来理解: ​ 攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。如下:其中Web A为存在CSRF漏洞的网站,Web B为攻击者构建的恶意网站,User C为Web A网站的合法用户。 CSRF攻击攻击原理 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A; 在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A; 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B; 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A; 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。 注意: 通过HTML标签的src属性发起的请求是不遵循同源策略的。 受害者 Bob 在银行有一笔存款,通过对银行的网站发送请求 http://bank.example/withdraw?account=bob&amp;amount=1000000&amp;for=bob2 可以使 Bob 把 1000000 的存款转到 bob2 的账号下。通常情况下,该请求发送到网站后,服务器会先验证该请求是否来自一个合法的 session,并且该 session 的用户 Bob 已经成功登陆。 ​ 黑客 Mallory 自己在该银行也有账户,他知道上文中的 URL 可以把钱进行转帐操作。Mallory 可以自己发送一个请求给银行:http://bank.example/withdraw?account=bob&amp;amount=1000000&amp;for=Mallory。但是这个请求来自 Mallory 而非 Bob,他不能通过安全认证,因此该请求不会起作用。 ​ 这时,Mallory 想到使用 CSRF 的攻击方式,他先自己做一个网站,在网站中放入如下代码: src=&quot;http://bank.example/withdraw?account=bob&amp;amount=1000000&amp;for=Mallory&quot;,并且通过广告等诱使 Bob 来访问他的网站。 当 Bob 访问该网站时,上述 url 就会从 Bob 的浏览器发向银行,而这个请求会附带 Bob 浏览器中的 cookie 一起发向银行服务器。 大多数情况下,该请求会失败,因为他要求 Bob 的认证信息。但是,如果 Bob 当时恰巧刚访问他的银行后不久,他的浏览器与银行网站之间的 session 尚未过期,浏览器的 cookie 之中含有 Bob 的认证信息。这时,悲剧发生了,这个 url 请求就会得到响应,钱将从 Bob 的账号转移到 Mallory 的账号,而 Bob 当时毫不知情。 等以后 ...
Introduce 2023年10月04日