新建分支,开始进行登录和退出相关功能的开发。
git checkout master
git checkout -b login-logout
当用户登录成功时,会话将被创建;
当用户退出登录时,会话会被销毁。
只是在这里会话并不会保存到数据库中,而是保存在浏览器上。
php artisan make:controller SessionsController
下面我们还需要对路由进行配置,添加一些接下来需要用到的路由,新增的路由分别对应会话控制器的三个动作:create, store, destroy。
接下来让我们先来完善会话控制器的 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),