Dart HttpClient with Basic Authentication issues a

2019-06-12 10:38发布

问题:

After a lot of browsing and reading docs, I came across this answer, which worked until I upgraded flutter.

My current issue is that the credentials do not reach my server. IntelliJ error:

I/FlutterActivityDelegate( 6944): onResume setting current activity to this
D/EGL_emulation( 6944): eglMakeCurrent: 0xa7f852a0: ver 2 0 (tinfo 0xa7f83250)
I/flutter ( 6944): doing login with credentials:: W and T
I/flutter ( 6944): my_response is:: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
I/flutter ( 6944): <title>429 Too Many Requests</title>
I/flutter ( 6944): <h1>Too Many Requests</h1>
I/flutter ( 6944): <p>1 per 1 minute</p>
E/flutter ( 6944): [ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception:
E/flutter ( 6944): type '(Exception) => void' is not a subtype of type '(Object) => FutureOr<dynamic>'

My server output for the request is:

 credentials::  - 
 192.168.1.175 - - [12/May/2018 10:56:23] "GET /users/login HTTP/1.1" 401 -
 192.168.1.175 - - [12/May/2018 10:56:23] "GET /users/login HTTP/1.1" 429 -

LE: the "credentials:: - " is a print done in the authentication level of the server. LLE: a curl request is working just fine

curl -u 123456:password http://localhost:5000/users/login 
{
 "is_logged_in": true, 
 "last_login": 1525980360
}

Response is:

credentials:: 123456 - password
127.0.0.1 - - [12/May/2018 13:00:50] "GET /users/login HTTP/1.1" 200 -

I am using the exact same code as in the link provided above.

回答1:

Here's a summary answer wrapping up the discussion in the comments.

It seems like the Too Many Requests error may have been triggered by the typical behaviour of HTTP client talking to server that requires authorization.

GET ->
    <- 401 Unauthorized + scheme + realm (and nonce if applicable)
GET with Authorization header ->
    <- 200 OK (if credentials are valid)

It seems like the server may have counted the first GET/401 towards some sort of limit of requests per minute.

However, a request from curl was successful, probably because curl preemptively sent the Authorization header with the first GET, instead of waiting to be asked with a 401. It can do this as long as the authorization scheme is Basic because it doesn't need any information from the server. (It wouldn't work with Digest because it cannot calculate the Authorization header without the nonce sent with the 401.)

So the question arises, is there a way to preemptively send Basic auth in package:http?

Normal way - only send in response to a 401

// create an IOClient with authentication
HttpClient inner = new HttpClient();
inner.authenticate = (uri, scheme, realm) {
  inner.addCredentials(
      uri, realm, new HttpClientBasicCredentials(username, password));
};
http.IOClient client = new http.IOClient(inner);

// and use it like this
http.Response response = await client.get('https://api.somewhere.io');
// todo - handle the response

Preemptive way - only works with Basic authentication

// use the normal http.get method, but add the header manually, with every request
http.Response response = await http.get(
  'https://api.somewhere.io',
  headers: {'authorization': basicAuthenticationHeader(username, password)},
);
// todo - handle the response

with the utility method

String basicAuthenticationHeader(String username, String password) {
  return 'Basic ' + base64Encode(utf8.encode('$username:$password'));
}


标签: dart flutter