admad / cakephp-social-auth
一个 CakePHP 插件,允许您使用 Facebook/Google/Twitter 等社交提供商进行身份验证。
Requires
- cakephp/cakephp: ^5.0
- socialconnect/auth: ^3.3
Requires (Dev)
- cakephp/cakephp-codesniffer: ^5.0
- phpunit/phpunit: ^10.1
README
一个 CakePHP 插件,使用 SocialConnect/auth 社交登录库允许您使用 Facebook/Google/Twitter 等社交提供商进行身份验证。
安装
运行
composer require admad/cakephp-social-auth
设置
通过在终端运行以下命令来加载插件:
bin/cake plugin load ADmad/SocialAuth
数据库
此插件需要迁移以生成 social_profiles
表,可以通过官方迁移插件生成,如下所示:
bin/cake migrations migrate -p ADmad/SocialAuth
使用方法
中间件配置
插件提供了一个 \ADmad\SocialAuth\Middleware\SocialAuthMiddleware
,它通过社交提供商处理身份验证过程。
您可以在 Application::middleware()
方法中配置中间件,如下所示:
// src/Application.php // Be sure to add SocialAuthMiddleware after RoutingMiddleware $middlewareQueue->add(new \ADmad\SocialAuth\Middleware\SocialAuthMiddleware([ // Request method type use to initiate authentication. 'requestMethod' => 'POST', // Login page URL. In case of auth failure user is redirected to login // page with "error" query string var. 'loginUrl' => '/users/login', // URL to redirect to after authentication (string or array). 'loginRedirect' => '/', // Boolean indicating whether user identity should be returned as entity. 'userEntity' => false, // User model. 'userModel' => 'Users', // Social profile model. 'socialProfileModel' => 'ADmad/SocialAuth.SocialProfiles', // Finder type. 'finder' => 'all', // Fields. 'fields' => [ 'password' => 'password', ], // Session key to which to write identity record to. 'sessionKey' => 'Auth', // The method in user model which should be called in case of new user. // It should return a User entity. 'getUserCallback' => 'getUser', // SocialConnect Auth service's providers config. https://github.com/SocialConnect/auth/blob/master/README.md 'serviceConfig' => [ 'provider' => [ 'facebook' => [ 'applicationId' => '<application id>', 'applicationSecret' => '<application secret>', 'scope' => [ 'email', ], 'options' => [ 'identity.fields' => [ 'email', // To get a full list of all possible values, refer to // https://developers.facebook.com/docs/graph-api/reference/user ], ], ], 'google' => [ 'applicationId' => '<application id>', 'applicationSecret' => '<application secret>', 'scope' => [ 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', ], ], ], ], // Instance of `\SocialConnect\Auth\CollectionFactory`. If none provided one will be auto created. Default `null`. 'collectionFactory' => null, // Whether social connect errors should be logged. Default `true`. 'logErrors' => true, ]));
登录链接
在您的登录页面上,您可以创建链接以使用所需的提供商启动身份验证。例如:
echo $this->Form->postLink( 'Login with Facebook', [ 'prefix' => false, 'plugin' => 'ADmad/SocialAuth', 'controller' => 'Auth', 'action' => 'login', 'provider' => 'facebook', '?' => ['redirect' => $this->request->getQuery('redirect')] ] );
我们在这里使用一个 POST
链接而不是正常链接,以防止搜索引擎爬虫和其他爬虫跟踪链接。如果您更喜欢使用 GET,您仍然可以通过将中间件配置为 'requestMethod' => 'GET'
来这样做。在这种情况下,建议向链接添加 nofollow
属性。
身份验证过程
根据登录 URL 中的提供者名称,将启动身份验证过程。
一旦用户通过提供者进行身份验证,中间件从身份提供者获取用户配置文件,并使用该配置文件尝试使用用户模型找到相应的用户记录。如果没有找到用户,它将调用您的用户模型的 getUser
方法。该方法接收社交配置文件模型实体和会话实例作为参数,并必须返回一个用户实体。
// src/Model/Table/UsersTable.php use \Cake\Datasource\EntityInterface; use \Cake\Http\Session; public function getUser(EntityInterface $profile, Session $session) { // Make sure here that all the required fields are actually present if (empty($profile->email)) { throw new \RuntimeException('Could not find email in social profile.'); } // If you want to associate the social entity with currently logged in user // use the $session argument to get user id and find matching user entity. $userId = $session->read('Auth.id'); if ($userId) { return $this->get($userId); } // Check if user with same email exists. This avoids creating multiple // user accounts for different social identities of same user. You should // probably skip this check if your system doesn't enforce unique email // per user. $user = $this->find() ->where(['email' => $profile->email]) ->first(); if ($user) { return $user; } // Create new user account $user = $this->newEntity(['email' => $profile->email]); $user = $this->save($user); if (!$user) { throw new \RuntimeException('Unable to save new user'); } return $user; }
除了在您的 UsersTable
中添加 getUser
方法之外,您还可以设置一个监听器来监听 SocialAuth.createUser
回调,并从监听器回调中返回一个 User
实体,类似于上面所示的方式。
身份验证成功后,用户身份将在您在中间件配置中指定的键(默认为 Auth.User
)下持久化到会话中。
之后,用户将被重定向到登录之前尝试访问的受保护页面或 loginRedirect
配置中指定的 URL。
在身份验证失败的情况下,用户将被重定向回登录 URL。
事件
SocialAuth.createUser
从社交身份验证提供者身份验证后,如果找不到相关的用户记录,则触发 SocialAuth.createUser
。作为在上文中提到的方式添加新的 createUser()
方法在您的 UsersTable
之外的替代方案,您可以使用此事件返回一个新用户的实体。
SocialAuth.afterIdentify
身份验证成功后,将发出一个带有用户实体的 SocialAuth.afterIdentify
事件。您可以为此事件设置监听器以执行所需的任务。监听器可以选择作为事件结果返回一个用户实体。
SocialAuth.beforeRedirect
在用户被重定向到所需URL之前完成认证过程后,将触发一个 SocialAuth.beforeRedirect
事件。例如,可以使用此事件来设置一个视觉通知,如闪现消息,向用户显示认证过程的结果。
以下是一个具有对上述方法的回调的监听器示例
// src/Event/SocialAuthListener.php namespace App\Event; use ADmad\SocialAuth\Middleware\SocialAuthMiddleware; use Cake\Datasource\EntityInterface; use Cake\Event\EventInterface; use Cake\Event\EventListenerInterface; use Cake\Http\ServerRequest; use Cake\I18n\FrozenTime; use Cake\ORM\Locator\LocatorAwareTrait; class SocialAuthListener implements EventListenerInterface { use LocatorAwareTrait; public function implementedEvents(): array { return [ SocialAuthMiddleware::EVENT_AFTER_IDENTIFY => 'afterIdentify', SocialAuthMiddleware::EVENT_BEFORE_REDIRECT => 'beforeRedirect', // Uncomment below if you want to use the event listener to return // an entity for a new user instead of directly using `createUser()` table method. // SocialAuthMiddleware::EVENT_CREATE_USER => 'createUser', ]; } public function afterIdentify(EventInterface $event, EntityInterface $user): EntityInterface { // Update last login time $user->set('last_login', new FrozenTime()); // You can access the profile using $user->social_profile $this->getTableLocator()->get('Users')->saveOrFail($user); return $user; } /** * @param \Cake\Event\EventInterface $event * @param string|array $url * @param string $status * @param \Cake\Http\ServerRequest $request * @return void */ public function beforeRedirect(EventInterface $event, $url, string $status, ServerRequest $request): void { // Set flash message switch ($status) { case SocialAuthMiddleware::AUTH_STATUS_SUCCESS: $request->getFlash()->error('You are now logged in.'); break; // Auth through provider failed. Details will be logged in // `error.log` if `logErrors` option is set to `true`. case SocialAuthMiddleware::AUTH_STATUS_PROVIDER_FAILURE: // Table finder failed to return user record. An e.g. of this is a // user has been authenticated through provider but your finder has // a condition to not return an inactivated user. case SocialAuthMiddleware::AUTH_STATUS_FINDER_FAILURE: $request->getFlash()->error('Authentication failed.'); break; case SocialAuthMiddleware::AUTH_STATUS_IDENTITY_MISMATCH: $request->getFlash()->error('The social profile is already linked to another user.'); break; } // You can return a modified redirect URL if needed. } public function createUser(EventInterface $event, EntityInterface $profile, Session $session): EntityInterface { // Create and save entity for new user as shown in "createUser()" method above return $user; } }
在您的 Application
类中附加监听器
// src/Application.php use App\Event\SocialAuthListener; use Cake\Event\EventManager; // In Application::bootstrap() or Application::middleware() EventManager::instance()->on(new SocialAuthListener());
扩展自定义提供者
为了启用自定义提供者(那些不包括在 SocialConnect/Auth
中的提供者),您可以通过传递自己的 SocialConnect\Auth\CollectionFactory
实例来扩展中间件配置。
例如,在 src/Authenticator/MyProvider.php
中创建您的自定义提供者。查看 vendor/socialconnect/auth/src/(OAuth1|OAuth2|OpenIDConnect)/Provider/
中的提供者以获取示例。
创建一个 CollectionFactory
实例。
$collectionFactory = new \SocialConnect\Auth\CollectionFactory(); $collectionFactory->register(\App\Authenticator\MyProvider::NAME, \App\Authenticator\MyProvider::class);
然后在上面的中间件配置中设置工厂实例
...
'collectionFactory' => $collectionFactory
...
版权
版权所有 2017-至今 ADmad