CakePHP 4 JWT集成:令牌“签名验证失败”

ttp71kqs  于 2023-02-23  发布在  PHP
关注(0)|答案(1)|浏览(137)

我尝试在我的CakePHP 4应用程序中实现基于JWT的身份验证。令牌成功生成,我也在www.example.com上验证了它。但是在请求应该验证令牌的路由时,它给出了以下错误"需要身份验证才能继续"。jwt.io as well. But while doing request for the route which should validate token it's giving following error "Authentication is required to continue".
在调查问题时,我使用了dd($this-〉Authentication);在控制器的初始化函数中,我在对象中看到以下原因。"签名验证失败"响应。
这案子能帮上忙吗
谢谢你,
下面是我的代码
routes.php
Web相关路由....
以下是与API相关的路由。

$routes->prefix('api', ['path' => '/api'], function ($routes) {
    $routes->setExtensions(['json']);
    // $routes->resources('register');
    $routes->post('/user/add', ['controller' => 'User', 'action' => 'add']);
    $routes->post('/user/login', ['controller' => 'User', 'action' => 'login']);
    $routes->post('/user/index', ['controller' => 'User', 'action' => 'index']);
    $routes->get('/user/logout', ['controller' => 'User', 'action' => 'logout']);

    $routes->fallbacks(DashedRoute::class);
});

源代码/应用程序. php

<?php

declare(strict_types=1);

/**
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 * @link      https://cakephp.org CakePHP(tm) Project
 * @since     3.3.0
 * @license   https://opensource.org/licenses/mit-license.php MIT License
 */

namespace App;

use Cake\Core\Configure;
use Cake\Core\ContainerInterface;
use Cake\Core\Exception\MissingPluginException;
use Cake\Datasource\FactoryLocator;
use Cake\Error\Middleware\ErrorHandlerMiddleware;
use Cake\Http\BaseApplication;
use Cake\Http\Middleware\BodyParserMiddleware;
use Cake\Http\Middleware\CsrfProtectionMiddleware;
use Cake\Http\MiddlewareQueue;
use Cake\ORM\Locator\TableLocator;
use Cake\Routing\Middleware\AssetMiddleware;
use Cake\Routing\Middleware\RoutingMiddleware;

use Authentication\AuthenticationService;
use Authentication\AuthenticationServiceInterface;
use Authentication\AuthenticationServiceProviderInterface;
use Authentication\Identifier\IdentifierInterface;
use Authentication\Middleware\AuthenticationMiddleware;
// use Cake\Http\MiddlewareQueue;
use Cake\Routing\Router;
use Psr\Http\Message\ServerRequestInterface;

/**
 * Application setup class.
 *
 * This defines the bootstrapping logic and middleware layers you
 * want to use in your application.
 */
class Application extends BaseApplication implements AuthenticationServiceProviderInterface
{
    /**
     * Load all the application configuration and bootstrap logic.
     *
     * @return void
     */
    public function bootstrap(): void
    {

        $this->addPlugin('Migrations');

        // Call parent to load bootstrap from files.
        parent::bootstrap();

        if (PHP_SAPI === 'cli') {
            $this->bootstrapCli();
        } else {
            FactoryLocator::add(
                'Table',
                (new TableLocator())->allowFallbackClass(false)
            );
        }

        /*
         * Only try to load DebugKit in development mode
         * Debug Kit should not be installed on a production system
         */
        if (\Cake\Core\Configure::read('debug')) {
            $this->addPlugin('DebugKit');
        }

        $this->addPlugin('Authentication');

        // Load more plugins here
    }

    /**
     * Setup the middleware queue your application will use.
     *
     * @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to setup.
     * @return \Cake\Http\MiddlewareQueue The updated middleware queue.
     */
    public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
    {
        $csrf = new CsrfProtectionMiddleware(['httponly' => true]);

        // Token check will be skipped when callback returns `true`.
        $csrf->skipCheckCallback(function ($request) {
            // Skip token check for API URLs.
            // return $request->getPath() == '/dwolla_webhook';
            return $request->getPath() == '/dwolla_webhook' || $request->getParam('prefix') == 'Api';
        });
        $middlewareQueue
            // Catch any exceptions in the lower layers,
            // and make an error page/response
            ->add(new ErrorHandlerMiddleware(\Cake\Core\Configure::read('Error')))

            // Handle plugin/theme assets like CakePHP normally does.
            ->add(new AssetMiddleware([
                'cacheTime' => \Cake\Core\Configure::read('Asset.cacheTime'),
            ]))

            // ->add(function (
            // \Psr\Http\Message\ServerRequestInterface $request,
            // \Psr\Http\Server\RequestHandlerInterface $handler
            // ) {
            //     try {
            //         // continue with the next middleware
            //         return $handler->handle($request);
            //     } catch (\Cake\Http\Exception\InvalidCsrfTokenException $exception) {

            //         // handle the catched exception
            //         $response = new \Cake\Http\Response();

            //         return $response->withStringBody('Oh noes, CSRF error!');
            //     }
            // })

            // Add routing middleware.
            // If you have a large number of routes connected, turning on routes
            // caching in production could improve performance. For that when
            // creating the middleware instance specify the cache config name by
            // using it's second constructor argument:
            // `new RoutingMiddleware($this, '_cake_routes_')`
            ->add(new RoutingMiddleware($this))

            // Parse various types of encoded request bodies so that they are
            // available as array through $request->getData()
            // https://book.cakephp.org/4/en/controllers/middleware.html#body-parser-middleware
            ->add(new BodyParserMiddleware())

            // Cross Site Request Forgery (CSRF) Protection Middleware
            // https://book.cakephp.org/4/en/controllers/middleware.html#cross-site-request-forgery-csrf-middleware
            // ->add(new CsrfProtectionMiddleware([
            //     'httponly' => true,
            // ]));
            ->add($csrf)
            ->add(new AuthenticationMiddleware($this));

        return $middlewareQueue;
    }

    /**
     * Returns a service provider instance.
     *
     * @param \Psr\Http\Message\ServerRequestInterface $request Request
     * @return \Authentication\AuthenticationServiceInterface
     */
    public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
    {
        $service = new AuthenticationService();

        // Load identifiers
        $service->loadIdentifier('Authentication.Password', [
            'fields'   => [
                'username' => 'username',
                'password' => 'password',
            ],
            // 'algorithm' => 'HS256',
            'resolver' => [
                'className' => 'Authentication.Orm',
                // 'finder'    => 'active',
                'userModel' => 'User',
            ],
            
        ]);

        // Load the authenticators
        $service->loadAuthenticator('Authentication.Form', [
            'fields' => [
                'username' => 'username',
                'password' => 'password',
            ],
            'returnPayload' => false,
            // 'loginUrl' => '/users/token.json'
        ]);

        $service->loadAuthenticator('Authentication.Jwt', [
            'secretKey' => file_get_contents(CONFIG . '/jwt.pem'),
            'header' => 'Authorization',
            // 'queryParam' => 'token',
            'tokenPrefix' => 'Bearer',
            'algorithm' => 'HS256',
            'returnPayload' => false,
        ]);

        $service->loadIdentifier('Authentication.JwtSubject', [
            // 'tokenField' => 'id',
            // 'dataField' => 'id',
            'algorithm' => 'HS256',
        ]);

        // Configure the service. (see below for more details)
        return $service;

    }

    /**
     * Register application container services.
     *
     * @param \Cake\Core\ContainerInterface $container The Container to update.
     * @return void
     * @link https://book.cakephp.org/4/en/development/dependency-injection.html#dependency-injection
     */
    public function services(ContainerInterface $container): void
    {
    }

    /**
     * Bootstrapping for CLI application.
     *
     * That is when running commands.
     *
     * @return void
     */
    protected function bootstrapCli(): void
    {
        try {
            $this->addPlugin('Bake');
        } catch (MissingPluginException $e) {
            // Do not halt if the plugin is missing
        }

        $this->addPlugin('Migrations');

        // Load more plugins here
    }
}

API控制器代码

<?php

namespace App\Controller;

use Cake\Controller\Controller;
use Cake\Event\EventInterface;

class ApiController extends Controller
{

    public function initialize(): void
    {
        parent::initialize();
        $this->loadComponent('RequestHandler');
        $this->loadComponent('Authentication.Authentication');
    }

    private function setCorsHeaders()
    {
        $this->response = $this->response->cors($this->request)
            ->allowOrigin(['*'])
            ->allowMethods(['*'])
            ->allowHeaders(['x-xsrf-token', 'Origin', 'Content-Type', 'X-Auth-Token', 'Access-Control-Allow-Headers', 'Authorization', 'HTTP_Authorization', 'X-Requested-With'])
            ->allowCredentials(['true'])
            ->exposeHeaders(['Link'])
            ->maxAge(300)
            ->build();
    }

    public function beforeRender(EventInterface $event)
    {
        // .......
        $this->setCorsHeaders();
    }
    public function beforeFilter(EventInterface $event)
    {
        // ......
        if ($this->request->is('OPTIONS')) {
            $this->setCorsHeaders();
            return $this->response;
        }
    }

}

用户控制器代码

<?php
namespace App\Controller\Api;

use Cake\View\JsonView;
use Firebase\JWT\JWT;
use App\Controller\ApiController;
use Lib\PpState\PpState;

class UserController extends ApiController
{

    // public function viewClasses(): array
    // {
    //     return [JsonView::class];
    // }

    public function initialize(): void
    {
        parent::initialize();
        $this->loadModel('User');
        // var_dump(debug_backtrace());
        // dd($_SERVER['HTTP_AUTHORIZATION']);
        dd($this->Authentication);
        $this->Authentication->allowUnauthenticated(['login', 'add']);
        // dd('hhesssssheh');
    }

    public function index()
    {
        // dd('ashsh');
        $this->Authentication->logout();
        // dd($this->Authentication->getResult());
        // dd($this->Authentication);
        $json = [
            'success' => true,
            'message' => 'welcome',
        ];
        $this->set(compact('json'));
        $this->viewBuilder()->setOption('serialize', 'json');
    }

    public function logout()
    {
        // JWT::destroy();
        // $decoded = JWT::decode($jwt, new Key($key, 'HS256'));
        // dd($decoded);
        // dd($this->request);
        //
        // $result = $this->Authentication->getResult();
        // dd($result);
        // $json = ['route' => 'logout'];
        // if ($result->isValid()) {
        //     $this->Authentication->logout();
        //     // $this->set('user', [
        //     //     'message' => 'You are successfully logout'
        //     // ]);
        //     $json = ['message' => 'You are successfully logout'];
        // }
        $json = [
            'success' => true,
            'message' => 'You are successfully logout',
        ];
        $this->set(compact('json'));
        $this->viewBuilder()->setOption('serialize', 'json');
        // $this->viewBuilder->setOption('serialize', 'user');

        // $this->set(compact('json'));
        // $this->viewBuilder()->setOption('serialize', 'json');

        // If the user is logged in send them away.
        // if ($result->isValid()) {
        //     $target = $this->Authentication->getLoginRedirect() ?? '/home';
        //     return $this->redirect($target);
        // }
        // if ($this->request->is('post')) {
        //     $this->Flash->error('Invalid username or password');
        // }
    }

    public function add()
    {
        if ($this->User->emailInUse($this->request->getData('username'))) {
            $json = [
                'success' => false,
                'message' => 'User email already exists, Please choose different email',
            ];
        } else {
            $user = $this->User->newEntity($this->request->getData());
            $newUser = $this->User->save($user);
            if (!empty($newUser->id)) {
                $privateKey = file_get_contents(CONFIG . '/jwt.key');
                $payload = [
                    'iss' => 'myapp',
                    'sub' => $newUser->id,
                    'iat' => time(),
                    'exp' => time() + 300,
                ];
                if (!in_array($this->request->getData('st'), PpState::getActiveStateAbbreviations())) {
                    $json = [
                        'success' => false,
                        'message' => 'Oh, bother! Poppins Payroll does not yet operate in your state. We’ve made note of your location, so we know where we’re needed. We look forward to being able to serve you soon.',
                    ];
                } else {
                    $json = [
                        'success' => true,
                        'token' => JWT::encode($payload, $privateKey, 'HS256'),
                    ];
                }
            } else {
                $json = [
                    'success' => false,
                    'message' => 'Issue in user registration',
                ];
            }
        }
        $this->set(compact('json'));
        $this->viewBuilder()->setOption('serialize', 'json');
    }

    public function login()
    {
        $result = $this->Authentication->getResult();
        if ($result->isValid()) {
            $privateKey = file_get_contents(CONFIG . '/jwt.key');
            $user = $result->getData();
            $payload = [
                'iss' => 'myapp',
                'sub' => $user->id,
                'iat' => time(),
                'exp' => time() + 60,
            ];
            $json = [
                'success' => true,
                'token' => JWT::encode($payload, $privateKey, 'HS256'),
            ];
        } else {
            $this->response = $this->response->withStatus(401);
            $json = [];
        }
        $this->set(compact('json'));
        $this->viewBuilder()->setOption('serialize', 'json');
    }
}

Postman API调用

我正在寻找CakePHP 4授权问题,以解决同时实现JWT令牌基础API

eaf3rand

eaf3rand1#

问题在于在Application.php中加载验证器的顺序。
您应该先加载Jwt,然后再加载Form验证器。

$service->loadAuthenticator('Authentication.Jwt', [
        'secretKey' => Security::getSalt(),
        'returnPayload' => false
    ]);

    $service->loadAuthenticator('Authentication.Form', [
        'fields' => $fields,
        'loginUrl' => '/users/login'
    ]);

相关问题