身份验证后重定向期间请求挂起
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.php
中login()
的最后一行更改为
return $this->redirect(Router::url('/', true)); // generate full URL
并将config/app.php
中的fullBaseUrl
设置为'@987654321@'
解决了这个问题。
【讨论】:
以上是关于身份验证后重定向期间请求挂起的主要内容,如果未能解决你的问题,请参考以下文章