开发者

how can i automate calling a url on a password protected asp.net-mvc site

开发者 https://www.devze.com 2023-04-13 05:57 出处:网络
i have an asp.net-mvc site with sqlserver backend and i am using membershipprovider for login, etc. I have a few automated things that i want to run daily or weekly as i can do this today if i:

i have an asp.net-mvc site with sqlserver backend and i am using membershipprovider for login, etc.

I have a few automated things that i want to run daily or weekly as i can do this today if i:

  1. Log In
  2. call URL

so lets say the URL is

www.mysite.com/MyController/RunCleanupScript

I know some people will suggest breaking the code of RunCleanupScript into a standalone script outside of the website but i wanted to see if there was a solution 开发者_开发百科to automating the equivalent to the manual login and then entering in this url to call this script?


Phil Haak has a post about a solution which may work for you - he also warns of the dangers associated. You could use this method to schedule the clean up task. If you move your clean-up code out of the controller then there is no need for the login - it can never be called externally. If you need to still be able to login and force the clean up, then moving the clean up code out of your controller is still the way to go. Your secured action and the scheduler code will both call the clean-up code.

Another option could be to create a windows service that hits the action and stores the required credentials in its config file.


Forms auth together with some scripts calling web pages to aquire a cookie may not be the most stable and maintainable approach for your requirements.

You could support basic auth that makes passing username and password from a script easy. For an example how to implement basic auth in asp.net mvc see this blog post.


You could write a console application which will perform 2 HTTP requests: first to login and second to fetch the protected resource:

using System;
using System.Collections.Specialized;
using System.Net;

public class WebClientEx: WebClient
{
    private readonly CookieContainer _cookieContainer = new CookieContainer();

    protected override WebRequest GetWebRequest(Uri address)
    {
        var request = base.GetWebRequest(address);
        ((HttpWebRequest)request).CookieContainer = _cookieContainer;
        return request;
    }
}

class Program
{
    static void Main()
    {
        using (var client = new WebClientEx())
        {
            var values = new NameValueCollection
            {
                { "username", "user" },
                { "password", "pwd" },
            };
            // Login
            client.UploadValues("http://example.com/account/logon", values);

            // Fetch the protected resource
            var result = client.DownloadString("http://example.com/home/foo");
            Console.WriteLine(result);
        }
    }
}


This code will login to a FormsAuthentication site, then use the AUTH cookie to hit any other URL on the site...

string appURL = "https://.../LogOn";

// UserName and Password should match the names of the inputs on your form
string strPostData = String.Format("UserName={0}&Password={1}", "login", "pass");

Cookie authCookie;
CookieContainer cookieJar = new CookieContainer();

// Prepare post to the login form
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(appURL);

req.Method = "POST";
req.ContentLength = strPostData.Length;
req.ContentType = "application/x-www-form-urlencoded";
req.CookieContainer = cookieJar;
req.AutomaticDecompression = DecompressionMethods.GZip
                             | DecompressionMethods.Deflate;

// Proxy - Optional
// req.Proxy.Credentials = CredentialCache.DefaultCredentials;

// Post to the login form.
StreamWriter swRequestWriter = new StreamWriter(req.GetRequestStream());
swRequestWriter.Write(strPostData);
swRequestWriter.Close();

// Get the response.
HttpWebResponse hwrWebResponse = (HttpWebResponse)req.GetResponse();


// Store the required AUTH cookie
authCookie = cookieJar.GetCookies(new Uri("... your cookie uri ..."))[".ASPXAUTH"];

Now you can access any other URL of the site using the AUTH cookie.

HttpWebRequest req = (HttpWebRequest)WebRequest.Create("... url ...");

req.CookieContainer.Add(new System.Net.Cookie(authCookie.Name,
                          authCookie.Value,
                          authCookie.Path, "localhost"));

HttpWebResponse resp = (HttpWebResponse) req.GetResponse();


PowerShell might be a good option for you. Here's a sample that demonstrates how you would post form values to the log-on page and then use the response cookie to make a second call to the admin page.

Note, I borrowed much of this sample from this post.

$LogonUrl = "http://yoursite.com/Account/LogOn"
$UserName = "AdminUser"
$Password = "pass@word1"
$AdminUrl = "http://yoursite.com/MyController/RunCleanupScript"

$cookies = New-Object System.Net.CookieContainer
$formData = "UserName=" + $UserName + "&Password=" + $Password

[net.httpWebRequest] $web1 = [net.webRequest]::create($LogonUrl)
$web1.method = "POST"
$web1.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
$web1.Headers.Add("Accept-Language: en-US")
$web1.Headers.Add("Accept-Encoding: gzip,deflate")
$web1.Headers.Add("Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7")
$web1.AllowAutoRedirect = $false
$web1.ContentType = "application/x-www-form-urlencoded"
$buffer = [text.encoding]::ascii.getbytes($formData)
$web1.ContentLength = $buffer.length
$web1.TimeOut = 50000
$web1.KeepAlive = $true
$web1.Headers.Add("Keep-Alive: 300");
$web1.CookieContainer = $CookieContainer

$reqStrm = $web1.getRequestStream()
$reqStrm.write($buffer, 0, $buffer.length)
$reqStrm.flush()
$reqStrm.close()
[net.httpWebResponse] $response = $web1.getResponse()

$respStrm = $response.getResponseStream()
$reader = new-object IO.StreamReader($respStrm)
$result = $reader.ReadToEnd()
$response.close()

$web2 = new-object net.webclient
$web2.Headers.add("Cookie", $response.Headers["Set-Cookie"])
$result = $web2.DownloadString("$AdminUrl")

Write-Output $result

This could also easily be turned into a Windows Console app as well. Either way, they are easy to schedule with Task Scheduler.

Hope this helps.


Why don't you give WatiN or Selenium a try? You can set up a login step very easy and then test if the other RunCleanupScript page is working properly.

WatiN's main page example:

[Test] 
public void SearchForWatiNOnGoogle()
{
  using (var browser = new IE("http://www.google.com"))
  {
    browser.TextField(Find.ByName("q")).TypeText("WatiN");
    browser.Button(Find.ByName("btnG")).Click();

    Assert.IsTrue(browser.ContainsText("WatiN"));
  }
}

You can then have something like:

[Test] 
public void TestRunCleanupScript()
{
  using (var browser = new IE("www.mysite.com/MyController/RunCleanupScript"))
  {
    DoLogin(browser)
    //navigate to cleanupscript page      
    //your assert
  }
}

public void DoLogin(browser)
{
  //navigate to login
  //type username and password and hit button
}


I am currently doing this in a production environment. In my case the solution was a no-brainer since MADAM had already been installed in order to allow normal RSS Readers to securely access RSS feeds on the site.

The trick to doing this is to enable Basic Authentication for the pages you want to call automatically using any external processes, that opens you up to a huge number of ways to access the site automatically; this VBScript file, for instance calls the maintenance URL and checks whether the response from the server is exactly SUCCESS.

Option Explicit

Dim result
result = PerformMaintenance("http://www.mysite.com/MyController/RunCleanupScript")
WScript.Quit(result)

Function PerformMaintenance(URL)

  Dim objRequest

  Set objRequest = CreateObject("Microsoft.XmlHttp")

  'I use a POST request because strictly speaking a GET shouldn't change anything on the server.
  objRequest.open "POST", URL, false, "LimitedDaemonUser", "SecretDaemonPassword"
  objRequest.Send

  if (objRequest.ResponseText = "SUCCESS") Then
    PerformMaintenance = 0
  Else
    PerformMaintenance = 1
  End If

  set objRequest = Nothing

End Function

Basic Authentication is easy enough to get working. Just include MADAM with your project, and configure it in your Web.config.

Adding these Web.config sections/parameters (IIS6) should get your example request working if you use a standard MembershipProvider. You just have to change MyNamespace.MembershipUserSecurityAuthority to a reference to an actual class. The source code for MembershipUserSecurityAuthority is included with MADAM in the demo web application's App_Code folder.

<configuration>
<configSections>
    <sectionGroup name="madam">
      <section name="userSecurityAuthority" type="System.Configuration.SingleTagSectionHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      <section name="formsAuthenticationDisposition" type="Madam.FormsAuthenticationDispositionSectionHandler, Madam" />
    </sectionGroup>
</configSections>
  <madam>
    <userSecurityAuthority realm="MyRealm" provider="MyNamespace.MembershipUserSecurityAuthority, MyNamespace" />
    <formsAuthenticationDisposition>
      <discriminators all="false">
        <discriminator inputExpression="Request.AppRelativeCurrentExecutionFilePath" pattern="~/MyController/RunCleanupScript$" type="Madam.RegexDiscriminator, Madam" />
        </discriminators>
    </formsAuthenticationDisposition>
  </madam>
  <system.web>
    <httpModules>
      <add name="FormsAuthenticationDisposition" type="Madam.FormsAuthenticationDispositionModule, Madam" />
      <add name="AuthenticationModule" type="Madam.BasicAuthenticationModule, Madam" />
    </httpModules>
  </system.web>
</configuration>
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号