Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Real - World ASP .NET—Building a Content Management System - StephenR. G. Fraser

.pdf
Скачиваний:
68
Добавлен:
24.05.2014
Размер:
4.59 Mб
Скачать

Figure 15-2: The Login User Control

As you can see in the Login User Control design (see Listing 15-3), it takes up as little room as possible yet is clear and functional at the same time.

Listing 15-3: The Login User Control Desi gn

<P> <B>

<FONT color=" darkslategray" size="2"> Account Login</FONT>

</B>

<BR>

<FONT color=" darkslategray" size="2">  Username:</FONT>

<BR>  

<asp:TextBox id=" tbUsername" runat=" server" Width="90%" BackColor=" LightCyan">

</asp:TextBox>

<BR>

<FONT color=" darkslategray" size="2"> Password: </FONT>

<BR>  

<asp:TextBox id=" tbPassword" runat=" server" Width="90%" TextMode=" Password" BackColor=" LightCyan">

</asp:TextBox>

<BR>  

<FONT color=" darkslategray"> <asp:CheckBox id=" cbPersist" runat=" server"

Text=" Remember Login" Font-Size="X-Small">

</asp:CheckBox>

</FONT>

<BR>

 

<asp:ImageButton id=" ibnSignIn" runat=" server"

ImageUrl=" Images/signin.gif">

</asp:ImageButton>

<BR>

 

<asp:ImageButton id=" ibnRegister" runat=" server"

ImageUrl=" Images/register.gif" >

</asp:ImageButton>

</P>

<P>

 

<asp:Label id=" ErrorMsg" runat=" server" ForeColor=" Red">

</asp:Label>

</P>

Another thing you might notice when you look at Figure 15-2 is that the sign-in and register buttons appear to be hyperlinks, but as you can see by the code in Listing 15-3, they are in fact image buttons. I could have used buttons, but I felt that buttons were bulky and distracting and that the hyperlinks better matched the look of the NavBar used by CMS.NET. I could have used a real hyperlink to jump to the registration Web page, but using the image button kept things consistent in the code.

The logic of the Login User Control Codebehind (see Listing 15-4) is handled in the two button event handlers. Because there is no common code that needs to be run by this User Control, the Page_Load() method is empty.

Listing 15-4: The Login User Content Codebehind

private void Page_Load(object sender, System.EventArgs e)

{

}

private void ibnSignIn_Click(object sender, ImageClickEventArgs e)

{

Account account = new Account(new AppEnv(Context).GetConnection());

if (account.Authenticated(tbUsername.Text, tbPassword.Text))

{

FormsAuthentication.SetAuthCookie(tbUsername.Text,

cbPersist.Checked);

Response.Redirect(Request.RawUrl);

}

else

ErrorMsg.Text = account.Message;

}

private void ibnRegister_Click(object sender, ImageClickEventArgs e)

{

Response.Redirect("Register.aspx");

}

The ibnSignIn_Click() method handles the signing in of a Web user. First, it verifies that the user is authentic (or, in other words, has previously registered). If the Web user is authenticated, a temporary session Authentication cookie is created for the user. Also, if the user checked the Remember Login check box, an authentication cookie is placed on the Web user's machine so that the user will not have to log in each time he returns to the site. The method ends by redirecting back to itself. This causes the Web page to reset with any changes due to being authenticated.

The ibnRegister_Click() method is simply a redirect to the registration Web page.

Multipurpose Login.aspx

CMS.NET took the approach that the entire CMS is one system. As a result, CMS.NET was forced to do some magic with the Authentication Web page because only one Authentication Web page is allowed per Web application, and you had already used it to build your administration system.

Another perfectly valid approach would have been to separate the administration application from the content display application and thus get a fresh Authentication Web page to work with.

As you can see in Figures 15-3 and 15-4, the Authentication Web page, better known as the Login page, has two distinct looks, yet they both derive from the same Web page. The Administration Login screen is a little more stark, while the Web User Login provides an explanation of where the user is as well as a way to register if he got here and doesn't have an account to proceed any further.

Figure 15-3: Login for Administration

Figure 15-4: Login for Web site user authentication

Chapter 12 covered the first version of the Login.aspx (see Listing 15-5). If you glance back to that chapter, you will see that not much has been changed in the way of Web design. As you will see, however, significant changes are needed in the Codebehind to allow for this dynamic Login Web page.

Listing 15-5: The Login Web Page Design

<form id=" login" method=" post" runat=" server" >

<IMG src=" Images/login.jpg">

<HR width="100%" SIZE="1">

<TABLE cellSpacing="1" cellPadding="1" width="95%" border="0" >

<TR>

<TD width="25%">

</TD >

<TD>

<H1>

<FONT color=" darkslategray">Login</FONT>

</H1> <P>

<asp:label id=" lbPrompt" runat=" server"></asp:label> </P>

<P>

<asp:validationsummary id=" ValidationSummary1" runat=" server" HeaderText=" The following error(s) occurred while logging in:">

</asp:validationsummary> </P>

<P>

<asp:label id=" ErrorMsg" runat=" server" ForeColor=" Red"> </asp:label>

</P>

<TABLE cellSpacing="1" cellPadding="5" width="300" border="0"> <TR>

<TD width="15%"> <P align=" right">

<STRONG>Username:</STRONG> </P>

</TD>

<TD width="85%">

<asp:textbox id=" tbUsername" runat=" server" Width="100%"> </asp:textbox>

</TD>

<TD width="2%">

<asp:requiredfieldvalidator id=" RequiredFieldValidator1" runat=" server" ControlToValidate=" tbUsername" Display=" Dynamic"

ErrorMessage=" You must enter a Username">*

</asp:requiredfieldvalidator>

</TD>

</TR > <TR> <TD>

<P align=" right">

<STRONG>Password: </STRONG> </P>

</TD> <TD>

<asp:textbox id=" tbPassword" runat=" server" Width="100%" TextMode=" Password" >

</asp:textbox>

</TD>

<TD>

<asp:requiredfieldvalidator id=" RequiredFieldValidator2" runat=" server" ControlToValidate=" tbPassword" Display=" Dynamic"

ErrorMessage=" You must enter a password" >*

</asp:requiredfieldvalidator>

</TD>

</TR >

<TR>

<TD colSpan="3">

<P align=" center">

<asp:checkbox id=" cbPersist" runat=" server"

Text=" Remember Login">

</asp:checkbox>

</P>

</TD>

</TR >

<TR>

<TD colSpan="3">

<P align=" center">

<asp:button id=" bnLogin" runat=" server" Text=" Login">

</asp:button>

 

<asp:button id=" bnRegister" runat=" server" Text=" Register"

Visible=" False" CausesValidation=" False">

</asp:button>

</P>

</TD>

</TR >

</TABLE>

</TD>

</TR >

</TABLE >

</form>

The design has only two changes. The first change is the addition of a label so that the Login Web page will be able to provide an explanation of where the user has been teleported. You need to remember that the user is expecting to go to a specific Web page, and all of a sudden, she is presented with this Login screen. Without a little bit of explanation, some users may get flustered and leave, and of course, that is the last thing you want to happen.

The other change to the Web design is the addition of the Register button; when clicked, it will cause the user to jump to the registration Web page. This button is a little different than the others you have seen so far. If you take a quick peek at the design code, you will notice two unusual attributes. The first is the Visible attribute. This attribute is a

way of adding a button that you may not want to display right away or that usually isn't displayed. It is safe to place a button like this on a Web page because an invisible button is not created by the ASP.NET parser and thus is not even placed on the Web page. Because it is not on the Web page, the user has no way to access it. Chapter 11 showed

how you can set the attribute in the Codebehind—this is how you do it in the design code.

The second attribute, CauseValidation, is new to CMS.NET. This handy attribute tells the Web page whether or not to perform form validations on the Web page. The default value is true, which is why you have not seen it until now. With the Login Web page, on the other hand, if you don't set this attribute to false for the Register button, validation occurs when the button is clicked. Because the username and password are empty, the validation will fail, and the button will not execute its code to go to the registration Web page. Instead, the Login page will be presented again, asking for a username and password. This is, obviously, not what you want to happen.

The Login Web page can be accessed in three distinct ways:

§Automatically by ASP.NET in the administration system

§Automatically by ASP.NET in the content display application

§Directly called by CMS.NET in the content display application

The Login Codebehind (see Listing 15-6) handles these three ways differently.

Listing 15-6: The Login Web Page Codebehind

private void Page_Load(object sender, System.EventArgs e)

{

URL = Request.QueryString["URL"];

if (URL != null)

URL.Trim();

if (!IsPostBack)

{

if (FormsAuthentication.GetRedirectUrl("",

false).IndexOf("admin.aspx") < 0)

{

lbPrompt.Text = "<h2>Accessing Protected Content.</h2>" +

"<h3>Login required.</h3>"; bnRegister.Visible = true;

}

}

}

private void bnRegister_Click(object sender, System.EventArgs e)

{

Response.Redirect("CDA/Register.aspx");

}

private void bnLogin_Click(object sender, System.EventArgs e)

{

if (Page.IsValid)

{

Account account = new Account(new AppEnv(Context).GetConnection());

if (account.Authenticated(tbUsername.Text, tbPassword.Text))

{

if (URL != null && URL.Length > 0)

{

FormsAuthentication.SetAuthCookie(tbUsername.Text,

cbPersist.Checked);

Response.Redirect(URL);

}

else FormsAuthentication.RedirectFromLoginPage(tbUsername.Text,

cbPersist.Checked);

}

else

ErrorMsg.Text = account.Message;

}

}

The Page_Load() method is mainly in charge of figuring out in which of the three ways the Web page was accessed. It then displays the Login in the appropriate fashion for that access method. It does this in a two-part process. First, it checks to see if it received a URL in the Request. When this happens, it means that Login was called directly by CMS.NET.

Second, it checks to see what Web page caused the redirect to the Login Web page using the FormsAuthentication.GetRedirectUrl() method. This method returns the full URL of the Web page that was supposed to be the destination of the last request, before it was trapped and sent to the Login Web page due to the user not being authenticated. If Login.aspx was called directly and was not the result of a user authentication trap, the GetRedirectURL() method returns Default.aspx.

CMS.NET knows that the Login Web page was automatically called from the administration system when the GetRedirectURL() method returns a URL containing the string admin.aspx

Now that CMS.NET knows how it was called, the Page_Load() method can continue and display the Login Web page appropriately.

The bnLogin_Click() method's job is to authenticate the user (by checking the username and password) and then create an authentication certificate (cookie). How the cookie is created depends on how the Login Web page was called. If it was automatically called by ASP.NET, CMS.NET uses the RedirectFromLoginPage() method, which creates the authentication cookie and then redirects it to the Web page that it was originally going to before it was intercepted by ASP.NET. On the other hand, if the Login Web page was called directly from CMS.NET, it uses the SetAuthCookie() method, which only creates the cookie. CMS.NET then uses the value of the URL variable that is received in the Request to redirect the Web page to its intended destination.

Logging Off

Because it is possible for the user to log in and store his login authentication cookie on his machine, it is a good idea to provide a way to remove this cookie. CMS.NET does this with the Logout User Control. As you can see in Figure 15-5, the Logout User

Control is simply an image button that replaces the Login button on the NavBar after the user has logged in.

Figure 15-5: The Logout User Control

The Logout Codebehind (see Listing 15-7) has nothing you have not covered before. It calls the FormsAuthentication.SignOut() method, which deletes the authentication cookie and then redirects the user back to the home page.

Listing 15-7: The Logout User Content Codebehind

private void ibnLogout_Click(object sender, ImageClickEventArgs e)

{

FormsAuthentication.SignOut();

Response.Redirect("Default.aspx");

}

Restricting Content to Registered Users

The changes required to add protection to the current version of CMS.NET are actually very minor. In all cases, it is just a few lines of code. This is no coincidence, though, because I designed CMS.NET with protection in mind.

To implement protection, CMS.NET uses ASP.NET's built-in authentication functionality. Basically, whenever a user accesses protected content, he is forced to run Web pages in a new directory with authentication enabled. Because authentication is on, only users who are authenticated can continue and view the content; all others will be interrupted with a Login screen that asks for a username and password.

Database Updates

The first things that need to be updated are the Content, Zone, and Domain database tables. Each needs a way to specify whether a row in its table should be protected or not. To do this is actually very easy. All you need to do to each of these database tables is add a Protected column, similar to what is shown in Table 15-1.

Table 15-1: The Protected Database Column

PROPERTY

Column Name

Data Type

Length

Key

Allow Nulls

Description

VALUE

Protected

int

4

false

false

When this value is greater than 0, the column is protected.

Adding the column sounds easy enough; the only problem is the Allow Nulls property. If you already have data in the table, adding this column causes an error when being saved because null values are found in the new Protected column. This happens because when a new column is added to a database, it is null filled.

To fix this problem, you have to create the column in three steps:

1.Create the Protected column with Allow Nulls set temporarily to a value of true.

2.Manually set all Protected columns in the database table to a value of 0 (or 1 if you want it protected).

3.Update the database definition of the Protected column by setting Allow Nulls to its proper value of false.

Updating the Domain and Zone Databases

The final version of CMS.NET will have an automated way to create and maintain Domain and Zone databases, but for now, the only way to populate these tables is via Visual Studio .NET's Server Explorer (see Figure 15-6).

Figure 15-6: Updating the Domain database table

The only difference (as far as a Domain or Zone database table is concerned) between a protected row and an unprotected row is that the Protected column of the row will have a value other than zero. For future explanation, I thought it a good idea to allow more than just a true/false value in this column. This will allow different levels of protection or different areas of access, depending on the user or group of users.

Automatic Protection of Content During Deployment

There is no way to protect a particular piece of content in CMS.NET. Instead, CMS.NET makes the assumption that if a piece of content is deployed to a zone that is protected, the piece of content is also protected. This assumption also stipulates that if it is protected in one zone, it is protected in all zones. I think this seems logical because allowing it to be read in one place while it is protected in another defeats the purpose of protecting the content in the first place.

The content protection process takes place in the DeployDeploy Codebehind method (see Listing 15-3). There is nothing much to the code. All the method does is check to see if the zone to which it is distributed is protected. If the zone is protected, it sets a flag that is later checked and, if true, protects the content but sets the Protected column to 1

Соседние файлы в предмете Программирование