C/S应用程序中进行HTTP登录,获取相应的资料。

来源:互联网 发布:黑马程序员 c 课件 编辑:程序博客网 时间:2024/04/27 23:21

       前段时间处理三星的一个设备,用其的库时,登陆时需要指定设备类型,应用程序里相关的资料里也没有保存设备的类型字段,应用里在我不知道这个设备型号时,问厂家怎么解决,回复我说一个一个的设备类型去连接吧! 很变态呀,几个类型的设备试下来时间很长。由于他的设备支持HTTP,RTSP协议,在HTTP管理时,有一个页面是可以看到其设备类型的,一想,每一种设备都有WEB服务,都是同一样的接口,这样先登录时,我在应用程序里做一个WEB请求来获取其设备型号先,再用TCP去连接,而这个WEB服务是基本一直打开的。

       看了一个其HTTP协议的说明,WEB的设备登录说是用RFC2069协议的"Standard http digest access authentication method of RFC2069"认证,了解的一下RFC2069,再用WireShark跟踪了登录过程, 其实也不是很Standard呀,很多细节不同的。

       Delphi里有一个TIdDigestAuthentication类,就是用来实现这个的,不过这个是标准的,还是和设备的有一定的差异,HTTP请求反证就是一个有特定格式的字符串,所以我做一个类重载了它的Authentication方法:

  TDigestAuth = class(TIdDigestAuthentication)
 private
  FWWWAuthenticate: string;
    FBASEURI: string;
 public
  constructor Create(WWWAuthenticate,buri: string);
  function Authentication: String;override;
  property WWWAuthenticate: string read FWWWAuthenticate write FWWWAuthenticate;
  property BASEURI: string read FBASEURI write FBASEURI;
 end;


自己再在这个方法里生成特定的请求就好了:

function TDigestAuth.Authentication: String;
var
  authtext,AuthorizationStr: string;
  A1,A2,Method: string;
  realm,nonce,uri,qop,cnonce,nc,response: string;
  ipos,Steps: integer;
begin
  authtext := WWWAuthenticate;
  ipos := Pos('nonce="',authtext);
  if ipos > 0 then
  begin
    Delete(authtext,1,ipos+6);
  end;
  ipos := pos('"',authtext);
  if ipos > 0 then
  begin
    nonce := Copy(authtext,1,ipos-1);
  end;
 
  realm := 'iPolis';
  qop := 'auth';
  uri := BASEURI;


  cnonce := '01a34a';
  Steps := 1;
  nc := Format('%.8d',[Steps]);
  A1 := MD5Print(MD5String(UserName + ':' + realm + ':' + Password));
  Method := 'GET';
  A2 := MD5Print(MD5String(Method + ':' + uri));
  response :=MD5Print(MD5String(A1+':'+nonce+':'+ nc + ':'+ cnonce + ':' + qop +':'+A2));


  AuthorizationStr := Format('Digest username="%s",realm="%s",nonce="%s",uri="%s",'+
    'cnonce="%s",nc=%s,response="%s",qop="%s"',
    [UserName,realm,nonce,uri,cnonce,nc,response,qop]);
  Result := AuthorizationStr;
end;


注意上面A1,A2,response的格式组成,这个是RFC上面是有标准的。可以参考这里:http://en.wikipedia.org/wiki/Digest_access_authentication

或下载一个RFC2069的文档看看。

上面这个方法返回的就是最终的请求登录时的HTTP内容了。


在应用的里面我再写一个如下的过程来登录和请求设备的类型:

function TXXXSAM.GetDeviceModelName: string;
  function FromHtmlResponseGetModelName(ResponseText: string): string;
  var
    ts: TStringList;
    i,iPos: integer;
    modelStr: string;
  begin
    modelStr := '';
    Result := '';
    ResponseText := StringReplace(ResponseText,#13#10,'#',[rfReplaceAll]);
    iPos := Pos('model',ResponseText);
    modelStr := Copy(ResponseText,iPos,Length(ResponseText));
    iPos := Pos('#',modelStr);
    Result := Copy(modelStr,7,iPos-7);
  end;
var
  URL,uri:string;
  IdHTTP: TIdHTTP;
  ResponseStream: TStringStream;
  DigestAuth: TDigestAuth;
begin
  Result := '';
  URL := Format('http://%s%s',[IP,SAMHTTPGETPARAM]);
  uri := SAMHTTPGETPARAM;
  IdHTTP := TIdHTTP.Create(nil);
  ResponseStream := TStringStream.Create('');
   try


    try
      ResponseStream.Seek(0,0);
      IdHTTP.Get(URL,ResponseStream);
    except
      ;
    end;
    if (IdHttp.ResponseCode = 401) and  //401 请求登录
      (pos('auth',IdHttp.Response.WWWAuthenticate.Text) > 0) then
    begin
        DigestAuth := TDigestAuth.Create(IdHttp.Response.WWWAuthenticate.Text,uri);
        IdHTTP.Request.Authentication := DigestAuth;
        IdHTTP.Request.Authentication.Username := self.UserName;
        IdHTTP.Request.Authentication.Password := self.PassWord;
        IdHTTP.Request.SetHeaders;
        try
          ResponseStream.Seek(0,0);
          IdHttp.Get(URL,ResponseStream);
        except
          ;
        end;


        if IdHttp.ResponseCode = 404 then //请求的页面不存在:三星的HTTP协议获取参数有多种类型,有的类型的地址不一样
        begin
          URL := Format('http://%s%s',[IP,SAMHTTPGETPARAM2]);
          uri := SAMHTTPGETPARAM2;
          DigestAuth.BASEURI := uri;
          IdHTTP.Request.SetHeaders;
          try
            ResponseStream.Seek(0,0);
            IdHttp.Get(URL,ResponseStream);
          except
            ;
          end;
        end;


        if IdHttp.ResponseCode = 200 then   //成功
          Result := FromHtmlResponseGetModelName(ResponseStream.DataString)
        else if IdHTTP.ResponseCode = 401 then
          LastMessageInfo := 'HTTP请求登录失败'
        else
        begin
          LastMessageInfo := 'HTTP请求信息失败:'+ IdHttp.ResponseText;
          writelog(SAMGetLastError(LastMessageInfo));
        end; 
    end
    else if IdHttp.ResponseCode = 200 then   //成功
    begin
        Result := FromHtmlResponseGetModelName(ResponseStream.DataString);
    end
    else
    begin
      LastMessageInfo := 'HTTP请求信息失败:'+ IdHttp.ResponseText;
      writelog(SAMGetLastError(LastMessageInfo));
    end;
   finally
    ResponseStream.Free;
    IdHTTP.Free;
   end;
end;

原创粉丝点击