typescript - Symfony3 JWT με LexikJWTAuthenticationBundle επιστρέφει 404 σε Preflight OPTIONS


Περιγραφή :

Δουλεύω με το LexikJWTAuthenticationBundle και προσπαθώ να δημιουργήσω ένα JWT token με τα διαπιστευτήρια που αποστέλλονται από την εφαρμογή μου Ionic2.

Η δημιουργία του token λειτουργεί καλά με το Curl και με το Postman.

curl -X POST http://localhost:8000/api/login_check -d _username=root -d _password=root

Ωστόσο, το Ionic2 στέλνει ένα preflight αίτημα OPTIONS πριν στείλει πραγματικά το αίτημα POST με τα διαπιστευτήρια. Αυτό παρεξηγείται από το Symfony3 και επιστρέφει ένα 404.

Σφάλμα Κονσόλας από το Ionic2:

ΕΠΙΛΟΓΕΣ http://localhost:8000/api/login_check 404 (Δεν Βρέθηκε) Το XMLHttpRequest δεν μπορεί να φορτώσει το http://localhost:8000/api/login_check.

Η απόκριση στο αίτημα προ-πτήσης δεν περνά τον έλεγχο πρόσβασης: Δεν υπάρχει επικεφαλίδα 'Access-Control-Allow-Origin' στον ζητούμενο πόρο. Επομένως, η προέλευση 'http://localhost:8100' δεν επιτρέπεται να έχει πρόσβαση. Η απόκριση είχε κωδικό κατάστασης HTTP 404.

0 - {"isTrusted":true}

ΕΞΑΙΡΕΣΗ: 0 - {"isTrusted":true}

Ερώτηση :

Τι πρέπει να αλλάξω στην εφαρμογή μου Symfony3 (ίσως κάτι στο routing.yml ή στο security.yml) για να μην επιστρέφει 404;

Κωδικός :

Ionic2 HttpService:

 @Injectable()
 export class HttpService {

  private baseUrl: string = "http://localhost:8000";

  constructor(private http: Http){

  }

  public create<T>(url: string, extractData, data: any = null): Observable<Array<T>>{

    let headers = new Headers({ 'Content-Type': 'application/json'});
    let options = new RequestOptions({ headers: headers });

    return this.http
      .post(this.baseUrl + url, data, options)
      .map(extractData)
      .catch(this.handleError)
  }
  //More methods here...
 }

Symfony routing.yml:

api_login_check:
    path: /api/login_check
    # This line is something I added to see if OPTIONS would work
    # However, it did not work.
    methods:  [POST, OPTIONS]

Παρόμοιο(α) Ερώτημα(τα):

Η πιστοποίηση JWT του Symfony2 επιστρέφει 404 στο Preflight

Απάντηση #1

Ρίξτε μια ματιά στο https://github.com/nelmio/NelmioCorsBundle για τις κατάλληλες κεφαλίδες. Ο διακομιστής πρέπει να απαντήσει με την κατάλληλη κεφαλίδα απόκρισης Access-Control-Allow-Origin όπως:

Access-Control-Allow-Origin: http://localhost:8100

να επιτραπεί στο αίτημα να πετύχει.

Περισσότερες πληροφορίες για τη συμπεριφορά του CORS: https://www.moesif.com/blog/technical/cors/Authoritative-Guide-to-CORS-Cross-Origin-Resource-Sharing-for-REST-APIs/

Απάντηση #2

Εξήγηση :

Μετά από μια ολόκληρη μέρα που έσπαγα το κεφάλι μου πάνω σε αυτό το θέμα, βρήκα μια λύση. Αν και είναι δυνατό να ενσωματωθεί το NelmioCorsBundle (δεν είμαι σίγουρος αν υποστηρίζει το Symfony3), το βρίσκω υπερβολικό μόνο για να παρακάμψω το πρόβλημα του Cors.

Σε μια πραγματική εφαρμογή για κινητά, δεν θα σταλεί αίτημα OPTIONS preflight. Αυτό συμβαίνει μόνο για ionic serve (από όσο κατάλαβα).

Η ιδέα είναι να στηθεί ένας διακομιστής μεσολάβησης (Proxy Server) για το Ionic2... πιο εύκολο να το λες παρά να το κάνεις. Φαίνεται ότι υπάρχουν πολλά ζητήματα σχετικά με αυτό. Ωστόσο, κατάφερα να τον κάνω να λειτουργεί.

Λύση :

Θα πρέπει να υπάρχει ένα ionic.config.json στο ριζικό κατάλογο του έργου σας Ionic2. Απλώς χρειάζεται να προσθέσετε μερικές γραμμές:

{
  "name": "websurgapp",
  "app_id": "",
  "v2": true,
  "typescript": true,
  //This is what I added.
  "proxies": [{
    "path": "*/api", //<- NOTICE THE */ !
    "proxyUrl": "http://localhost:8000/api"
  }]
}

Στη συνέχεια απλώς στείλτε το αίτημα όπως εξής:

HttpService:

@Injectable()
export class HttpService {http://localhost:8000;
  constructor(private http: Http){
  }
  public create<T>(url: string, data: any = null, contentType: string = 'application/json'): Observable<Array<T>>{
    let headers = new Headers({ 'Content-Type': contentType});
    let options = new RequestOptions({ headers: headers });
    console.log(data);
    return this.http
      .post(url, data, options)
      .map((res:Response)=>res.json())
      .catch(this.handleError)
  }
}

ΥπηρεσίαΤαυτοποίησης:

@Injectable()
export class AuthService {
  constructor(public httpService: HttpService) {
    console.log('Hello AuthService Provider');
  }
  private currentUser: User;
  public login(credentials): any {
    console.log("Login called.");
    //TODO: Upgrade symfony to 3.3 to be able to use JSON.
    let body = new URLSearchParams();
    body.append('_username', credentials._username);
    body.append('_password', credentials._password);
    let result = this.httpService.create("/api/login_check", body, 'application/x-www-form-urlencoded');
    return result;
  }
 }

Σημειώστε ότι δεν στέλνω JSON στο αίτημά μου. Αυτή τη στιγμή δεν υποστηρίζεται από το πακέτο. Ωστόσο, η έκδοση 3.3 του symfony (Εγώ χρησιμοποιώ την 3.1.) το υποστηρίζει.

Αναλύοντάς το:

  1. Η εφαρμογή μου Symfony τρέχει στο http://localhost:8000
  2. Η εφαρμογή μου Ionic 2 τρέχει στο http://localhost:8100
"path": "*/api" 

Απλώς αναφέρει ότι οποιοδήποτε αίτημα στείλουμε που περιέχει "/api/" θα μετατραπεί στο proxyUrl.

Για παράδειγμα, για να δημιουργήσω ένα διακριτικό, χρειάζεται να στείλω τα διαπιστευτήριά μου στο "/api/login_check".

Τι δοκίμασα προηγουμένως:

Προηγουμένως είχα στέλνει το αίτημα στο http://localhost:8000/api/login_check, το οποίο φυσικά θα στέλνει ένα αίτημα OPTIONS προετοιμασίας προκαλώντας ένα σωρό σφάλματα.

Στη συνέχεια προσπάθησα να καθορίσω απλώς "/api/login_check", αλλά το αίτημα http πρόσθετε "http://localhost:8100" μπροστά.. η εφαρμογή μου στέλνοντας ένα αίτημα στον εαυτό της.. το οποίο φυσικά δεν δούλεψε.

Εξηγημένη λύση:

Με τον διακομιστή μεσολάβησης ενεργό, απλώς στέλνω ένα αίτημα στο "/api/login_check" χωρίς τίποτα μπροστά, και αυτό αποστέλλεται στην εφαρμογή μου Symfony3 ως POST request χωρίς κανένα preflight OPTIONS request.