Bearweb网站架构
Bearweb是一个为个人网站与小型组织设计的轻量级的数据库驱动PHP网站架构。Bearweb使用简单的数据库与代码结构,致力于降低网站维护难度与成本。
Bearweb, Website, 网站, Framework, 架构, Database-driver, 数据库驱动, Web dev, 网站开发
--by Captdam @ 2023-05-25 07:35 GMT editedBearweb是一个为个人网站与小型组织设计的轻量级的数据库驱动PHP网站架构。Bearweb使用简单的数据库与代码结构,致力于降低网站维护难度与成本。
我在2017年本科时开始开发Bearweb作为我的个人博客与社团网站的底层架构。因此,这个架构再设计与开发时就主要考虑个人与小型组织网站对功能与维护成本的需求。最找我也使用过一些现成的架构,但是复杂的数据库结构带来的维护成本和迁移成本,以及各种已知的未修复的漏洞让我选择自己造轮子。Bearweb现在已经过数次迭代,每一次迭代我都尝试加入一些新功能,删除一些不常用的功能,简化代码结构,降低模组之间耦合度。
这个页面主要用于介绍Bearweb的工作方式与底层架构。
处理过程
首先,简单地来说,浏览网页就是客户端(浏览器)向服务器发送对资源的请求。当打开一个页面,例如一个URL为http://bearweb.com/bearweb-zh
的页面,实际就是客户端(浏览器)在向服务器请求下载http://bearweb.com/bearweb-zh
所储存的资源。如果浏览器下载这个资源后发现这是一个网页页面(HTML),那么浏览器就会渲染这个页面;如果下载的是多媒体资源,如图片,那么就显示出来;如果是js脚本,那么就执行。
Bearweb是一个基于PHP的网站架构。当服务器接收到请求后,服务器接口(例如Apache)就会将这个请求转发给指定的PHP脚本。对于Bearweb,所有的请求都会被发送给index.php。这个脚本只是一个入口,它负责配置了一些服务器设置,例如数据库路径,模板路径。实际的处理,则会交给bearweb.class.php
进行处理。
在bearweb.class.php
中包括了核心架构的三个组件:
Bearweb_Site
- 站点组件:这个组件定义了如何处理这个包含这个URL的请求。Bearweb_Session
- 会话组件:这个组件用于区分不同的会话并记录每一次请求。同时,这个组件也可以记录当前会话的用户。Bearweb_User
- 用户组件:这个组件用于对用户进行管理。这个组件依赖于会话组件中记录的当前会话的用户。这个组件主要用于向特定用户开放一些需要权限的资源,例如一些API或者是内部资料。
首先由这三个核心架构组件对请求进行处理。概括来讲,这三个组件将会从数据库中读取请求的资源,记录当前请求,并且判断当前会话的用户是否有该资源的权限。如果用户没有权限,那么就返回一个错误;如果有,那么就进入下一步。
网站的功能多种多样。每一种资源都由不同的处理方式,Bearweb的数据库中只保存了资源的数据,但是如何展示这个资源的方式却多种多样。例如,一篇博客应该使用博客界面专用的header与footer,海报页面应该不包含header与footer,图片资源则应该完全不适用HTML界面而是直接输出。而且,网站功能也不仅仅是提供某项资源的下载。例如,作者可以向网站上传博客,因此网站需要支持上传。再例如用户登录组织网站,这就需要修改上面提到的会话的用户。将所有功能都写入Bearweb的核心架构显然不是什么好想法。Bearweb的核心架构只提供对请求的基本处理,而具体的处理则交给处理模板。
Bearweb支持开发者自定义处理模板(虽然也可以直接修改bearweb.class.php
中核心架构的代码但是这就太绿皮了!),默认储存于template
文件夹。处理模板包含三大类:
page
- 页面模板:这个模板使用了一个自定义的网页HTML框架,包含了用户不可见的html head
代码,例如style
,script
,meta
;和用户可见的所有网页页面都有的内容,例如导航栏,页头,页尾。object
- 对象模板:与页面模板page
相似,但这个模板不输出HTML代码,而是直接输出对象数据,例如图片或js代码。api
- API模板:这个模板用于用户与网站后端系统的交互,例如用户登录,上传博客与文件对象。
在运行处理模板后,Bearweb就完成了对该请求的处理。Bearweb或是返回用户请求的资源,或是按照用户的请对服务器上一个资源进行处理,或是在有任何错误的情况下返回一个自定义的错误页面与错误代码。
核心架构
Bearweb有三大核心架构组件:站点组件、会话组件、用户组件。每一个组件都有自己的数据库。除了站点组件是必要组件外,另外两个组件都可以被禁用(Bearweb将会在禁用或出错的情况下使用默认值)。
首先,Bearweb将需要初始化三大组件(连接组件数据库)。如果站点组件Bearweb_Site
初始化失败,Bearweb将会使用“错误页面”的模板。如果会话组件Bearweb_Session
或用户组件Bearweb_User
初始化出错,或是没有定义数据库路径,Bearweb将会使用默认值(不记录请求,不建立会话,默认用户为游客)。除了一些API(例如上传文件),Bearweb将不会报错,这里使用了“Fail-safe的想法”,因为大部分情况下非必要组件不应该影响网站功能。例如网站磁盘空间不足的情况下,虽然不能再记录更多的会话历史,但是不应该影响用户正常浏览网站,影响网站在线率。
站点组件 - Bearweb_Site
Bearweb定义,每一个资源应该都有一个URL,同一个URL不能用于不同的资源(API每一个URL为一个endpoint)。因此,站点组件的数据库储存了网站上所有的URL所对应的资源。例如,http://bearweb.com/bearweb-zh
是一个博客文章,那么站点组件的数据库储下URL键为bearweb-zh
的记录就包括了这一篇博客的资源内容(标题、关键字、内容、修改时间……)与处理逻辑(处理模板、资源状态、所有者)。
站点组件的功能就是将这些数据从数据库里读出来,并将数据从数据库内的储存格式转化为处理模板使用的格式(例如,数组使用文本结构储存,时间使用timestamp int结构储存)。此外,在处理API请求时,站点组件还有反向将资源数据转化为储存格式并写入数据库的功能。
除此之外,站点组件还可以根据资源的状态决定是否进行重定向,亦或是根据会话组件提供的信息判断当前客户是否可以使用该资源。
会话组件 - Bearweb_Session
Session: 当一个新用户首次访问站点,会话组件就会这次会话此建立一个在会话数据库Session表中随机唯一的会话ID。该用户接下来的请求都会使用同一会话ID,并且每次都会更新这个会话ID的使用时间。会话ID是通过cookie的方式储存在客户端的,包括一个用户端JS脚本可见的SessionID和一个JS脚本不可见的SessionKey(用于防止JS注入窃取会话)。如果这个SessionsID过期,或是客户端与服务端的SessionKay与SessionID不匹配,那么会话组件将认为用户的会话ID不合法,并会重新生成一个会话ID。
Request: 每当用户发出一个请求,会话组件就会为这次请求建立一个在对话数据库Request表中随机唯一的请求ID,并记录本次请求的会话ID,用户IP,内容摘要。
如果用户禁用了cookie,那么会话组件将不会从客户端接收到将SessionID与SessionKay。因此,会话组件将认为用户是第一次访问网站,因此会在每一个请求都生成一个新的会话ID;如果会话组件Bearweb_Session
被禁用或是出错,那么将不会生成会话ID与请求ID,并使用默认值。这两种情况下,都将认为用户为游客。
会话组件与用户隐私
当代互联网的一大问题就是用户隐私问题。会话组件无疑能用于追踪用户在Bearweb站点的行为,例如用户在何时访问了Bearweb,访问了哪些页面。因为Session ID是基于cookie的,因此用户可以通过禁用cookie的方式避免被会话组件追踪。但是,相应的,也就不能使用基于会话的功能(例如无法登录,永远为游客。对于某些网站,用户需要首先访问网站获得token才能访问多媒体资源,这种情况下将永远无法加载这些资源)。基于cookie的会话控制是网站开发的常用方法,因为Bearweb需要进行用户访问控制,所以Bearweb需要使用cookie以进行会话控制。
除此之外,用户的IP也将被记录。用户IP是包含在任何网络包中的数据,IP记录了包裹的发送方与接收方,因此IP不可被禁用。当然,用户可以使用VPN,这样就只有VPN的IP被暴露而不是用户自己的IP。不过,VPN提供方还是能查看到用户的IP。
Bearweb只使用会话控制作为用户登录与访问管理。除非用户有登录并且用户组件有记录用户的信息,否则会话都是匿名的。
Side note:互联网不存在“无痕浏览”!浏览即下载,下载即访问。所谓“无痕”只是客户端没有记录,服务端与所有的路由节点都能留下记录。
用户组件 - Bearweb_User
用户组件用于记录用户的ID,用户名,密码,所在分组等信息。
用户组件建立在会话组件之上。会话组件记录了当前会话的用户,这个值将被会话组件用于从用户组件的数据库中读取该用户的信息。如果会话组件或是用户组件被禁用或出错,那么用户组件将使用默认用户(游客)。
用户组件记录了用户所在的组,这一信息被用于资源的访问控制。对于需要访问权限的资源,用户所在的组必须为资源开放的组中任一一个。
处理模板
处理模板用于定义该如何处理数据库里面的资源数据,并将处理完的数据返回给客户端。Bearweb的处理模板分为三个母模板:页面模板、对象模板、API模板。母模板下定义了子模版以决定更细节的处理流程。当然,为了增加网站功能性,用户也可以修改或加入自定义模板。
页面模板 - page
对于大部分网站来说,每一个页面的布局都是差不多的。不同的页面有不同的正文内容,但是页头与页尾是一模一样的。页面模板的目的就是包含这些对于所有页面来说都一模一样的部分(页头,页尾),这样就不需要为每一个页面都单独加入页头与页尾的HTML代码了。此外,当需要修改页面布局时,只需要修改页面模板,就可以对所有页面的布局同时进行修改,提高了网站页面的一致性。
页面模板将固定设定输出内容的mime type
为text/html
。页面模板将根据资源的创建时间与修改时间决定缓存策略。
对象模板 - object
对象模板用于输出站点的非网页资源,例如图片、JS脚本。当然,也可以使用对象模板输出不包含页面模板的页头与页尾的页面。
页面模板将根据站点模块提供的资源信息决定输出内容的mime type
。站点模板将根据资源的创建时间与修改时间决定缓存策略。
API模板 - api
API模板用于定义站点的endpoint,作为客户端与服务端后台交互的接口。API模板是完全自定义的。