flutter对http组件的网络请求对象的封装,
请求头header中添加令牌参数 authorization
/// This client authenticates requests by injecting `Authentication` header to
/// requests.
///
/// Requests to URLs not under [serverBaseUrl] will not be authenticated.
class _AuthenticatedClient extends http.BaseClient {
/// Constructs Http client wrapper that injects `authorization` header to
/// requests and handles authentication errors.
///
/// [_credential] might be `null`. In that case `authorization` header will not
/// be injected to requests.
_AuthenticatedClient(this._inner, this._credential);
final http.BaseClient _inner;
/// Authentication scheme that could be used for authenticating requests.
final Credential? _credential;
/// Detected that [_credential] are invalid, happens when server responds 401.
bool _detectInvalidCredentials = false;
@override
Future<http.StreamedResponse> send(http.BaseRequest request) async {
// Let's last time make sure that, we're allowed to use credential for this
// request.
//
// This check ensures that this client will only authenticate requests sent
// to given serverBaseUrl. Otherwise credential leaks might ocurr when
// archive_url hosted on 3rd party server that should not receive
// credentials of the first party.
if (_credential != null &&
_credential!.canAuthenticate(request.url.toString())) {
request.headers[HttpHeaders.authorizationHeader] =
await _credential!.getAuthorizationHeaderValue();
}
final response = await _inner.send(request);
if (response.statusCode == 401) {
_detectInvalidCredentials = true;
}
if (response.statusCode == 401 || response.statusCode == 403) {
_throwAuthException(response);
}
return response;
}
/// Throws [AuthenticationException] that includes response status code and
/// message parsed from WWW-Authenticate header usign
/// [RFC 7235 section 4.1][RFC] specifications.
///
/// [RFC]: https://datatracker.ietf.org/doc/html/rfc7235#section-4.1
void _throwAuthException(http.BaseResponse response) {
String? serverMessage;
if (response.headers.containsKey(HttpHeaders.wwwAuthenticateHeader)) {
try {
final header = response.headers[HttpHeaders.wwwAuthenticateHeader]!;
final challenge =
AuthenticationChallenge.parseHeader(header).firstWhereOrNull(
(challenge) =>
challenge.scheme == 'bearer' &&
challenge.parameters['realm'] == 'pub' &&
challenge.parameters['message'] != null,
);
if (challenge != null) {
serverMessage = challenge.parameters['message'];
}
} on FormatException {
// Ignore errors might be caused when parsing invalid header values
}
}
if (serverMessage != null) {
// Only allow printable ASCII, map anything else to whitespace, take
// at-most 1024 characters.
serverMessage = String.fromCharCodes(
serverMessage.runes.map((r) => 32 <= r && r <= 127 ? r : 32).take(1024),
);
}
throw AuthenticationException(response.statusCode, serverMessage);
}
@override
void close() => _inner.close();
}
这段代码定义了一个名为 _AuthenticatedClient 的 Dart 类,它用于包装 http.BaseClient 的实例,以便对 HTTP 请求进行身份验证。它检查给定请求的 URL 是否在指定的服务器基础 URL 下,如果是,则注入一个包含从提供的 Credential 对象获取的凭据的 Authorization 头。如果包装的 http.BaseClient 发送状态码为 401 或 403 的响应,则该类将抛出一个包含状态码和从响应的 WWW-Authenticate 头解析出的消息的 AuthenticationException。
以下是该类的关键组件:
http.BaseClient _inner:这是 _AuthenticatedClient 包装的 http.BaseClient 的实例。它用于发送 HTTP 请求并接收响应。
Credential? _credential:这是一个可选的 Credential 对象,用于存储身份验证请求所需的信息,例如访问令牌或 API 密钥。如果未提供此对象或请求的 URL 不在指定的服务器基础 URL 下,则 _AuthenticatedClient 将不会向请求中注入 Authorization 头。
Future<http.StreamedResponse> send(http.BaseRequest request):该方法使用包装的 _inner 客户端发送 HTTP 请求。在发送请求之前,它检查 _credential 是否可以对请求的 URL 进行身份验证,如果可以,则向请求中注入 Authorization 头。在接收到响应后,它检查响应的状态码是否为 401 或 403,如果是,则抛出一个 AuthenticationException。
_throwAuthException(http.BaseResponse response):该方法抛出一个包含状态码和从响应的 WWW-Authenticate 头解析出的消息的 AuthenticationException。它使用 AuthenticationChallenge 类来解析头,并从 bearer 方案的 realm 和 message 参数中提取消息。
网友评论