身份验证后重定向期间请求挂起

Posted

技术标签:

【中文标题】身份验证后重定向期间请求挂起【英文标题】:Request hangs during redirect after authentication 【发布时间】:2020-02-03 13:24:52 【问题描述】:

我正在开发一个带有 dockerized 开发环境的 React/Cakephp 应用程序。对于身份验证,我使用 OpenID Connect 提供程序来建立用户身份,然后按照this article 中的建议将其封装在 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
    


src/Application.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/Middleware/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/Controller/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());
    

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

【问题讨论】:

【参考方案1】:

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

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

并将config/app.php 中的fullBaseUrl 设置为'@987654321@' 解决了这个问题。

【讨论】:

以上是关于身份验证后重定向期间请求挂起的主要内容,如果未能解决你的问题,请参考以下文章

通过 Google 打开 auth 2.0 身份验证后重定向 url 无效

React Router - 身份验证后重定向延迟

在 Glassfish 上进行领域身份验证后重定向

KeyCloak 在身份验证代码流错误后重定向回身份提供者

身份验证后重定向到受保护的页面

身份验证后重定向到所需页面[重复]