Laravel 文档阅读:HTTP 会话

翻译、衍生自:https://learnku.com/docs/laravel/5.5/session

简介

HTTP 协议是无状态(stateless)协议,而会话(Session)提供了一种在多个请求之间存储用户信息的办法。Laravel 开箱支持用几款不同的软件(Memcached、Redis 和数据库)提供管理会话的能力,而且基于这几款软件封装的 API 是一样的。

配置

会话的配置文件是 config/session.php,这里面配置项你可以深情款款地看一遍。Laravel 默认使用的会话驱动是 file,对于许多项目来说,够用了。对于大型项目,为了获得更好的会话性能,可以考虑使用 memchached 或者 redis 驱动。

会话数据保存在哪个地方,取决于 driver 配置项取值。Laravel 开箱支持以下驱动:

  • file - 会话数据保存在 storage/framework/sessions 目录下。
  • cookie - 会话数据经过加密保存在 Cookie 中。
  • database - 会话数据保存在关系型数据库中。
  • memchached / redis - 会话数据存储在这两个基于缓存的存储设备中。
  • array - 会话数据临时保存在数组中,不会被持久化。

提示! array 驱动在测试时使用的,它不会将会话里的数据持久化。

驱动预设

数据库

当使用 database 会话驱动的时候,你需要在数据库创建一张表格,存储这些会话数据。下面是使用 Schema 声明会话表的脚本:

Schema::create('sessions', function ($table) {
    $table->string('id')->unique();
    $table->unsignedInteger('user_id')->nullable();
    $table->string('ip_address', 45)->nullable();
    $table->text('user_agent')->nullable();
    $table->text('payload');
    $table->integer('last_activity');
});

你可以使用 Artisan 命令生成这个迁移文件:

php artisan session:table

php artisan migrate

Redis

在使用 Redis 之前,需要先用 Composer 安装 predis/predis 包(~1.0)。然后在 config/database.php 中配置你的 Redis 连接,最后为 config/session.phpconnection 字段指定使用哪个 Redis 连接。

使用 Session

获得数据

有两种方式操作会话数据:全局 session 辅助函数或者通过 Request 实例。首先,先来看通过 Request 实例的方式,Request 实例在控制器方法里可以依赖注入。记住,控制器方法里的依赖是通过服务容器自动注入的。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * Show the profile for the given user.
     *
     * @param  Request  $request
     * @param  int  $id
     * @return Response
     */
    public function show(Request $request, $id)
    {
        $value = $request->session()->get('key');

        //
    }
}

get 方法还接收第二个参数,它是默认值——当会话中没有要取的 Key 时,就返回这个值。第二个参数还可以是一个闭包,当会话中没有要取的 Key 时,就执行这个闭包里的内容,闭包的返回结果就是默认值:

$value = $request->session()->get('key', 'default');

$value = $request->session()->get('key', function () {
    return 'default';
});

全局 session 辅助函数

你也可以使用全局 session 辅助函数获得、保存会话数据。当给 session() 函数传递一个字符串参数的时候,它会返回会话中指定 Key 的值。当传递给 session() 的是一个包含键值对的数组,数组里的值都会保存到会话里:

Route::get('home', function () {
    // Retrieve a piece of data from the session...
    $value = session('key');

    // Specifying a default value...
    $value = session('key', 'default');

    // Store a piece of data in the session...
    session(['key' => 'value']);
});

提示! 通过 HTTP 请求实例和全局 session 辅助函数操作会话数据的方法几乎没有区别。两种方法都可以在你的测试用例中获得,使用 assertSessionHas 方法测试。

获得所有会话数据

使用 all 方法:

$data = $request->session()->all();

$data = session()->all();

判断会话里是否存在指定 Key

有两个方法用来判断:hasexists

  • has 方法:当指定的 Key 存在于会话中,且值不为 null 时,返回 false,否则返回 true

    if ($request->session()->has('users')) {
    //
    }

  • exists 方法:当指定的 Key 存在于会话中,就返回 true,否则返回 false

    if ($request->session()->exists('users')) {
    //
    }

保存数据

向会话里保存数据使用 put 方法,或者使用 session 辅助函数:

// Via a request instance...
$request->session()->put('key', 'value');

// Via the global helper...
session(['key' => 'value']);

向数组类型的会话数据里推入数据

如果会话里有一个字段值是一个数组的话,如果要往这个数组里推入数据,就需要用到 push 方法。例如,user.teams 这个 Key 保存的是队名,现在我们给这个数组里再加入一个元素:

$request->session()->push('user.teams', 'developers');

获得 & 删除 Key

pull 方法会在获得 Key 值的同时从会话里删除这个 Key 数据记录。

$value = $request->session()->pull('key', 'default');

闪存数据

有时,你可能希望保存的会话数据只在下一个请求中有效,这时可以用 flash 方法。用这个方法保存的数据只在下一个的请求中能够得到,之后就被删除。闪存数据主要用在保存短生命周期的消息比较有用:

$request->session()->flash('status', 'Task was successful!');

如果要让闪存数据多维持几个请求周期,可以使用 reflash 方法,这个方法会让所有的闪存数据延长一个请求周期。如果只延长特定的一些闪存数据,就用 keep 方法:

$request->session()->reflash();

$request->session()->keep(['username', 'email']);

删除数据

forget 方法用来删除会话数据,如果要删除所有会话数据,请使用 flush 方法:

$request->session()->forget('key');

$request->session()->flush();

重新生成会话 ID

重新生成会话 ID 的原因,通常是为了避免恶意用户利用 会话固定 手段攻击你的项目。

如果你使用了内置的 LoginController 的话,它会在你认证的时候自动重新生成会话 ID;如果,你需要手动重新生成会话 ID 的话,请使用 regenerate 方法。

$request->session()->regenerate();

添加自定义会话驱动

实现驱动

你的自定义驱动要实现 SessionHandlerInterface 这个接口。这个接口包含几个需要实现的简单方法。一个 MongoDB 的实现代码看起来是想这样的:

<?php

namespace App\Extensions;

class MongoHandler implements SessionHandlerInterface
{
    public function open($savePath, $sessionName) {}
    public function close() {}
    public function read($sessionId) {}
    public function write($sessionId, $data) {}
    public function destroy($sessionId) {}
    public function gc($lifetime) {}
}

注意! Laravel 没有为你的驱动扩展创建默认文件目录。你可以按照你的心意放置,在这里我们是创建了一个 Extensions 目录保存我们的 MongoHandler 文件。

由于所有的方法看起来都不知道是什么意思,我这里全部讲一遍:

  • open 方法用在基于文件的会话存储系统。因为 Laravel 自带 file 会话存储,所以这个方法保持为空就可以了。这个方法有些不使用,不是一个很好的接口设计(之后再讨论),但是 PHP 要求我们去实现它。
  • close 方法跟 open 方法一样,可以直接忽略,保持为空即可。对于大多数驱动,都用不着。
  • read 方法应该返回匹配指定 $sessionId 的那个会话数据的字符串形式,在你的驱动程序中查询或存储会话数据时,无需进行任何序列化或其他编码,因为 Laravel 给你做了。
  • write 方法应该将给定的 $data 字符串数据关联到 $sessionId,然后持久化、保存到存储系统里。比如 MongoDB、Dynamo 等。再一次说明,你不应该执行任何序列化操作——Laravel 会为你处理好。
  • destroy 方法应该从持久化系统里删除与 $sessionId 关联的会话数据。
  • gc 方法应该删除在 $lifetime 之前的所有会话数据,它是一个时间戳。对于自带过期功能的系统,像 Memcached 和 Redis,这个方法保持为空就 OK 了。

注册驱动

驱动实现好后,就可以注册它了。为 Laravel 注册驱动要用到 Session 门面的 extend 方法。你需要在服务提供者的 boot 方法里调用 extend 方法。可以在现成的 AppServiceProvider 中,或者其他新建的服务提供者里:

<?php

namespace App\Providers;

use App\Extensions\MongoSessionStore;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;

class SessionServiceProvider extends ServiceProvider
{
    /**
     * Perform post-registration booting of services.
     *
     * @return void
     */
    public function boot()
    {
        Session::extend('mongo', function ($app) {
            // Return implementation of SessionHandlerInterface...
            return new MongoSessionStore;
        });
    }

    /**
     * Register bindings in the container.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

会话驱动注册好后,就可以在 config/session.php 配置文件里使用 mongo 驱动了。

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由 Summer 于 6年前 加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 1

我的 session 配置打开了关闭浏览器 session失效就是不生效
'lifetime' => env('SESSION_LIFETIME', 60),

'expire_on_close' => true,
4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!