tigron / skeleton-application-web
Skeleton 的 Web 应用程序
Requires
- tigron/skeleton-core: >=4
Suggests
README
此骨架应用程序将创建一个 Web 应用程序。Web 应用程序将包含模块、模板和事件。
安装
通过 Composer 安装
composer require tigron/skeleton-application-web
设置应用程序
您的 Web 应用程序应遵循以下目录结构
- application_path from skeleton-core
- APP_NAME
- config
- event
- media
- css
- font
- image
- javascript
- module
- template
重要的是要理解,每个创建的类都应该在其正确的命名空间中。以下命名空间应被使用
module: \App\{APP_NAME}\Module
event: \App\{APP_NAME}\Event
配置
对于此应用程序,需要 skeleton-core 应用程序配置。更多信息可以在下面找到
路由
一个将 routes
映射到 modules
的数组。可以使用路由定义来生成漂亮的 URL 或翻译版本。最佳用法可以通过示例描述。
[
'web_module_index' => [
'$language/default/route/to/index',
'$language/default/route/to/index/$action',
'$language/default/route/to/index/$action/$id',
'$language[en]/test/routing/engine',
'$language[en]/test/routing/engine/$action',
'$language[en]/test/routing/engine/$action/$id',
],
],
用法
路由到正确的应用程序
基于请求的 Host
标头,将启动正确的应用程序。这是应用程序配置文件(如上所示)中的 hostnames
数组将发挥作用的地方。
如果 skeleton-core
能够根据请求中提供的 Host
标头找到匹配的应用程序,则此应用程序将被启动。
如果您的应用程序已配置 base_uri
,则也会考虑这一点。例如:可以通过将其 base_uri
设置为 /admin
来区分 CMS 应用程序。
路由到正确的模块
没有文件扩展名且不匹配 media
文件的请求将路由到模块和匹配的方法。模块根据请求 URI 确定,排除所有 $_GET 参数。模块是一个应该从 \Skeleton\Core\Application\Web\Module
继承的类。
以下是一些示例来说明这一点
如您在最后两个示例中看到的那样,index
模块有点特殊,因为如果它们位于子文件夹中,则可以使用它们代替基础模块。可以通过配置指令 module_default
配置 index
。
路由到正确的方法
模块可以包含多个可以处理请求的方法。每个这些请求都有一个以 'display' 开头的方法名。方法根据 $_GET['action'] 变量定义。
以下是一些示例
CSRF
skeleton-application-web
软件包可以自动注入和验证它收到的每个 POST
请求的 CSRF 令牌。已经定义了各种事件,您可以通过这些事件来控制 CSRF 流。以下可以找到这些事件的列表。
CSRF 默认情况下全局禁用。如果您想启用它,只需通过配置指令 csrf_enabled
将 csrf_enabled
标志设置为 true 即可。
启用后,它将适用于所有您的应用程序。如果您只想为特定应用程序禁用它,请在应用程序配置中将 csrf_enabled
标志设置为 false
。
有多个事件可用于控制 CSRF 行为,以下已进行了说明。
当启用时,具有正确令牌作为值的隐藏表单元素将自动注入到找到的每个 <form>...</form>
块中。这允许它在不需要更改您代码的情况下工作。
如果您需要访问令牌值和名称,您可以从自动分配给模板的 env
变量中访问它们。以下列出了可用的变量:
- env.csrf_header_token_name
- env.csrf_post_token_name
- env.csrf_session_token_name
- env.csrf_token
有一个需要注意的问题是 XMLHttpRequest
调用(或 AJAX
)。如果您的应用程序使用 jQuery
,您可以使用下面的示例自动为每个相关的 XMLHttpRequest
注入一个头。
首先,使令牌值和名称对您的视图可用。这样做的一个好地方可能是文档的 <head>...</head>
块。
<!-- CSRF token values -->
<meta name="csrf-header-token-name" content="{{ env.csrf_header_token_name }}">
<meta name="csrf-token" content="{{ env.csrf_token }}">
接下来,我们可以使用 jQuery
的 $.ajaxSend()
。这允许您配置将应用于每个后续的 $.ajax()
调用(或其衍生,如 $.post()
)的设置。
$(document).ajaxSend(function(e, xhr, settings) {
if (!(/^(GET|HEAD|OPTIONS|TRACE)$/.test(settings.type)) && !this.crossDomain) {
xhr.setRequestHeader($('meta[name="csrf-header-token-name"]').attr('content'), $('meta[name="csrf-token"]').attr('content'));
}
});
请注意对请求类型和跨域请求的检查。这可以避免将令牌随不需要它的请求发送。
重放
内置的重放检测试图通过用户双击提交按钮来解决重复表单提交的问题。通常,这种情况在用户界面中不会被捕获。
默认情况下禁用重放检测,如果您想启用它,将 replay_enabled
配置指令切换为 true。
您可以通过将各自的配置中的 replay_enabled
标志设置为 false
来为单个应用程序禁用重放检测。
当启用重放检测时,它将向每个它可以找到的 form
元素注入一个隐藏的 __replay-token
元素。每个令牌将是唯一的。一旦提交,该令牌将被添加到先前看到的令牌列表中。如果在 30 秒内再次出现相同的令牌,重放检测将被触发。
如果您的应用程序定义了 replay_detected
事件,这将被调用。应用程序必须决定采取什么行动。一个建议是将用户重定向到 HTTP 引用值,如果存在的话。
事件
可以在应用程序执行过程中的特定关键点创建事件以执行任务。此应用程序支持在 skeleton-core 中描述的所有可用事件。此外,以下事件也是可用的:
I18n上下文
get_translator_extractor
获取此应用程序的 Translator\Extractor。如果没有提供,将为应用程序的模板目录创建一个 Translator\Extractor\Twig。
public function get_translator_extractor(): \Skeleton\I18n\Translator\Extractor
get_translator_storage
获取此应用程序的 Translator\Storage。如果没有提供,将创建一个 Translator\Storage\Po,但前提是已配置默认存储路径。
public function get_translator_storage(): \Skeleton\I18n\Translator\Storage
get_translator
获取此应用程序的 Translator 对象。如果不需要翻译,则返回 null。默认情况下,使用上述方法的存储和提取器创建一个翻译器。
public function get_translator(): ?\Skeleton\I18n\Translator
detect_language
检测应用程序的语言。这是分 3 步进行的
- 是否有通过 $_GET['language'] 请求的语言
- 是否在 $_SESSION['language'] 中存储了语言
- 在 $_SERVER['HTTP_ACCEPT_LANGUAGE'] 和所有可用语言之间协商语言。
返回的语言将存储在会话中。
public function detect_language(): \Skeleton\I18n\LanguageInterface
模块上下文
bootstrap
在启动模块之前调用 bootstrap
方法。
public function bootstrap(\Skeleton\Core\Web\Module $module): void
teardown
当模块完成时调用 teardown
方法。
public function bootstrap(\Skeleton\Core\Web\Module $module): void
access_denied
每当请求一个用户无法访问的模块时,都会调用 access_denied
方法。模块中的可选 secure()
方法表示用户是否被授权访问。
public function access_denied(\Skeleton\Core\Web\Module $module): void
not_found
当请求一个不存在的模块时,会调用 not_found
方法。
public function not_found(): void
安全上下文
csrf_validate_enabled
csrf_validate_enabled
方法覆盖了完整的验证执行过程,这对于排除特定的路径非常有用。下面是一个示例实现。
public function csrf_validate_enabled(): bool {
$excluded_paths = [
'/no/csrf/*',
];
foreach ($excluded_paths as $excluded_path) {
if (fnmatch ($excluded_path, $_SERVER['REQUEST_URI']) === true) {
return false;
}
}
return true;
}
csrf_validate_success
csrf_validate_success
方法允许您在验证成功后覆盖检查结果。它期望返回一个布尔值。
public function csrf_validate_success(): bool
csrf_validate_failed
csrf_validate_failed
方法允许您在验证失败后覆盖检查结果。它期望返回一个布尔值。
public function csrf_validate_failed(): bool
csrf_generate_session_token
csrf_generate_session_token
方法允许您覆盖会话令牌的生成,并生成自定义值。它期望返回一个字符串。
public function csrf_generate_session_token(): string
csrf_inject
csrf_inject
方法允许您覆盖渲染模板的HTML表单中自动注入的隐藏CSRF令牌元素。它期望返回一个字符串,包含要发送回客户端的渲染HTML。
public function csrf_inject($html, $post_token_name, $post_token): string
csrf_validate
csrf_validate
方法允许您覆盖CSRF令牌的验证过程。它期望返回一个布尔值。
public function csrf_validate($submitted_token, $session_token): bool
replay_detected
replay_detected
方法允许您捕获重放检测事件。例如,如果存在HTTP引用头,您可以将用户重定向到该值。
public function replay_detected() {
if (!empty($_SERVER['HTTP_REFERER'])) {
Session::redirect($_SERVER['HTTP_REFERER'], false);
} else {
Session::redirect('/');
}
}
replay_inject
replay_inject
方法允许您覆盖渲染模板的HTML表单中自动注入的隐藏重放令牌元素。它期望返回一个字符串,包含要发送回客户端的渲染HTML。
public function csrf_inject($html, $post_token_name, $post_token): string
session_cookie
session_cookie
方法允许您在会话开始之前设置会话cookie参数。通常,这会用于SameSite cookie属性。
public function session_cookie(): void