路由中间件之 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 网站上。

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


暂无话题~