Quantcast
Channel: EC-CUBE アーカイブ - あずみ.net
Viewing all articles
Browse latest Browse all 271

【EC-CUBE4】Yahoo!ID連携を実装する方法

$
0
0

EC-CUBE4でYahoo!ID連携を実装する方法です。

細かい説明はしていませんのでご了承ください。

ファイル設置場所やファイル名はネームスペースやクラス名をご確認ください。

必要なライブラリをComposerでインストール

bin/console eccube:composer:require knpuniversity/oauth2-client-bundle:1.4
bin/console eccube:composer:require tavii/oauth2-yconnect

 

bundles.phpにライブラリを追加

<?php
// app/config/eccube/bundles.php

/*
 * This file is part of EC-CUBE
 *
 * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
 *
 * http://www.ec-cube.co.jp/
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

return [
   // ...
    KnpU\OAuth2ClientBundle\KnpUOAuth2ClientBundle::class => ['all' => true]
];

 

knpu_oauth2_client.yamlを追加

YahooJapan!デベロッパーネットワークのアプリケーションの管理にてClient IDとシークレットを取得して設定してください。

 

knpu_oauth2_client:
  clients:
    yconnect_client:
      type: generic
      provider_class: Customize\Security\OAuth2\Client\Provider\YConnect
      client_id: *****************************************
      client_secret: *************************************
      redirect_route: yahoo_callback

 

インストールしたoauth2-yconnectを若干修正

<?php


namespace Customize\Security\OAuth2\Client\Provider;


class YConnect extends \Tavii\OAuth2\Client\Provider\YConnect
{
    const YAHOO_LOGIN_USERINFO = 'yahoo_login.userinfo';

    public $version = 'v2';
}

 

Customerエンティティにyahoo_user_idプロパティを追加

<?php

namespace Customize\Entity;

use Customize\Entity\Master\CustomerType;
use Doctrine\ORM\Mapping as ORM;
use Eccube\Annotation\EntityExtension;

/**
* @EntityExtension("Eccube\Entity\Customer")
*/
trait CustomerTrait
{
    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $yahoo_user_id;

    public function getYahooUserId(): ?string
    {
        return $this->yahoo_user_id;
    }

    public function setYahooUserId(?string $yahoo_user_id): self
    {
        $this->yahoo_user_id = $yahoo_user_id;

        return $this;
    }
}

 

 

YahooAuthenticatorを用意

<?php


namespace Customize\Security\Authenticator;


use Customize\Security\OAuth2\Client\Provider\YConnect;
use Doctrine\ORM\EntityManagerInterface;
use Eccube\Entity\Customer;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use KnpU\OAuth2ClientBundle\Exception\InvalidStateException;
use KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserProviderInterface;

class YahooAuthenticator extends SocialAuthenticator
{
    /**
     * @var ClientRegistry
     */
    private $clientRegistry;

    /**
     * @var EntityManagerInterface
     */
    private $entityManager;

    /**
     * @var RouterInterface
     */
    private $router;

    /**
     * @var SessionInterface
     */
    private $session;

    public function __construct(
        ClientRegistry $clientRegistry,
        EntityManagerInterface $entityManager,
        RouterInterface $router,
        SessionInterface $session
    )
    {
        $this->clientRegistry = $clientRegistry;
        $this->entityManager = $entityManager;
        $this->router = $router;
        $this->session = $session;
    }

    public function supports(Request $request)
    {
        return $request->attributes->get('_route') === 'yahoo_callback';
    }

    /**
     * @inheritDoc
     */
    public function start(Request $request, AuthenticationException $authException = null)
    {
        return new RedirectResponse(
            $this->router->generate("yahoo"),
            Response::HTTP_TEMPORARY_REDIRECT
        );
    }

    /**
     * @inheritDoc
     */
    public function getCredentials(Request $request)
    {
        try {
            return $this->fetchAccessToken($this->getYahooClient());
        } catch (InvalidStateException $e) {
            return false;
        }

    }

    /**
     * @inheritDoc
     */
    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        // stateが正しくない時がある
        if ($credentials) {
            $userInfo = $this->getYahooClient()
                ->fetchUserFromToken($credentials);
        } else {
            return null;
        }

        // メールアドレス認証していない場合がある
        if(!$userInfo->getEmailVerified()) {
            return null;
        }

        // ヤフー連携済みの場合
        $Customer = $this->entityManager->getRepository(Customer::class)
            ->findOneBy(['yahoo_user_id' => $userInfo->getSub()]);
        if ($Customer) {
            return $Customer;
        }

        $Customer = $this->entityManager->getRepository(Customer::class)
            ->findOneBy(['email' => $userInfo->getEmail()]);

        // 会員登録していない場合、会員登録ページへ
        if (!$Customer) {
            $this->session->set(YConnect::YAHOO_LOGIN_USERINFO, $userInfo->toArray());
            return null;
        }

        // 通常の会員登録済みの場合はユーザー識別子を保存
        $Customer->setYahooUserId($userInfo->getSub());
        $this->entityManager->persist($Customer);
        $this->entityManager->flush();

        return $Customer;
    }

    /**
     * @inheritDoc
     */
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        $message = strtr($exception->getMessageKey(), $exception->getMessageData());

        if ($this->session->get(YConnect::YAHOO_LOGIN_USERINFO)) {
            return new RedirectResponse($this->router->generate("entry"));
        } else {
            return new RedirectResponse($this->router->generate("yahoo"));
        }
    }

    /**
     * @inheritDoc
     */
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        $targetUrl = $this->router->generate("mypage");

        return new RedirectResponse($targetUrl);

    }

    private
    function getYahooClient()
    {
        return $this->clientRegistry
            ->getClient('yconnect_client');
    }
}

 

security.yamlを修正

guardを追加しています。

security:
    encoders:
        # Our user class and the algorithm we'll use to encode passwords
        # https://symfony.com/doc/current/security.html#c-encoding-the-user-s-password
        Eccube\Entity\Member:
          id: Eccube\Security\Core\Encoder\PasswordEncoder
        Eccube\Entity\Customer:
          id: Eccube\Security\Core\Encoder\PasswordEncoder
    providers:
        # https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
        # In this example, users are stored via Doctrine in the database
        # To see the users at src/App/DataFixtures/ORM/LoadFixtures.php
        # To load users from somewhere else: https://symfony.com/doc/current/security/custom_provider.html
        member_provider:
            id: Eccube\Security\Core\User\MemberProvider
        customer_provider:
            id: Eccube\Security\Core\User\CustomerProvider
    # https://symfony.com/doc/current/security.html#initial-security-yml-setup-authentication
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        admin:
            pattern: '^/%eccube_admin_route%/'
            anonymous: true
            provider: member_provider
            form_login:
                check_path: admin_login
                login_path: admin_login
                csrf_token_generator: security.csrf.token_manager
                default_target_path: admin_homepage
                username_parameter: 'login_id'
                password_parameter: 'password'
                use_forward: true
                success_handler: eccube.security.success_handler
                failure_handler: eccube.security.failure_handler
            logout:
                path: admin_logout
                target: admin_login
        customer:
            pattern: ^/
            anonymous: true
            provider: customer_provider
            remember_me:
                secret: '%kernel.secret%'
                lifetime: 3600
                name: eccube_remember_me
                remember_me_parameter: 'login_memory'
            form_login:
                check_path: mypage_login
                login_path: mypage_login
                csrf_token_generator: security.csrf.token_manager
                default_target_path: homepage
                username_parameter: 'login_email'
                password_parameter: 'login_pass'
                use_forward: true
                success_handler: eccube.security.success_handler
                failure_handler: eccube.security.failure_handler
            logout:
                path: logout
                target: homepage
            guard:
                authenticators:
                    - Customize\Security\Authenticator\YahooAuthenticator

    access_decision_manager:
        strategy: unanimous
        allow_if_all_abstain: false

 

Yahoo!ID連携用のコントローラーを用意

<?php


namespace Customize\Controller;


use Customize\OAuth2\Client\Provider\YConnect;
use Eccube\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

/**
 * @Route("/yahoo")
 *
 * Class YahooController
 * @package Customize\Controller
 */
class YahooController extends AbstractController
{
    /**
     * @var TokenStorageInterface
     */
    private $tokenStorage;

    public function __construct(
        TokenStorageInterface $tokenStorage
    )
    {
        $this->tokenStorage = $tokenStorage;
    }

    /**
     * @Route("/", name="yahoo")
     *
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function index()
    {
        return $this->get('oauth2.registry')
            ->getClient('yconnect_client')
            ->redirect([
                "scope" => "openid profile email address"
            ]);
    }

    /**
     * @Route("/callback", name="yahoo_callback")
     */
    public function callback()
    {
        if($this->isGranted("IS_AUTHENTICATED_FULLY")) {
            return $this->redirectToRoute("mypage");
        }else{
            return $this->redirectToRoute("yahoo");
        }
    }
}

 

EntryTypeを拡張

Yahoo!ID連携経由で会員登録する場合はyahoo_user_idを登録するためEntryTypeを拡張します。

 

<?php

namespace Customize\Form\Extension;

use Customize\Entity\Master\CustomerType;
use Customize\Security\OAuth2\Client\Provider\YConnect;
use Customize\Repository\Master\CustomerTypeRepository;
use Eccube\Entity\Customer;
use Eccube\Form\Type\AddressType;
use Eccube\Form\Type\Front\EntryType;
use Eccube\Form\Type\NameType;
use Eccube\Form\Type\PostalType;
use Eccube\Form\Type\RepeatedEmailType;
use Eccube\Repository\Master\PrefRepository;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

class EntryTypeExtension extends AbstractTypeExtension
{
    /**
     * @var SessionInterface
     */
    private $session;

    /**
     * @var CustomerTypeRepository
     */
    private $customerTypeRepository;

    /**
     * @var PrefRepository
     */
    private $prefRepository;

    public function __construct(
        SessionInterface $session,
        CustomerTypeRepository $customerTypeRepository,
        PrefRepository $prefRepository
    ) {
        $this->session = $session;
        $this->customerTypeRepository = $customerTypeRepository;
        $this->prefRepository = $prefRepository;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $userInfo = $this->session->get(YConnect::YAHOO_LOGIN_USERINFO);
        if($userInfo) {
            // 名前セット
            $options = $builder->get('name')->getOptions();
            $options['lastname_options']['data'] = isset($userInfo["family_name"]) ? $userInfo["family_name"] : "";
            $options['firstname_options']['data'] = isset($userInfo["given_name"]) ? $userInfo["given_name"] : "";
            $builder->add('name', NameType::class, $options);

            // 郵便番号セット
            $options = $builder->get('postal_code')->getOptions();
            $options['data'] = isset($userInfo["address"]["postal_code"]) ? $userInfo["address"]["postal_code"] : "";
            $builder->add('postal_code', PostalType::class, $options);

            // 住所セット
            $options = $builder->get('address')->getOptions();
            if(isset($userInfo["address"]["region"])) {
                $Pref = $this->prefRepository->findOneBy(["name" => $userInfo["address"]["region"]]);
                $options["pref_options"]["data"] = $Pref;
            }
            $options["addr01_options"]["data"] = isset($userInfo["address"]["locality"]) ? $userInfo["address"]["locality"] : "";
            $builder->add('address', AddressType::class, $options);

            // メールアドレスセット
            $options = $builder->get('email')->getOptions();
            $options["first_options"]['data'] = $userInfo["email"];
            $options["second_options"]['data'] = $userInfo["email"];
            $builder->add('email', RepeatedEmailType::class, $options);

            $builder
                ->add('yahoo_user_id', HiddenType::class, [
                    "data" => $userInfo["sub"],
                    "eccube_form_options" => [
                        "auto_render" => true,
                        'form_theme' => "Form/Entry/hidden_type.html.twig"
                    ]
                ]);
        }
    }

    /**
    * {@inheritdoc}
    */
    public function getExtendedType()
    {
        return EntryType::class;
    }
}

 

yahoo_user_id項目をHiddenにしたいのでテンプレートを用意

Form/Entry/hidden_type.html.twig

{% block form_row %}
    {{ form_widget(form) }}
    {{ form_errors(form) }}
{% endblock %}

 

以上で完成のはずです。

 

Facebookログインなども上記を参考に修正すれば実装できるとかと思います。

投稿 【EC-CUBE4】Yahoo!ID連携を実装する方法あずみ.net に最初に表示されました。


Viewing all articles
Browse latest Browse all 271

Latest Images

Trending Articles