> For the complete documentation index, see [llms.txt](https://tony915.gitbook.io/laravel-4-2/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tony915.gitbook.io/laravel-4-2/learning_more/authentication.md).

# 登入驗證

## 設定

很多時候我們都需要網站的登入功能，因為實在太常用了，所以 Laravel 直接就實作了整個登入機制，讓我們可以很快速的建立登入功能。

### 設定檔

```
app/config/auth.php
```

在這個檔案中，預設了一個可以儲存使用者的 Model 及資料表的名稱，

```
'model' => 'User',
'table' => 'users',
```

你可以在 app/models 中找到 User.php 這個 Model。而資料表的名稱就叫 users。如果有需要可以自行修改。

！重要，在建立 users 資料表時，必須建立一個 remember\_token 的字串欄位，長度 100、nullable。這個欄位是用來儲存 session 的 token。在 migrations 檔中可以使用

```
$table->rememberToken();
```

來建立。

## 儲存密碼

當使用者輸入密碼時，如果直接存入資料庫會有被偷盜的風險。Laravel 提供 Hash 類別，使用 Bcrypt hashing 的方式對密碼加密。

### 加密

在使用者要設定密碼時，將該密碼加密後儲存：

```
$password = Hash::make('secret');
```

這樣原本的 'secret' 這個字串，就會變成像這樣 '$2y$10$ytWDg0ufsazdFkYsVnW\.2eJhnc4gm5vZqHoEEycxqtCZCigWofGK6'

### 驗證

在使用者輸入登入密碼時，驗證該密碼：

```
if (Hash::check('secret', $password))
{
    //密碼正確
}
```

## 實戰使用者登入

我們來將之前練習的迷你部落格加上一個登入頁面，讓登入的使用者才能新增、修改文章，未登入的則只能看到文章。

步驟：

* 建立 migration 檔來新增 users 資料表
* 使用 seeder 來新增一位使用者
* 建立 Route
* 建立 Controller
* 建立登入表單頁面

### 建立 migration 檔來新增 users 資料表

開啟終端機，進入網站根目錄，輸入：

```
php artisan migrate:make create_users_table
```

會建立一個檔案 app/database/migrations/2015\_01\_08\_060754\_create\_users\_table.php，編輯它：

```
<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateUsersTable extends Migration {

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function($table){
          $table->increments('id');
          $table->string('username');
          $table->string('email');
          $table->string('password');

          //記得要加 remember_token 這個欄位，可手動指定
          //$table->string('remember_token', 100)->nullable();
          //或使用內建方法
          $table->rememberToken();
          $table->timestamps();
      });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('users');
    }

}
```

接下來到終端機中，輸入：

```
php artisan migrate
```

如果沒出現錯誤，現在你在資料庫管理軟體中就可以看到 users 資料表了。

### 使用 seeder 來新增一位使用者

在 app/database/seeds 目錄中，新增一個檔案 UserTableSeeder.php：

```
<?php

class UserTableSeeder extends Seeder {

    public function run()
    {
        DB::table('users')->delete();

        User::create([
            'username' => 'Tony',
            'email'    => 'tony@mail.com',
            'password' => Hash::make('password'),
        ]);
    }
}
```

好了之後，編輯 app/database/seeds/DatabaseSeeder.php，在 run() 方法中加入：

```
$this->call('UserTableSeeder');
```

完成。回到終端機，輸入：

```
php artisan db:seed
```

現在資料庫中 users 資料表已經建立一位使用者了。

### 建立 Route

打開 app/routes.php ，加入：

```
Route::get('login', 'LoginController@show');
Route::post('login', 'LoginController@login');
Route::get('logout', 'LoginController@logout');
```

我們會在 LoginController\@show 方法中回傳一個登入頁面；在 @login 方法中做登入驗證；在 @logout 方法中登出使用者。

### 建立 Controller

在 app/controllers 下建立 LoginController.php：

```
<?php

class LoginController extends BaseController {

    public function show()
    {

    }

    public function login()
    {

    }

    public function logout()
    {

    }

}
```

對應到 Route 中所指定的方法。

#### 1.登入頁

現在依序來加入功能。首先是 LoginController\@show：

```
public function show()
{
    return View::make('admin.login');
}
```

在 app/views 新建一個目錄 admin，在其中新增一個檔案 login.blade.php，結果是 app/views/admin/login.blade.php，內容如下：

```
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Blog Login</title>
    <style type="text/css">
    .fail {width:200px; margin: 20px auto; color: red;}
    form {font-size:16px; color:#999; font-weight: bold;}
    form {width:160px; margin:20px auto; padding: 10px; border:1px dotted #ccc;}
    form input[type="text"], form input[type="password"] {margin: 2px 0 20px; color:#999;}
    form input[type="submit"] {width: 100%; height: 30px; color:#666; font-size:16px;}
    </style>
</head>
<body>
    @if ($errors->has('fail'))
        <div class="fail">{{ $errors->first('fail') }}</div>
    @endif
    {{ Form::open(['url'=>'login', 'method'=>'post']) }}
        {{ Form::label('email', 'Email') }}
        {{ Form::text('email') }}
        {{ Form::label('password', 'Password') }}
        {{ Form::password('password') }}
        {{ Form::submit('Login') }}
    {{ Form::close() }}
</body>
</html>
```

完成後看起來像這樣：

![](/files/-M49VImn5slV87K5GlYj)

#### 2.驗證並登入

接著編輯 LoginController\@login，處理使用者的登入驗證：

```
public function login()
{
    $input = Input::all();

    $rules = ['email'=>'required|email',
              'password'=>'required'
              ];

    $validator = Validator::make($input, $rules);

    if ($validator->passes()) {
        $attempt = Auth::attempt([
            'email' => $input['email'],
            'password' => $input['password']
        ]);

        if ($attempt) {
            //驗證成功
            return Redirect::intended('post');
        }

        //驗證失敗
        return Redirect::to('login')
                ->withErrors(['fail'=>'Email or password is wrong!']);
    }

    //輸入的資料不符合
    return Redirect::to('login')
                ->withErrors($validator)
                ->withInput(Input::except('password'));
}
```

我們將 email 及 password 兩個欄位設為必填(required)，同時使用 email 這個規則去驗證郵件格式是否正確。

輸入的資料都符合規定後，使用 Auth 類別來做驗證。如果驗證成功，Auth::attempt() 方法會回傳 true，這時就可以把它導向內部頁面，這個範例是 post 頁面。假如驗證失敗，就導回登入頁面，同時回傳錯誤訊息。

如果輸入的資料就已經不符合規定，那也不必驗證啦，直接返回登入頁面。不過，在 ->withInput 的部份，我們用 Input::except('password') 來排除密碼欄位內容的回傳，因為不需要。

在成功驗證並登入之後，如果要取得登入者的資訊，可以使用 Auth::user() 來取得，如下：

```
Auth::user()->email;
```

#### 3.登出

最後來處理登出的功能，編輯 LoginController\@logout：

```
public function logout()
{
    Auth::logout();
    return Redirect::to('login');
}
```

使用 Auth::logout() 將使用者登出，然後導回登入頁面。

接著在頁面中顯示登出連結，編輯 app/views/site/home.blade.php：

```
@if(Auth::check())
    {{ Auth::user()->username}} 已登入，{{ HTML::link('logout', '登出') }}
@endif
```

Auth::check() 可以檢查使用者是否已經登入。

#### 4.頁面的登入檢查

完成了嗎？還沒，我們還沒讓 post 頁面受到登入機制的管制，現在(在登出的狀態)你可以直接輸入網址：

```
http://localhost/blog/public/post
```

你發現即使我們尚未登入，依然可以看到 post 頁面。

通常會每個必須被管制的頁面都要加檢查機制，但每個頁面都去 Auth::check() 不僅沒效率，也太麻煩了，我們直接從入口 (Route) 就截斷。編輯 app/routes.php，針對要管制的頁面 post 加入：

```
Route::get('post', ['before'=>'auth', 'uses'=>'HomeController@index']);
```

'before'=>'auth'，表示在進入這個 route 之前，要先執行 auth 這個過濾器(定義在 app/filters.php)。

如果要一個一個 route 去寫也太麻煩了，改用 group：

```
Route::group(['before'=>'auth'], function(){
    Route::get('post', 'HomeController@index');
    Route::get('post/create', 'HomeController@create');
    Route::post('post', 'HomeController@store');
    Route::get('post/{id}', 'HomeController@show');
    Route::get('post/{id}/edit', 'HomeController@edit');
    Route::put('post/{id}', 'HomeController@update');
    Route::delete('post/{id}', 'HomeController@destroy');
});
```

這樣在這個 group 之下的所有頁面都會受到 auth 過濾器的檢查。

在 \[Route 進階] 那個章節，有提到可以將同一位址往上提，於是可以修改成：

```
Route::group(['prefix'=>'post', 'before'=>'auth'], function(){
    Route::get('/', 'HomeController@index');
    Route::get('create', 'HomeController@create');
    Route::post('/', 'HomeController@store');
    Route::get('{id}', 'HomeController@show');
    Route::get('{id}/edit', 'HomeController@edit');
    Route::put('{id}', 'HomeController@update');
    Route::delete('{id}', 'HomeController@destroy');
});
```

當然，如果你的條件符合，也可以使用 resource 的方式：

```
Route::group(['before'=>'auth'], function(){
    Route::resource('post', 'HomeController');
});
```

完成，現在你已經無法直接輸入網址來進入頁面，請登入。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tony915.gitbook.io/laravel-4-2/learning_more/authentication.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
