AppServiceProvider.php 7.4 KB

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