AppServiceProvider.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <?php
  2. namespace App\Providers;
  3. use App\Services\PaliSeriesesService;
  4. use App\Services\RedisCache;
  5. use App\Services\RomanizeService;
  6. use App\Services\UserService;
  7. use App\Tools\QueryBuilderMacro;
  8. use App\View\Composers\BlogViewComposer;
  9. use Carbon\CarbonImmutable;
  10. use Godruoyi\Snowflake\LaravelSequenceResolver;
  11. use Godruoyi\Snowflake\Snowflake;
  12. use Illuminate\Cache\RateLimiting\Limit;
  13. use Illuminate\Database\Query\Builder as QueryBuilder;
  14. use Illuminate\Http\Request;
  15. use Illuminate\Support\Facades\Date;
  16. use Illuminate\Support\Facades\DB;
  17. use Illuminate\Support\Facades\RateLimiter;
  18. use Illuminate\Support\Facades\View;
  19. use Illuminate\Support\ServiceProvider;
  20. use Illuminate\Validation\Rules\Password;
  21. use Illuminate\Support\Facades\URL;
  22. class AppServiceProvider extends ServiceProvider
  23. {
  24. /**
  25. * Register any application services.
  26. */
  27. public function register(): void
  28. {
  29. /*
  30. |--------------------------------------------------------------------------
  31. | Snowflake ID Generator
  32. |--------------------------------------------------------------------------
  33. */
  34. $this->app->singleton('snowflake', function () {
  35. return (new Snowflake(
  36. config('mint.snowflake.data_center_id'),
  37. config('mint.snowflake.worker_id')
  38. ))
  39. ->setStartTimeStamp(
  40. strtotime(config('mint.snowflake.start')) * 1000
  41. )
  42. ->setSequenceResolver(
  43. new LaravelSequenceResolver(
  44. $this->app->get('cache')->store()
  45. )
  46. );
  47. });
  48. /*
  49. |--------------------------------------------------------------------------
  50. | Romanize Service
  51. |--------------------------------------------------------------------------
  52. */
  53. $this->app->singleton(RomanizeService::class);
  54. /*
  55. |--------------------------------------------------------------------------
  56. | Redis Cache(业务代码专用,门面 App\Facades\RedisCache,连 Redis Cluster)
  57. |--------------------------------------------------------------------------
  58. | 只登记这个类,不替换框架的 cache 绑定:RateLimiter、schedule 互斥锁、
  59. | snowflake 序列等框架内部仍走 Laravel Cache(config/cache.php)。
  60. */
  61. $this->app->singleton(RedisCache::class);
  62. /*
  63. |--------------------------------------------------------------------------
  64. | Pali Serieses Service
  65. |--------------------------------------------------------------------------
  66. */
  67. $this->app->singleton(PaliSeriesesService::class);
  68. /*
  69. |--------------------------------------------------------------------------
  70. | User Service
  71. |--------------------------------------------------------------------------
  72. |
  73. | 必须是 singleton:它在请求内维护一张 uuid => 摘要的身份映射,同一个用户
  74. | 在一次请求里被解析多次时只查一次库、只签一次头像 URL。每次 make 新实例
  75. | 那张映射就永远是空的。
  76. |
  77. */
  78. $this->app->singleton(UserService::class);
  79. }
  80. /**
  81. * Bootstrap any application services.
  82. */
  83. public function boot(): void
  84. {
  85. /*
  86. |--------------------------------------------------------------------------
  87. | Laravel 13 Default Production Behaviors
  88. |--------------------------------------------------------------------------
  89. */
  90. $this->configureDefaults();
  91. // Force HTTPS scheme in production
  92. if ($this->app->environment('production')) {
  93. URL::forceScheme('https');
  94. }
  95. /*
  96. |--------------------------------------------------------------------------
  97. | Custom Query Builder Macros
  98. |--------------------------------------------------------------------------
  99. */
  100. QueryBuilder::mixin(
  101. $this->app->make(QueryBuilderMacro::class)
  102. );
  103. /*
  104. |--------------------------------------------------------------------------
  105. | View Composers
  106. |--------------------------------------------------------------------------
  107. */
  108. View::composer('blog.*', BlogViewComposer::class);
  109. View::composer('layouts.blog', BlogViewComposer::class);
  110. /*
  111. |--------------------------------------------------------------------------
  112. | Rate Limiters
  113. |--------------------------------------------------------------------------
  114. |
  115. | 找回密码会往外发邮件:按 IP 限住撞库式的批量请求,按邮箱限住对同一个人的
  116. | 邮件轰炸。设新密码的请求不带 email,只按 IP 计。
  117. |
  118. */
  119. RateLimiter::for('password-resets', fn (Request $request): array => array_filter([
  120. Limit::perMinute(10)->by('ip:'.$request->ip()),
  121. $request->filled('email')
  122. ? Limit::perHour(5)->by('email:'.mb_strtolower((string) $request->input('email')))
  123. : null,
  124. ]));
  125. // 注册验证码同样往外发邮件,口径与找回密码一致
  126. RateLimiter::for('email-certifications', fn (Request $request): array => array_filter([
  127. Limit::perMinute(10)->by('ip:'.$request->ip()),
  128. $request->filled('email')
  129. ? Limit::perHour(5)->by('email:'.mb_strtolower((string) $request->input('email')))
  130. : null,
  131. ]));
  132. // 比对验证码、建账号:按 IP 限,挡住对 6 位验证码的穷举(单个码另有 5 次试错上限)
  133. RateLimiter::for('sign-up', fn (Request $request): Limit => Limit::perMinute(20)->by('ip:'.$request->ip()));
  134. // 登录:按「账号 + IP」挡住对单个账号的猜密码,按 IP 挡住换着账号撞库
  135. RateLimiter::for('sessions', fn (Request $request): array => [
  136. Limit::perMinute(5)->by('login:'.mb_strtolower((string) $request->input('login')).'|'.$request->ip()),
  137. Limit::perMinute(30)->by('ip:'.$request->ip()),
  138. ]);
  139. }
  140. /**
  141. * Configure default behaviors for production-ready applications.
  142. */
  143. protected function configureDefaults(): void
  144. {
  145. /*
  146. |--------------------------------------------------------------------------
  147. | Use Immutable Dates
  148. |--------------------------------------------------------------------------
  149. */
  150. Date::use(CarbonImmutable::class);
  151. /*
  152. |--------------------------------------------------------------------------
  153. | Prevent destructive DB commands in production
  154. |--------------------------------------------------------------------------
  155. */
  156. DB::prohibitDestructiveCommands(
  157. app()->isProduction(),
  158. );
  159. /*
  160. |--------------------------------------------------------------------------
  161. | Strong password defaults in production
  162. |--------------------------------------------------------------------------
  163. */
  164. Password::defaults(
  165. fn (): ?Password => app()->isProduction()
  166. ? Password::min(12)
  167. ->mixedCase()
  168. ->letters()
  169. ->numbers()
  170. ->symbols()
  171. ->uncompromised()
  172. : null,
  173. );
  174. }
  175. }