如果令牌在URL中作为查询字符串出现,则阻止Laravel API处理令牌

时间:2017-05-03 15:58:30

标签: laravel cors laravel-5.3 jwt

我可以处理令牌,如果它作为查询字符串在URL中出现,或者如果它作为Authentication标题出现在标题中,并且标记前缀为Bearer,我只想要能够在标题中收到它。

这是我的app/Http/Controllers/API/V1/AuthenticationController.php文件:

<?php

namespace app\Http\Controllers\API\V1;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Tymon\JWTAuth\Exceptions\JWTException;
use App\Models\Role;
use App\Models\User;
use App\Traits\Controllers\ApiParseBody;
use App\Traits\Controllers\ApiException;
use App\Traits\Controllers\ApiEvaluateCredentials;
use Tymon\JWTAuth\JWTAuth;
use App\Exceptions\Unauthorized\InvalidCredentials;
use App\Exceptions\InternalServerError\CouldNotCreateToken;
use Illuminate\Contracts\Hashing\Hasher;

class AuthenticationController extends Controller
{
    use ApiParseBody;
    use ApiEvaluateCredentials;
    use ApiException;

    /**
     * The user implementation.
     *
     * @var User
     */
    protected $user;


    /**
     * The role implementation.
     *
     * @var Role
     */
    protected $role;

    /**
     * The hash implementation.
     *
     * @var Hash
     */
    protected $hash;

    /**
     * The jwtauth implementation.
     *
     * @var JWTAuth
     */
    protected $jwtauth;

    /**
     * Instantiate a new controller instance.
     *
     * @return void
     */
    public function __construct(
        User $user,
        Role $role,
        Hasher $hash,
        JWTAuth $jwtauth
    ) {
        $this->middleware('jwt.auth', ['except' => ['signin', 'signup']]);
        $this->user = $user;
        $this->role = $role;
        $this->hash = $hash;
        $this->jwtauth = $jwtauth;
    }

    /**
     * Signin user.
     *
     * @param Request $request
     *
     * @return Response
     */
    public function signin(Request $request)
    {
        $attributes = array('email', 'password');
        $credentials = $this->parseBody($attributes, $request);
        $this->validateCredentialsArePresent($credentials);
        try {
            if (! $token = $this->jwtauth->attempt($credentials)) {
                throw new InvalidCredentials('invalid_credentials');
            }
        } catch (JWTException $e) {
                throw new CouldNotCreateToken('could_not_create_token');
        }
        return response()->json(compact('token'));
    }

    /**
     * Signup user. Default role is 'common'.
     *
     * @param Request $request
     *
     * @return Response
     */
    public function signup(Request $request)
    {
        $attributes = array('email', 'password');
        $params = $this->parseBody($attributes, $request);
        $this->validateCredentialsArePresent($params);
        $this->evaluateCredentials($params);
        $credentials = array(
            'email' => $params['email'],
            'password' => $this->hash->make($params['password'])
        );
        $this->validateUserAlreadyExists($credentials);
        $commonRole = $this->role->where('name', 'common')->firstOrFail();
        $user = new User($credentials);
        $commonRole->users()->save($user);
        return response()->json(array( 'message' => 'User signed up.'));
    }
}

这是我的config/cors.php文件:

<?php

return [
   'defaults' => [
       'supportsCredentials' => false,
       'allowedOrigins' => [],
       'allowedHeaders' => [],
       'allowedMethods' => [],
       'exposedHeaders' => [],
       'maxAge' => 0,
       'hosts' => [],
   ],

   'paths' => [
       'v1/*' => [
           'allowedOrigins' => ['*'],
           'allowedHeaders' => [
               'Origin',
               'Content-Type',
               'Accept',
               'Authorization',
               'X-Request-With'
           ],
           'allowedMethods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
           'exposedHeaders' => ['Authorization'],
           'maxAge' => 3600,
       ],
   ],
];

以下图片将显示我的意思,以防万一我不清楚我要传输的内容。

这个展示了我如何使用Postman对Heroku上的应用程序进行GET。你会看到我正在使用标题Authorization

enter image description here

我想要阻止的是通过在URL中发送令牌来获得相同的结果:

enter image description here

我甚至不知道这是否可行,所以我真的很感激这方面的任何指导。

2 个答案:

答案 0 :(得分:3)

我所做的是创建一个middleware以拒绝所有请求#34;令牌&#34;作为查询字符串中的关键参数。

首先我们要创建中间件:

php artisan make:middleware BeforeMiddleware正如您可能注意到的是中间件之前,这意味着它将在请求到达应用程序之前运行:

<?php

namespace App\Http\Middleware;

use Closure;
use App\Exceptions\BadRequest\RejectTokenAsQuerystring;

class BeforeMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param \Illuminate\Http\Request  $request
     * @param \Closure  $next
     *
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if ($request->token) {
            throw new RejectTokenAsQuerystring('reject_token_as_querystring');
        }
        return $next($request);
    }
}

我还必须将我创建的中间件添加到我的内核中:

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array
     */
    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \Barryvdh\Cors\HandleCors::class,
    ];

    /**
     * The application's route middleware groups.
     *
     * @var array
     */
    protected $middlewareGroups = [
        'api' => [
            'throttle:60,1',
            'bindings',
        ],
    ];

    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        'reject-token-in-url' => \App\Http\Middleware\BeforeMiddleware::class,
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'jwt.auth' => \Tymon\JWTAuth\Middleware\GetUserFromToken::class,
        'jwt.refresh' => \Tymon\JWTAuth\Middleware\RefreshToken::class,
    ];
}

最后,全局定义的中间件可用于我的路由定义:

<?php

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::group(
    [
        'domain' => getenv('API_DOMAIN'),
        'middleware' => ['cors', 'reject-token-in-url'],
        'prefix' => '/v1',
        'namespace' => 'V1'
    ],
    function () {
    }
);

我也实现了自己的错误定义,因此我列出了我想在应用程序中触发的所有可能错误,并在config/errors.php文件中定义如下:

<?php

return [
    "reject_token_as_querystring" => [
            "title"  => "Reject token as querystring.",
            "detail"  => "Token MUST be passed in the Header of the request."
    ]
];

然后您需要定义自定义的Exception类:

<?php

namespace App\Exceptions;

use Exception;

abstract class CustomException extends Exception
{
    /**
     * The id of the error that is being triggered.
     *
     * @var string
     */
    protected $errorId;

    /**
     * Status code for the triggered error.
     *
     * @var string
     */
    protected $status;

    /**
     * Title of the error.
     *
     * @var string
     */
    protected $title;

    /**
     * Detailed description about the error.
     *
     * @var string
     */
    protected $detail;

    /**
     * Instantiate a new Exception with the provided message.
     *
     * @param @string $message
     *
     * @return void
     */
    public function __construct($message)
    {
        parent::__construct($message);
    }

    /**
     * Get the status
     *
     * @return Int
     */
    public function getStatus()
    {
        return (int) $this->status;
    }

    /**
     * Return the Exception as an array
     *
     * @return Array
     */
    public function toArray()
    {
        return [
            'id'     => $this->id,
            'status' => $this->status,
            'title'  => $this->title,
            'detail' => $this->detail
        ];
    }

    /**
     * Build the Exception.
     *
     * @param array $args
     *
     * @return string
     */
    protected function build(array $args)
    {
        $this->id = array_shift($args);
        $error = config(sprintf('errors.%s', $this->id));
        $this->title  = $error['title'];
        $this->detail = vsprintf($error['detail'], $args);
        return $this->detail;
    }
}

您将使用该类扩展自定义错误:

<?php

namespace App\Exceptions\BadRequest;

use App\Exceptions\CustomException;

class BadRequestException extends CustomException
{
    /**
     * Status error number.
     *
     * @var string
     */
    protected $status = '400';

    /**
     * Instantiate a new 'bad request exception'.
     *
     * @return void
     */
    public function __construct()
    {
        $message = $this->build(func_get_args());

        parent::__construct($message);
    }
}

为了创建保存错误的类:

<?php

namespace App\Exceptions\BadRequest;

use App\Exceptions\BadRequest\BadRequestException;

class RejectTokenAsQuerystring extends BadRequestException
{

}

最后,如果您尝试使用网址中的令牌密钥请求信息,您将获得:

{
  "id": "reject_token_as_querystring",
  "status": "400",
  "title": "Reject token as querystring.",
  "detail": "Token MUST be passed in the Header of the request."
}

enter image description here

答案 1 :(得分:0)

将给定的中间件放在app/Http/Middleware/GetUserFromToken.php。这将替换默认中间件,并能够忽略查询字符串中的令牌。此中间件与默认值相同99%。只需浏览一下,您就会明白它的工作原理。

然后替换

'jwt.auth' => \Tymon\JWTAuth\Middleware\GetUserFromToken::class,

'jwt.auth' => \App\Http\Middleware\GetUserFromToken::class,

中间件

<?php

namespace App\Http\Middleware;

use Tymon\JWTAuth\Middleware\BaseMiddleware;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;

class GetUserFromToken extends BaseMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, \Closure $next)
    {
        $this->auth->setRequest($request);

        try {
            $this->auth->parseToken('bearer', 'authorization', '');
        } catch (JWTException $e) {
            return $this->respond('tymon.jwt.absent', 'token_not_provided', 400);
        }

        try {
            $user = $this->auth->authenticate();
        } catch (TokenExpiredException $e) {
            return $this->respond('tymon.jwt.expired', 'token_expired', $e->getStatusCode(), [$e]);
        } catch (JWTException $e) {
            return $this->respond('tymon.jwt.invalid', 'token_invalid', $e->getStatusCode(), [$e]);
        }

        if (! $user) {
            return $this->respond('tymon.jwt.user_not_found', 'user_not_found', 404);
        }

        $this->events->fire('tymon.jwt.valid', $user);

        return $next($request);
    }
}