Menu

路由中间件之 StartSession

简介

[
    'App\Http\Middleware\EncryptCookies',
    'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
    'Illuminate\Session\Middleware\StartSession',
    'Illuminate\View\Middleware\ShareErrorsFromSession',
    'App\Http\Middleware\VerifyCsrfToken',
    'Illuminate\Routing\Middleware\SubstituteBindings',
    'App\Http\Middleware\RedirectIfAuthenticated',
]

本章,我们围绕 StartSession 中间件进行分析和学习。

StartSession 的 handle 方法

public function handle($request, Closure $next)
{
    // 记录 session 已启动
    $this->sessionHandled = true;

    if ($this->sessionConfigured()) {
        $request->setLaravelSession(
            $session = $this->startSession($request)
        );

        $this->collectGarbage($session);
    }

    // 此为分界线,以上是请求进来时的 session 处理,以下是回复响应时的 session 处理。
    $response = $next($request);

    if ($this->sessionConfigured()) {
        $this->storeCurrentUrl($request, $session);

        $this->addCookieToResponse($response, $session);
    }

    return $response;
}

请求进来时的 Session 处理

// 判断 session.php 配置文件有没有内容,有则执行 if 里面的代码
if ($this->sessionConfigured()) {
    // 将 session 对象绑定到 request 对象上,方便后续处理
    $request->setLaravelSession(
        // 获取并处理 session 对象
        $session = $this->startSession($request)
    );

    // 概率清除过期 session
    $this->collectGarbage($session);
}

如何获取并处理 session 对象的呢

protected function startSession(Request $request)
{
    // 从 getSession 方法中获取 session 对象
    return tap($this->getSession($request), function ($session) use ($request) {
        // 根据需要将 request 对象绑定到 session 对象上
        $session->setRequestOnHandler($request);

        // 根据前端 cookie 传递过来的 sessionId 在服务器上加载 session。
        $session->start();
    });
}
public function getSession(Request $request)
{
    // 在 SessionManager 中,依据 session 配置的驱动,获取 session 对象
    return tap($this->manager->driver(), function ($session) use ($request) {
        // 从 cookie 中获取 sessionId
        $session->setId($request->cookies->get($session->getName()));
    });
}

上面两个方法的运行顺序--遵循由外至里的运行顺序,然后将结果再由里至外的方式传递回来。往外传递结果的时候,会运行 tap 函数里面的闭包,对 session 对象进行进一步加工。

那么最终的 session 对象到底是谁呢?我们来看一个方法就知道了

Illuminate\Session\SessionManager

protected function buildSession($handler)
{
    if ($this->app['config']['session.encrypt']) {
        return $this->buildEncryptedSession($handler);
    }

    // Store 对象就是最后返回的 session 对象了。
    return new Store($this->app['config']['session.cookie'], $handler);
}

回复响应时的 session 处理

// 判断 session.php 配置文件有没有内容,有则执行 if 里面的代码
if ($this->sessionConfigured()) {
    // 如果当前请求是 get 请求,且成功匹配到路由,且非ajax,则保存当前完整 url 至 session 对象中
    $this->storeCurrentUrl($request, $session);

    // 获取 sessionId 和 sessionName 添加至响应 cookie 给浏览器,实现与浏览器的记忆会话
    $this->addCookieToResponse($response, $session);
}

storeCurrentUrl

protected function storeCurrentUrl(Request $request, $session)
{
    // get请求、成功匹配路由、非ajax请求
    if ($request->method() === 'GET' && $request->route() && ! $request->ajax()) {
        // 保存 url
        $session->setPreviousUrl($request->fullUrl());
    }
}

addCookieToResponse

protected function addCookieToResponse(Response $response, Session $session)
{
    // 判断 session 会话是否直接用的 cookie
    if ($this->usingCookieSessions()) {
        $this->manager->driver()->save();
    }

    // 判断 session 驱动是否为正确
    if ($this->sessionIsPersistent($config = $this->manager->getSessionConfig())) {

        // 设置 sessionId 至 cookie 中。
        $response->headers->setCookie(new Cookie(
            $session->getName(), $session->getId(), $this->getCookieExpirationDate(),
            $config['path'], $config['domain'], $config['secure'] ?? false,
            $config['http_only'] ?? true, false, $config['same_site'] ?? null
        ));
    }
}

关于 Laravel Session 一点见解:

Session(会话)

Laravel 实现会话方式我举个形象例子来说明。我们去一家非常喜爱的饭店吃饭时,饭店推出了会员折扣活动,需要办理会员卡。办理者会获得饭店颁发的一张含有唯一标识符会员磁卡,磁卡仅仅记录一串标识符,真正会员数据存在饭店的数据库中。那么下一次会员来就餐时,凭借会员磁卡上的标识符,来获取会员的信息,其中包括就餐记录、剩余余额等等。

这段例子,其中会员就是浏览器;饭店就是 Laravel;会员磁卡就是 Laravel 的 Cookie,会员磁卡标识符就是 SessionId,真正的数据存在 Laravel 中。

那么真正数据在哪?

Laravel

如图,sessions 目录中的一串串不认识的字符串文件,对应着一个个会员数据。其中字符串文件名就是会员磁卡的标识符也就是 sessionId啦。文件中的内容记录着经过 php 序列化后的用户数据。

本篇如有错误、不当或者需补充的内容,请各位同僚多提宝贵意见。

本文章首发在 LearnKu.com 网站上。
上一篇 下一篇
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 0
发起讨论 只看当前版本


暂无话题~