[0032]UserとAdmin以外のログイン認証のカスタマイズ追加

■カスタマイズ

この例のLalavelフレームワークのバージョンは5.1です。

既にUserとAdminは利用済で、今回の例では支払専用ログイン画面を新しく用意します。ログイン画面は全員共通ではなく、ユーザごとに異なるURLで名前(姓)と(名)の2つを入力し、ログイン認証するものを作ります。

必要なファイルを作っていきます。

/app/Http/routes.php

              
Route::get('payment_split/login/{book_no}',  ['as' => 'payment_split.login', 'uses' => 'PaymentSplit\IndexController@getLogin']);
Route::post('payment_split/login/{book_no}', ['as' => 'payment_split.login', 'uses' => 'PaymentSplit\IndexController@postLogin']);
Route::get('payment_split/logout/{book_no}', 'PaymentSplit\IndexController@getLogout');
Route::group(array('middleware' => 'payment_split'), function(){
	Route::get('payment_split/top/{book_no}', 'PaymentSplit\IndexController@top');
});
              
            

支払専用ログインと支払専用ログアウトと、認証名をpayment_split(任意)にし、認証済の場合に支払専用トップ画面が表示されるようにroutes.phpに追加します。

支払専用ログイン認証用のテーブルpayment_splits(任意)をデータベースに追加します。

emailとpasswordはそのままだとこの後楽だったので、カラム名と意図があってないが、、、この例では以下を作成。

カラム名    型     説明

pay_split_no  varchar(40)  PrimaryKeyで認証で利用(同ドメイン内なので、UserとAdminと被らないKeyを指定するため、Autoincrimentの数字ではないものに。

email      varchar(255)  名前(姓)

password    varchar(60)  残しておく。(名)を入れておく

g_name     varchar(50)   (名)

remember_token varchar(100)

updated_by   int(11)

created_at   timestamp

updated_at   timestamp

App\Modelsにモデルを作成します。無くてもOKだが、Selectで利用したいので作成。

PaymentSplit.php

              
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class PaymentSplit extends Model
{
	protected $table = 'payment_splits';

	protected $primaryKey = 'pay_split_no';
}

              
            

app/Http/Kernel.phpの$routeMiddleware内にroutes.phpで指定した任意のMiddlewareの名前でセットします。今回の例では'middleware' => 'payment_split'なので、payment_splitです。

Kernel.php

              
protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'admin' => \App\Http\Middleware\AdminAuthenticate::class,
        'emergency' => \App\Http\Middleware\EmergencyAuthenticate::class,
        'payment_split' => \App\Http\Middleware\PaymentSplitAuthenticate::class,
        'basicauth' => \App\Http\Middleware\BasicAuthMiddleware::class,
    ];
    			
            

Kernel.phpで設定した\App\Http\Middleware\PaymentSplitAuthenticate.php(名前は任意)を作成します。

PaymentSplitAuthenticate.php

              
<?php

namespace App\Http\Middleware;

use Closure;

class PaymentSplitAuthenticate
{
    /**
     * Handle an incoming request.
     *
     * @param    \Illuminate\Http\Request  $request
     * @param    \Closure  $next
     * @return  mixed
     */
    public function handle($request, Closure $next)
    {
        $url = @$_SERVER["REQUEST_URI"];
        $exps = explode("/", $url);
        $book_no = $exps[count($exps)-1];
    	if(\Auth::driver('payment_split')->check()){
    	    //何もしない
        }else{
            return redirect()->guest('payment_split/login/'.$book_no);
        }

        return $next($request);
    }
}

    			
            

payment_splitの認証をチェックし、認証していない場合はpayment_split/login/[$book_no]のログイン画面へ遷移します。

App\Providers\AppServiceProvider.phpのbootに\Auth::driver('payment_split')で指定したpayment_split(名前は任意)を追加します。

AppServiceProvider.php

              
public function boot()
    {
        $this->app['auth']->extend('admin',function($app){
            return new Guard(new AdministratorProvider,$app['session.store']);
        });
            $this->app['auth']->extend('emergency',function($app){
        	return new Guard(new EmergencyProvider,$app['session.store']);
        });
        $this->app['auth']->extend('payment_split',function($app){
            return new Guard(new PaymentSplitProvider,$app['session.store']);
        });
    }

    			
            

AppServiceProvider.phpで追加したApp\PaymentSplitProvider.php(名前は任意)を作成します。

PaymentSplitProvider.php

              
<?php namespace App;

// use Illuminate\Auth\UserInterface;
// use Illuminate\Auth\UserProviderInterface;

use DB;
use Hash;
use Log;

use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
use Illuminate\Auth\GenericUser;

class PaymentSplitProvider implements UserProvider
{

    //table
    protected $table = "payment_splits";

    /**
     * Retrieve a user by their unique identifier.
     *
     * @param    mixed  $identifier
     * @return  \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveById($identifier)
    {
        //PaymentSplitUserで指定したpay_split_noが$identifierに入る
        $user = DB::table($this->table)
                                 ->where('pay_split_no',$identifier)
                                 ->first();

        return $this->getGenericUser($user);
    }

    /**
     * Retrieve a user by by their unique identifier and "remember me" token.
     *
     * @param    mixed   $identifier
     * @param    string  $token
     * @return  \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveByToken($identifier, $token)
    {
        $user = DB::table($this->table)
                                ->where('pay_split_no', $identifier)
                                ->where('remember_token', $token)
                                ->first();

        return $this->getGenericUser($user);
    }

    /**
     * Update the "remember me" token for the given user in storage.
     *
     * @param    \Illuminate\Contracts\Auth\Authenticatable  $user
     * @param    string  $token
     * @return  void
     */
    public function updateRememberToken(UserContract $user, $token)
    {
        DB::table($this->table)
                            ->where('pay_split_no', $user->getAuthIdentifier())
                            ->update(['remember_token' => $token]);
    }

    /**
     * Retrieve a user by the given credentials.
     *
     * @param    array  $credentials
     * @return  \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveByCredentials(array $credentials)
    {
        // First we will add each credential element to the query as a where clause.
        // Then we can execute the query and, if we found a user, return it in a
        // generic "user" object that will be utilized by the Guard instances.
        $query = DB::table($this->table);

        foreach ($credentials as $key => $value)
        {
            if ( ! str_contains($key, 'password'))
            {
                $query->where($key, $value);
            }
        }

        // Now we are ready to execute the query to see if we have an user matching
        // the given credentials. If not, we will just return nulls and indicate
        // that there are no matching users for these given credential arrays.
        $user = $query->first();

        return $this->getGenericUser($user);
    }

    /**
     * Get the generic user.
     *
     * @param    mixed  $user
     * @return  \Illuminate\Auth\GenericUser|null
     */
    protected function getGenericUser($user)
    {
        if ($user !== null)
        {
            //return new GenericUser((array) $user);
            return new PaymentSplitUser((array)$user);
        }
    }

    /**
     * Validate a user against the given credentials.
     *
     * @param    \Illuminate\Contracts\Auth\Authenticatable  $user
     * @param    array  $credentials
     * @return  bool
     */
    public function validateCredentials(UserContract $user, array $credentials)
    {//PWチェックなし
        $plain = @$credentials['password'];

        // Log::debug('$plain='.$plain);
        // Log::debug('hash(plain)='.Hash::make($plain));
        // Log::debug('getAuthPass='.$user->getAuthPassword());
        //return Hash::check($plain,$user->getAuthPassword());
        return 1;
    }

}

    			
            

認証はPKのpay_split_noカラムを利用するので、retrieveByIdなどpay_split_noカラムを設定し、今回はPWは利用しないがemailとpasswordはそのままだと楽だったので、validateCredentials内のPWチェックは無しにし毎回OKになるように変更。

PaymentSplitProvider.phpで追加したApp\PaymentSplitUser.php(名前は任意)を作成します。

PaymentSplitUser.php

              
<?php namespace App;

use Illuminate\Auth\GenericUser;

class PaymentSplitUser extends GenericUser
{

    public function getAuthIdentifier(){
        //PaymentSplitProviderで指定した「$table = "payment_splits";」テーブルのここで指定したカラム名が、PaymentSplitProviderの$identifierに入る。
        //$identifierはAuthで保持しておくデータ。認証はKeyが他と被ると他も入れるようになってしまうので、緊急連絡先・管理用・ユーザ用と被らないKeyを指定する!
        //緊急連絡先と管理用はマイナスをつけて数字で被らないように手動で管理している。ユーザ用はマイナス無しの数字なので、支払専用はstringで被らないKeyにする!
        return $this->attributes['pay_split_no'];
    }
}

    			
            

だいぶ用意できました。残りは通常通り、コントローラ(Controller)とビュー(View)です。ルーティングの設定で指定した通り、/app/Http/Controllers/PaymentSplit/IndexController.php 名前を「IndexController」にします。

IndexController.php

              
<?php

namespace App\Http\Controllers\PaymentSplit;

use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use DB;
use View;
use App\Models\Book;
use App\Models\BookNegotiation;
use App\Models\BookPassenger;
use App\Models\PaymentSplit;
use App\Services\ApiVeritransCard;
use BaseClass;
use Validator;
use Illuminate\Pagination\LengthAwarePaginator;
use App\Http\Requests\Admin\AuthRequest;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
use Log;

class IndexController extends Controller{

	public function getLogin($book_no) {
		if($admin = Auth::driver('payment_split')->user() != ""){
		    return redirect()->to('/payment_split/top/'.$book_no);
		}else{
		    return view('payment_split.login', compact('book_no'));
		}
	}

	public function postLogin($book_no,AuthRequest $request) {
	    Auth::driver('payment_split')->attempt(['email'=>$request->email,'g_name'=>$request->password],true);
		$admin = Auth::driver('payment_split')->user();
		if($admin != ""){
		    $book_passanger = $this->_login_chk($book_no,$admin);
		    if(empty($book_passanger)){
		        Auth::driver('payment_split')->logout();
		        return redirect()->to('/payment_split/login/'.$book_no)->withErrors(['email'=>'メールアドレス : ログイン情報もしくは決済済かご確認ください。']);
		    }
		    return redirect()->to('/payment_split/top/'.$book_no);
		}
		return redirect()->to('/payment_split/login/'.$book_no)->withErrors(['email'=>'メールアドレス : ログイン情報不一致により失敗しました。再度入力してください。']);
	}

	public function getLogout($book_no) {
		Auth::driver('payment_split')->logout();
		return redirect()->to('/payment_split/login/'.$book_no);
	}

	public function _login_chk($book_no,$admin){
	    $ps = PaymentSplit::where('pay_split_no',@$admin->pay_split_no)->first();
	    $exp = explode("_", @$admin->pay_split_no);
	    $psbook_no = $exp[0];
	    $psid = @$exp[1];
	    //ログイン情報とURLの予約番号が一致するか確認
	    if(empty($ps) || $book_no != $psbook_no){
	        return "";
	    }
	    $bookPassangers = BookPassenger::where('book_no',$book_no)->where('split_sts',2)->where('id',$psid)->first();
	    //未決済の場合
	    if(empty($bookPassangers)){
	        return "";
	    }
	    return $bookPassangers;
	}

	public function top($book_no, Request $request){
	    $admin = Auth::driver('payment_split')->user();
	    $book_passanger = $this->_login_chk($book_no,$admin);
	    if(empty($book_passanger)){
	        Auth::driver('payment_split')->logout();
	        return redirect()->to('/payment_split/login/'.$book_no)->withErrors(['email'=>'メールアドレス : ログイン情報もしくは決済済かご確認ください。']);
	    }
	    return view('payment_split.index')->with([
	        'book_no'=>$book_no,
	        'book_passanger'=>$book_passanger
	    ]);
	}
}
    			
            

最後にViewを作成します。認証前のログイン画面と認証後のTOP画面です。コントローラの設定で指定した通り、/resources/views/payment_split/login.blade.php(名前は任意)を作成します。

login.blade.php

 
              
<?php
$email_msg = "";
$pw_msg = "";
if (count($errors) > 0){
    foreach ($errors->all() as $error){
        $exp = explode(" : ", $error);
        if($exp[0] == "メールアドレス"){
            $email_msg = $exp[1];
        }else{
            $pw_msg = $exp[1];
        }
    }
}
?>
{!! Form::open(['action' => ['PaymentSplit\IndexController@postLogin','book_no'=>@$book_no],'class'=>'login_form','role' =>'form','autocomplete'=>'off']) !!}
 <div class="form_wrap name">
	<div class="form_label"><span>姓(ローマ字)</span></div>
		<?php $cls = "";
		if(!empty($email_msg)){
				$cls = " error";
		}?>
		<div class="form_input{{$cls}}">
		{!! Form::text('email', old('email') , ['placeholder'=>'例)TANAKA']) !!}
		<div class="error_msg">{{$email_msg}}</div>
		</div>
	</div>
	<div class="form_wrap name">
		<div class="form_label"><span>名(ローマ字)</span></div>
		<?php $cls = "";
		if(!empty($pw_msg)){
		    $cls = " error";
		}?>
		<div class="form_input{{$cls}}">
			<input type="text" class="form-control" name="password" value="{{ old('password') }}" placeholder="例)HANA">
			<div class="error_msg">{{$pw_msg}}</div>
		</div>
	</div>
	<div class="form_btn">{!! Form::submit("ログイン", ['class' => 'submit_btn']) !!}</div>
{!! Form::close() !!}

    			
            

コントローラの設定で指定した通り、/resources/views/payment_split/index.blade.php(名前は任意)を作成します。

index.blade.php

 
              
<!DOCTYPE html>
<html lang="ja">
<head></head>
<body>TOP画面だよ
</html>
    			
            

楽なので、ログイン画面の姓(ローマ字)はemail、名(ローマ字)はpasswordのまま、コントローラ側でAuth::driver('payment_split')->attempt(['email'=>$request->email,'g_name'=>$request->password],true);

emailはDBのカラムもemailを参照し、passwordはDBのカラムg_nameを参照するようにした。姓名でログイン認証し、さらに_login_chk関数で細かいチェックをしてから画面を開いています。

もっと良い方法が絶対あるが、時間もないのでやりたいことは出来たし完了!