git |
---|
6d7a00b539d8281cc8d7037e82b978fec3c00866 |
Laravel Sanctum предлагает легковесную систему аутентификации для SPA (одностраничных приложений), мобильных приложений и простых API на основе токенов. Sanctum позволяет каждому пользователю вашего приложения создавать несколько токенов API для своей учетной записи. Этим токенам могут быть предоставлены полномочия / области, которые определяют, какие действия токенам разрешено выполнять.
Laravel Sanctum существует для решения двух отдельных задач. Давайте обсудим каждую из них, прежде чем углубиться в изучение пакета.
Во-первых, Sanctum – это простой пакет, который вы можете использовать для выдачи токенов API своим пользователям без осложнений с OAuth. Этот функционал вдохновлен GitHub и другими приложениями, которые выдают «токены личного доступа». Например, представьте, что в «настройках учетной записи» вашего приложения есть экран, на котором пользователь может сгенерировать токен API для своей учетной записи. Вы можете использовать Sanctum для создания этих токенов и управления ими. Эти токены обычно имеют очень длительный срок действия (годы), но могут быть отозваны пользователем самостоятельно в любое время.
Laravel Sanctum предлагает этот функционал через сохранение токенов API пользователя в единой таблице базы данных и аутентифицируя входящие HTTP-запросы через заголовок Authorization
, который должен содержать действительный токен API.
Во-вторых, Sanctum также обеспечивает простой метод аутентификации одностраничных приложений (SPA), взаимодействующих с API Laravel. Эти SPA могут существовать в том же репозитории, что и ваше приложение Laravel, или могут быть полностью отдельным репозиторием, например SPA, созданные с помощью Next.js или Nuxt.
Для этого функционала Sanctum не использует никаких токенов. Вместо этого Sanctum использует встроенные в Laravel службы аутентификации сессии на основе файлов cookie. Обычно для этого Sanctum использует охранника web
аутентификации Laravel. Это обеспечивает преимущества защиты от CSRF, аутентификации сессии, а также защищает от утечки учетных данных аутентификации через XSS.
Sanctum будет пытаться аутентифицироваться с помощью файлов cookie только тогда, когда входящий запрос исходит от вашей собственной клиентской части SPA. Когда Sanctum проверяет входящий HTTP-запрос, он сначала проверяет файл cookie аутентификации, а если он отсутствует, то Sanctum проверяет заголовок Authorization
на наличие действительного токена API.
Note
Совершенно нормально использовать Sanctum только для аутентификации токена API или только для аутентификации SPA. Тот факт, что вы используете Sanctum, не означает, что вы должны использовать оба функционала, которые он предлагает.
Вы можете установить Laravel Sanctum с помощью Artisan-команды install:api
:
php artisan install:api
Далее, если вы планируете использовать Sanctum для аутентификации SPA, обратитесь к разделу Аутентификация SPA этой документации.
Хотя обычно это не требуется, но вы можете расширить модель PersonalAccessToken
, используемую внутри Sanctum:
use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken;
class PersonalAccessToken extends SanctumPersonalAccessToken
{
// ...
}
Затем, вы можете указать Sanctum использовать вашу пользовательскую модель с помощью метода usePersonalAccessTokenModel
, предоставленного Sanctum. Обычно этот метод следует вызывать в методе boot
файла AppServiceProvider
вашего приложения:
use App\Models\Sanctum\PersonalAccessToken;
use Laravel\Sanctum\Sanctum;
/**
* Загрузка любых служб приложения.
*/
public function boot(): void
{
Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
}
Note
Вы не должны использовать токены API для аутентификации собственного SPA. Вместо этого используйте функционал аутентификации SPA Sanctum.
Sanctum позволяет выдавать токены API / персональные токены доступа, которые могут использоваться для аутентификации API-запросов к вашему приложению. При выполнении запросов с использованием токенов API, токен должен быть включен в заголовок Authorization
как Bearer
-токен.
Чтобы начать выдачу токенов для пользователей, ваша модель User
должна использовать трейт Laravel\Sanctum\HasApiTokens
:
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}
Для выдачи токена вы можете использовать метод createToken
. Метод createToken
возвращает экземпляр Laravel\Sanctum\NewAccessToken
. Токены API хешируются с использованием хеширования SHA-256 перед сохранением в вашей базе данных, но вы можете получить доступ к текстовому значению токена, используя свойство plainTextToken
экземпляра NewAccessToken
. Вы должны отобразить это значение пользователю сразу после создания токена:
use Illuminate\Http\Request;
Route::post('/tokens/create', function (Request $request) {
$token = $request->user()->createToken($request->token_name);
return ['token' => $token->plainTextToken];
});
Вы можете получить доступ ко всем токенам пользователя с помощью отношения Eloquent tokens
трейта HasApiTokens
:
foreach ($user->tokens as $token) {
// ...
}
Sanctum позволяет вам назначать «полномочия» (abilities) токенам. Полномочия служат той же цели, что и «права доступа» OAuth Scopes. Вы можете передать массив, содержащий строковые ключи полномочий, в качестве второго аргумента методу createToken
:
return $user->createToken('token-name', ['server:update'])->plainTextToken;
При обработке входящего запроса, аутентифицированного Sanctum, вы можете определить, обладает ли токен указанными полномочиями, используя метод tokenCan
:
if ($user->tokenCan('server:update')) {
// ...
}
Sanctum также включает в себя два посредника, которые можно использовать для проверки подлинности входящего запроса с помощью токена, которому предоставлена данная возможность. Для начала определите следующие псевдонимы посредника в файле bootstrap/app.php
вашего приложения:
use Laravel\Sanctum\Http\Middleware\CheckAbilities;
use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility;
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'abilities' => CheckAbilities::class,
'ability' => CheckForAnyAbility::class,
]);
})
Посредник abilities
может быть назначен маршруту для проверки того, что токен входящего запроса имеет все перечисленные возможности:
Route::get('/orders', function () {
// У токена есть возможности как для проверки статуса, так и для размещения заказов...
})->middleware(['auth:sanctum', 'abilities:check-status,place-orders']);
Посредник ability
может быть назначен маршруту для проверки того, что токен входящего запроса имеет "по крайней мере одну" из перечисленных возможностей:
Route::get('/orders', function () {
// Токен имеет возможность "проверить статус" или "разместить заказы"...
})->middleware(['auth:sanctum', 'ability:check-status,place-orders']);
Для удобства метод tokenCan
всегда будет возвращать true
, если входящий аутентифицированный запрос был от вашего собственного SPA, и вы используете встроенную аутентификацию SPA Sanctum.
Однако, это не обязательно означает, что ваше приложение должно позволять пользователю выполнять действие. Как правило, политики авторизации вашего приложения определяют, предоставлены ли токену полномочия, а также проверяют, разрешено ли самому экземпляру пользователя выполнять действие.
Например, если мы представим себе приложение, которое управляет серверами, это может означать проверку того, что токен авторизован для обновления серверов и что сервер принадлежит пользователю:
return $request->user()->id === $server->user_id &&
$request->user()->tokenCan('server:update')
Сначала может показаться странным допущение вызова метода tokenCan
, возвращающего всегда true
для запросов, инициированных пользовательским интерфейсом; однако, удобно иметь возможность всегда предполагать, что токен API доступен и может быть проверен с помощью метода tokenCan
. Применяя этот подход, вы всегда можете вызвать метод tokenCan
в политиках авторизации вашего приложения, не беспокоясь о том, был ли запрос инициирован из пользовательского интерфейса вашего приложения или был инициирован одним из сторонних потребителей вашего API.
Чтобы защитить маршруты через обязательную аутентификацию входящих запросов, вы должны назначить охранника (guard) аутентификации sanctum
вашим защищаемым маршрутам в файлах маршрутов routes/web.php
и routes/api.php
. Этот охранник гарантирует, что входящие запросы аутентифицируются либо как запросы с cookie, если запрос поступает от веб-страницы, сформированной Laravel, либо как запросы с токеном API, если запрос поступает от внешнего SPA- или мобильного приложения.
Вам может быть интересно, почему мы предлагаем вам аутентифицировать маршруты в файле routes/web.php
вашего приложения, используя охранник sanctum
. Помните, что Sanctum сначала попытается аутентифицировать входящие запросы, используя типичный файл cookie аутентификации сессии Laravel. Если этот файл cookie отсутствует, то Sanctum попытается аутентифицировать запрос, используя токен в заголовке Authorization
запроса. Кроме того, аутентификация всех запросов с помощью Sanctum гарантирует, что мы всегда можем вызвать метод tokenCan
для экземпляра текущего аутентифицированного пользователя:
use Illuminate\Http\Request;
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');
Вы можете «отозвать» токены, удалив их из своей базы данных, используя отношение tokens
трейта Laravel\Sanctum\HasApiTokens
:
// Отзыв всех токенов ...
$user->tokens()->delete();
// Отозвать токен, который использовался для аутентификации текущего запроса ...
$request->user()->currentAccessToken()->delete();
// Отзыв определенного токена ...
$user->tokens()->where('id', $tokenId)->delete();
По умолчанию токены Sanctum никогда не истекают и могут быть аннулированы только путем отзыва токена. Однако, если вы хотите настроить время истечения для токенов API вашего приложения, вы можете сделать это через опцию expiration
, определенную в файле конфигурации sanctum
вашего приложения. Эта опция конфигурации определяет количество минут, после которых выданный токен будет считаться истекшим:
'expiration' => 525600,
Если вы хотите указать время истечения каждого токена отдельно, вы можете сделать это, передав время истечения в качестве третьего аргумента методу createToken
:
return $user->createToken(
'token-name', ['*'], now()->addWeek()
)->plainTextToken;
Если вы настроили время истечения токена для вашего приложения, вы также можете запланировать задачу для очистки истекших токенов вашего приложения. К счастью, Sanctum включает в себя команду Artisan sanctum:prune-expired
, которую вы можете использовать для выполнения этой задачи. Например, вы можете настроить запланированную задачу для удаления всех записей об истекших токенах в базе данных, которые были истекшие как минимум 24 часа:
use Illuminate\Support\Facades\Schedule;
Schedule::command('sanctum:prune-expired --hours=24')->daily();
Sanctum также обеспечивает простой метод аутентификации одностраничных приложений (SPA), взаимодействующих с API Laravel. Эти SPA могут существовать в том же репозитории, что и ваше приложение Laravel, или могут быть полностью отдельным репозиторием.
Для этого функционала Sanctum не использует никаких токенов. Вместо этого Sanctum использует встроенные в Laravel службы аутентификации сессии на основе файлов Cookies. Такой подход к аутентификации обеспечивает преимущества защиты от CSRF, аутентификации сессии, а также защищает от утечки учетных данных аутентификации через XSS.
Warning
Для аутентификации вашего одностраничного приложения (SPA) и API, они должны иметь один и тот же домен верхнего уровня. Однако они могут находиться на разных поддоменах. Кроме того, убедитесь, что вы отправляете заголовок Accept: application/json
и либо заголовок Referer
, либо заголовок Origin
в вашем запросе.
Во-первых, вы должны настроить, из каких доменов ваш SPA будет делать запросы. Вы можете указать эти домены, используя параметр stateful
в конфигурационном файле sanctum
. Этот параметр конфигурации определяет, какие домены будут поддерживать аутентификацию с «фиксацией» на основе файлов cookie сессии Laravel при выполнении запросов к вашему API.
Warning
Если вы обращаетесь к своему приложению через URL-адрес, содержащий порт (например, 127.0.0.1:8000
), то вы должны убедиться, что указали номер порта вместе с доменом.
Затем вы должны указать Laravel, что входящие запросы от вашего SPA могут аутентифицироваться с использованием файлов cookie сеанса Laravel, при этом позволяя запросам третьих сторон или мобильных приложений аутентифицироваться с использованием токенов API. Этого можно легко добиться, вызвав метод посредника statefulApi
в файле bootstrap/app.php
вашего приложения:
->withMiddleware(function (Middleware $middleware) {
$middleware->statefulApi();
})
Если у вас возникли проблемы с аутентификацией вашего SPA, который выполняется на отдельном поддомене, вы, вероятно, неправильно сконфигурировали параметры CORS (совместное использование ресурсов между разными источниками) или cookie сессий.
Файл конфигурации config/cors.php
по умолчанию не публикуется. Если вам нужно настроить параметры CORS Laravel, вам следует опубликовать полный файл конфигурации cors
с помощью Artisan-команды config:publish
:
php artisan config:publish cors
Затем, вам необходимо убедиться, что конфигурация CORS вашего приложения возвращает заголовок Access-Control-Allow-Credentials
со значением True
. Этого можно добиться, установив для параметра supports_credentials
значение true
в конфигурационном файле config/cors.php
вашего приложения.
Кроме того, вы должны включить опции withCredentials
и withXSRFToken
в глобальном экземпляре axios
вашего приложения. Обычно это делается в файле resources/js/bootstrap.js
. Если вы не используете Axios для выполнения HTTP-запросов из вашего фронтенда, вы должны выполнить аналогичную настройку для вашего собственного HTTP-клиента:
axios.defaults.withCredentials = true;
axios.defaults.withXSRFToken = true;
Наконец, вы должны убедиться, что конфигурация домена cookie сессии вашего приложения поддерживает любой поддомен вашего корневого домена. Вы можете сделать это, добавив к домену префикс .
в конфигурационном файле config/session.php
вашего приложения:
'domain' => '.domain.com',
Для аутентификации вашего SPA страница «входа» вашего SPA должна сначала сделать запрос к /sanctum/csrf-cookie
для инициализации защиты от CSRF для приложения:
axios.get('/sanctum/csrf-cookie').then(response => {
// Login...
});
Во время этого запроса Laravel установит cookie XSRF-TOKEN
, содержащий текущий токен CSRF. Этот токен следует передавать в заголовке X-XSRF-TOKEN
при последующих запросах, что некоторые клиентские библиотеки HTTP, такие, как Axios и Angular HttpClient, будут делать автоматически за вас. Если ваша HTTP-библиотека JavaScript не задает автоматически значение, то вам нужно будет вручную установить заголовок X-XSRF-TOKEN
, чтобы он соответствовал значению XSRF-TOKEN
cookie, установленному этим маршрутом.
После того как защита от CSRF была инициализирована, вы должны сделать POST-запрос к маршруту /login
вашего приложения Laravel. Этот маршрут /login
может быть реализован вручную или с использованием пакета безголовой аутентификации, такого как Laravel Fortify.
Если запрос на вход будет успешным, то пользователь будет аутентифицирован, и последующие запросы к маршрутам вашего приложения будут автоматически аутентифицироваться через cookie сессии, которые приложение Laravel отправило клиентской стороне. Кроме того, поскольку ваше приложение уже сделало запрос к маршруту /sanctum/csrf-cookie
, то последующие запросы должны автоматически получать защиту от CSRF, пока ваш HTTP-клиент JavaScript отправляет значение XSRF-TOKEN
cookie в заголовке X-XSRF-TOKEN
.
Конечно, если сессия вашего пользователя истекает из-за отсутствия активности, то последующие запросы к приложению Laravel могут получить ответ об ошибках 401
или 419
HTTP. В этом случае вы должны перенаправить пользователя на страницу входа в SPA.
Warning
Вы можете написать свою реализацию /login
; однако, вы должны убедиться, что она аутентифицирует пользователя, используя стандартные службы аутентификации на основе сессии, которые предлагает Laravel. Обычно это означает использование охранника аутентификации web
.
Чтобы защитить маршруты так, чтобы все входящие запросы аутентифицировались, вы должны назначить охранника аутентификации sanctum
к вашим маршрутам API в вашем файле routes/api.php
. Этот охранник гарантирует, что входящие запросы аутентифицируются либо как запросы с «фиксацией» на основе файлов cookie сессии, либо содержат действительный заголовок токена API, если запрос поступает от третьей стороны.
use Illuminate\Http\Request;
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');
Если вашему SPA необходимо аутентифицировать трансляцию по частным каналам или каналам присутствия, вам следует удалить запись channels
из метода withRouting
, содержащегося в файле bootstrap/app.php
вашего приложения. Вместо этого вам следует вызвать метод withBroadcasting
, чтобы указать правильного посредника для маршрутов широковещания вашего приложения:
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
// ...
)
->withBroadcasting(
__DIR__.'/../routes/channels.php',
['prefix' => 'api', 'middleware' => ['api', 'auth:sanctum']],
)
Затем, чтобы запросы авторизации Pusher были успешными, вам нужно будет предоставить определение authorizer
Pusher при инициализации Laravel Echo. Это позволит вашему приложению настроить Pusher для использования экземпляра axios
, ориентированного на междоменные запросы:
window.Echo = new Echo({
broadcaster: "pusher",
cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
encrypted: true,
key: import.meta.env.VITE_PUSHER_APP_KEY,
authorizer: (channel, options) => {
return {
authorize: (socketId, callback) => {
axios.post('/api/broadcasting/auth', {
socket_id: socketId,
channel_name: channel.name
})
.then(response => {
callback(false, response.data);
})
.catch(error => {
callback(true, error);
});
}
};
},
})
Вы также можете использовать токены Sanctum для аутентификации запросов вашего мобильного приложения к вашему API. Процесс аутентификации запросов мобильного приложения аналогичен аутентификации запросов стороннего API; однако, есть небольшие различия в том, как вы будете выдавать токены API.
Для начала создайте маршрут, который принимает электронную почту / имя пользователя, пароль и имя устройства, а затем обменивает эти учетные данные на новый токен Sanctum. «Имя устройства», присвоенное этой конечной точке, предназначено для информационных целей и может иметь любое желаемое значение. В общем, значение имени устройства должно быть именем, которое узнает пользователь, например «iPhone 12 Nuno».
Как правило, вы делаете запрос к конечной точке токена с экрана «входа в систему» вашего мобильного приложения. Конечная точка вернет токен API в виде простого текста, который затем может быть сохранен на мобильном устройстве и использован для выполнения дополнительных API-запросов:
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
Route::post('/sanctum/token', function (Request $request) {
$request->validate([
'email' => 'required|email',
'password' => 'required',
'device_name' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (! $user || ! Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['The provided credentials are incorrect.'],
]);
}
return $user->createToken($request->device_name)->plainTextToken;
});
Когда мобильное приложение использует токен для запроса API к вашему приложению, оно должно передать токен в заголовке Authorization
как Bearer
-токен.
Note
При выдаче токенов для мобильного приложения вы также можете указать полномочия токена.
Как ранее было задокументировано, вы можете защитить маршруты так, чтобы все входящие запросы аутентифицировались, назначив маршрутам охранника аутентификации sanctum
:
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');
Чтобы пользователи могли отзывать токены API, выданные для мобильных устройств, вы можете перечислить их по имени вместе с кнопкой «Отозвать» в разделе «Настройки учетной записи» пользовательского интерфейса веб-приложения, например. Когда пользователь нажимает кнопку «Отозвать», вы можете удалить токен из базы данных. Помните, что вы можете получить доступ к токенам API пользователя через отношения tokens
трейта Laravel\Sanctum\HasApiTokens
:
// Отзыв всех токенов ...
$user->tokens()->delete();
// Отзыв определенного токена ...
$user->tokens()->where('id', $tokenId)->delete();
Во время тестирования метод Sanctum::actingAs
может использоваться для аутентификации пользователя и указания, какие полномочия должны быть предоставлены его токену:
use App\Models\User;
use Laravel\Sanctum\Sanctum;
test('task list can be retrieved', function () {
Sanctum::actingAs(
User::factory()->create(),
['view-tasks']
);
$response = $this->get('/api/task');
$response->assertOk();
});
use App\Models\User;
use Laravel\Sanctum\Sanctum;
public function test_task_list_can_be_retrieved(): void
{
Sanctum::actingAs(
User::factory()->create(),
['view-tasks']
);
$response = $this->get('/api/task');
$response->assertOk();
}
Если вы хотите предоставить токену все полномочия, то вы должны указать *
в списке полномочий метода actingAs
:
Sanctum::actingAs(
User::factory()->create(),
['*']
);