Install from composer
composer require lyrasoft/firewallThen copy files to project
php windwalker pkg:install lyrasoft/firewall -t routes -t migrationsAdd this line to admin & front middleware if you don't want to override languages:
$this->lang->loadAllFromVendor('lyrasoft/firewall', 'ini');
// OR
$this->lang->loadAllFromVendor(\Lyrasoft\Firewall\FirewallPackage::class, 'ini');Or run this command to copy languages files:
php windwalker pkg:install lyrasoft/firewall -t langEdit resources/menu/admin/sidemenu.menu.php
$menu->link($this->trans('unicorn.title.grid', title: $this->trans('firewall.redirect.title')))
->to($nav->to('redirect_list')->var('type', 'main'))
->icon('fal fa-angles-right');
$menu->link($this->trans('unicorn.title.grid', title: $this->trans('firewall.ip.rule.title')))
->to($nav->to('ip_rule_list')->var('type', 'main'))
->icon('fal fa-network-wired');Add RedirectMiddleware to etc/app/main.php
use Lyrasoft\Firewall\Middleware\RedirectMiddleware;
// ...
'middlewares' => [
\Windwalker\DI\create(
RedirectMiddleware::class,
ignores: [
'admin/*'
]
),
// ...
],Now you can add redirect records at admin:
- Add
/at start, the path will compare from site base root (not domain root). - If you enable the
Regex:- Add
*will compare a path segment with any string. - Add
**will compare cross-segments. - You can add custom regex rules, like:
/foo/(\d+)
- Add
Thr dest path can be relative path: foo/bar or full URL: https://simular.co/foo/bar.
If you enabne the Regex, you may use variables start with $ to insert matched string. For example, a foo/*/edit/(\d+), can redirect to new/path/$1/edit/$2
Only 404: Only redirect if a page is 404, if page URL exists, won't redirect.Handle Locale: If this site is multi-language, this params will auto auto detect the starting ;anguage prefix and auto add it to dest path, you may use{lang}in dest path to custom set lang alias position.
Redirect tables has type colimn, you can use admin/redirect/list/{type} to manage different types.
And if you want to choose types for middleware, you can do this:
// ...
'middlewares' => [
\Windwalker\DI\create(
RedirectMiddleware::class,
type: 'other_type',
ignores: [
'admin/*'
]
),
// ...
],The type supports string|Enum|array|null|false, if you send NULL into it, means all redirect records. If you send FALSE, means don't use DB recods.
You can use custom redirect list, custom list will auto-enable the regex:
This settings will merge DB list and custom list.
// ...
'middlewares' => [
\Windwalker\DI\create(
RedirectMiddleware::class,
type: 'flower',
list: [
'foo/bar' => 'hello/world',
'foo/yoo/*' => 'hello/mountain/$1',
],
ignores: [
'admin/*'
]
),
// ...
],This settings will disable DB list and only use custom list.
// ...
'middlewares' => [
\Windwalker\DI\create(
RedirectMiddleware::class,
type: false,
list: [
'foo/bar' => 'hello/world',
'foo/yoo/*' => 'hello/mountain/$1',
],
ignores: [
'admin/*'
]
),
// ...
],Custom List can use Closure to generate list:
// ...
'middlewares' => [
\Windwalker\DI\create(
RedirectMiddleware::class,
// ...
list: raw(function (FooService $fooService) {
return ...;
});
),
// ...
],The custom list redirect status code default is 301, if you want to use other status, set it to
REDIRECT_DEFAULT_STATUS env varialbe.
If there has some reason you can not wait RedirectResponse return, you may use instant redirect:
// ...
'middlewares' => [
\Windwalker\DI\create(
RedirectMiddleware::class,
// ...
instantRedirect: true,
),
// ...
],If you wanr to disable this middleware in debug mode, add this options:
\Windwalker\DI\create(
RedirectMiddleware::class,
enabled: !WINDWALKER_DEBUG
),Add afterHit hook that you can do somthing or log if redirect hit.
\Windwalker\DI\create(
RedirectMiddleware::class,
afterHit: raw(function (string $dest, \Redirect $redirect) {
\Windwalker\Core\Manager\Logger::info('Redirect to: ' . $dest);
})
),To enable IP Rules, add FirewallMiddleware to front.route.php
use Lyrasoft\Firewall\Middleware\FirewallMiddleware;
// ...
->middleware(
FirewallMiddleware::class,
)
// ...Select Allow or Block, and enter the IP Range format:
The supported formats:
| Type | Syntax | Details |
|---|---|---|
| IPV6 | ::1 |
Short notation |
| IPV4 | 192.168.0.1 |
|
| Range | 192.168.0.0-192.168.1.60 |
Includes all IPs from 192.168.0.0 to 192.168.0.255 and from 192.168.1.0 to 198.168.1.60 |
| Wild card | 192.168.0.* |
IPs starting with 192.168.0 Same as IP Range 192.168.0.0-192.168.0.255 |
| Subnet mask | 192.168.0.0/255.255.255.0 |
IPs starting with 192.168.0 Same as 192.168.0.0-192.168.0.255 and 192.168.0.* |
| CIDR Mask | 192.168.0.0/24 |
IPs starting with 192.168.0 Same as 192.168.0.0-192.168.0.255 and 192.168.0.*and 192.168.0.0/255.255.255.0 |
We use mlocati/ip-lib as IP Range parser.
You can also access different type from ip-rule/list/{type}.
And set type name to middleware
->middleware(
FirewallMiddleware::class,
type: 'foo'
)type can also supports string, array and enum. Use NULL to select all, FALSE to disable DB.
If you want to manually set ip list, FirewallMiddleware custom list must use 2 lists, allowList and blockList.
->middleware(
FirewallMiddleware::class,
type: false,
allowList: [
'0.0.0.0',
'144.122.*.*',
],
blockList: [
'165.2.90.45',
'222.44.55.66',
],
)If you wanr to disable this middleware in debug mode, add this options:
\Windwalker\DI\create(
FirewallMiddleware::class,
enabled: !WINDWALKER_DEBUG
),Add afterHit hook that you can do somthing or log if an IP was be blocked.
\Windwalker\DI\create(
FirewallMiddleware::class,
afterHit: raw(function (AppRequest $appRequest) {
\Windwalker\Core\Manager\Logger::info('Attack from: ' . $appRequest->getClientIp());
})
),Both middlewares has a cacheTtl param, default is 3600 seconds.
\Windwalker\DI\create(
FirewallMiddleware::class,
cacheTtl: 3600
),Everytime you edit Redirect or IpRule will auto clear all caches.
The cache files is located at caches/firewall/, and you can add firewall to clear cache command in composer.json
"post-autoload-dump": [
...
"php windwalker cache:clear renderer html firewall" <-- Add firewall
],Cache will disable in debug mode or when ttl set to 0.

