Community Server专题三:HttpModule

巭孬的大哥哥

巭孬的大哥哥

2016-02-19 09:51

今天天气好晴朗处处好风光,好天气好开始,图老师又来和大家分享啦。下面给大家推荐Community Server专题三:HttpModule,希望大家看完后也有个好心情,快快行动吧!
从专题三开始分析Community Server的一些具体的技术实现,根据IIS对请求的处理流程,从HttpModule&  HttpHandler切入话题,同时你也可以通过一系列的专题了解CS的运行过程,不只如此,所有的.Net 1.1 构架的Web App都是以同样的顺序执行的。

先了解一下IIS系统。它是一个程序,负责对网站的内容进行管理并且处理对客户的请求做出反应。当用户对一个页面提出请求时,IIS做如下反应(不考虑权限问题):

1.把对方请求的虚拟路径转换成物理路径

2.根据物理路径搜索请求的文件

3.找到文件后,获取文件的内容

4.生成Http头信息。

5.向客户端发送所有的文件内容:首先是头信息,然后是Html内容,最后是其它文件的内容。

6.客户端IE浏览器获得信息后,解析文件内容,找出其中的引用文件,如.js .css .gif等,向IIS请求这些文件。

7.IIS获取请求后,发送文件内容。

8.当浏览器获取所有内容后,生成内容界面,客户就看到图像/文本/其它内容了。

但是IIS本身是不支持动态页面的,也就是说它仅仅支持静态html页面的内容,对于如.asp,.aspx,.cgi,.php等,IIS并不会处理这些标记,它就会把它当作文本,丝毫不做处理发送到客户端。为了解决这个问题。IIS有一种机制,叫做ISAPI的筛选器,这个东西是一个标准组件(COM组件),当在在访问IIS所不能处理的文件时,如asp.net 1.1 中的IIS附加ISAPI筛选器如图:



Asp.net 服务在注册到IIS的时候,会把每个扩展可以处理的文件扩展名注册到IIS里面(如:*.ascx、*.aspx等)。扩展启动后,就根据定义好的方式来处理IIS所不能处理的文件,然后把控制权跳转到专门处理代码的进程中。让这个进程开始处理代码,生成标准的HTML代码,生成后把这些代码加入到原有的 Html中,最后把完整的Html返回给IIS,IIS再把内容发送到客户端。

    有上面对ISAPI的简单描述,我们把HttpModule& HttpHandler分开讨论,并且结合CS进行具体的实现分析。

HttpModule:

HttpModule实现了ISAPI Filter的功能,是通过对IhttpModule接口的继承来处理。下面打开CS中的CommunityServerComponents项目下的CSHttpModule.cs文件(放在HttpModule目录)


//------------------------------------------------------------------------------
// copyright company="Telligent Systems"
//     Copyright (c) Telligent Systems Corporation.  All rights reserved.
// /copyright 
//------------------------------------------------------------------------------

using System;
using System.IO;
using System.Web;
using CommunityServer.Components;
using CommunityServer.Configuration;

namespace CommunityServer 
{

    // *********************************************************************
    //  CSHttpModule
    //
    /**//// summary
    /// This HttpModule encapsulates all the forums related events that occur 
    /// during ASP.NET application start-up, errors, and end request.
    /// /summary
    // ***********************************************************************/
    public class CSHttpModule : IHttpModule 
    {
        Member variables and inherited properties / methods#region Member variables and inherited properties / methods

        public String ModuleName 
        { 
            get { return "CSHttpModule"; } 
        }    


        // *********************************************************************
        //  ForumsHttpModule
        //
        /**//// summary
        /// Initializes the HttpModule and performs the wireup of all application
        /// events.
        /// /summary
        /// param name="application"Application the module is being run for/param
        public void Init(HttpApplication application) 
        { 
            // Wire-up application events
            //
            application.BeginRequest += new EventHandler(this.Application_BeginRequest);
            application.AuthenticateRequest += new EventHandler(Application_AuthenticateRequest);
            application.Error += new EventHandler(this.Application_OnError);
            application.AuthorizeRequest += new EventHandler(this.Application_AuthorizeRequest);

            //settingsID = SiteSettingsManager.GetSiteSettings(application.Context).SettingsID;
            Jobs.Instance().Start();
            //CSException ex = new CSException(CSExceptionType.ApplicationStart, "Appication Started " +  AppDomain.CurrentDomain.FriendlyName);
            //ex.Log();
        }

        //int settingsID;
        public void Dispose() 
        {
            //CSException ex = new CSException(CSExceptionType.ApplicationStop, "Application Stopping " +  AppDomain.CurrentDomain.FriendlyName);
            //ex.Log(settingsID);
            Jobs.Instance().Stop();
        }

        Installer#region Installer




        #endregion


        #endregion

        Application OnError#region Application OnError
        private void Application_OnError (Object source, EventArgs e) 
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            CSException csException = context.Server.GetLastError() as CSException;

            if(csException == null)
                csException = context.Server.GetLastError().GetBaseException() as CSException;

            try
            {
                if (csException != null)
                {
                    switch (csException.ExceptionType) 
                    {
                        case CSExceptionType.UserInvalidCredentials:
                        case CSExceptionType.AccessDenied:
                        case CSExceptionType.AdministrationAccessDenied:
                        case CSExceptionType.ModerateAccessDenied:
                        case CSExceptionType.PostDeleteAccessDenied:
                        case CSExceptionType.PostProblem:
                        case CSExceptionType.UserAccountBanned:
                        case CSExceptionType.ResourceNotFound:
                        case CSExceptionType.UserUnknownLoginError:
                        case CSExceptionType.SectionNotFound:
                            csException.Log();
                            break;
                    }
                } 
                else 
                {
                    Exception ex = context.Server.GetLastError();
                    if(ex.InnerException != null)
                        ex = ex.InnerException;

                    csException = new CSException(CSExceptionType.UnknownError, ex.Message, context.Server.GetLastError());

                    System.Data.SqlClient.SqlException sqlEx = ex as System.Data.SqlClient.SqlException;
                    if(sqlEx == null || sqlEx.Number != -2) //don't log time outs
                        csException.Log();
                }
            }
            catch{} //not much to do here, but we want to prevent infinite looping with our error handles

            CSEvents.CSException(csException);
        }


        #endregion


        Application AuthenticateRequest#region Application AuthenticateRequest

        private void Application_AuthenticateRequest(Object source, EventArgs e) 
        {
            HttpContext context = HttpContext.Current;
            Provider p = null;
            ExtensionModule module = null;

            // If the installer is making the request terminate early
            if (CSConfiguration.GetConfig().AppLocation.CurrentApplicationType == ApplicationType.Installer) {
                return;
            }

            // Only continue if we have a valid context
            //
            if ((context == null) || (context.User == null))
                return;

            try 
            {
                // Logic to handle various authentication types
                //
                switch(context.User.Identity.GetType().Name.ToLower())
                {

                        // Microsoft passport
                    case "passportidentity":
                        p = (Provider) CSConfiguration.GetConfig().Extensions["PassportAuthentication"];
                        module = ExtensionModule.Instance(p);
                        if(module != null)
                            module.ProcessRequest();
                        else
                            goto default;
                        break;

                        // Windows
                    case "windowsidentity":
                        p = (Provider) CSConfiguration.GetConfig().Extensions["WindowsAuthentication"];
                        module = ExtensionModule.Instance(p);
                        if(module != null)
                            module.ProcessRequest();
                        else
                            goto default;
                        break;

                        // Forms
                    case "formsidentity":
                        p = (Provider) CSConfiguration.GetConfig().Extensions["FormsAuthentication"];
                        module = ExtensionModule.Instance(p);
                        if(module != null)
                            module.ProcessRequest();
                        else
                            goto default;
                        break;

                        // Custom
                    case "customidentity":
                        p = (Provider) CSConfiguration.GetConfig().Extensions["CustomAuthentication"];
                        module = ExtensionModule.Instance(p);
                        if(module != null)
                            module.ProcessRequest();
                        else
                            goto default;
                        break;

                    default:
                        CSContext.Current.UserName = context.User.Identity.Name;
                        break;

                }

            } 
            catch( Exception ex ) 
            {
                CSException forumEx = new CSException( CSExceptionType.UnknownError, "Error in AuthenticateRequest", ex );
                forumEx.Log();

                throw forumEx;
            }

            //            // Get the roles the user belongs to
            //            //
            //            Roles roles = new Roles();
            //            roles.GetUserRoles();
        }
        #endregion

        Application AuthorizeRequest#region Application AuthorizeRequest
        private void Application_AuthorizeRequest (Object source, EventArgs e) {


            if (CSConfiguration.GetConfig().AppLocation.CurrentApplicationType == ApplicationType.Installer)
            {
                //CSContext.Create(context);
                return;
            }


            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            CSContext csContext = CSContext.Current;
            //bool enableBannedUsersToLogin = CSContext.Current.SiteSettings.EnableBannedUsersToLogin;

//            // If the installer is making the request terminate early
//            if (csContext.ApplicationType == ApplicationType.Installer) {
//                return;
//            }

            //csContext.User = CSContext.Current.User;

            CSEvents.UserKnown(csContext.User);

            ValidateApplicationStatus(csContext);

            // Track anonymous users
            //
            Users.TrackAnonymousUsers(context);

            // Do we need to force the user to login?
            //

            if (context.Request.IsAuthenticated) 
            {
                string username = context.User.Identity.Name;
                if (username != null) 
                {
                    string[] roles = CommunityServer.Components.Roles.GetUserRoleNames(username);
                    if (roles != null && roles.Length  0) 
                    {
                        csContext.RolesCacheKey = string.Join(",",roles);
                    }
                }
            }
        }

        #endregion

        Application BeginRequest#region Application BeginRequest
        private void Application_BeginRequest(Object source, EventArgs e) 
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            
            CSConfiguration config = CSConfiguration.GetConfig();

            // If the installer is making the request terminate early
            if (config.AppLocation.CurrentApplicationType == ApplicationType.Installer)
            {
                //CSContext.Create(context);
                return;
            }

            CheckWWWStatus(config,context);

            

            CSContext.Create(context, ReWriteUrl(context));

                                    
        }

        private void CheckWWWStatus(CSConfiguration config, HttpContext context)
        {
            if(config.WWWStatus == WWWStatus.Ignore)
                return;

            const string withWWW = "http://www.";
            const string noWWW = "http://";
            string rawUrl = context.Request.Url.ToString().ToLower();
            bool isWWW = rawUrl.StartsWith(withWWW);

            
            if(config.WWWStatus == WWWStatus.Remove && isWWW)
            {
                context.Response.Redirect(rawUrl.Replace(withWWW, noWWW));
            }
            else if(config.WWWStatus == WWWStatus.Require && !isWWW)
            {
                context.Response.Redirect(rawUrl.Replace(noWWW, withWWW));
            }

        
        }

        ReWriteUrl#region ReWriteUrl
        private bool ReWriteUrl(HttpContext context)
        {

            // we're now allowing each individual application to be turned on and off individually. So before we allow
            // a request to go through we need to check if this product is disabled and the path is for the disabled product,
            // if so we display the disabled product page.
            //
            // I'm also allowing the page request to go through if the page request is for an admin page. In the past if you 
            // disabled the forums you were locked out, now with this check, even if you're not on the same machine but you're accessing
            // an admin path the request will be allowed to proceed, where the rest of the checks will ensure that the user has the
            // permission to access the specific url.

            // Url Rewriting
            //
            //RewriteUrl(context);

            string newPath = null;
            string path = context.Request.Path;
            bool isReWritten = SiteUrls.RewriteUrl(path,context.Request.Url.Query,out newPath);

            //very wachky. The first call into ReWritePath always fails with a 404.
            //calling ReWritePath twice actually fixes the probelm as well. Instead, 
            //we use the second ReWritePath overload and it seems to work 100% 
            //of the time.
            if(isReWritten && newPath != null)
            {
                string qs = null;
                int index = newPath.IndexOf('?');
                if (index = 0)
                {
                    qs = (index  (newPath.Length - 1)) ? newPath.Substring(index + 1) : string.Empty;
                    newPath = newPath.Substring(0, index);
                }
                context.RewritePath(newPath,null,qs);
            }

            return isReWritten;
        }

        #endregion

        private void ValidateApplicationStatus(CSContext cntx)
        {
            if(!cntx.User.IsAdministrator)
            {
                string disablePath = null;
                switch(cntx.Config.AppLocation.CurrentApplicationType)
                {
                    case ApplicationType.Forum:
                        if(cntx.SiteSettings.ForumsDisabled)
                            disablePath = "ForumsDisabled.htm";
                        break;
                    case ApplicationType.Weblog:
                        if(cntx.SiteSettings.BlogsDisabled)
                            disablePath = "BlogsDisabled.htm";
                        break;
                    case ApplicationType.Gallery:
                        if(cntx.SiteSettings.GalleriesDisabled)
                            disablePath = "GalleriesDisabled.htm";
                        break;
                    case ApplicationType.GuestBook:
                        if(cntx.SiteSettings.GuestBookDisabled)
                            disablePath = "GuestBookDisabled.htm";
                        break;
                    case ApplicationType.Document:                   //新增 ugoer
                        if(cntx.SiteSettings.DocumentDisabled)
                            disablePath = "DocumentsDisabled.htm";
                        break;
                }

                if(disablePath != null)
                {

                    string errorpath = cntx.Context.Server.MapPath(string.Format("~/Languages/{0}/errors/{1}",cntx.Config.DefaultLanguage,disablePath));
                    using(StreamReader reader = new StreamReader(errorpath))
                    {
                        string html = reader.ReadToEnd();
                        reader.Close();

                        cntx.Context.Response.Write(html);
                        cntx.Context.Response.End();
                    }
                }
            }
        }

        #endregion


    }

}


在Web.Config中的配置:



        httpModules
            add name="CommunityServer" type="CommunityServer.CSHttpModule, CommunityServer.Components" /
            add name="Profile" type="Microsoft.ScalableHosting.Profile.ProfileModule, MemberRole, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b7c773fb104e7562"/
            add name="RoleManager" type="Microsoft.ScalableHosting.Security.RoleManagerModule, MemberRole, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b7c773fb104e7562" /
        /httpModules




CSHttpModule.cs   UML:



要实现HttpModule功能需要如下步骤:

1.编写一个类,实现IhttpModule接口 

2.实现Init 方法,并且注册需要的方法 

3.实现注册的方法 

4.实现Dispose方法,如果需要手工为类做一些清除工作,可以添加Dispose方法的实现,但这不是必需的,通常可以不为Dispose方法添加任何代码。 

5.在Web.config文件中,注册您编写的类

到这里我们还需要了解一个Asp.Net的运行过程:



在图中第二步可以看到当请求开始的时候,马上就进入了HttpModule,在CS中由于实现了HttpModule的扩展CSHttpModule.cs 类,因此当一个web请求发出的时候(如:一个用户访问他的blog),CS系统首先调用CSHttpModule.cs类,并且进入

public void Init(HttpApplication application)

该方法进行初始化事件:

application.BeginRequest += new EventHandler(this.Application_BeginRequest);

application.AuthenticateRequest += new EventHandler(Application_AuthenticateRequest);

application.Error += new EventHandler(this.Application_OnError);

application.AuthorizeRequest += new EventHandler(this.Application_AuthorizeRequest);

 

有事件就要有对应的处理方法:

private void Application_BeginRequest(Object source, EventArgs e)

private void Application_AuthenticateRequest(Object source, EventArgs e)

private void Application_OnError (Object source, EventArgs e)

private void Application_AuthorizeRequest (Object source, EventArgs e)

 

事件被初始化后就等待系统的触发,请求进入下一步此时系统触发Application_BeginRequest事件,事件处理内容如下:


private void Application_BeginRequest(Object source, EventArgs e) 
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            
            CSConfiguration config = CSConfiguration.GetConfig();

            // If the installer is making the request terminate early
            if (config.AppLocation.CurrentApplicationType == ApplicationType.Installer)
            {
                //CSContext.Create(context);
                return;
            }

            CheckWWWStatus(config,context);

            

            CSContext.Create(context, ReWriteUrl(context));

                                    
        }

        private void CheckWWWStatus(CSConfiguration config, HttpContext context)
        {
            if(config.WWWStatus == WWWStatus.Ignore)
                return;

            const string withWWW = "http://www.";
            const string noWWW = "http://";
            string rawUrl = context.Request.Url.ToString().ToLower();
            bool isWWW = rawUrl.StartsWith(withWWW);

            
            if(config.WWWStatus == WWWStatus.Remove && isWWW)
            {
                context.Response.Redirect(rawUrl.Replace(withWWW, noWWW));
            }
            else if(config.WWWStatus == WWWStatus.Require && !isWWW)
            {
                context.Response.Redirect(rawUrl.Replace(noWWW, withWWW));
            }

        
        }

        ReWriteUrl#region ReWriteUrl
        private bool ReWriteUrl(HttpContext context)
        {

            // we're now allowing each individual application to be turned on and off individually. So before we allow
            // a request to go through we need to check if this product is disabled and the path is for the disabled product,
            // if so we display the disabled product page.
            //
            // I'm also allowing the page request to go through if the page request is for an admin page. In the past if you 
            // disabled the forums you were locked out, now with this check, even if you're not on the same machine but you're accessing
            // an admin path the request will be allowed to proceed, where the rest of the checks will ensure that the user has the
            // permission to access the specific url.

            // Url Rewriting
            //
            //RewriteUrl(context);

            string newPath = null;
            string path = context.Request.Path;
            bool isReWritten = SiteUrls.RewriteUrl(path,context.Request.Url.Query,out newPath);

            //very wachky. The first call into ReWritePath always fails with a 404.
            //calling ReWritePath twice actually fixes the probelm as well. Instead, 
            //we use the second ReWritePath overload and it seems to work 100% 
            //of the time.
            if(isReWritten && newPath != null)
            {
                string qs = null;
                int index = newPath.IndexOf('?');
                if (index = 0)
                {
                    qs = (index  (newPath.Length - 1)) ? newPath.Substring(index + 1) : string.Empty;
                    newPath = newPath.Substring(0, index);
                }
                context.RewritePath(newPath,null,qs);
            }

            return isReWritten;
        }

        #endregion

这个事件主要做两个事情

a:为发出请求的用户初始化一个Context,初始化Context用到了线程中本地数据槽(LocalDataStoreSlot),把当前用户请求的上下文(contextb)保存在为此请求开辟的内存中。

b:判断是否需要重写 URL(检查是否需要重写的过程是对SiteUrls.config文件中正则表达式和对应Url处理的过程),如果需要重写URL,就执行asp.net级别上的RewritePath方法获得新的路径,新的路径才是真正的请求信息所在的路径。这个专题不是讲URL Rewrite,所以只要明白URL在这里就进行Rewrite就可以了,具体的后面专题会叙述。

处理完 Application_BeginRequest 后进程继向下执行,随后触发了Application_AuthenticateRequest(如果有朋友不明白这个执行过程,可以通过调试中设置多个断点捕获事件执行的顺序。如果你还不会调试,可以留言偷偷的告诉我,嘿嘿。), Application_AuthenticateRequest事件初始化一个context的Identity,其实CS提供了很多的 Identity支持,包括Microsoft passport,但是目前的版本中使用的是默认值 System.Web.Security.FormsIdentity。具体代码如下:

private void Application_AuthenticateRequest(Object source, EventArgs e) 
        {
            HttpContext context = HttpContext.Current;
            Provider p = null;
            ExtensionModule module = null;

            // If the installer is making the request terminate early
            if (CSConfiguration.GetConfig().AppLocation.CurrentApplicationType == ApplicationType.Installer) {
                return;
            }

            // Only continue if we have a valid context
            //
            if ((context == null) || (context.User == null))
                return;

            try 
            {
                // Logic to handle various authentication types
                //
                switch(context.User.Identity.GetType().Name.ToLower())
                {

                        // Microsoft passport
                    case "passportidentity":
                        p = (Provider) CSConfiguration.GetConfig().Extensions["PassportAuthentication"];
                        module = ExtensionModule.Instance(p);
                        if(module != null)
                            module.ProcessRequest();
                        else
                            goto default;
           &nbs
展开更多 50%)
分享

猜你喜欢

Community Server专题三:HttpModule

Web开发
Community Server专题三:HttpModule

Community Server专题一:概述Community Server

Web开发
Community Server专题一:概述Community Server

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

Community Server专题二:体系结构

Web开发
Community Server专题二:体系结构

SQL Server安全专题:SQL Server 2000的安全配置

编程语言 网络编程
SQL Server安全专题:SQL Server 2000的安全配置

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

[专题]SQL SERVER实用经验技巧集

SQLServer
[专题]SQL SERVER实用经验技巧集

SQL Server安全专题"SQL Server数据库的一些攻击

编程语言 网络编程
SQL Server安全专题"SQL Server数据库的一些攻击

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

struts2 spring整合fieldError问题

struts2 spring整合fieldError问题

mysql root用户的密码修改和消除

mysql root用户的密码修改和消除
下拉加载更多内容 ↓