2
0

AppServiceProvider.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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. /*
  91. |--------------------------------------------------------------------------
  92. | Custom Query Builder Macros
  93. |--------------------------------------------------------------------------
  94. */
  95. QueryBuilder::mixin(
  96. $this->app->make(QueryBuilderMacro::class)
  97. );
  98. /*
  99. |--------------------------------------------------------------------------
  100. | View Composers
  101. |--------------------------------------------------------------------------
  102. */
  103. View::composer('blog.*', BlogViewComposer::class);
  104. View::composer('layouts.blog', BlogViewComposer::class);
  105. /*
  106. |--------------------------------------------------------------------------
  107. | Rate Limiters
  108. |--------------------------------------------------------------------------
  109. |
  110. | 找回密码会往外发邮件:按 IP 限住撞库式的批量请求,按邮箱限住对同一个人的
  111. | 邮件轰炸。设新密码的请求不带 email,只按 IP 计。
  112. |
  113. */
  114. RateLimiter::for('password-resets', fn (Request $request): array => array_filter([
  115. Limit::perMinute(10)->by('ip:'.$request->ip()),
  116. $request->filled('email')
  117. ? Limit::perHour(5)->by('email:'.mb_strtolower((string) $request->input('email')))
  118. : null,
  119. ]));
  120. // 注册验证码同样往外发邮件,口径与找回密码一致
  121. RateLimiter::for('email-certifications', fn (Request $request): array => array_filter([
  122. Limit::perMinute(10)->by('ip:'.$request->ip()),
  123. $request->filled('email')
  124. ? Limit::perHour(5)->by('email:'.mb_strtolower((string) $request->input('email')))
  125. : null,
  126. ]));
  127. // 比对验证码、建账号:按 IP 限,挡住对 6 位验证码的穷举(单个码另有 5 次试错上限)
  128. RateLimiter::for('sign-up', fn (Request $request): Limit => Limit::perMinute(20)->by('ip:'.$request->ip()));
  129. // 登录:按「账号 + IP」挡住对单个账号的猜密码,按 IP 挡住换着账号撞库
  130. RateLimiter::for('sessions', fn (Request $request): array => [
  131. Limit::perMinute(5)->by('login:'.mb_strtolower((string) $request->input('login')).'|'.$request->ip()),
  132. Limit::perMinute(30)->by('ip:'.$request->ip()),
  133. ]);
  134. }
  135. /**
  136. * Configure default behaviors for production-ready applications.
  137. */
  138. protected function configureDefaults(): void
  139. {
  140. /*
  141. |--------------------------------------------------------------------------
  142. | Use Immutable Dates
  143. |--------------------------------------------------------------------------
  144. */
  145. Date::use(CarbonImmutable::class);
  146. /*
  147. |--------------------------------------------------------------------------
  148. | Prevent destructive DB commands in production
  149. |--------------------------------------------------------------------------
  150. */
  151. DB::prohibitDestructiveCommands(
  152. app()->isProduction(),
  153. );
  154. /*
  155. |--------------------------------------------------------------------------
  156. | Strong password defaults in production
  157. |--------------------------------------------------------------------------
  158. */
  159. Password::defaults(
  160. fn (): ?Password => app()->isProduction()
  161. ? Password::min(12)
  162. ->mixedCase()
  163. ->letters()
  164. ->numbers()
  165. ->symbols()
  166. ->uncompromised()
  167. : null,
  168. );
  169. }
  170. }