1. Trang chủ
  2. » Công Nghệ Thông Tin

Apress Expert C sharp 2005 (Phần 12) docx

50 276 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 50
Dung lượng 0,96 MB

Nội dung

If you want to use Windows authentication, change the configuration to this: <add key="CslaAuthentication" value="Windows" /> Of course, that change would require coding changes. To start, the PTPrincipal and PTIdentity classes should be removed from ProjectTracker.Library, as they would no longer be needed. Also, the login/logout functionality implemented in this chapter would become unnecessary. Specifically, the Login form and the code to display that form would be removed from the UI project. Local Data Portal The web.config file also controls how the application uses the data portal. To have the website interact directly with the database, use the following (with your connection string changed to the connection string for your database): <?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="CslaAuthentication" value="Csla" /> </appSettings> <connectionStrings> <add name="PTracker" connectionString="your connection string" providerName="System.Data.SqlClient" /> <add name="Security" connectionString="your connection string" providerName="System.Data.SqlClient" /> </connectionStrings> Because LocalProxy is the default for the data portal, no actual data portal configuration is required, so the only settings in the configuration file are to control authentication and to provide the database connection strings. ■Tip In the code download for this book, the PTracker and Security database files are in the solution directory, not in the website’s App_Data directory. This means that you can’t use a local data portal from the website without first copying the database files into the App_Data directory and changing the connection strings accordingly. Remote Data Portal (with Remoting) To have the data portal use an application server and communicate using the remoting channel, the configuration would look like this: <?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="CslaAuthentication" value="Csla" /> <add key="CslaDataPortalProxy" value="Csla.DataPortalClient.RemotingProxy, Csla"/> <add key="CslaDataPortalUrl" value="http://localhost/RemotingHost/RemotingPortal.rem"/> </appSettings> <connectionStrings> </connectionStrings> The key lines for the remoting configuration are in bold. Of course, you need to change localhost to the name of the application server on which the data portal host is installed, and the RemotingHost text needs to be replaced with the name of your virtual root on that server. CHAPTER 10 ■ WEB FORMS UI524 6323_c10_final.qxd 2/27/06 1:33 PM Page 524 Before using this configuration, the remoting host virtual root must be created and configured. I’ll show how this is done in Chapter 12. Remote Data Portal (with Enterprise Services) Similarly, to use the Enterprise Services channel the configuration would look like this: <?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="CslaAuthentication" value="Csla" /> <add key="CslaDataPortalProxy" value="EnterpriseServicesHost.EnterpriseServicesProxy, EnterpriseServicesHostcs"/> </appSettings> <connectionStrings> </connectionStrings> Before using this configuration, an Enterprise Services host must be created and registered with COM+. The resulting COM+ application must be registered with COM on each client work- station. The basic steps were discussed in Chapter 4, and I’ll show how this is done in Chapter 12. Remote Data Portal (with Web Services) Finally, to use Web Services, the configuration would look like this: <?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="CslaAuthentication" value="Csla" /> <add key="CslaDataPortalProxy" value="Csla.DataPortalClient.WebServicesProxy, Csla"/> <add key="CslaDataPortalUrl" value="http://localhost/WebServicesHost/WebServicePortal.asmx"/> </appSettings> <connectionStrings> </connectionStrings> As with remoting, you need to change localhost and WebServicesHost to the actual server name and virtual root name used by your application. Also, the virtual root and web service asmx file must be created and configured. I’ll show how this is done in Chapter 12. The most important thing to realize about the site configuration is that the data portal can be changed from local to remote (using any of the network channels) with no need to change any UI or business object code. PTWeb Site Setup The UI application can be found within the ProjectTracker solution. The project is named PTWeb. The site references the ProjectTracker.Library project, as shown in Figure 10-8. This causes Visual Studio to automatically put the associated Csla.dll files into the Bin directory as well, because Csla.dll is referenced by ProjectTracker.Library. CHAPTER 10 ■ WEB FORMS UI 525 6323_c10_final.qxd 2/27/06 1:33 PM Page 525 Hosting in IIS The PTWeb website will only run within IIS, not within the ASP.NET Development Server (commonly known as Cassini or VS Host). The reason for this is explained later in the chapter in the “Forms- Based Authentication” section. To host a website in IIS during development, you need to take the following steps: 1. Set up a virtual root in IIS that points to the directory containing the PTWeb project files. 2. Set the virtual root to use ASP.NET 2.0, using the ASP.NET tab of the virtual root properties dialog in the IIS management console. 3. Set the website’s start options using the project properties dialog in Visual Studio 2005. Change the setting to use a custom server so it starts up using IIS with a URL such as http://localhost/PTWeb. With the basic website setup complete, let’s go through the creation of the Web Forms UI. First, I’ll discuss the use of a master page, and then I’ll cover the process of logging a user in and out using forms-based authentication. With the common code out of the way, I’ll discuss the process of maintaining the roles and project data in detail. At that point, you should have a good understanding of how to create both grid-based and detail pages. Master Page To ensure that all pages in the site have the same basic layout, navigation, and authentication options, a master page is used. The master page provides these consistent elements, and all the rest of the pages in the site are content pages. This means they fit within the context of the master page itself, adding content where appropriate. CHAPTER 10 ■ WEB FORMS UI526 Figure 10-8. Referencing ProjectTracker.Library 6323_c10_final.qxd 2/27/06 1:33 PM Page 526 Look back at Figures 10-6 and 10-7 to see the visual appearance of the pages. Both Default.aspx and ProjectEdit.aspx are content pages, adding their content to that already provided by MasterPage.master: <%@ Master Language="C#" CodeFile="MasterPage.master.cs" Inherits="MasterPage" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head id="Head1" runat="server"> <title>Untitled Page</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> </head> <body> <form id="form1" runat="server"> <div id="mainTable"> <div id="header"> <asp:Label ID="PageTitle" runat="server"> </asp:Label> </div> <div id="navigation"> <div id="navigationContent"> <asp:TreeView ID="TreeView1" runat="server" DataSourceID="SiteMapDataSource1" ShowExpandCollapse="False" SkipLinkText="" > <NodeStyle CssClass="nav" /> </asp:TreeView> </div> </div> <div id="subnavigation"> <div id="logout"> <asp:LoginStatus ID="LoginStatus1" runat="server" OnLoggingOut="LoginStatus1_LoggingOut" /> </div> </div> <div id="content"> <asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server"> </asp:ContentPlaceHolder> </div> </div> <asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" ShowStartingNode="False" /> </form> </body> </html> MasterPage.master defines the header/title bar at the top of the page. The area immediately beneath the header/title bar contains the Login button, and there is a navigation area down the left. Perhaps most importantly, it also defines a content area containing a ContentPlaceHolder control: <asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server"> </asp:ContentPlaceHolder> This is the area where content pages provide their content, and it is the main body of the page. You’ll see how each content page provides content for this area later in the chapter. CHAPTER 10 ■ WEB FORMS UI 527 6323_c10_final.qxd 2/27/06 1:33 PM Page 527 Theme Support ASP.NET 2.0 supports the concept of themes for a website, where the visual appearance of the site is defined by a theme: a group of files in a theme-specific subdirectory beneath the App_Themes direc- tory in the virtual root. A theme is a group of style sheets, graphics, and control skins that describe the appearance of a site. A given site can have many themes, and you can even allow the user to choose between them if you so desire. Notice how all of the regions in the master page are set up using div tags. No appearance char- acteristics are specified in the page itself. Instead, the actual appearance is defined by a CSS style sheet contained within the current theme for the site. The PTWeb site includes and uses a Basic theme. The use of the Basic theme is set up in web.config: <pages theme="Basic" styleSheetTheme="Basic"> The theme property sets the default runtime theme, while styleSheetTheme sets the theme for use at design time in Visual Studio. The styleSheetTheme property should be removed when the website is deployed to a production server. The files defining this theme are in the App_Themes/Basic folder beneath the virtual root. Yo u should notice that the names of the css and skin files match the name of the theme folder itself. Having the names match allows ASP.NET to automatically realize that it needs to use these files when the theme is selected for the website. The files in this theme are listed in Table 10-4. Table 10-4. Files in the Basic Theme File Description Basic.css The style sheet for the site Basic.skin The skins for GridView, DetailsView, and Login controls Images\background.jpg The background graphic for the header region Images\corner.png The graphic for the rounded corner in the upper-left Combined, these files define the look and feel of the site. This includes defining the appearance of the regions in MasterPage.master. For instance, the header region is defined in the css file like this: #header { background-image: url('images/background.jpg'); background-repeat: no-repeat; height: 64px; line-height: 60px; text-align: left; color: #FFFFFF; font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 36px; font-weight: bold; font-style: italic; padding-left: 10px } A control skin defines the appearance of specific controls in the website, such as GridView, TextBox, and so forth. For instance, the appearance of the Login control is defined in the skin file like this: CHAPTER 10 ■ WEB FORMS UI528 6323_c10_final.qxd 2/27/06 1:33 PM Page 528 <asp:Login runat="server" BackColor="#DEDEDE" BorderColor="Black" BorderStyle="Solid" BorderWidth="1px" Font-Names="Verdana" Font-Size="10pt"> <TitleTextStyle BackColor="Black" Font-Bold="True" Font-Names="Verdana" Font-Size="10pt" ForeColor="White" /> </asp:Login> Each type of control in Web Forms has different options you can set in a skin file, allowing you to set the appearance of each control in many ways. By making the site theme-enabled, you can easily change the appearance of the site later by creating a new theme directory and similar theme files, and setting the theme property in web.config to use the new theme. Header Region The header region of the page is the title area across the top. It contains a single Label control named PageTitle. This control displays the title of the current content page, based on the Title property set for that page. The following code is included in MasterPage.master to load this value: protected void Page_Load(object sender, EventArgs e) { PageTitle.Text = Page.Title; } As each content page loads, not only does the Load event for the content page run, but so does the Load event for the master page. This means that code can be placed in the master page to run when any content page is loaded—in this case, to set the title at the top of the page. Navigation Region The navigation region displays the navigation links down the left side of each page. To do this, a web.sitemap file and associated SiteMapDataSource control are used to load the overall structure of the site into memory. This data is then data bound to a TreeView control for display to the user. The web.sitemap file is an XML file that contains a node for each page to be displayed in the navigation region: <?xml version="1.0" encoding="utf-8" ?> <siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" > <siteMapNode url="" title="" description=""> <siteMapNode url="~/Default.aspx" title="Home" description="Main page" /> <siteMapNode url="~/ProjectList.aspx" title="Project list" description="Project list" /> <siteMapNode url="~/ResourceList.aspx" title="Resource list" description="Resource list" /> <siteMapNode url="~/RolesEdit.aspx" title="Project roles" description="Project roles" /> </siteMapNode> </siteMap> The site map concept can be used to define hierarchical website structures, but in this case, I’m using it to define a flat structure. Notice how each <siteMapNode> element defines a page— except the first one. That root node is required in the file, but since I’m defining a flat structure, it really doesn’t represent a page and is just a placeholder. If you were to define a hierarchical page structure, that node would typically point to Default.aspx. CHAPTER 10 ■ WEB FORMS UI 529 6323_c10_final.qxd 2/27/06 1:33 PM Page 529 Notice that MasterPage.master includes a SiteMapDataSource control: <asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" ShowStartingNode="False" /> This special data control automatically reads the data from the web.sitemap file and makes it available to controls on the page. The ShowStartingNode property is set to False, indicating that the root node in web.sitemap is to be ignored. That’s perfect, because that node is empty and shouldn’t be displayed. In this case, a TreeView control in the navigation region is bound to the SiteMapDataSource, so it displays the items listed in web.sitemap to the user. LoginStatus Control In the subnavigation region of MasterPage.master, you’ll see a LoginStatus control: <asp:LoginStatus ID="LoginStatus1" runat="server" OnLoggingOut="LoginStatus1_LoggingOut" /> This is one of the login controls provided with ASP.NET 2.0, and its purpose is to allow the user to log into and out of the site. The control automatically displays the word Login if the user is logged out, and Logout if the user is logged in. When clicked, it also automatically redirects the user to a login web page defined in web.config. I’ll cover the web.config options later. Because the control automatically directs the user to the appropriate login page to be logged in, no code is required for that process. However, code is required to handle the case in which the user clicks the control to be logged out. This code goes in the master page: protected void LoginStatus1_LoggingOut( object sender, LoginCancelEventArgs e) { ProjectTracker.Library.Security.PTPrincipal.Logout(); Session["CslaPrincipal"] = Csla.ApplicationContext.User; System.Web.Security.FormsAuthentication.SignOut(); } This code covers a lot of ground. First, the Logout() method of PTPrincipal is called, which sets the current principal on the current Thread object to an unauthenticated PTPrincipal object. This was discussed in Chapter 8 and used in PTWin in Chapter 9. However, when the user is logged in, their principal object is stored in a Session field so it can be easily reloaded on every page request. The details on how this works are discussed later in the chapter. When the user logs out, that Session field is updated to reference the new principal object. ■Note If you want to avoid Session, you can choose to reload the user’s identity and roles from the security database on every page request. While that avoids the use of Session, it can put a substantial workload on your security database server. In PTWeb, I have opted to use Session to minimize the load on the database. The final step is to tell ASP.NET itself that the user is no longer authenticated. This is done by call- ing FormsAuthentication.SignOut(). This method invalidates the security cookie used by ASP.NET to indicate that the user has been authenticated. The result is that ASP.NET sees the user as unauthenti- cated on all subsequent page requests. This covers the logout process, but the login process requires some more work. While the Login➥ Status control handles the details of directing the user to a login page, that page must be created. CHAPTER 10 ■ WEB FORMS UI530 6323_c10_final.qxd 2/27/06 1:33 PM Page 530 Login Page Like the PTWin smart client, the PTWeb site is designed to use custom authentication, so I can illus- trate the custom authentication support provided by CSLA .NET. I’ll also briefly discuss the use of Windows integrated security and the ASP.NET membership service. In Web Forms, when using custom authentication, you need to configure the site appropriately using web.config, and implement a login web page to collect and validate the user’s credentials. That’s the purpose behind Login.aspx. Forms-Based Authentication When using forms-based authentication, users are often automatically redirected to a login form before being allowed to access any other pages. Alternately, anonymous users can be allowed to use the site, and they can choose to log into the site to gain access to extra features or functionality. The specific behaviors are defined by web.config. Before moving on, remember that the following implementation only works within IIS. The ASP.NET Development Server provided with Visual Studio has various limitations; among them is the inability to load custom security objects from assemblies in the Bin directory. This means you can’t use the ASP.NET Development Server to test or debug custom principal objects, custom mem- bership providers, or other custom security objects if they’re in an assembly referenced from the project. Though this is an unfortunate limitation, it can be argued that the ASP.NET Development Server is not intended for anything beyond hobbyist or casual usage, and that IIS should be used for any seri- ous business development. ■Note An alternative solution is to install the assembly containing your custom principal and identity classes into the .NET Global Assembly Cache (GAC). For PTWeb, this would mean giving ProjectTracker.Library a strong name and using the gacutil.exe command line utility to install the assembly into the GAC. ProjectTracker. Library would need to be updated in the GAC after each time you build the assembly. I find that using IIS is a far simpler solution than using the GAC. Configuring the Site Using forms-based security in ASP.NET means that web.config includes elements like this: <authentication mode="Forms"> <forms loginUrl="Login.aspx" name="ptracker"/> </authentication> <authorization> <allow users="*"/> </authorization> This tells ASP.NET to use forms-based authentication (mode="Forms"), yet to allow unauthenti- cated users ( <allow users="*"/>). ■Note To require users to log in before seeing any pages, replace <allow users="*"/> with <deny users="?"/>. It is important that you also ensure that the security on the virtual root itself (within IIS) is con- figured to allow anonymous users. If IIS blocks anonymous users, then it doesn’t really matter what kind of security you use within ASP.NET. CHAPTER 10 ■ WEB FORMS UI 531 6323_c10_final.qxd 2/27/06 1:33 PM Page 531 ■Note Remember that IIS security runs first, and then any ASP.NET security is applied. With the web.config options shown previously, users can use the site without logging in, but the concept of logging in is supported. The goal is the same as with PTWin in Chapter 9: allow all users to do certain actions, and allow authenticated users to do other actions based on their roles. When a user chooses to log in, the <forms> tag specifies that they will be directed to Login.aspx, which will collect and validate their credentials. Figure 10-9 shows the appearance of Login.aspx. Now this is where things get kind of cool. There is no code behind Login.aspx. This page uses the ASP.NET Login control: <asp:Login ID="Login1" runat="server"> </asp:Login> This control is designed to automatically use the default ASP.NET membership provider for the site. ■Caution The user’s credentials flow from the browser to the web server in clear text—they are not automati- cally encrypted. Due to this, it is recommended that Login.aspx be accessed over an SSL (Secure Sockets Layer) connection so that data traveling to and from the browser is encrypted during the login process. You can write code to handle the events of the Login control if you desire, but a membership provider offers a cleaner solution overall. Of course, the membership provider that comes with CHAPTER 10 ■ WEB FORMS UI532 Figure 10-9. Layout of the Login page 6323_c10_final.qxd 2/27/06 1:33 PM Page 532 ASP.NET doesn’t understand PTPrincipal and PTIdentity objects, so PTWeb includes its own custom membership provider. Custom Membership Provider A membership provider is an object that inherits from System.Web.Security.MembershipProvider to handle all aspects of membership. These aspects include: • Validating user credentials • Adding a new user • Deleting a user • Changing a user’s password • And more . . . Of course, PTPrincipal doesn’t understand all these things, and ProjectTracker.Library doesn’t implement a full set of membership objects either. If you want to support all these capabilities, you should create your own security library with appropriate objects. But PTPrincipal does understand how to validate a user’s credentials. Fortunately, it is possible to implement a subset of the complete membership provider functionality, and that’s what I do in PTWeb. The PTMembershipProvider class is in the App_Code directory, so ASP.NET automatically compiles it and makes it available to the website. This class inherits from MembershipProvider and overrides the ValidateUser() method: public class PTMembershipProvider : MembershipProvider { public override bool ValidateUser( string username, string password) { bool result = PTPrincipal.Login(username, password); HttpContext.Current.Session["CslaPrincipal"] = Csla.ApplicationContext.User; return result; } // other methods … } All other methods are overridden to throw an exception indicating that they aren’t imple- mented by this provider. Notice how the ValidateUser() method already accepts username and password parameters. This is convenient because the Login() method of PTPrincipal accepts those parameters as well. The code simply calls the Login() method and records the result; true if the user was logged in, false otherwise. Remember from Chapter 8 that the Login() method sets the User property of Csla.ApplicationContext, thus automatically setting either the Thread object’s CurrentPrincipal property or the HttpContext.Current.User property to an authenticated PTPrincipal if the user’s credentials were valid; otherwise, it is set to an unauthenticated PTPrincipal. Since this code will be running within ASP.NET, it is the HttpContext value that is set to the user’s principal. The code then sets a Session field, CslaPrincipal, to contain this principal value so it will be available to subsequent pages. Then the result value is returned. The ASP.NET membership infrastructure relies on this return value to know whether the user’s credentials were valid or not. Before this custom membership provider can be used, it must be defined in web.config as follows: CHAPTER 10 ■ WEB FORMS UI 533 6323_c10_final.qxd 2/27/06 1:33 PM Page 533 [...]... Project object is saved ProjectDataSource The ProjectDataSource control takes care of data binding that deals with the Project object itself The page handles its DeleteObject, InsertObject, SelectObject, and UpdateObject events For instance, the SelectObject handler looks like this: protected void ProjectDataSource_SelectObject( object sender, Csla.Web.SelectObjectArgs e) { e.BusinessObject = GetProject();... by GetProjectList(): private ProjectTracker.Library.ProjectList GetProjectList() { object businessObject = Session["currentObject"]; if (businessObject == null || !(businessObject is ProjectTracker.Library.ProjectList)) { businessObject = ProjectTracker.Library.ProjectList.GetProjectList(); Session["currentObject"] = businessObject; } return (ProjectTracker.Library.ProjectList)businessObject; } This... custom principal class; second, you need to add some code to Global.asax A custom principal class to wrap the ASP.NET principal object would look like this: [Serializable()] public class MembershipPrincipal : Csla.Security.BusinessPrincipalBase { private System.Security.Principal.IPrincipal _principal; public MembershipPrincipal( System.Security.Principal.IPrincipal principal) : base(principal.Identity)... SaveProject(obj); } The current Project object is retrieved, and then the Remove() method is called on the Resources collection to remove the specified child object SaveProject() is then called to commit the change UpdateObject is a bit more complex: protected void ResourcesDataSource_UpdateObject( object sender, Csla.Web.UpdateObjectArgs e) { Project obj = GetProject(); int rid = int.Parse(e.Keys["ResourceId"].ToString());... void ResourcesDataSource_SelectObject( object sender, Csla.Web.SelectObjectArgs e) { Project obj = GetProject(); e.BusinessObject = obj.Resources; } It first gets the current Project object by calling GetProject() Then it simply provides the Resources collection to the data source control, which in turn provides it to any UI controls requiring the data The DeleteObject and UpdateObject event handlers are... Roles collection from ProjectTracker.Library to a GridView control on the page The RolesDataSource data source control is defined on the page like this: . this: [Serializable()] public class MembershipPrincipal : Csla.Security.BusinessPrincipalBase { private System.Security.Principal.IPrincipal _principal; public MembershipPrincipal( System.Security.Principal.IPrincipal. Application_AcquireRequestState( object sender, EventArgs e) { System.Security.Principal.IPrincipal principal; try { principal = (System.Security.Principal.IPrincipal) HttpContext.Current.Session["CslaPrincipal"]; } catch { principal. the case of an exception. As you’ll see, the data access code uses try catch blocks to catch exceptions that occur dur- ing any data updates (insert, update, or delete). The text of the exception

Ngày đăng: 06/07/2014, 00:20