wassapaks / laravel_api_boilerplate_firebase

这是一个Laravel API模板,您可以用它快速构建您的第一个API。基于Laravel 11框架构建。这是我为了我的一个项目开发的。这个API需要Firebase进行身份验证,但您可以配置使用JWT、Cognito、Sanctum或您自己的选择。因为大多数组件和框架都是开源的,所以我分享这个项目,以帮助有相同需求的人,欢迎提交PR进行改进和添加我遗漏的组件。

dev-development 2024-08-07 12:48 UTC

This package is auto-updated.

Last update: 2024-09-07 13:03:47 UTC


README

这是一个Laravel API模板,您可以用它快速构建API。基于Laravel 11框架构建。这是我为我的个人项目开发的。这个API需要Firebase进行身份验证,但您可以选择使用JWT、Cognito、Sanctum或其他方式。因为这个项目的大部分组件和框架都是开源的,所以分享它以帮助有相同需求的人,欢迎提交PR进行改进和添加我遗漏的组件。

在构建这个API时,我考虑了以下REST组件清单

  • Firebase身份验证
  • 创建了一个基本的CRUD端点
  • 用户管理
  • 基于IP和用户使用Redis集成速率限制器
  • 使用Redis的缓存示例
  • 应用CSP、CORS和安全头部
  • 使用头部进行版本控制
  • 在日志监控中使用Sentry
  • 将门和策略应用于API
  • 应用仓库设计模式
  • 应用可搜索的API文档
  • 使用https://github.com/willdurand/Hateoas应用Hateoas

要求

在开始之前,您需要以下内容。

安装

请确保您已安装composer

composer create-project wassapaks/laravel_api_boilerplate_firebase

然后执行php artisan migrate --seed以初始化表和填充表。

配置您的.env文件

要运行此项目,您需要将以下环境变量添加到您的.env文件中。

Firebase

下载您的Firebase身份验证json凭证,并将其放置在storage app firebase中

FIREBASE_CREDENTIALS=storage/app/firebase-auth.json
FIREBASE_CONTINUE_URL="https://:8080"

Redis

REDIS_CLIENT=predis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

Sentry

SENTRY_LARAVEL_DSN=
SENTRY_TRACES_SAMPLE_RATE=1.0
SENTRY_PROFILES_SAMPLE_RATE=1.0VITE_SENTRY_DSN_PUBLIC="${SENTRY_LARAVEL_DSN}"

版本头部

X_API_VERSION=v1

版本控制

在您的路由文件夹中,您将看到api.phpapi.v2.php,这是我实现版本控制的方式,它在bootstrap/app.phpwithrouting()中进行映射,当发起请求时。您需要在您的Postman请求中包含X_API_VERSION,其值应该是文件名的第二段。最初您的身份验证在api.v2.php上,Books CRUD示例在api.php上,在您的头部应该是X_API_VERSION: v1

要添加新版本,只需创建新文件,例如:api.v3.php

以下是bootstrap/app.php中版本控制实现的示例。

->withRouting(
	commands: __DIR__  .  '/../routes/console.php',
	health: '/up',
	using: function (Request  $request) {
		$apiVersion = $request->header('X-Api-Version', env('X_API_VERSION'));
		$routeFile = $apiVersion != env('X_API_VERSION') ? 
			base_path(sprintf('routes/%s.%s.php', 'api', $apiVersion)) : 
			base_path(sprintf('routes/%s.php', 'api'));

		  if (!file_exists($routeFile)) {
			return  response()->json([
				'error_message' => 'Wrong Version Request.',
				], 400);
		}
		Route::middleware('api')
			->prefix('api')
			->group($routeFile);
},)

在您的请求中添加以下头部:X_API_VERSION:

Firebase身份验证和用户路由

我已经将身份验证和路由放置在api.v2.php中。要测试,请将请求头中的X_API_VERSION设置为v2。

  • POST api/auth/signin请求令牌
  • GET api/auth/verify验证令牌
  • POST api/user-management/users创建用户和Firebase用户
  • GET api/user-management/users/{$id}显示单个用户
  • DELETE api/user-management/users/{$id}删除用户
  • GET api/user-management/users显示所有用户

CORS配置

Laravel 11 已经可以响应 CORS

<?php
return [
/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your settings for cross-origin resource sharing
| or "CORS". This determines what cross-origin operations may execute
| in web browsers. You are free to adjust these settings as needed.
|
| To learn more: https://mdn.org.cn/en-US/docs/Web/HTTP/CORS
|
*/
'paths' => ['api/*', 'sanctum/csrf-cookie'],

'allowed_methods' => ['POST','GET', 'PATCH', 'PUT', 'DELETE', 'OPTIONS'],  

'allowed_origins' => ['*'],

'allowed_origins_patterns' => [],

'allowed_headers' => ['Origin', 'X-Api-Version','Authorization', 'Content-Type', 'Accept'],

'exposed_headers' => [],

'max_age' => 0,

'supports_credentials' => false,
];

安全头和CSP

您可以在 app/Http/Middleware/SecurityHeadersMiddleware.php 中修改安全头。

// For Reference:
// https://cheatsheetseries.owasp.ac.cn/cheatsheets/REST_Security_Cheat_Sheet.html#security-headers

$response->headers->set('Content-Security-Policy', "default-src 'none'; frame-ancestors 'none'");
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
$response->headers->set('X-Content-Type-Options', 'no-sniff');
$response->headers->set('Content-Type', 'application/json');
$response->headers->set('X-Frame-Options:', 'DENY');
$response->headers->set('Referrer-Policy:', 'no-referrer');
$response->headers->set('Permissions-Policy', 'aaccelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=()');

$response->header('X-Api-Version', $request->header('X-Api-Version', 'v1'));
$response->header('X-Api-Latest-Version', config('app.latestApiVersion'));

图书路由(CRUD示例)

routes/api.php

Route::middleware(['firebase.auth', 'throttle:api'])->group(function () {
	Route::get('/books', [BookController::class, 'index']);
	Route::get('/books/{id}', [BookController::class, 'show']);
	Route::post('/books', [BookController::class, 'store']);
	Route::put('/books/{id}', [BookController::class, 'update']);
	Route::delete('/books/{id}', [BookController::class, 'destroy']);
});

以下是事情发生的方式

  • app/Http/Controllers/BookController.php 处理您的输入和输出
  • app/Interfaces/BookRepositoryInterface.phpapp/Providers/RepositoryServiceProvider.php 中与 app/Repositories/BookRepository.php 绑定,处理您的资源数据处理
  • app/Http/Resources/BookResource.php 处理资源数据的返回,您可以在其中添加Hateoas
  • app/Http/Requests/StoreBookRequest.phpapp/Http/Requests/UpdateBookRequest 处理验证和用户策略

创建路由

  • 您可以使用现有版本或在路由目录中添加新的 api.[new version].php
  • 在您的API中,您可以指定身份验证和节流中间件
Route::middleware(['firebase.auth', 'throttle:api'])->group(function ()

速率限制器

  • 速率限制器可以在 app/Http/Providers/RouteServiceProvider.php 中进行配置
public  function  boot(): void
{
	RateLimiter::for('api', function (Request  $request) {
		$limit = ($request->bearerToken()) ? 1000 : 50;
			return  Limit::perMinute($limit)->by($request->ip())->response(function (Request $request, array  $headers) {
				return  ApiResponseClass::tooManyRequest($request->ip());
			});
	});
}
  • 或者您可以在控制器的特定端点放置特定的速率限制器,例如
public  function  login(Request  $request): ApiResponseClass

{
	if (RateLimiter::tooManyAttempts('login-attempt:'.$request->ip(), $perMinute = 5)) {
		return ApiResponseClass::tooManyRequest($request->ip());
	}
	RateLimiter::increment('login-attempt:'.$request->ip());
}

Hateoas

使用 HATEOAS 库。您可以查看库文档以获取更多详细信息。

用法

  1. 将您的超媒体放在 app/Hateoas/ 目录下。您可以使用注释、XML、Yaml。我使用的示例是使用注释。

示例

<?php
declare(strict_types=1);
namespace  App\Hateoas;

use Hateoas\Configuration\Annotation  as Hateoas;
use JMS\Serializer\Annotation  as Serializer;

/**
* @Hateoas\Relation("self", href = "expr('/api/books/' ~ object.getId())")
*/

class  Books
{
	/** @Serializer\XmlAttribute */
	private  $id;
	public  function  __construct($id)
	{
		$this->id = $id;
	}

	public  function  getId()
	{
		return  $this->id;
	}

}
  1. 在您的请求类中,您可以添加Hateos并将其合并到响应数据中。
public  function  toArray(Request  $request): array
{
	$data = [
		'id' => $this->id,
		'name' => $this->name,
		'author' => $this->author,
		'publish_date' => $this->publish_date,
	];
	
	$links = new  HateoasClass(new  Books($this->id));
	return  $links->getLinks() ? array_merge($data, $links->getLinks()) : $data;
}

反馈

如果您有任何改进建议,请随时提出建议或打开PR!