php redis删除数据本身

mkshixfv  于 2021-06-09  发布在  Redis
关注(0)|答案(0)|浏览(260)

我有一个用PHP7.3编写的应用程序,我想用redis来存储会话,我没有用redis作为会话处理程序,也没有使用 $_SESSION 在所有(我实际上尝试,但问题是相同的,所以我返工,但它没有帮助)。
我现在正在做的是:
当用户尝试登录时
正在尝试通过凭据从数据库中获取用户
如果凭据正确,我将生成随机uuid
在redis中存储用户数据1800秒,将uuid附加到redis键
将该uuid存储在用户cookies中
成功登录后,用户通过重定向到网站的安全部分 SessionAuthenticationMiddleware ,以及中间件检查:
如果用户有cookie(如果没有,则表示用户未登录)
使用该cookie值生成redis密钥并尝试从会话中获取用户(如果密钥不存在,则表示用户未登录)
如果redis中存在一个用户,我会再次将ttl重置为1800秒
检查我从redis获得的数据是否是现有用户(如果不是,我将从redis中删除它,这也意味着该用户没有登录)
如果全部正确,用户将重定向到页面的安全部分
对每个浏览器页面刷新进行相同的检查
问题是,出于某种原因,我随机地从redis中删除所有数据,例如,我登录并刷新页面10次,在第11次redis删除所有数据和应用程序注销我。这是随机发生的有时我可以登录5分钟,有时几秒钟。我在负责从redis中删除数据的代码中添加了日志条目,但从来没有调用过它。它发生在aws上(我使用自己的redis安装在ec2示例上),在我的本地机器上,一切正常。
不确定是否需要代码,但我将与您分享:
loginservice-从数据库获取用户并将其传递给 UserSession 服务:

public function login(string $email, string $password): void
    {
        $userData = $this->usersRepository->verifyCredentialsAndGetUser($email, $password);
        if (empty($userData)) {
            throw new Exception('Incorrect credentials provided');
        }
        $this->userSession->setUserLoginSession($userData);
    }

usersession-编排会话和cookie存储以保存/获取/检查用户会话

class UserSession
{
    public const AUTH_KEY = 'SOME_KEY';
    /**
     * @var RedisSessionHandler
     */
    private $session;
    /**
     * @var AuthCookie
     */
    private $authCookie;

    public function __construct(RedisSessionHandler $session, AuthCookie $authCookie)
    {
        $this->session = $session;
        $this->authCookie = $authCookie;
    }

    public function setUserLoginSession(array $userData): void
    {
        $this->authCookie->generateUserCookie();
        $uuid = $this->authCookie->getUserCookie();
        $this->session->write(self::AUTH_SESSION_KEY . ':' . $uuid, json_encode($userData));
    }

    public function clearUserLoginSession(): void
    {
        $uuid = $this->authCookie->getUserCookie();
        $this->authCookie->clearUserCookie();
        $this->session->destroy(self::AUTH_SESSION_KEY . ':' . $uuid);
    }

    public function getCurrentUser(): ?array
    {
        if ($this->authCookie->getUserCookie() === false) {
            return null;
        }
        $uuid = $this->authCookie->getUserCookie();
        $userData =  $this->session->read(self::AUTH_SESSION_KEY . ':' . $uuid);
        if (!empty($userData)) {
            return json_decode($userData, true);
        }

        return null;
    }

    public function isAuthorized(): bool
    {
        if ($this->getCurrentUser() === null) {
            return false;
        }

        $uuid = $this->authCookie->getUserCookie();
        $authorized =  $this->session->read(self::AUTH_SESSION_KEY . ':' . $uuid);
        if (empty($authorized) || $authorized === false) {
            return false;
        }

        return true;
    }
}

authcookie-用于操作用户身份验证相关数据的 Package 器,从cookie中保存/获取/删除

class AuthCookie
{
    public const AUTH_COOKIE_USER = '_user_session';

    private $ttl = 1800; // 30 minutes default
    /**
     * @var CookieInterface
     */
    private $cookie;

    public function __construct(CookieInterface $cookie)
    {
        $this->cookie = $cookie;
    }

    public function generateUserCookie(bool $forever = false): bool
    {
        $uuid = Uuid::uuid4();

        if ($forever === true) {
            return $this->cookie->forever(self::AUTH_COOKIE_USER, $uuid->toString());
        }
        return $this->cookie->set(self::AUTH_COOKIE_USER, $uuid->toString(), $this->ttl);
    }

    public function hasUserCookie(): bool
    {
        return $this->getUserCookie() !== null;
    }

    public function getUserCookie(): ?string
    {
        return $this->cookie->get(self::AUTH_COOKIE_USER);
    }

    public function clearUserCookie(): bool
    {
        return $this->cookie->delete(self::AUTH_COOKIE_USER);
    }
}

cookie-用于存储任何类型的cookie的基本cookie类

class Cookie implements CookieInterface
{
    /**
     * $_COOKIE global variable
     *
     * @var array
     */
    private $cookie;

    public function __construct()
    {
        $this->cookie = $_COOKIE;
    }

    public function get(string $name): ?string
    {
        if (!isset($this->cookie[$name])) {
            return null;
        }

        return strip_tags(stripslashes($this->cookie[$name]));
    }

    public function set(
        string $name,
        ?string $value,
        int $expire = 0,
        ?string $path = null,
        ?string $domain = null,
        ?bool $secure = false,
        ?bool $httpOnly = false
    ): bool {
        setcookie(
            $name,
            $value ?? '',
            $this->calculateExpirationTime($expire),
            $path ?? '',
            $domain ?? '',
            $secure ?? false,
            $httpOnly ?? false
        );
        $this->cookie[$name] = $value;

        return true;
    }

    public function forever(string $name, string $value): bool
    {
        $this->set( $name, $value, 31536000 * 5);
        $this->cookie[$name] = $value;
    }

    public function delete(string $name): bool
    {
        unset($this->cookie[$name]) ;
        return $this->set($name, null, time() - 15 * 60 );
    }

    private function calculateExpirationTime( $expire = 0 ): int
    {
        return (int)($expire > 0 ? time() + $expire : -1 );
    }
}

redissessionhandler-负责操作与会话相关的数据从redis中保存/获取/删除。目前,我没有将其设置为php会话处理程序( session_set_save_handler() ),但我试过了,问题是一样的。

class RedisSessionHandler implements \SessionHandlerInterface
{
    private $ttl = 1800; // 30 minutes default
    private $db;
    private $prefix;
    /**
     * @var LoggerInterface
     */
    private $logger;

    public function __construct(LoggerInterface $logger, \Redis $db, string $prefix = 'PHPSESSID:', int $ttl = 1800) {
        $this->db = $db;
        $this->prefix = $prefix;
        $this->ttl = $ttl;
        $this->logger = $logger;
    }

    public function open($savePath, $sessionName): bool
    {
        return true;
    }

    public function close(): bool
    {
        return true;
    }

    public function read($id) {
        $id = $this->getRedisKey($id);
        $this->logger->debug(
            sprintf(
                '%s Reading data for id %s',
                __CLASS__,
                $id
            )
        );
        $sessData = $this->db->get($id);
        $this->logger->debug(
            sprintf(
                '%s Result of reading data for id %s is: %s',
                __CLASS__,
                $id,
                $sessData
            )
        );
        if (empty($sessData)) {
            return '';
        }
        $this->logger->debug(
            sprintf(
                '%s time to leave for id %s is %s ',
                __CLASS__,
                $id,
                $this->db->ttl($id)
            )
        );
        $this->db->expire($id, $this->ttl);
        return $sessData;
    }

    public function write($id, $data) : bool
    {
        $id = $this->getRedisKey($id);
        $this->logger->debug(
            sprintf(
                '%s Writing data %s for id %s',
                __CLASS__,
                $data,
                $id
            )
        );
        $result = $this->db->set($id, $data, $this->ttl);
        $this->logger->debug(
            sprintf(
                '%s Write result for id %s is %s and expiration %s',
                __CLASS__,
                $id,
                $result === true ? 'true' : 'false',
                $this->ttl
            )
        );

        return true;
    }

    public function destroy($id): bool
    {
        $id = $this->getRedisKey($id);
       // this method never called, no log entiries written so a assume that application is not 
       // deleting redis key
        $this->logger->debug(
            sprintf(
                '%s Destroy id %s ',
                __CLASS__,
                $id
            )
        );
        $this->db->del($id);
        $this->close();
        return true;
    }

    public function gc($maxLifetime): bool
    {
        return true;
    }

    protected function getRedisKey($key)
    {
        if (empty($this->prefix)) {
            return $key;
        }
        return $this->prefix . $key;
    }
}

sessionauthenticationmiddleware-负责检查用户是否登录并重定向到适当的位置。

final class SessionAuthenticationMiddleware implements MiddlewareInterface
{
    /**
     * @var UserSession
     */
    private $userSession;
    /**
     * @var RouteCollectorInterface
     */
    private $routeCollector;
    /**
     * @var UserRepositoryInterface
     */
    private $userRepo;

    public function __construct(
        UserSession $session,
        RouteCollectorInterface $routeCollector,
        UserRepositoryInterface $userRepositoryInterface
    ) {
        $this->userSession = $session;
        $this->routeCollector = $routeCollector;
        $this-userRepo = userRepositoryInterface
    }

    /**
     * @inheritDoc
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        /**@var Route $currentRoute */
        $currentRoute = $request->getAttribute(RouteContext::ROUTE);
        $isAuthorized = $this->isAuthorised();
        // if route is guarded and user not authorised redirect to login
        if ($isAuthorized === false && $this->isGuardedRoute($currentRoute)) {
            return $this->notAuthorizedResponse($request);
        }
        // if route is not guarded but user is authorized redirect to admin dashboard,
        // also we need to check if provided user exist in our system,
        // if not destroy the session and redirect to login
        if ($isAuthorized && $this->isGuardedRoute($currentRoute) === false) {
            return $this->redirectToDashboard();
        }

        return $handler->handle($request);
    }

    private function isGuardedRoute(Route $route): bool
    {
        return in_array($route->getName() , ['get.login', 'post.login'], true) === false;
    }

    private function isAuthorised(): bool
    {
        if (!$this->userSession->isAuthorized()) {
            return false;
        }

        $userSession = $this->userSession->getCurrentUser();
        if (empty($userSession)) {
            return false;
        }
        try {
            $userExist=  $this-userRepo->getUser(
                Uuid::fromString(
                    $userSession['id']
                )
            );

            if ($userExist === false) {
                $this->userSession->clearUserLoginSession();
                return false;
            }
        } catch (ApplicationException $exception) {
            $this->logger->info($exception->getMessage(), ['exception' => $exception]);
            $this->userSession->clearUserLoginSession();
            return false;
        } catch (ApiException $exception) {
            $this->logger->warning($exception->getMessage(), ['exception' => $exception]);
            $this->userSession->clearUserLoginSession();
            return false;
        }

        return true;
    }

    private function notAuthorizedResponse(RequestInterface $request): Response
    {
        $response = new Response();
        if ($request->getHeaderLine('X-Requested-With') === 'xmlhttprequest') {
            $response->getBody()->write(
                json_encode(
                    [
                        'error' => [
                            'message' => 'Not authorized'
                        ],
                    ],
                    JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
                )
            );

            return $response->withHeader('Content-Type', 'application/json')
                ->withStatus(302);
        }

        return $response
            ->withHeader('Location', $this->routeCollector->getRouteParser()->urlFor('get.login'))
            ->withStatus(302);
    }

    private function redirectToDashboard(): Response
    {
        $response = new Response();
        return $response
            ->withHeader('Location', $this->routeCollector->getRouteParser()->urlFor('get.dashboard'))
            ->withStatus(302);
    }
}

我已经花了好几个小时来研究什么是错误的,我认为redis/php配置本身可能有问题,但代码中没有,但我不知道我需要寻找什么。

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题