読み込みが終了しない場合は、しばらく待つか、リロードを行なってください。
If loading does not finish, wait for a while or reload.
エンジニア向けの情報を発信するブログです。
どなたでも発信できます。
お好きに利用していただれば幸いです。

📁 ec-cube/app/Customize/Form/Extension/Front/CustomerLoginTypeExtension.php
<?php
namespace Customize\Form\Extension\Front;
use Eccube\Form\Type\Front\CustomerLoginType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\TextType;
class CustomerLoginTypeExtension extends AbstractTypeExtension
{
/**
* {@inheritdoc}
*/
public static function getExtendedTypes()
{
return [CustomerLoginType::class];
}
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(
'two_factor_token',
TextType::class,
[
'mapped' => false,
]
);
}
}
📁 ec-cube/app/Customize/Controller/Mypage/LoginController.php
<?php
namespace Customize\Controller\Mypage;
use Eccube\Controller\AbstractController;
use Eccube\Entity\Customer;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Eccube\Repository\CustomerRepository;
use Eccube\Repository\Master\CustomerStatusRepository;
use Eccube\Entity\Master\CustomerStatus;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Eccube\Repository\BaseInfoRepository;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Validator\Constraints as Assert;
class LoginController extends AbstractController
{
private $customerRepository;
private $customerStatusRepository;
private $encoderFactory;
private $mailer;
private $baseInfoRepository;
private $validator;
public function __construct(
CustomerRepository $customerRepository,
CustomerStatusRepository $customerStatusRepository,
EncoderFactoryInterface $encoderFactory,
\Swift_Mailer $mailer,
BaseInfoRepository $baseInfoRepository,
ValidatorInterface $validator
)
{
$this->customerRepository = $customerRepository;
$this->customerStatusRepository = $customerStatusRepository;
$this->encoderFactory = $encoderFactory;
$this->mailer = $mailer;
$this->baseInfoRepository = $baseInfoRepository;
$this->validator = $validator;
}
/**
* @param Request $request
* @Route("/check/customer/password", name="check_customer_password", methods={"POST"})
*/
public function checkCustomerPassword(Request $request)
{
if (!$request->isXmlHttpRequest()) {
return $this->json(['status' => 'NG'], 400);
}
$this->isTokenValid();
$login_email = $request->get('login_email');
$login_pass = $request->get('login_pass');
if (!isset($login_email, $login_pass)) {
return $this->json(['status' => 'NG'], 500);
}
$customer = $this->customerRepository->findOneBy(
[
'email' => $login_email,
'Status' => $this->customerStatusRepository->find(CustomerStatus::REGULAR),
]
);
if (!$customer instanceof Customer) {
return $this->json(['status' => 'NG'], 500);
}
$encoder = $this->encoderFactory->getEncoder($customer);
if ($encoder->isPasswordValid($customer->getPassword(), $login_pass, $customer->getSalt())) {
$token = mt_rand(100000,999999);
$base_info = $this->baseInfoRepository->get();
$message = (new \Swift_Message())
->setSubject('トークンの送信')
->setFrom([$base_info->getEmail01() => $base_info->getShopName()])
->setTo([$customer->getEmail()])
->setBody($token);
$this->session->set('login_token', $token);
$count = $this->mailer->send($message, $failures);
return $this->json(['status' => 'OK']);
}
return $this->json(['status' => 'NG'], 500);
}
/**
* @param Request $request
* @return \Symfony\Component\HttpFoundation\RedirectResponse
* @Route("/check/customer/token", name="check_customer_token", methods={"POST"})
*/
public function login(Request $request)
{
$token = $request->get('two_factor_token');
$errors = $this->validator->validate(
$token,
[
new Assert\NotBlank(),
new Assert\Type(['type' => 'numeric']),
]
);
if ($errors->count() === 0 && $token === (string)$this->session->get('login_token')) {
$this->session->remove('login_token');
return $this->redirectToRoute(
'mypage_login',
[
'request' => $request
],
307
);
}
$this->session->remove('login_token');
$this->session->getFlashBag()->set('bad_flash', 'ばーかちゃんとしろばか');
return $this->redirectToRoute(
'mypage_login',
[
'request' => $request,
]
);
}
}
/**
* @param Request $request
* @Route("/check/customer/password", name="check_customer_password", methods={"POST"})
*/
public function checkCustomerPassword(Request $request)
{
// ajaxの通信かバリデート
if (!$request->isXmlHttpRequest()) {
return $this->json(['status' => 'NG'], 400);
}
// csrf_tokenのバリデート
$this->isTokenValid();
// requestから入力されたemailとpasswordを取得
$login_email = $request->get('login_email');
$login_pass = $request->get('login_pass');
// emailとpasswordどちらもparameterがあるかのばりデート
if (!isset($login_email, $login_pass)) {
return $this->json(['status' => 'NG'], 500);
}
// emailから本会員登録されているcustomerがいるか検索
$customer = $this->customerRepository->findOneBy(
[
'email' => $login_email,
'Status' => $this->customerStatusRepository->find(CustomerStatus::REGULAR),
]
);
// customerがいるかのバリデート
if (!$customer instanceof Customer) {
return $this->json(['status' => 'NG'], 500);
}
// encoderなるものを取得
$encoder = $this->encoderFactory->getEncoder($customer);
// customerのpasswordがあっているか判定
if ($encoder->isPasswordValid($customer->getPassword(), $login_pass, $customer->getSalt())) {
// tokenを生成
$token = mt_rand(100000,999999);
// eccubeの設定を取得
$base_info = $this->baseInfoRepository->get();
// mailerを設定
$message = (new \Swift_Message())
->setSubject('トークンの送信')
->setFrom([$base_info->getEmail01() => $base_info->getShopName()])
->setTo([$customer->getEmail()])
// mailの本文に生成したtokenをセット
->setBody($token);
// sessionにtokenをセット
$this->session->set('login_token', $token);
// mail送信
$count = $this->mailer->send($message, $failures);
// 成功ステータスを返す
return $this->json(['status' => 'OK']);
}
return $this->json(['status' => 'NG'], 500);
}
/**
* @param Request $request
* @return \Symfony\Component\HttpFoundation\RedirectResponse
* @Route("/check/customer/token", name="check_customer_token", methods={"POST"})
*/
public function login(Request $request)
{
// トークンを取得
$token = $request->get('two_factor_token');
// 必須,数字バリデーション
$errors = $this->validator->validate(
$token,
[
new Assert\NotBlank(),
new Assert\Type(['type' => 'numeric']),
]
);
// バリデーションを突破かつ送信されたトークンとセッションのトークンが同じなら
if ($errors->count() === 0 && $token === (string)$this->session->get('login_token')) {
// もうセッションのトークンは不要なので削除する
$this->session->remove('login_token');
// デフォルトのログインアクションにpostとしてリダイレクト
return $this->redirectToRoute(
'mypage_login',
[
'request' => $request
],
307 // <- こうするとpostでリダイレクトできる
);
}
// ダメだったら、セッションのトークンを削除
$this->session->remove('login_token');
// 適当にダメでしたフラッシュをセット
$this->session->getFlashBag()->set('bad_flash', 'ばーかちゃんとしろばか');
// デフォルトのログインアクションにリダイレクト
return $this->redirectToRoute(
'mypage_login',
[
'request' => $request,
]
);
}
📁 ec-cube/app/template/default/Mypage/login.twig
{#
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.
#}
{% extends 'default_frame.twig' %}
{% set body_class = 'mypage' %}
{% block main %}
{# jsを追加 #}
<script>
$(function () {
{# トークン送信ボタンを押したら #}
$('#send_token').on('click', function () {
{# emailとpasswordのフォームの値を取得 #}
let login_email_value = $('#login_email').val();
let login_pass_value = $('#login_pass').val();
{# 値があったら #}
if (login_email_value.length && login_pass_value.length) {
{# ajaxでpassword判定アクションにリクエストを送信 #}
$.ajax({
url: '{{ url('check_customer_password') }}',
type: 'POST',
data: {
'login_email': login_email_value,
'login_pass': login_pass_value,
},
dataType: 'json',
}).done(function (data) {
{# 成功したらトークンの入力を促す #}
$('.ec-icon').after(`<span style="color: green">トークンプリーズ</span>`);
}).fail(function (data) {
{# ダメだったら煽る #}
$('.ec-icon').after(`<span style="color: red">ばーかばーか帰ればか</span>`);
});
}
});
});
</script>
<div class="ec-role">
{# ログイン失敗時のフラッシュがあれば表示 #}
{% if app.session.flashBag.has('bad_flash') %}
<div class="alert alert-danger" role="alert">
{% for message in app.session.flashBag.get('bad_flash') %}
{{ message }}
{% endfor %}
</div>
{% endif %}
<div class="ec-pageHeader">
<h1>{{ 'ログイン'|trans }}</h1>
</div>
<div class="ec-off2Grid">
<div class="ec-off2Grid__cell">
{# 送信先を変更 #}
<form name="login_mypage" id="login_mypage" method="post" action="{{ url('check_customer_token') }}">
{% if app.session.flashBag.has('eccube.login.target.path') %}
{% for targetPath in app.session.flashBag.peek('eccube.login.target.path') %}
<input type="hidden" name="_target_path" value="{{ targetPath }}" />
{% endfor %}
{% endif %}
<div class="ec-login">
<div class="ec-login__icon">
<div class="ec-icon"><img src="{{ asset('assets/icon/user.svg') }}" alt=""></div>
</div>
<div class="ec-login__input">
<div class="ec-input">
{{ form_widget(form.login_email, {'attr': {'style' : 'ime-mode: disabled;', 'placeholder' : 'メールアドレス', 'autofocus': true}}) }}
{{ form_widget(form.login_pass, {'attr': {'placeholder' : 'パスワード' }}) }}
{# トークンフォーム追加 #}
{{ form_widget(form.two_factor_token, {'attr': {'placeholder' : 'トークン' }}) }}
</div>
{% if BaseInfo.option_remember_me %}
<div class="ec-checkbox">
<label>
{% if is_granted('IS_AUTHENTICATED_REMEMBERED') %}
<input type="hidden" name="login_memory" value="1">
{% else %}
{{ form_widget(form.login_memory, { 'label': '次回から自動的にログインする'|trans }) }}
{% endif %}
</label>
</div>
{% endif %}
{% for reset_complete in app.session.flashbag.get('password_reset_complete') %}
<p>{{ reset_complete|trans }}</p>
{% endfor %}
{% if error %}
<p class="ec-errorMessage">{{ error.messageKey|trans(error.messageData, 'validators')|nl2br }}</p>
{% endif %}
</div>
<div class="ec-grid2">
<div class="ec-grid2__cell">
<div class="ec-login__actions">
<button type="submit"
class="ec-blockBtn--cancel">{{ 'ログイン'|trans }}</button>
{# トークン送信ボタン追加 #}
<button type="button" class="ec-blockBtn--cancel" id="send_token">トークン送信</button>
</div>
</div>
<div class="ec-grid2__cell">
<div class="ec-login__link"><a class="ec-link"
href="{{ url('forgot') }}">{{ 'ログイン情報をお忘れですか?'|trans }}</a>
</div>
<div class="ec-login__link"><a class="ec-link"
href="{{ url('entry') }}">{{ '新規会員登録'|trans }}</a>
</div>
</div>
</div>
</div>
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
</form>
</div>
</div>
</div>
{% endblock %}





