跟社区学laravel博客实战5

新建分支,开始进行登录和退出相关功能的开发。

git checkout master
git checkout -b login-logout

当用户登录成功时,会话将被创建;

当用户退出登录时,会话会被销毁。

只是在这里会话并不会保存到数据库中,而是保存在浏览器上。

php artisan make:controller SessionsController

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

跟社区学laravel博客实战5

接下来让我们先来完善会话控制器的 create 动作,为用户创建一个登录页面。

在用户填写登录表单时,只需要用户提供个人邮箱账号和密码信息即可。

由于我们前面给邮箱做了唯一性限制,因此能够保证所有的注册用户邮箱都不相同,

为了确认登录者为邮箱拥有者本人,我们需要将邮箱与密码进行匹配,

匹配成功的用户将通过认证并登录。
接下来让我们完善一开始创建的会话控制器,加入 create 动作,并返回一个指定的登录视图。

$ vi app/Http/Controllers/SessionsController.php


<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class SessionsController extends Controller
{
    public function create()
    {
        return view('sessions.create');
    }
}





$ mkdir resources/views/sessions
$ vi resources/views/sessions/create.blade.php


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

@section('content')
<div class="offset-md-2 col-md-8">
  <div class="card ">
    <div class="card-header">
      <h5>登录</h5>
    </div>
    <div class="card-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




上面构建的登录表单有一行代码需要我们特别关注。
<form method="POST" action="{{ route('login') }}">

接下来手动身份认证

$ vi app/Http/Controllers/SessionsController.php


<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class SessionsController extends Controller
{
    public function create()
    {
        return view('sessions.create');
    }

    public function store(Request $request)
    {
       $credentials = $this->validate($request, [
           'email' => 'required|email|max:255',
           'password' => 'required'
       ]);

       return;
    }
}



我们可以看到,
在 store 动作中的数据验证与之前的有所不同,
因为在这里只需要保证用户输入的值不为空且格式正确即可

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

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

attempt 方法会接收一个数组来作为第一个参数,该参数提供的值将用于寻找数据库中的用户数据。

使用 email 字段的值在数据库中查找;
如果用户被找到:
1). 先将传参的 password 值进行哈希加密,然后与数据库中 password 字段中已加密的密码进行匹配;
2). 如果匹配后两个值完全一致,会创建一个『会话』给通过认证的用户。会话在创建的同时,也会种下一个名为 laravel_session 的 HTTP Cookie,以此 Cookie 来记录用户登录状态,最终返回 true;
3). 如果匹配后两个值不一致,则返回 false;
如果用户未找到,则返回 false。

结合 attempt 方法对用户身份进行认证的具体代码实现如下,使用 Auth 前需要对其进行引用(注意文件顶部引入 use Auth;):

app/Http/Controllers/SessionsController.php


<?php

namespace App\Http\Controllers;
.
.
.
use Auth;

class SessionsController extends Controller
{
    .
    .
    .
    public function store(Request $request)
    {
       $credentials = $this->validate($request, [
           'email' => 'required|email|max:255',
           'password' => 'required'
       ]);


//借助 Laravel 提供的 Auth 的 attempt 方法可以让我们很方便的完成用户的身份认证操作
//attempt 方法会接收一个数组来作为第一个参数,该参数提供的值将用于寻找数据库中的用户数据

       if (Auth::attempt($credentials)) {
           // 登录成功后的相关操作
       } else {
           // 登录失败后的相关操作
       }

       return;
    }
}

在用户登录成功之后我们还需要将用户重定向至其个人页面,

让用户可以在第一时间查看到自己的个人信息。

而当用户登录失败时,则需要将页面重定向回登录页面,让他尝试重新登录。

接下来让我们接着完善 store 方法,加入消息提示和页面重定向操作。

app/Http/Controllers/SessionsController.php



<?php

namespace App\Http\Controllers;
.
.
.
class SessionsController extends Controller
{
    .
    .
    .
    public function store(Request $request)
    {
       $credentials = $this->validate($request, [
           'email' => 'required|email|max:255',
           'password' => 'required'
       ]);

       if (Auth::attempt($credentials)) {
           session()->flash('success', '欢迎回来!');
           return redirect()->route('users.show', [Auth::user()]);
       } else {
           session()->flash('danger', '很抱歉,您的邮箱和密码不匹配');
           return redirect()->back()->withInput();
       }
    }
}

我们在 store 方法内使用了 Laravel 提供的 Auth::user() 方法来获取 当前登录用户 的信息,

并将数据传送给路由。
这时如果尝试输入错误密码则会显示登录失败的提示信息。

使用 withInput() 后模板里 old('email') 将能获取到上一次用户提交的内容,这样用户就无需再次输入邮箱等内容:

 

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

resources/views/layouts/_header.blade.php


<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
  <div class="container ">
    <a class="navbar-brand" href="{{ route('home') }}">Weibo App</a>
    <ul class="navbar-nav justify-content-end">
      @if (Auth::check())
        <li class="nav-item"><a class="nav-link" href="#">用户列表</a></li>
        <li class="nav-item dropdown">
          <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
            {{ Auth::user()->name }}
          </a>
          <div class="dropdown-menu" aria-labelledby="navbarDropdown">
            <a class="dropdown-item" href="{{ route('users.show', Auth::user()) }}">个人中心</a>
            <a class="dropdown-item" href="#">编辑资料</a>
            <div class="dropdown-divider"></div>
            <a class="dropdown-item" id="logout" href="#">
              <form action="{{ route('logout') }}" method="POST">
                {{ csrf_field() }}
                {{ method_field('DELETE') }}
                <button class="btn btn-block btn-danger" type="submit" name="button">退出</button>
              </form>
            </a>
          </div>
        </li>
      @else
        <li class="nav-item"><a class="nav-link" href="{{ route('help') }}">帮助</a></li>
        <li class="nav-item" ><a class="nav-link" href="{{ route('login') }}">登录</a></li>
      @endif
    </ul>
  </div>
</nav>

我们先把注意力放在用户退出登录按钮的具体实现上:

<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">

 

集成 Bootstrap 的 JavaScript 库#

Laravel 6.x 默认已经在 resources/js/bootstrap.js 文件中为我们配置好了 jQuery 和 Bootstrap。
我们只需要在 app.js 中对其进行加载即可:
resources/js/app.js

require('./bootstrap');

完成之后,需要重启 npm run watch-poll 让其编译新增的 app.js 文件。可使用 ctrl + c 退出 watch-poll 任务。然后重新运行:
$ npm run watch-poll

在我们重新运行 watch-poll 任务之后,app.js 文件将会被编译到应用的 public 文件夹下。现在我们要在全局默认视图中引用编译后的 app.js 文件。

resources/views/layouts/default.blade.php


<!DOCTYPE html>
<html>
  <head>
    <title>@yield('title', 'Weibo App') - Laravel 入门教程</title>
    <link rel="stylesheet" href="{{ mix('css/app.css') }}">
  </head>

  <body>
    @include('layouts._header')

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

    <script src="{{ mix('js/app.js') }}"></script>
  </body>
</html>

 

注册后自动登录

现在的注册功能已经可以正常使用,但我们希望在用户注册成功后能够自动登录,

这样的应用用户体验会更棒。

在 Laravel 中,如果要让一个已认证通过的用户实例进行登录,可以使用以下方法:

Auth::login($user);

让我们接着对用户控制器的 store 方法进行更改,让用户注册成功后自动登录。

app/Http/Controllers/UsersController.php



<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\User;
use Auth;

class UsersController extends Controller
{
    public function create()
    {
        return view('users.create');
    }

    public function show(User $user)
    {
        return view('users.show', compact('user'));
    }

    public function store(Request $request)
    {
        $this->validate($request, [
            'name' => 'required|max:50',
            'email' => 'required|email|unique:users|max:255',
            'password' => 'required|confirmed|min:6'
        ]);

        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => bcrypt($request->password),
        ]);

        Auth::login($user);
        session()->flash('success', '欢迎,您将在这里开启一段新的旅程~');
        return redirect()->route('users.show', [$user]);
    }
}

Git 代码版本控制#

接着让我们将本次更改纳入版本控制中:
 

$ git add -A
$ git commit -m "登录成功后导航逻辑"

 

Laravel 默认提供的 Auth::logout() 方法来实现用户的退出功能。

接下来让我们加上会话控制器的最后一个动作 - destroy,并实现用户退出登录的相关逻辑:

app/Http/Controllers/SessionsController.php



<?php

namespace App\Http\Controllers;
.
.
.
class SessionsController extends Controller
{
    .
    .
    .
    public function destroy()
    {
        Auth::logout();
        session()->flash('success', '您已成功退出!');
        return redirect('login');
    }
}



我们在用户退出之后,会在顶部显示相关的消息提醒,并将用户重定向到登录页面。

Git 代码版本控制#
接着让我们将本次更改纳入版本控制中:

git add -A
git commit -m "用户退出登录"

 

我们可以通过使用 Laravel 提供的『记住我』功能来保存一个记忆令牌,用于长时间记录用户登录的状态。Laravel 默认为用户生成的迁移文件中已包含 remember_token 字段,该字段将用于保存『记住我』令牌。

resources/views/sessions/create.blade.php



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

@section('content')
<div class="offset-md-2 col-md-8">
  <div class="card ">
    <div class="card-header">
      <h5>登录</h5>
    </div>
    <div class="card-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>

          <div class="form-group">
            <div class="form-check">
              <input type="checkbox" class="form-check-input" name="remember" id="exampleCheck1">
              <label class="form-check-label" for="exampleCheck1">记住我</label>
            </div>
          </div>

          <button type="submit" class="btn btn-primary">登录</button>
      </form>

      <hr>

      <p>还没账号?<a href="{{ route('signup') }}">现在注册!</a></p>
    </div>
  </div>
</div>
@stop

前面我们介绍过的

Auth::attempt() 方法可接收两个参数,

第一个参数为需要进行用户身份认证的数组,

第二个参数为是否为用户开启『记住我』功能的布尔值。

接下来让我们修改会话控制器中的 store 方法,为 Auth::attempt() 添加『记住我』参数。

app/Http/Controllers/SessionsController.php



<?php

namespace App\Http\Controllers;
.
.
.
class SessionsController extends Controller
{
    .
    .
    .
    public function store(Request $request)
    {
       $credentials = $this->validate($request, [
           'email' => 'required|email|max:255',
           'password' => 'required'
       ]);

       if (Auth::attempt($credentials, $request->has('remember'))) {
           session()->flash('success', '欢迎回来!');
           return redirect()->route('users.show', [Auth::user()]);
       } else {
           session()->flash('danger', '很抱歉,您的邮箱和密码不匹配');
           return redirect()->back()->withInput();
       }
    }
    .
    .
    .
}

Git 代码版本控制#
接着让我们将本次更改纳入版本控制中:
 

$ git add -A
$ git commit -m "登录时记住我"

 

现在,我们已完成整个用户注册、登录和退出功能。下面让我们需要将改动的代码进行提交并切换到主分支上进行合并。
 

$ git checkout master
$ git checkout .
$ git merge login-logout

我们修改了 app.scss 和 app.js ,

如果切换到 master 上并且运行着 npm run watch-poll 的话,会引起文件冲突,

git checkout . 是清空本地修改,

也就是放弃切换到 master 后 npm run watch-poll 生成的内容。

 

 

 

教你修改 Laravel "记住我" Cookie 的过期时间.

https://learnku.com/articles/5039/teach-you-to-change-the-laravel-remember-me-cookie-expiration-time

 

登录保持的时间我要是想自定义呢?要怎么写?

如果是 SESSION ,config/session.php 中 ,session 两小时内有效

'lifetime' => env('SESSION_LIFETIME', 120),

 

上一篇:[PostgreSQL] PostgreSQL 之 触发器分表性能优化


下一篇:重学前端,从 javascriptinfo 开始。