-->

Symfony3 JWT with LexikJWTAuthenticationBundle ret

2019-07-28 06:28发布

问题:

Description:

I'm working with the LexikJWTAuthenticationBundle and trying to generate a JWT token with credentials sent from my Ionic2 App.

Generating the token works fine with Curl and with Postman.

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

However, Ionic2 sends a preflight OPTIONS request before actually sending the POST request with the credentials. This is misunderstood by Symfony3 and sends back a 404.

Console Error From Ionic2:

OPTIONS http://localhost:8000/api/login_check 404 (Not Found) XMLHttpRequest cannot load http://localhost:8000/api/login_check.

Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8100' is therefore not allowed access. The response had HTTP status code 404.

0 - {"isTrusted":true}

EXCEPTION: 0 - {"isTrusted":true}

Question:

What must I change in my Symfony3 Application (maybe something in my routing.yml or security.yml) to not send back a 404?

Code:

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]

Similar Question(s):

Symfony2 JWT Authentication Returning 404 on Preflight

回答1:

Take a look at https://github.com/nelmio/NelmioCorsBundle to at the appropriate headers. The server needs to respond with the appropriate Access-Control-Allow origin response header such as:

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

to allow the request to succeed.

More info on CORS behavior: https://www.moesif.com/blog/technical/cors/Authoritative-Guide-to-CORS-Cross-Origin-Resource-Sharing-for-REST-APIs/



回答2:

Explanation:

So after a full day of breaking my head open on this issue, I found a solution. While it is possible incorporate the NelmioCorsBundle (unsure if it supports Symfony3), I find it overkill just to bypass the Cors problem.

In a real world phone application, there won't be an OPTIONS preflight request sent. This only occurs for ionic serve (from what I understood).

The idea is to set up a Proxy Server for Ionic2.. easier said than done. There seems to be a lot of issues regarding it. I've been able to get it working though.

Solution:

There should be an ionic.config.json at the root directory of your Ionic2 project. You just need to add a few lines:

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

Then simply send the request like so:

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

AuthService:

@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;
  }
 }

Notice I do not send JSON in my request. It currently is not supported by the bundle. However, symfony version 3.3. (I'm on 3.1.) does support it.

Breaking it down:

  1. My symfony app runs on http://localhost:8000
  2. My Ionic 2 app runs on http://localhost:8100

"path": "*/api" 

Simply states that whatever request we send out that happens to have a "/api/" in it will be converted to the proxyUrl.

For example, to generate a token, I need to send my credentials to "/api/login_check".

What I previously tried:

I had previously been sending the request to http://localhost:8000/api/login_check, which of course would send a preflight OPTIONS request causing a whole bunch of errors.

I then tried specifiying simply "/api/login_check", but the http request would add "http://localhost:8100" infront.. my app sending a request to itself.. which of course didn't work.

Solution explained:

With the proxy server up, I simply send a request to "/api/login_check" without anything infront, and it gets sent to my symfony3 application as a POST request without any preflight OPTIONS request.