| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- <?php
- namespace App\Providers;
- use App\Services\PaliSeriesesService;
- use App\Services\RedisCache;
- use App\Services\RomanizeService;
- use App\Services\UserService;
- use App\Tools\QueryBuilderMacro;
- use App\View\Composers\BlogViewComposer;
- use Carbon\CarbonImmutable;
- use Godruoyi\Snowflake\LaravelSequenceResolver;
- use Godruoyi\Snowflake\Snowflake;
- use Illuminate\Cache\RateLimiting\Limit;
- use Illuminate\Database\Query\Builder as QueryBuilder;
- use Illuminate\Http\Request;
- use Illuminate\Support\Facades\Date;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Facades\RateLimiter;
- use Illuminate\Support\Facades\View;
- use Illuminate\Support\ServiceProvider;
- use Illuminate\Validation\Rules\Password;
- class AppServiceProvider extends ServiceProvider
- {
- /**
- * Register any application services.
- */
- public function register(): void
- {
- /*
- |--------------------------------------------------------------------------
- | Snowflake ID Generator
- |--------------------------------------------------------------------------
- */
- $this->app->singleton('snowflake', function () {
- return (new Snowflake(
- config('mint.snowflake.data_center_id'),
- config('mint.snowflake.worker_id')
- ))
- ->setStartTimeStamp(
- strtotime(config('mint.snowflake.start')) * 1000
- )
- ->setSequenceResolver(
- new LaravelSequenceResolver(
- $this->app->get('cache')->store()
- )
- );
- });
- /*
- |--------------------------------------------------------------------------
- | Romanize Service
- |--------------------------------------------------------------------------
- */
- $this->app->singleton(RomanizeService::class);
- /*
- |--------------------------------------------------------------------------
- | Redis Cache(业务代码专用,门面 App\Facades\RedisCache,连 Redis Cluster)
- |--------------------------------------------------------------------------
- | 只登记这个类,不替换框架的 cache 绑定:RateLimiter、schedule 互斥锁、
- | snowflake 序列等框架内部仍走 Laravel Cache(config/cache.php)。
- */
- $this->app->singleton(RedisCache::class);
- /*
- |--------------------------------------------------------------------------
- | Pali Serieses Service
- |--------------------------------------------------------------------------
- */
- $this->app->singleton(PaliSeriesesService::class);
- /*
- |--------------------------------------------------------------------------
- | User Service
- |--------------------------------------------------------------------------
- |
- | 必须是 singleton:它在请求内维护一张 uuid => 摘要的身份映射,同一个用户
- | 在一次请求里被解析多次时只查一次库、只签一次头像 URL。每次 make 新实例
- | 那张映射就永远是空的。
- |
- */
- $this->app->singleton(UserService::class);
- }
- /**
- * Bootstrap any application services.
- */
- public function boot(): void
- {
- /*
- |--------------------------------------------------------------------------
- | Laravel 13 Default Production Behaviors
- |--------------------------------------------------------------------------
- */
- $this->configureDefaults();
- /*
- |--------------------------------------------------------------------------
- | Custom Query Builder Macros
- |--------------------------------------------------------------------------
- */
- QueryBuilder::mixin(
- $this->app->make(QueryBuilderMacro::class)
- );
- /*
- |--------------------------------------------------------------------------
- | View Composers
- |--------------------------------------------------------------------------
- */
- View::composer('blog.*', BlogViewComposer::class);
- View::composer('layouts.blog', BlogViewComposer::class);
- /*
- |--------------------------------------------------------------------------
- | Rate Limiters
- |--------------------------------------------------------------------------
- |
- | 找回密码会往外发邮件:按 IP 限住撞库式的批量请求,按邮箱限住对同一个人的
- | 邮件轰炸。设新密码的请求不带 email,只按 IP 计。
- |
- */
- RateLimiter::for('password-resets', fn (Request $request): array => array_filter([
- Limit::perMinute(10)->by('ip:'.$request->ip()),
- $request->filled('email')
- ? Limit::perHour(5)->by('email:'.mb_strtolower((string) $request->input('email')))
- : null,
- ]));
- // 注册验证码同样往外发邮件,口径与找回密码一致
- RateLimiter::for('email-certifications', fn (Request $request): array => array_filter([
- Limit::perMinute(10)->by('ip:'.$request->ip()),
- $request->filled('email')
- ? Limit::perHour(5)->by('email:'.mb_strtolower((string) $request->input('email')))
- : null,
- ]));
- // 比对验证码、建账号:按 IP 限,挡住对 6 位验证码的穷举(单个码另有 5 次试错上限)
- RateLimiter::for('sign-up', fn (Request $request): Limit => Limit::perMinute(20)->by('ip:'.$request->ip()));
- // 登录:按「账号 + IP」挡住对单个账号的猜密码,按 IP 挡住换着账号撞库
- RateLimiter::for('sessions', fn (Request $request): array => [
- Limit::perMinute(5)->by('login:'.mb_strtolower((string) $request->input('login')).'|'.$request->ip()),
- Limit::perMinute(30)->by('ip:'.$request->ip()),
- ]);
- }
- /**
- * Configure default behaviors for production-ready applications.
- */
- protected function configureDefaults(): void
- {
- /*
- |--------------------------------------------------------------------------
- | Use Immutable Dates
- |--------------------------------------------------------------------------
- */
- Date::use(CarbonImmutable::class);
- /*
- |--------------------------------------------------------------------------
- | Prevent destructive DB commands in production
- |--------------------------------------------------------------------------
- */
- DB::prohibitDestructiveCommands(
- app()->isProduction(),
- );
- /*
- |--------------------------------------------------------------------------
- | Strong password defaults in production
- |--------------------------------------------------------------------------
- */
- Password::defaults(
- fn (): ?Password => app()->isProduction()
- ? Password::min(12)
- ->mixedCase()
- ->letters()
- ->numbers()
- ->symbols()
- ->uncompromised()
- : null,
- );
- }
- }
|