В конце прошлого лета я задумался над простым способом авторизации пользователей форума в мобильном приложении. Как раз в это время вышла версия Laravel 5.3 вместе с пакетом Laravel Passport, где подобное предлагалось из коробки. Раньше я не работал с OAuth 2, так что начал не спеша разбираться. Решил испытать механизм на крысах, в небольшой игре на Unity про Крысу на Стене. Сама игра — простейший раннер, но механизм авторизации может представлять некоторый интерес, если ранее не сталкивался с этим. Я пользовался официальной документацией и статьей про Passport. На хабре подходящей статьи до сих пор не появилось, поэтому решил сам скомпоновать материал, реализовав для интереса добавление рекордов и базовое взаимодействие с клиентом на Unity. Ввиду моей неторопливости это растянулось почти на год, так что сейчас в примерах используются уже Laravel 5.5 и Unity 2017.1.
В первой части статьи разберёмся, как с помощью токена авторизации добавить рекорд пользователя на сайт.
Установка Laravel и Laravel Passport
Создаём новый проект Laravel, заводим БД под него.
- Устанавливаем глобально вспомогательный установщик Laravel через консоль при помощи Composer
composer global require "laravel/installer"
- Для создания проекта через установщик достаточно набрать команду в формате
laravel new <название проекта>
laravel new ratwall-laravel
Дальнейшие команды выполняем из созданной директории. Заходим туда
cd ratwall-laravel
- В файле
.env
прописываем данные для подключения к базе данных. В этом гайде мы будем работать с SQLite, пропишем
DB_CONNECTION=sqlite DB_DATABASE=/полный/путь/к/папке/проекта/database/database.sqlite
После этого создадим соответствующий пустой файл (командойtouch database/database.sqlite
из консоли или, в случае Windows, из любого редактора файлdatabase.sqlite
в директорииdatabase
) - Теперь можно запустить проект локально командой
php artisan serve
Он будет доступен по адресу http://127.0.0.1:8000/ - Для нашего проекта будет достаточно базовой системы аутентификации на сайте, поэтому запускаем команду
php artisan make:auth
- Добавляем зависимости Laravel Passport через Сomposer.
composer require laravel/passport
-
-
Базовая система аутентификации создала свои миграции. Импортируем в базу данных вместе с миграциями Passport.
php artisan migrate
- Теперь можно запустить установщик Passport.
php artisan passport:install
Установщик создаст пару ключей, в дальнейшем мы будем работать с ключом типа Password grant client, так что можно сразу записать ключ для Client ID = 2. - Добавим в модель пользователя
App\User
трейтLaravel\Passport\HasApiTokens
с методами Passport. Класс будет выглядеть так:
<?php namespace App; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; use Laravel\Passport\HasApiTokens; class User extends Authenticatable { use Notifiable, HasApiTokens; // ... // оригинальный код // ... }
- Следующий шаг — назначение роутов. Здесь это делается добавлением метода
Passport::routes
в загрузочный метод (boot
) сервис-провайдера
App\Providers\AuthServiceProvider
. Сервис-провайдер будет выглядеть следующим образом:
<?php namespace App\Providers; use Illuminate\Support\Facades\Gate; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Laravel\Passport\Passport; class AuthServiceProvider extends ServiceProvider { // ... // оригинальный код // ... public function boot() { $this->registerPolicies(); Passport::routes(); } }
- Наконец, в файле
config/auth.php
устанавливаем драйвер у гардаapi
какpassport
.
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'passport', 'provider' => 'users', ], ],
Password Grant Token
Есть несколько способов авторизации через Passport. Мы рассмотрим простейший способ авторизации через логин-пароль, посредством Password Grant Token. Этот способ позволяет пользователю заполнить форму авторизации прямо в вашем приложении. При установке Laravel Passport один клиент для этого способа авторизации уже был создан (Password grand client, Client ID = 2), можно использовать его. Для создании нового нужно запустить
php artisan passport:client --password
Введите название для клиента, чтобы завершить создание. Команда выведет Client ID и Client Secret. Запомните их, они понадобятся и в этой статье, и в следующей, при формировании запроса от клиента игры.
Модель рекордов
Создадим модель для будущих рекордов. Ключ -m
означает, что будет также создана соответствующая миграция. При добавлении ключа -c
также будет создан контроллер, но в нашем упрощенном случае это не нужно.
php artisan make:model Record -m
После создании модели нужно подготовить миграцию. Отредактируем файл database/migrations/<дата>_create_records_table.php
. По-умолчанию у миграции прописано создание поля id
(для первичного ключа) и сразу два поля для сохранения времени создания и редактирования записи (timestaps
). Для простейшего хранения данных о рекордах добавим два числовых поля — score
(значение рекорда) и user_id
(идентификатор пользователя, поставившего рекорд). После этого миграция будет выглядеть следующим образом:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateRecordsTable extends Migration
{
public function up()
{
Schema::create('records', function (Blueprint $table) {
$table->increments('id');
// Значение рекорда - только положительное
$table->integer('score')->unsigned();
// Идентификатор пользователя - поле может быть пустым
$table->integer('user_id')->unsigned()->nullable();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('records');
}
}
Импортируем в БД с помощью команды
php artisan migrate
Отредактируем саму модель App\Record
. Добавим метод user()
для связи с моделью пользователя по полю user_id
.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Record extends Model
{
// Пользователь
public function user()
{
return $this->belongsTo(User::class);
}
}
Контроллер рекордов
Для добавления рекорда в базу создадим два роута для API, практически одинаковых, но один из них предназначен для добавления рекорда анонимным пользователем, а другой — авторизированным. В этом туториале мы не будем писать отдельный контроллер для рекордов, всю обработку вынесем в анонимную функцию роута. Для этого в файл routes/api.php
добавим:
Route::post('/anonymrecord', function (Request $request) {
// Создаем новую запись рекорда
$record = new \App\Record();
// Добавляем результат
$record->score = $request->get('score');
// Сохраняем запись
$record->save();
// Возвращаем сообщение
return response()->json([
'message' => 'Рекорд добавлен!',
], 201);
});
Route::middleware('auth:api')->post('/record', function (Request $request) {
// Создаем новую запись рекорда
$record = new \App\Record();
// Добавляем результат
$record->score = $request->get('score');
// Если запись добавил авторизированный пользователь, указываем его
$record->user_id = \Auth::id();
// Сохраняем запись
$record->save();
// Возвращаем сообщение
return response()->json([
'message' => 'Пользователь '. \Auth::user()->name .' добавил рекорд!',
], 201);
});
Здесь мы передаем рекорд в POST-запросе, добавляем его в базу и возвращаем сообщение об успехе. Мы добавили два практически идентичных роута — первый для анонимного добавления, второй для добавления рекорда авторизированным пользователем. Авторизированный пользователь может воспользоваться и анонимным роутом, но в нем не будет корректно обрабатываться информация о пользователе, поэтому в качестве варианта решения мы и разделили роуты. При вызове второго роута анонимным пользователем вас будет перенаправлять на форму авторизации.
В этом файле уже был один роут — user
, для получения информации об авторизированном пользователе. Мы ещё вернемся к нему позже.
Можно проверить работоспособность запроса для добавления анонимного рекорда с помощью http-клиента типа Postman (в дальнейшем примеры именно с ним), передав POST-запрос с заданным числовым значением score в форме по адресу http://127.0.0.1:8000/api/anonymrecord. В ответ мы получим json
с сообщением, что рекорд добавлен.
Для отображения добавленных рекордов на сайте напишем роут в routes/web.php
с запросом всех рекордов из базы. Он использует шаблон records.blade.php
, которое мы создадим в следующем шаге.
Route::get('/records', function () {
$records = \App\Record::all();
return view('records', compact('records'));
});
Представление рекордов
Создадим шаблон resources/views/records.blade.php
для отображения таблицы рекордов.
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Рекорды</div>
<div class="panel-body">
<table class="table table-striped table-hover">
<tr>
<th>Пользователь</th>
<th>Рекорд</th>
<th>Дата</th>
</tr>
@foreach ($records as $record)
<tr>
<td></td>
<td></td>
<td></td>
</tr>
@endforeach
</table>
</div>
</div>
</div>
</div>
</div>
@endsection
Если рекорд добавил авторизированный пользователь, выводим его имя, иначе — Аноним.
Теперь по адресу http://127.0.0.1:8000/records нам доступна таблица рекордов — пустая или с теми записями, что мы добавили через Postman.
Эти рекорды добавлялись от Анонима, т.к. мы обращались к роуту анонимного добавления, а не к роуту авторизированного пользователя с токеном авторизации через Postman. Займемся вторым случаем.
Авторизация через OAuth 2
Зарегистрируемся на сайте, используя стандартный механизм Laravel, перейдя по адресу http://127.0.0.1:8000/register. Для примера я указал e-mail habr@habrahabr.ru, пароль habrahabr, имя Habr.
Для того, чтобы получить токен авторизации через Password Grant Token, нужно отправить POST-запрос на адрес http://127.0.0.1:8000/oauth/token. В тело запроса нужно добавить следующие данные:
grant_type
— указываем тип password, т.к. мы используем Password Grant Token;client_id
— идентификатор Client ID из раздела Password Grant Token;client_secret
— ключ Client Secret, из раздела Password Grant Token;username
— в качестве логина для авторизации в базовом функционале Laravel используется e-mail;password
— пароль,scope
— области применения токена (для задания разрешений), оставим *, т.е. все.
Отправим такой запрос через Postman. Если всё заполнено правильно, в ответ мы получим токен обновления, токен авторизации, время действия токена в секундах и тип токена — Bearer. Нам нужен токен авторизации — Access Token.
Проверим токен в действии, получив по нему информации об авторизированном пользователе. Выполним GET-запрос через Postman по адресу http://127.0.0.1:8000/api/user. При этом в раздел Headers нового запроса нужно добавить ключ Authorization
со значением Bearer <токен авторизации>
(то есть тип токена, пробел, сам токен). В результате мы получим информацию о пользователе, которому соответствует этот токен авторизации.
Добавление рекорда авторизированным пользователем
Теперь можно выполнить POST-запрос для добавления рекорда авторизированным пользователем через Postman. Заполним Headers нового POST-запроса аналогично последнему, в качестве адреса используем http://127.0.0.1:8000/api/record. В ответном сообщении теперь отображается имя пользователя, которое соответствует переданному токену.
Если зайти на страницу рекордов, можно увидеть, что у последнего добавленного рекорда стоит имя пользователя.
В следующей части будем работать с нашим небольшим API уже из Unity.
Готовый проект можно скачать на гитхабе.