laravel框架學習之路(一)前後臺使用者認證分離

kaihongChan發表於2019-02-16

準備工作:
1、下載laravel框架
2、配置好專案(資料庫連線以及虛擬主機)

開始:
前臺使用者認證laravel已經為我們寫好了,此部分可參考官方文件

建立模型(以adminstrator為例)

php artisan make:model Models/Adminstrator -m 

編寫administrator表遷移

Schema::create(`administrators`, function (Blueprint $table) {
    $table->engine = `InnoDB`;
    $table->increments(`id`);
    $table->string(`login_name`)->unique();
    $table->string(`display_name`)->nullable();
    $table->string(`password`);
    $table->string(`avatar`)->nullable();
    $table->rememberToken();
    $table->tinyInteger(`status`)->default(1);
    $table->timestamps();
});

編寫administrator模型

<?php

namespace AppModels;

use IlluminateAuthAuthenticatable;
use IlluminateDatabaseEloquentModel;
use IlluminateContractsAuthAuthenticatable as AuthenticatableContract;

class Administrator extends Model implements AuthenticatableContract
{
    use Authenticatable;

    protected $fillable = [
        `login_name`, `password`
    ];

    protected $hidden = [
        `password`, `remember_token`
    ];
}

建立後臺配置檔案admin.php,在return陣列中寫入

`auth` => [
    `guards` => [
        `administrator` => [
            `driver`   => `session`,
            `provider` => `administrators`,
        ],
    ],

    `providers` => [
        `administrators` => [
            `driver` => `eloquent`,
            `model`  => AppModelsAdministrator::class,
        ],
    ],
],

建立後臺使用者認證中介軟體Authenticate,執行:

php artisan make:middleware Admin/Authenticate

修改handle方法:

<?php

/**
 * 使用者認證中介軟體
 */

namespace AppHttpMiddlewareAdmin;

use Closure;
use IlluminateSupportFacadesAuth;
    
class Authenticate
{
    /**
     * Handle an incoming request.
     *
     * @param  IlluminateHttpRequest  $request
     * @param  Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if (Auth::guard(`administrator`)->guest() && !$this->shouldPassThrough($request)) {
            return redirect()->guest(admin_base_path(`login`));
        }
        return $next($request);
    }

    /**
     * Determine if the request has a URI that should pass through verification.
     *
     * @param IlluminateHttpRequest $request
     *
     * @return bool
     */
    protected function shouldPassThrough($request)
    {
        $excepts = [
            admin_base_path(`login`),
            admin_base_path(`logout`),
        ];
        foreach ($excepts as $except) {
            if ($except !== `/`) {
                $except = trim($except, `/`);
            }
            if ($request->is($except)) {
                return true;
            }
        }
        return false;
    }
}

建立AdminServiceProvider,執行:

php artisan make:provider AdminServiceProvider

編寫:

<?php

namespace AppProviders;

use IlluminateSupportServiceProvider;

class AdminServiceProvider extends ServiceProvider
{
    /**
     * 路由中介軟體
     * @var array
     */
    protected $routeMiddleware = [
        `admin.auth` => AppHttpMiddlewareAdminAuthenticate::class,
        `admin.pjax` => SpatiePjaxMiddlewareFilterIfPjax::class,
        `admin.log` => AppHttpMiddlewareAdminLogOperation::class,
    ];

    /**
     * 中介軟體組
     * @var array
     */
    protected $middlewareGroups = [
        `admin` => [
            `admin.auth`,
            `admin.pjax`,
            `admin.log`,
        ],
    ];

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        if (file_exists($routes = base_path(`routes/admin.php`))) {
            $this->loadRoutesFrom($routes);
        }
    }

    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        $this->loadAdminAuthConfig();

        $this->registerRouteMiddleWare();
    }

    /**
     * 註冊路由中介軟體
     */
    public function registerRouteMiddleWare()
    {
        // 註冊路由中介軟體
        foreach ($this->routeMiddleware as $key => $middleware) {
            app(`router`)->aliasMiddleware($key, $middleware);
        }

        // 註冊路由中介軟體組
        foreach ($this->middlewareGroups as $key => $middleware) {
            app(`router`)->middlewareGroup($key, $middleware);
        }
    }

    /**
     * Setup auth configuration.
     *
     * @return void
     */
    protected function loadAdminAuthConfig()
    {
        config(array_dot(config(`admin.auth`, []), `auth.`));
    }
}

config/app.php中註冊provider和facades

`providers` => [
    ...,
    AppProvidersAdminServiceProvider::class,
]

`aliases` => [
    ...,
    `Admin` => AppFacadesAdminAdmin::class,
]

建立登入表單驗證類,執行:

php artisan make:request AdministratorLoginRequest

重寫其父類rulesmessages方法:

<?php

namespace AppHttpRequests;

use IlluminateFoundationHttpFormRequest;

class AdministratorLoginRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            `login_name` => `required`,
            `password` => [`required`, `min:6`] //密碼必須,最小長度為6
        ];
    }

    public function messages()
    {
        return [
            `login_name.required` => `請輸入使用者名稱`,
            `password.required` => `請輸入密碼`,
            `password.min` => `密碼至少6位`,
        ];
    }
}

建立登入控制器,執行:

php artisan make:controller Admin/Auth/LoginController

編寫登入方法:

<?php

namespace AppHttpControllersAdminAuth;

use AppHttpRequestsAdministratorLoginRequest;
use IlluminateHttpRequest;
use AppHttpControllersController;
use IlluminateSupportFacadesAuth;

class LoginController extends Controller
{
    /**
     * 登入表單
     * @return IlluminateContractsViewFactory|IlluminateViewView
     */
    public function getLoginForm()
    {
        if (!Auth::guard(`administrator`)->guest()) {
            return redirect(config(`admin.route.prefix`));
        }

        return view(admin_view_base_path(`auth.login.index`));
    }

    /**
     * 登入操作
     * @param AdministratorLoginRequest $loginRequest
     * @return IlluminateHttpRedirectResponse|IlluminateRoutingRedirector
     */
    public function postLogin(AdministratorLoginRequest $loginRequest)
    {
        $postData = $loginRequest->only(`login_name`, `password`, `remember`);

        $result = Auth::guard(`administrator`)->attempt($postData, $loginRequest->filled(`remember`));

        if ($result) {
            admin_toast(`登入成功`);
            $loginRequest->session()->regenerate();
            return redirect()->intended(config(`admin.route.prefix`));
        }else{
            return redirect()->back()->withInput()
                ->withErrors([
                    `login_name` => `使用者名稱或密碼錯誤`
                ]);
        }
    }

    /**
     * 登出登入
     * @return IlluminateHttpRedirectResponse|IlluminateRoutingRedirector
     */
    public function postLogout()
    {
        Auth::guard(`administrator`)->logout();

        session()->forget(`url.intented`);

        return redirect(config(`admin.route.prefix`));
    }
}

至此,認證業務已經完成,接下來進行完善,使系統能夠跑起來

建立檢視檔案(ps:1、控制器中使用的admin_view_base_path為自定義函式,指向後臺檢視檔案主目錄。2、檢視採用AdminLTE框架)
login.blade:form

<form action="{{ admin_base_path(`login`) }}" method="post">
            @csrf

            <div class="form-group has-feedback">
                <input type="text" class="form-control {{ $errors->has(`login_name`) ? ` is-invalid` : `` }}"
                       placeholder="登 錄 名" name="login_name" value="{{ old(`login_name`) }}" required autofocus>
                <span class="glyphicon glyphicon-envelope form-control-feedback"></span>

                @if ($errors->has(`login_name`))
                <span class="invalid-feedback">
                    <strong class="text-danger">{{ $errors->first(`login_name`) }}</strong>
                </span>
                @endif
            </div>

            <div class="form-group has-feedback">
                <input type="password" class="form-control {{ $errors->has(`password`) ? ` is-invalid` : `` }}"
                       placeholder="密 碼" name="password" required>
                <span class="glyphicon glyphicon-lock form-control-feedback"></span>

                @if ($errors->has(`password`))
                    <span class="invalid-feedback">
                        <strong class="text-danger">{{ $errors->first(`password`) }}</strong>
                    </span>
                @endif
            </div>

            <div class="row">
                <div class="col-xs-8">
                    <div class="checkbox icheck">
                        <label>
                            <input type="checkbox" name="remember" {{ old(`remember`) ? `checked` : `` }}> 記住我
                        </label>
                    </div>
                </div>

                <!-- /.col -->
                <div class="col-xs-4">
                    <button type="submit" class="btn btn-primary btn-block btn-flat">登 錄</button>
                </div>
                <!-- /.col -->
            </div>
        </form>

建立後臺home控制器,執行:

php artisan make:controller Admin/Home/HomeController

編寫index方法返回檢視

<?php

namespace AppHttpControllersAdminHome;

use IlluminateHttpRequest;
use AppHttpControllersController;

class HomeController extends Controller
{
    //
    public function index()
    {
        return view(admin_view_base_path(`home.index`));
    }

}

若僅作登入之後跳轉測試,home檢視中保留登出按鈕即可,例:
home.blade

<div class="pull-right">
    <a class="btn btn-default btn-flat" href="/admin/logout"
       onclick="event.preventDefault();
                         document.getElementById(`logout-form`).submit();">
        {{ __(`注 銷`) }}
    </a>
</div>

<form id="logout-form" class="hide" action="{{ admin_base_path(`logout`) }}" method="POST" style="display: none;">
    @csrf
</form>

編輯配置檔案config.admin.php,加入以下配置:

`route` => [
    `prefix` => `admin`,

    `namespace` => `App\Http\Controllers\Admin`,

    `middleware` => [`web`, `admin`],
],

編寫路由檔案routes/admin.php

<?php

use IlluminateRoutingRouter

Route::group([
    `prefix`        => config(`admin.route.prefix`),
    `namespace`     => config(`admin.route.namespace`),
    `middleware`    => config(`admin.route.middleware`),
], function (Router $router) {
    $router->get(`/`, `HomeHomeController@index`);
    $router->get(`login`, `AuthLoginController@getLoginForm`);
    $router->post(`login`, `AuthLoginController@postLogin`);
    $router->post(`logout`, `AuthLoginController@postLogout`);
});

資料表遷移與填充: 修改database/seeds/DatabaseSeeder

<?php

use IlluminateDatabaseSeeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application`s database.
     *
     * @return void
     */
    public function run()
    {
        // $this->call(UsersTableSeeder::class);
        DB::table(`administrators`)->insert(
            [
                `login_name` => `admin`,
                `display_name` => `超級管理員`,
                `password` => bcrypt(`123456`),
                `created_at` => date(`Y-m-d H:i:s`, time()),
                `updated_at` => date(`Y-m-d H:i:s`, time()),
            ]
        );
    }
}

執行:

php artisan migrate //資料表遷移
php artisan db:seed //資料填充

如果前臺使用了laravel框架使用者認證的話,需要重寫框架生成的LoginControllerlogout方法。防止前臺使用者登出的時候清除後臺使用者的session。(ps:框架自帶使用者認證在登出時會清空所有session)

/**
 * 重寫前臺登出操作
 * @param Request $request
 * @return IlluminateHttpRedirectResponse|IlluminateRoutingRedirector
 */
public function logout(Request $request)
{
    $this->guard()->logout();

    $request->session()->forget(`uri.intented`);

    return redirect(`/`);
}

至此,後臺使用者登入全部完成。enjoy it!

相關文章