delphi 当密码为俄语时,通过HTTPS的授权失败TIdHTTP

2skhul33  于 2023-03-02  发布在  其他
关注(0)|答案(1)|浏览(123)

我尝试用TidHTTP(Indy 10.6.0和 Delphi XE 5)测试我的Web服务,代码如下:

GIdDefaultTextEncoding := encUTF8;
HTTP.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
Http.Request.UserName := AUser;
Http.Request.Password := APass;
Http.Request.Accept := 'text/javascript';
Http.Request.ContentType := 'application/json';
Http.Request.ContentEncoding := 'utf-8';
Http.Request.URL := 'https://sameService';
Http.MaxAuthRetries := 1;
Http.Request.BasicAuthentication := True;
TIdSSLIOHandlerSocketOpenSSL(HTTP.IOHandler).SSLOptions.Method := sslvSSLv3;
HTTP.HandleRedirects := True;

UTF-8中的“AUser”和“APass”。当“APass”具有相同的俄语字符时,我无法登录。通过“HTTP分析”,我看到:

...
Authorization: Basic cDh1c2VyOj8/Pz8/PzEyMw==

从Base 64解码(base64decode.org),我们可以看到:

p8user:??????123

为什么DefStringEncoding不起作用?

unguejic

unguejic1#

TIdHTTP的认证系统没有TIdIOHandler或其DefStringEncoding属性的概念。
在内部,TIdBasicAuthentication使用TIdEncoderMIME.Encode(),但不指定任何编码。TIdEncoder.Encode()默认为8位编码,因此不受GIdDefaultTextEncoding的影响。
如果您需要使用BASIC身份验证发送UTF-8编码的密码,则必须手动编码UTF-8数据并将生成的八位字节存储到string中,然后8位编码器可以按原样处理八位字节,例如:

Http.Request.Password := BytesToStringRaw(IndyTextEncoding_UTF8.GetBytes(APass));

另一方面,例如Indy的DIGEST身份验证使用TIdHashMessageDigest5.HashStringAsHex(),而TIdHash.HashString()并不默认为任何特定的编码,它依赖于GIdDefaultTextEncoding
因此,您必须根据所使用的身份验证来小心编码密码。为了解决这种差异,您可以尝试不对TIdHTTP.Request.Password本身进行编码,而是在使用BASIC身份验证时在TIdHTTP.OnAuthorization事件中对密码进行编码,例如:

Http.Request.Password := APass;

...

procedure TMyForm.HttpAuthorization(Sender: TObject;
  Authentication: TIdAuthentication; var Handled: Boolean);
begin
  if Authentication is TIdBasicAuthentication then
  begin
    Authentication.Password := BytesToStringRaw(IndyTextEncoding_UTF8.GetBytes(TheDesiredPasswordHere));
    Handled := True;
  end;
end;
    • 更新**:

在内部,TIdBasicAuthentication使用TIdEncoderMIME.Encode(),但是没有指定任何编码
TIdBasicAuthentication在2016年更新,现在将编码传递给TIdEncoderMIME.Encode()。当HTTP服务器请求BASIC身份验证时,TIdBasicAuthentication现在会检查服务器的WWW-Authenticate报头是否包含以下属性之一:charsetaccept-charsetencodingenc(按此顺序)。如果找到,则将指定的字符集传递给Encode(),否则使用ISO-8859-1(代码中有一个TODO,用于在用户名或密码包含ISO-8859-1中不存在的任何字符时使用UTF-8)。
如果要确保BASIC身份验证中使用UTF-8,最好将Request.BasicAuthentication设置为False,并使用Request.CustomHeaders提供自己的Authorization头,例如:

Http.Request.BasicAuthentication := False;
Http.Request.CustomHeaders.Values['Authorization'] := 'Basic ' + TIdEncoderMIME.EncodeString(AUser + ':' + APass, IndyTextEncoding_UTF8);

或者,您 * 可能 * 能够在TIdHTTP.OnAuthorization事件(在解析服务器的WWW-Authenticate标头后触发)中更新受保护的TIdBasicAuthentication.FCharset成员,例如:

Http.Request.Password := APass;

...

type
  TIdBasicAuthenticationAccess = class(TIdBasicAuthentication)
  end;

procedure TMyForm.HttpAuthorization(Sender: TObject;
  Authentication: TIdAuthentication; var Handled: Boolean);
begin
  if Authentication is TIdBasicAuthentication then
  begin
    TIdBasicAuthenticationAccess(Authentication).FCharset := 'utf-8';
    Authentication.Password := TheDesiredPasswordHere;
    Handled := True;
  end;
end;

相关问题