2017-05-03 55 views
5

Jestem w stanie przetworzyć token, jeśli jest on w adresie URL jako ciąg lub występuje w nagłówku jako Authentication z prefiksem tokenu z Bearer, a ja chcę tylko móc go odebrać w nagłówku.Zapobiegam przetwarzaniu tokena przez Laravel API, jeśli jest on pod adresem URL w postaci zapytania o numer

To mój app/Http/Controllers/API/V1/AuthenticationController.php file:

<?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.')); 
    } 
} 

To mój config/cors.php file:

<?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, 
     ], 
    ], 
]; 

następujące obrazy pokaże, co mam na myśli, na wszelki wypadek, że nie było jasne, z tym, co Próbuję przekazać.

Ten pokazuje, w jaki sposób używam Postmana do wykonywania GET do aplikacji na Heroku. widać, że używam nagłówek Authorization:

enter image description here

i co chcę, aby zapobiec jest, aby uzyskać ten sam wynik, wysyłając token w URL w następujący sposób:

enter image description here

Nie wiem nawet, czy to możliwe, więc naprawdę doceniłbym wszelkie wskazówki w tej sprawie.

+0

spojrzeć https://laracasts.com/discuss/channels/general-discussion/l5- unikając-csrf-middleware-na-api-post-trasach? page = 2 – Brian

+0

@Brian dziękuję, jestem na tym – nisevi

+0

@Brian Próbowałem co t Hej wspomnieć w poście, ale nadal mam takie samo zachowanie. – nisevi

Odpowiedz

2

To, co zrobiłem, to stworzyć middleware, aby odrzucić wszystkie żądania z "tokenem" jako kluczowym parametrem w zapytaniu.

Najpierw musimy stworzyć middleware:

php artisan make:middleware BeforeMiddleware i jak można zauważyć, to przed middleware, to znaczy, że ma zamiar uruchomić przed wniosek uderza aplikację:

<?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); 
    } 
} 

ja również musiał dodać middleware, że stworzony na moje jądra:

<?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, 
    ]; 
} 

I wreszcie middleware jak jest zdefiniowane globalnie mogą być stosowane w definicji moje trasy jako:

<?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() { 
    } 
); 

ja też wdrożyliśmy własną definicję błędu, więc mam listę wszystkich możliwych błędów, które chcę wywołać w mojej aplikacji i są zdefiniowane następująco w moim pliku 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." 
    ] 
]; 

Następnie trzeba zdefiniować niestandardową klasę 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; 
    } 
} 

I będziesz używać tej klasy rozszerzyć swoje błędy niestandardowe:

<?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); 
    } 
} 

Aby utworzyć klasę, która posiada błąd sam:

<?php 

namespace App\Exceptions\BadRequest; 

use App\Exceptions\BadRequest\BadRequestException; 

class RejectTokenAsQuerystring extends BadRequestException 
{ 

} 

Wreszcie, jeśli próbują żądać informacji z kluczem token w url dostaniesz:

{ 
    "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

+0

Można pominąć dużo pracy i usunąć całą część wyjątku i zwrócić błąd bezpośrednio w oprogramowaniu pośredniczącym. – Sandeesh

+0

@Sandeesh Nie stworzyłem sposobu, w jaki obsługuję wyjątki dla tego konkretnego problemu, właśnie dodałem sposób, w jaki to robię, aby być bardziej opisowym, jeśli ktoś zadaje sobie pytanie, skąd bierze się wyjątek. Wiem, że mogę po prostu odrzucić lub przerwać żądanie, ale wolę dostosować błędy i samemu sobie z nimi poradzić. – nisevi

+0

@Sandeesh, więc to, co zaimplementowałem tutaj, jest tylko małą częścią całej obsługi błędów, którą wykonuję w mojej aplikacji: D ... – nisevi

0

Umieść podane oprogramowanie pośrednie pod numerem app/Http/Middleware/GetUserFromToken.php. To zastępuje domyślne oprogramowanie pośrednie możliwością zignorowania tokena w ciągu zapytania. To oprogramowanie pośrednie jest w 99% takie samo jak domyślne. Wystarczy rzut oka, aby zrozumieć, jak to działa.

Następnie zastąpić

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

z

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

Middleware

<?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); 
    } 
}