由于 HTTP 协议是无状态的,我们无法在两个页面之间保证用户身份的同步,因此我们需要借助会话在浏览器中临时存储用户的身份信息,进而保证在同一浏览器中,用户在不同页面具有相同的登录状态。

一、RESTful

下面我们还需要对路由进行配置,添加一些接下来需要用到的路由,新增的路由分别对应会话控制器的三个动作:create, store, destroy。

app/Http/routes.php

<?php
Route::get('/', 'StaticPagesController@home')->name('home');
Route::get('/help', 'StaticPagesController@help')->name('help');
Route::get('/about', 'StaticPagesController@about')->name('about');

// 用户注册
Route::get('/signup', 'UsersController@create')->name('signup');

// 资源路由
resource('users', 'UsersController');


// 会话控制,登录,退出
get('login', 'SessionsController@create')->name('login');
post('login', 'SessionsController@store')->name('login');
delete('logout', 'SessionsController@destroy')->name('logout');

新增的路由功能如下:

HTTP    请求URL    动作                               作用
GET        /login     SessionsController@create      显示登录页面
POST      /login      SessionsController@store       创建新会话(登录)
DELETE    /logout     SessionsController@destroy     销毁会话(退出登录)

我们可以使用 Laravel 提供的 route:list 命令来查看已添加的路由。

clipboard.png

我们可以从上面的列表清晰的看到所有在 routes.php 中被定义好的路由,这将帮助我们更好的理解应用的基础架构。

Laravel 遵从 RESTful 架构的设计原则,将数据看做一个资源,由 URI 来指定资源。对资源进行的获取、创建、修改和删除操作,分别对应 HTTP 协议提供的 GET、POST、PATCH 和 DELETE 方法。当我们要查看一个 id 为 1 的用户时,需要向 /users/1 地址发送一个 GET 请求,当 Laravel 的路由接收到该请求时,默认会把该请求传给控制器的 show 方法进行处理。

Laravel 为我们提供了 resource 方法来定义用户资源路由。

新增的 resource 方法将遵从 RESTful 架构为用户资源生成路由。该方法接收两个参数,第一个参数为资源名称,第二个参数为控制器名称。

resource('users', 'UsersController');

上面代码将等同于:

get('/users', 'UsersController@index')->name('users.index');
get('/users/{id}', 'UsersController@show')->name('users.show');
get('/users/create', 'UsersController@create')->name('users.create');
post('/users', 'UsersController@store')->name('users.store');
get('/users/{id}/edit', 'UsersController@edit')->name('users.edit');
patch('/users/{id}', 'UsersController@update')->name('users.update');
delete('/users/{id}', 'UsersController@destroy')->name('users.destroy');

可以看到使用 resource 方法让我们少写了很多代码,且严格按照了 RESTful 架构对路由进行设计。

生成的资源路由列表信息如下所示:

clipboard.png

这时我们访问 http://sample.app/users/1 页面,会出现如下页面。这是因为该路由请求由 UsersController 里面的 show 方法来处理.

二、两种页面提示信息

1.Validate()验证参数错误提示

如果Vlalidate()验证参数错误,则会在视图页面抛出erros的错误信息,并退出程序,这时,我们需要在视图页面判断是否有错误抛出,如果有则显示错误信息。由于错误提示是一个通用的,所以,这里我们封装一个错误的局部视图页面,然后在其他页面引入,这样可以大大增加代码的复用性和简洁性。

// 输入参数验证
        $this->validate($request, [
            'email' => 'required|email|max:255',
            'password' => 'required'
        ]);

错误提示页面:
views/shared/errors.blade.php

@if(count($errors) > 0)
  <div class="alert alert-danger">
    <lu>
      @foreach($errors->all() as $error)
        <li>
          {{ $error }}
        </li>
      @endforeach
    </lu>
  </div>
@endif

在登录页面引用errors.blade.php:

@extends('layouts.default')
@section('title', '登录')

@section('content')
<div class="col-md-offset-2 col-md-8">
  <div class="panel panel-default">
    <div class="panel-heading">
      <h5>登录</h5>
    </div>
    <div class="panel-body">
      @include('shared.errors')

      <form method="POST" action="{{ route('login') }}">
        {{ csrf_field() }}

        <div class="form-group">
          <label for="email">邮箱:</label>
          <input type="text" name="email" class="form-control" value="{{ old('email') }}">
        </div>

        <div class="form-group">
          <label for="password">密码:</label>
          <input type="password" name="password" class="form-control"  value="{{ old('password') }}" />
        </div>
        <button type="submit" class="btn btn-primary">登录</button>
      </form>
      <hr />
      <p>还没账号?<a href="{{ route('signup') }}">现在注册!</a></p>

    </div>

  </div>
</div>

@stop

2.session()->flash()闪现提示消息

如在用户登录成功或失败时,我们需要给用户一个友好的提示信息,以往我都是在服务端返回一个JS的弹出框,给出提示信息,这里Laravel为我们提供了一个非常好的提示信息,就是使用会话的flash()方法。

由于 HTTP 协议是无状态的,所以 Laravel 提供了一种用于临时保存用户数据的方法 - 会话(Session),并附带支持多种会话后端驱动,可通过统一的 API 进行使用。

我们可以使用 session() 方法来访问会话实例。而当我们想存入一条缓存的数据,让它只在下一次的请求内有效时,则可以使用 flash 方法。flash 方法接收两个参数,第一个为会话的键,第二个为会话的值,我们可以通过下面这行代码的为会话赋值

session()->flash('success', '欢迎,您将在这里开启一段新的旅程~');

之后我们可以使用 session()->get('success') 通过键名来取出对应会话中的数据,取出的结果为 欢迎,您将在这里开启一段新的旅程~。

接下来的消息提示我们会用会话进行闪存,并分别为其设定好指定的键。danger, warning, success, info 这四个键名在 Bootstrap 分别具有不同样式展现效果,因此后面我们将使用这几个键名作为消息提示的专有设定。

现在让我们加入消息提醒视图,让会话消息在视图上进行展示。

resources/views/shared/messages.blade.php

@foreach (['danger', 'warning', 'success', 'info'] as $msg)
  @if(session()->has($msg))
    <div class="flash-message">
      <p class="alert alert-{{ $msg }}">
        {{ session()->get($msg) }}
      </p>
    </div>
  @endif
@endforeach

session()->has($msg) 可用于判断会话中 $msg 键对应的值是否为空,若为空则在页面上不进行显示。最后,我们通过 session()->get($msg) 来取出对应的值并在页面上进行显示。

现在,让我们在全局通用视图中加入消息提醒视图。

resources/views/layouts/default.blade.php

<!DOCTYPE html>
<html>
  <head>
    <title>@yield('title', 'Sample App') - Laravel 入门教程</title>
    <link rel="stylesheet" href="/css/app.css">
  </head>
  <body>
    @include('layouts._header')

    <div class="container">
      <div class="col-md-offset-1 col-md-10">
        @include('shared.messages')
        @yield('content')
        @include('layouts._footer')
      </div>
    </div>
  </body>
</html>

当我们再次注册一个验证通过的用户时,能够看到消息提醒在页面上成功展示。

三、登录信息

1.密码验证方法Auth::attempt()

借助 Laravel 提供的 Authattempt 方法可以让我们很方便的完成用户的身份认证操作,如下所示:

if (Auth::attempt(['email' => $email, 'password' => $password])) {
    // 该用户存在于数据库,且邮箱和密码相符合
}

attempt 方法会接收一个数组来作为第一个参数,该参数提供的值将用于寻找数据库中的用户数据。因此在上面的例子中,用户信息将使用 email 字段的值在数据库中进行查找,如果用户被找到,在将 password 的值进行哈希加密并与数据库中已加密过的密码进行匹配,如果匹配后两个值完全一致,则会创建一个通过认证的会话给用户。会话获取到之后,即视为用户登录成功。当用户身份认证成功 attempt 方法会返回 true,反之则返回 false。

会话控制器登录操作:app/Http/Controllers/SessionsController.php

<?php

namespace App\Http\Controllers;

use Auth;
.
.
.
class SessionsController extends Controller
{
    
   // 展示登录视图
    public function create()
    {
        return view('sessions.create');
    }

    // 存储数据
    public function store(Request $request)
    {
        // 输入参数验证
        $this->validate($request, [
            'email' => 'required|email|max:255',
            'password' => 'required'
        ]);

        $credentials = [
          'email'    => $request->email,
          'password' => $request->password,
        ];

        // 账号密码验证
        if(Auth::attempt($credentials))
        {
            dd(Auth::user());

            // 登录成功后的操作
             session()->flash('success', '欢迎回来!');
             return redirect()->route('users.show', [Auth::user()]);

        }
        else
        {

            // 登录失败后的操作
            session()->flash('danger', '很抱歉,您的邮箱和密码不匹配');
            return redirect()->back();
        }

    }
}

clipboard.png

2.获取登录成功后用户的信息

Larvel 为我们提供了用Auth::user() 方法来获取当前登录用户的信息

Auth::user()

我们可以使用dd(Auth::user());打印看下获取到的当前登录用户信息:

clipboard.png

3.判断用户是否登录Auth::check()

Laravel 提供了 Auth::check() 方法用于判断当前用户是否已登录,已登录返回 true,未登录返回 false

4.退出登录Auth::logout()

我们可以通过调用 Laravel 默认提供的 Auth::logout() 方法来实现用户的退出功能。

四、使用隐藏域来伪造 DELETE 请求

退出登录: Auth::logout();
在做登录退出操作时,需要删除用户的登录信息,如下表单:

<form action="{{ route('logout') }}" method="POST">
  {{ csrf_field() }}
  {{ method_field('DELETE') }}
  <button class="btn btn-block btn-danger" type="submit" name="button">退出</button>
</form>

可以看到用户退出登录的按钮实际上是一个表单的提交按钮,在点击退出按钮之后浏览器将向 /logout 地址发送一个 POST 请求。但由于 RESTful 架构中会使用 DELETE 请求来删除一个资源,当用户退出时,实际上相当于删除了用户登录会话的资源,因此这里的退出操作需要使用 DELETE 请求来发送给服务器由于浏览器不支持发送 DELETE 请求,因此我们需要使用一个隐藏域来伪造 DELETE 请求

Blade 模板中,我们可以使用 method_field 方法来创建隐藏域。

{{ method_field('DELETE') }}

其转化为 HTML 代码如下:

<input type="hidden" name="_method" value="DELETE">

Corwien
6.3k 声望1.6k 粉丝

为者常成,行者常至。