首页 > 解决方案 > 身份验证后重定向期间请求挂起

问题描述

我正在开发一个带有 dockerized 开发环境的 React/CakePHP 应用程序。对于身份验证,我使用 OpenID Connect 提供程序来建立用户身份,然后按照本文中的建议将其封装在 JWT 中。使用CakePHP/Authentication插件,我将请求从“ https://mydomain.local/ ”重定向到“ https://mydomain.local/login ”,后者处理 OIDC 逻辑。一旦通过身份验证,用户将再次被重定向回站点根目录,现在 JWT 在两个 cookie 中。

我的问题是请求挂在最后的重定向上。如果我在设置 cookie 后禁用重定向并手动导航回根目录,则请求工作正常,并且我的应用程序通过 JWT 正确看到经过身份验证的用户。

对于我的开发环境,我使用 Caddy 容器作为代理来终止 https,并使用 php-apache 容器来托管应用程序本身。两个服务器的日志都没有显示发生的最终请求。

以下是我的代码的相关部分:

docker_compose.yml:

services:
  caddy:
    image: "abiosoft/caddy:latest"
    volumes:
      - ./caddy/certs:/root/certs
      - ./caddy/Caddyfile:/etc/Caddyfile
      - ./caddy/logs:/var/log
    ports:
      - "443:2015"
    depends_on:
      - web
  web:
    build:
      context: .
    links:
      - db
    volumes:
      - "./src:/var/www/html/src:rw"
  db:
    image: mysql:latest

球童/球童文件:

mydomain.local {
    log /var/log/access.log
    # Mkcert - https://github.com/FiloSottile/mkcert
    tls /root/certs/mydomain.local.pem /root/certs/mydomain.local-key.pem

    proxy / http://web:80 {
        transparent
    }

}

源/应用程序.php:

public function middleware($middlewareQueue)
    {
        $middlewareQueue
            ->add(new ErrorHandlerMiddleware(null, Configure::read('Error')))
            ->add(new AssetMiddleware([
                'cacheTime' => Configure::read('Asset.cacheTime')
            ]))
            ->add(new RoutingMiddleware($this))
            ->prepend(new JwtMiddleware())
            ->add(new AuthenticationMiddleware($this));

        return $middlewareQueue;
    }

    public function getAuthenticationService(ServerRequestInterface $request, ResponseInterface $response)
    {
        $service = new AuthenticationService([
            'unauthenticatedRedirect' => Router::url(['controller' => 'Main', 'action' => 'login']),
            'queryParam' => 'redirect',
        ]);

        $service->loadIdentifier('Authentication.JwtSubject', [
            'tokenField' => 'id',
            'dataField' => 'sub',
            'resolver' => 'Authentication.Orm',
        ]);
        $service->loadAuthenticator('Authentication.Jwt', [
            'header' => 'Authorization',
            'queryParam' => 'token',
            'tokenPrefix' => 'Bearer',
            'algorithms' => ['HS256'],
            'returnPayload' => 'false',
            'secretKey' => Security::getSalt(),
        ]);

        return $service;
    }

src/中间件/JwtMiddleware.php:

use Lcobucci\JWT\Parser;
use Lcobucci\JWT\ValidationData;

class JwtMiddleware
{
    public function __invoke(RequestInterface $request, ResponseInterface $response, $next)
    {
        $jwt[0] = $request->getCookie('sa');
        $jwt[1] = $request->getCookie('sb');

        if (!empty($jwt[0]) && !empty($jwt[1])) {
            $data = new ValidationData();
            $data->setIssuer('mydomain');
            $data->setAudience('mydomain.local');
            $data->setId('mydomain');

            $jwt = implode('.', $jwt);
            $token = (new Parser())->parse((string) $jwt);

            if ($token->validate($data)) {
                $request = $request->withAddedHeader('Authorization', 'Bearer ' . $jwt);
                $response = $response->withCookie((new Cookie('sa'))
                    ->withValue($token->getPayload())
                    ->withExpiry(new \DateTime('+30 minutes'))
                    ->withPath('/')
                    ->withHttpOnly(false)
                );
            }
        }

        return $next($request, $response);
    }
}

src/控制器/MainController.php:

use Jumbojett\OpenIDConnectClient;
use Jumbojett\OpenIDConnectClientException;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key;

/**
 * Main Controller
 *
 * @property UsersTable $Users
 */
class MainController extends AppController
{
    public function beforeFilter(Event $event)
    {
        $this->Authentication->allowUnauthenticated(['login']);

        return parent::beforeFilter($event);
    }

    /**
     * Index method
     *
     * @return Response|null
     */
    public function index()
    {
        $filePath = WWW_ROOT . '/static.html';
        $file = new File($filePath);

        $index = $file->read();
        $file->close();

        return $this->response->withStringBody($index);
    }

    /**
     * Login method
     *
     * @return Response|null
     * @throws OpenIDConnectClientException
     */
    public function login()
    {
        $oidc = new OpenIDConnectClient(
            env('OIDC_URL'),
            env('OIDC_CLIENT'),
            env('OIDC_SECRET')
        );
        $oidc->addScope('openid');
        $oidc->addScope('profile');
        $oidc->authenticate();

        $this->loadModel('Users');

        $user = $this->Users->find()
            ->where(['auth_id' => $oidc->requestUserInfo('sub')])
            ->firstOrFail();

        $signer = new Sha256();
        $time = time();
        $token = (new Builder())
            ->issuedBy('mydomain')
            ->permittedFor('mydomain.local')
            ->identifiedBy('mydomain')
            ->issuedAt($time)
            ->expiresAt($time + 3600)
            ->withClaim('sub', $user->id)
            ->getToken($signer, new Key(Security::getSalt()));

        $signature = explode('.', $token->__toString())[2];
        $sa = (new Cookie('sa'))
            ->withValue($token->getPayload())
            ->withExpiry(new \DateTime('+30 minutes'))
            ->withPath('/')
            ->withHttpOnly(false);
        $sb = (new Cookie('sb'))
            ->withValue($signature)
            ->withPath('/')
            ->withHttpOnly(true);

        $this->response = $this->response
            ->withCookieCollection(new CookieCollection([$sa, $sb]));

        /**** HANG OCCURS ON THIS LINE ****/
        return $this->redirect($this->Authentication->getLoginRedirect());
    }
}

非常感谢任何建议/建议!!!

标签: phpdockerauthenticationcakephpjwt

解决方案


问题是重定向不安全,因为应用服务器正在运行 HTTP(SSL 在代理处终止)。login()将in的最后一行更改MainController.php

return $this->redirect(Router::url('/', true)); // generate full URL

并着手解决问题fullBaseUrlconfig/app.php'https://mydomain.local'


推荐阅读