c# - 使用 C# 以编程方式登录 Microsoft Online 网站

标签 c# forms web authentication httpwebrequest

我正在尝试编写一个实用程序,它将尝试登录到 Microsoft Online Admin 网站并报告是否可以访问。

主要使用本文中的代码,http://odetocode.com/articles/162.aspx和一些屏幕抓取我拼凑了以下内容。不幸的是它不起作用,最终响应显示我仍在查看登录页面而不是目标页面。

任何帮助都会很棒。提前致谢。

    private void LoginToSite()
    {
        const string LOGIN_URL = "https://admin.microsoftonline.com/Login.aspx";
        const string USERNAME = "<username>";
        const string PASSWORD = "<password>";
        const string TARGET_PAGE_URL = "https://admin.noam.microsoftonline.com/Home/Home.aspx";

        // first, request the login form to get the viewstate value
        HttpWebRequest webRequest = WebRequest.Create(LOGIN_URL) as HttpWebRequest;
        StreamReader responseReader = new StreamReader(
              webRequest.GetResponse().GetResponseStream()
           );
        string responseData = responseReader.ReadToEnd();
        responseReader.Close();

        // extract the viewstate value and build out POST data
        string viewState = ExtractViewState(responseData);
        string postData =
              String.Format(
                 "__VIEWSTATE={0}&AdminCenterLoginControl$UserNameTextBox={1}&AdminCenterLoginControl$PasswordTextbox={2}&__EVENTTARGET=AdminCenterLoginControl_ActionButton",
                 viewState, USERNAME, PASSWORD
              );

        // have a cookie container ready to receive the forms auth cookie
        CookieContainer cookies = new CookieContainer();

        // now post to the login form
        webRequest = WebRequest.Create(LOGIN_URL) as HttpWebRequest;
        webRequest.Method = "POST";
        webRequest.ContentType = "application/x-www-form-urlencoded";
        webRequest.CookieContainer = cookies;

        // write the form values into the request message
        StreamWriter requestWriter = new StreamWriter(webRequest.GetRequestStream());
        requestWriter.Write(postData);
        requestWriter.Close();

        // we don't need the contents of the response, just the cookie it issues
        webRequest.GetResponse().Close();

        // now we can send out cookie along with a request for the protected page
        webRequest = WebRequest.Create(TARGET_PAGE_URL) as HttpWebRequest;
        webRequest.CookieContainer = cookies;
        responseReader = new StreamReader(webRequest.GetResponse().GetResponseStream());

        // and read the response
        responseData = responseReader.ReadToEnd();
        responseReader.Close();

        MessageBox.Show(responseData);
    }

    private string ExtractViewState(string s)
    {
        string viewStateNameDelimiter = "__VIEWSTATE";
        string valueDelimiter = "value=\"";

        int viewStateNamePosition = s.IndexOf(viewStateNameDelimiter);
        int viewStateValuePosition = s.IndexOf(
              valueDelimiter, viewStateNamePosition
           );

        int viewStateStartPosition = viewStateValuePosition +
                                     valueDelimiter.Length;
        int viewStateEndPosition = s.IndexOf("\"", viewStateStartPosition);

        return HttpUtility.UrlEncodeUnicode(
                 s.Substring(
                    viewStateStartPosition,
                    viewStateEndPosition - viewStateStartPosition
                 )
              );
    }

编辑

    private void LoginToSite()
    {
        const string LOGIN_URL = "https://admin.microsoftonline.com/login.aspx?ReturnUrl=%2fDefault.aspx";
        const string USERNAME = "<username>";
        const string PASSWORD = "<password>";

        // Request the login form to get the viewstate value
        HttpWebRequest webRequest = WebRequest.Create(LOGIN_URL) as HttpWebRequest;
        string response1 = new StreamReader(webRequest.GetResponse().GetResponseStream()).ReadToEnd();

        // Extract the viewstate value and build our POST data
        string viewState = ExtractViewState(response1);
        string postData = String.Format(
                 "__VIEWSTATE={0}&AdminCenterLoginControl$UserNameTextBox={1}&AdminCenterLoginControl$PasswordTextbox={2}&__EVENTTARGET=AdminCenterLoginControl_ActionButton",
                 viewState, USERNAME, PASSWORD);

        // Set up the Request properties
        webRequest = WebRequest.Create(LOGIN_URL) as HttpWebRequest;
        webRequest.Method = "POST";
        webRequest.ContentType = "application/x-www-form-urlencoded";
        CookieContainer cookies = new CookieContainer();
        webRequest.CookieContainer = cookies;

        // Post back to the form
        using (StreamWriter requestWriter = new StreamWriter(webRequest.GetRequestStream()))
        {
            requestWriter.Write(postData);
        }

        // Read response
        string response2 = new StreamReader(webRequest.GetResponse().GetResponseStream()).ReadToEnd();

        MessageBox.Show(response2);
    }

最佳答案

MicrosoftOnline.com 似乎不使用 Windows Live ID(又名 Passport)进行登录。这是一种耻辱,因为有可用的库使客户端应用程序登录 LiveID 变得非常简单。

您的代码首先访问登录页面,从响应中删除 cookie,然后尝试导航至目标页面。这与用户行为的正常流程不符。通常情况下,用户点击链接进入目标页面,如果用户未登录,网站将请求重定向到登录页面。登录后,登录页面重定向回最初请求的目标页面。

当您在浏览器中访问 admin.microsoftonline.com 时,您可以通过查看登录 URL 来了解这一点。您会立即被重定向到登录页面,但登录页面上的完整 URL 是:https://admin.microsoftonline.com/login.aspx?ReturnUrl=%2fDefault.aspx

注意末尾的 ReturnUrl 查询参数。这告诉登录页面在登录完成后重定向回哪个页面。

我不知道登录页面是否需要重定向,但由于这是实际最终用户交互(有效)的主要路径,而不是您的代码所采用的路径,因此需要考虑这一点。除其他外,重定向到登录/重定向回目标技术将负责自动为目标域设置浏览器 cookie。

附注我还注意到 Microsoft 在线服务的电子邮件管理部分使用不同的登录 URL。从此页面 (http://www.microsoft.com/online/signin.aspx) 单击 Exchange Hosted Services Administrative Center 链接会将您带到 http:admin.messaging.microsoft.com,它会立即重定向到登录 URL https://sts.messaging.microsoft.com/login.aspx?ReturnUrl=%2fDefault.aspx%3fwa%3dwsignin1.0%26wtrealm%3dhttps%253a%252f%252fadmin.messaging.microsoft.com%26wctx%3drm%253d0%2526id%253dpassive%2526ru%253d%25252f%26wct%3d2010-10-27T17%253a11%253a50Z&wa=wsignin1.0&wtrealm=https%3a%2f%2fadmin.messaging.microsoft.com&wctx=rm%3d0%26id%3dpassive%26ru%3d%252f&wct=2010-10-27T17%3a11%3a50Z

域名 sts.messaging.microsoft.com 表明 Microsoft Online Services 的 Exchange 托管服务部分正在使用安全 token 服务,这表明该登录系统能够进行联合单点登录在不同的服务之间。您可以使用类似 Windows Identity Foundation (WIF) 的方式连接到此客户端组件。这会与 Microsoft 在线服务的其余部分一起使用吗?我不知道。

关于c# - 使用 C# 以编程方式登录 Microsoft Online 网站,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4035638/

相关文章:

javascript - Kentico reCaptcha 不会阻止表单提交

javascript - AngularJS 和外部 API - 让它像 PostMan 一样工作

javascript - getElementById 为 Canvas 元素返回 null

jquery - HTML 表单单选按钮可通过 jquery 提交或隐藏

asp.net-mvc-3 - 通过 Ajax 在 ASP.NET MVC3 中上传文件不起作用

c# - setter 上的“System.StackOverflowException”

C# SQL 插入命令

css - 使用 HTML 和 CSS 溢出到正文中?

c# - 为什么单例不能直接实现 IObjectReference?

c# - Entity Framework 。查询加载了子类属性的父实体