На прошлой неделе я рассмотрел аутентификацию пользователей через социальный провайдер. Я использовал Twitter для примера, но доступно также много других социальных провайдеров аутентификации, которые используют общий протокол Oauth. На прошлой неделе я остановился на моменте, когда мы успешно аутентифицировали пользователя через социальный провайдер, и теперь у нас есть объект, содержащий некоторые ограниченные данные о пользователе. На этой неделе я собираюсь закончить процесс регистрации, реализуя новый дочерний класс сервиса-регистратора из статьи двухнедельной давности. ==Сбор дополнительной информации от пользователя== Когда вы запрашиваете данные пользователя через Twitter API, вам будут переданы только те данные, которые уже доступны публично. Это значит, что вы не получите email-адрес пользователя автоматически. Для того чтобы получить email-адрес пользователя, вам надо самим запросить его в ходе процесса регистрации в вашем приложении. Таким образом, когда пользователь успешно аутентифицировался через Twitter, и я получил %%oauth_token%% и %%oauth_token_secret%%, я перенаправляю пользователя в форму для завершения регистрации в Cribbb. Сначала надо создать новый путь и добавить перенаправление в метод %%callback()%% в %%AuthenticateController%% из статьи недельной давности: %% Route::get('auth/register', [ 'uses' => 'AuthenticateController@register', 'as' => 'authenticate.register' ]); %% .(alert) **Примечание:** убедитесь, что вы разместили этот путь над путём %%(t)auth/{provider}%% в вашем файле %%(t)routes.php%%! В итоге метод %%callback()%% должен выглядеть так: %% /** * Получение обратного вызова от провайдера аутентификации * * @return Redirect */ public function callback($provider) { try { $provider = $this->manager->get($provider); $token = $provider->getTokenCredentials( Session::get('credentials'), Input::get('oauth_token'), Input::get('oauth_verifier') ); $user = $provider->getUserDetails($token); Session::put('username', $user->nickname); Session::put('oauth_token', $token->getIdentifier()); Session::put('oauth_token_secret', $token->getSecret()); Session::save(); return Redirect::route('authenticate.register'); } catch(Exception $e) { return App::abort(404); } } %% Дальше мне надо создать метод %%register()%% для возвращения корректного представления: %% /** * Возвращение формы, чтобы пользователь мог закончить регистрацию * * @return View */ public function register() { return View::make('authenticate.register', ['username' => Session::get('username')]); } %% В этом методе я также передаю %%username%% из сессии. Это позволит мне автоматически заполнить форму именем пользователя из Twitter, но у него также будет возможность задать другое имя пользователя, обновив форму. И в итоге файл представления должен выглядеть приблизительно так: %% {{ Form::open(array('route' => 'authenticate.store')) }} @if($errors->any()) @endif
{{ Form::label('email', 'Email Address') }} {{ Form::text('email') }}
{{ Form::label('username', 'Username') }} {{ Form::text('username', $username) }}
{{ Form::submit('Complete') }} {{ Form::close() }} %% Этот файл представления - просто ваша базовая форма Laravel. Здесь я запрашиваю имя пользователя и email-адрес. Мне не нужен пароль пользователя, в этом и есть смысл использования социальных провайдеров аутентификации. ==Сбор данных для создания нового пользователя== Дальше мне надо создать путь, который будет принимать запрос %%POST%% от этой формы: %% Route::post('auth', [ 'uses' => 'AuthenticateController@store', 'as' => 'authenticate.store' ]); %% Чтобы создать нового пользователя, мне надо будет собрать данные из обоих запросов %%POST%%, а также из сессии. Для этого я создам новый метод %%store()%% в %%AuthenticateController%%, чтобы принимать запрос %%POST%%: %% /** * Сохранение данных пользователя и аутентификация при успехе * * @return Redirect */ public function store() { $data = [ 'username' => Input::get('username'), 'email' => Input::get('email'), 'oauth_token' => Session::get('oauth_token'), 'oauth_token_secret' => Session::get('oauth_token_secret') ]; } %% Первое, что надо сделать в этом методе, это создать массив %%$data%% для разных частей данных из %%Input%% и %%Session%%. ==Создание SocialProviderRegistrator== В первой статье по созданию процесса регистрации я создал %%CredentialsRegistrator%% для регистрации нового пользователя с вводом логина, email и пароля. Для регистрации пользователя через социальный провайдер всё, что мне надо сделать, это создать новый дочерний класс от %%Registrator%%, с названием %%SocialProviderRegistrator%%: %% userRepository = $userRepository; $this->validators = $validators; } /** * Создание новой сущности пользователя * * @param array $data * @return Illuminate\Database\Eloquent\Model */ public function create(array $data) { if($this->runValidationChecks($data)) { return $this->userRepository->create($data); } } } %% Этот класс, по существу, совпадает с классом %%CredentialsRegistrator%%, так как процесс регистрации на данном этапе достаточно прост. Разделение этих двух процессов в двух различных служебных классах дает нам возможность позволить этим двум процессам регистрации отличаться друг от друга в будущем. Регистрация через социальный провайдер требует, чтобы %%oauth_token%% и %%oauth_token_secret%% присутствовали в данных пользователя. Для проверки данных, переданных в метод %%create()%%, мы можем создать новый класс %%Validator%%: %% 'required', 'oauth_token_secret' => 'required' ]; } %% Наконец, %%SocialProviderRegistrator%% может быть привязан к контейнеру IoC в %%RegistratorsServiceProvider%%: %% /** * Регистрация сервиса CredentialsRegistrator * * @return void */ public function registerSocialProviderRegistrator() { $this->app->bind('Cribbb\Registrators\SocialProviderRegistrator', function($app) { return new SocialProviderRegistrator( $this->app->make('Cribbb\Repositories\User\UserRepository'), [ new UsernameValidator($app['validator']), new EmailValidator($app['validator']), new OauthTokenValidator($app['validator']) ] ); }); } %% В этом примере я повторно использовал %%UsernameValidator%% и %%EmailValidator%%, но ещё я внедрил %%OauthTokenValidator%%. ==Внедрение сервиса в контроллер== Создав %%SocialProviderRegistrator%%, мы можем внедрить его в %%AuthenticateController%%: %% use Cribbb\Authenticators\Manager; use Cribbb\Registrators\SocialProviderRegistrator; class AuthenticateController extends BaseController { /** * Экземпляр Provider Manager * * @param Cribbb\Authenticators\Manager */ protected $manager; /** * Экземпляр Registrator * * @param Cribbb\Registrators\SocialProviderRegistrator */ protected $registrator; /** * Создание нового экземпляра AuthenticateController * * @param Cribbb\Authenticators\Manager * @return void */ public function __construct(Manager $manager, SocialProviderRegistrator $registrator) { $this->beforeFilter('invite'); $this->manager = $manager; $this->registrator = $registrator; } %% Наконец, мы можем использовать экземпляр %%$this->register%% для создания нового пользователя: %% /** * Сохранение данных пользователя и аутентификация при успехе * * @return Redirect */ public function store() { $data = [ 'username' => Input::get('username'), 'email' => Input::get('email'), 'oauth_token' => Session::get('oauth_token'), 'oauth_token_secret' => Session::get('oauth_token_secret') ]; $user = $this->registrator->create($data); if($user) { Auth::login($user); return Redirect::route('home.index'); } return Redirect::route('authenticate.register')->withInput() ->withErrors($this->registrator->errors()); } %% Если пользователь создан успешно, мы можем аутентифицировать его и перенаправить на главную страницу приложения. Если данные пользователя некорректны, мы можем перенаправить его обратно на форму, где будут выведены данные и список ошибок, что позволит пользователю исправить ошибки или попробовать снова с другими данными. ==Заключение== В этой статье мы на основе фундамента из статьи двухнедельной давности создали процесс аутентификации через социальный провайдер и регистрации пользователя в нашем приложении. Этот процесс регистрации значительно упрощён, потому что мы уже сделали большую работу, создав гибкий процесс регистрации новых пользователей с вводом логина и пароля. Это значит, что для добавления последующих различных типов процессов регистрации нам понадобится только создать соответствующие части, а не изобретать колесо каждый раз. Мы также можем использовать те же проверяющие классы, поэтому нам не придётся дублировать код для каждого типа процесса регистрации. Эта серия статей посвящена созданию полностью Open Source приложения ((http://cribbb.com/==Cribbb)). Все статьи будут доступны в сети, и весь код будет доступен на ((https://github.com/cribbb/cribbb==GitHub)).