.net 常用認證方法 forgery token 認證

常用的在 asp .net 與 MVC .net中常用的就是 from 認證 與 Windows認證。

對於form認證, 我們需要在web.config下面 system.web節點 加入認證配置。

    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login" name=".Acc" protection="All" timeout="30" cookieless="UseCookies" slidingExpiration="true" ticketCompatibilityMode="Framework40" />
    </authentication>        
    
    <httpCookies httpOnlyCookies="true" />
    <sessionState timeout="30" cookieName=".Acc_SessionId" />

在需要認證的方法 或者 class上面,使用 Authorize 屬性方法,不需要認證可以用AllowAnonymous 屬性,不需要認證。默認情況 不加也是匿名認證。登錄成功後,我們會設置form認證的用戶名,完成授權。

FormsAuthentication.SetAuthCookie(userName, false);

有時候需要自己重寫認證方法,可以在是class 繼承實現類中,重寫 OnAuthorization 方法,也可以,使用屬性類 的方法 來重寫。 下面可以判斷請求的方式是ajax還是直接通過http 過來的請求。

        protected override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
            if (filterContext.Result != null)
            {
                return;
            }
            if (filterContext.ActionDescriptor.GetCustomAttributes(typeof(NotRenewSessionAttribute), true).Any())
            {
                return;
            }

            if (filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(NotRenewSessionAttribute), true).Any())
            {
                return;
            }

            if (!User.Identity.IsAuthenticated || string.IsNullOrEmpty(ApplicationContext.UserName) || string.IsNullOrEmpty(ApplicationContext.GetSessionId()))
            {
                if (Request.IsAjaxRequest())
                {
                    filterContext.HttpContext.Response.StatusCode = 200;
                    var ajaxResultModel = new AjaxResultModel();
                    ajaxResultModel.Message = Messages.Admin.SessionTimeoutOrKilled.Format();
                    SessionException exception = new SessionException(Errors.ErrorCodes.E0501, SessionStatus.Inactive, ajaxResultModel.Message);
                    ExceptionInfo exceptionInfo = new ExceptionInfo(exception);
                    //ajaxResultModel.ExceptionInfo = exceptionInfo;
                    ajaxResultModel.Code = AjaxStatusCode.SessionTimeout;
                    ajaxResultModel.IsSuccess = false;
                    ajaxResultModel.MessageInfo = ajaxResultModel.Message;
                    filterContext.Result = Json(new { data = ajaxResultModel }, JsonRequestBehavior.AllowGet);//new JsonResult { Data = ajaxResultModel, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
                }
                else
                {
                    filterContext.Result = new HttpUnauthorizedResult();
                }
            }
        }

        protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (filterContext.Result != null)
            {
                return;
            }
            if (filterContext.ActionDescriptor.GetCustomAttributes(typeof(NotRenewSessionAttribute), true).Any())
            {
                return;
            }

            if (filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(NotRenewSessionAttribute), true).Any())
            {
                return;
            }
            string sessionId = this.ApplicationContext.GetSessionId();
            if (!string.IsNullOrEmpty(sessionId))
            {
                try
                {
                    SessionManager.Renew(sessionId);
                }
                catch (SessionException)
                {
                    this.ApplicationContext.SetSessionId(null);
                    throw;
                }
            }
            else
            {
                throw new SessionException(Errors.ErrorCodes.E0503);
            }

            base.OnActionExecuting(filterContext);
        }

        protected override void HandleUnknownAction(string actionName)
        {
            if (this.GetType() != typeof(AcceleratorBaseController))
            {
                //handle unknow controller
                Response.RedirectToRoute(new { controller = "Home", action = "PageNotFound" });
            }
        }
這裏分享下,session 共享問題,前段時間發現,兩個站點都在同一臺server的iis服務器上面,當登錄一個站點時候,另一個如果使用默認form認證的時候,另一個站點也是被授權了,這就是因爲他們的session是共享的導致,最後原因發現因爲在web配置文件中,兩個站點配置了同一樣的machineKey導致的,system.web節點。

      <machineKey decryption="AES" decryptionKey="CFE8BCC5155F55D55FAECDB2E5E75867EEAE527D917949A5465248105C2867A1" validation="SHA1" validationKey="9C594C1C1A1C796A7C72C1D4D2568C9E8618BDFF8EBB92C8091CE68B02D5EE7307D1FFB4FDBA174E87C7F3878CC3260EBB7ED713BCB6ADE628E6E89578731E4DC1" />

可以在iis的站點目錄下找到machine key配置,然後重新生成key。即可解決問題。這樣兩個站點就不會同用一個session了。



對於windows 認證,只要在web config下面開啓 windows認證即可,你可以直接制定用戶,登錄時候會彈出默認的windows認證的驗證,需要認證。

推薦測試工具,https://insomnia.rest/download/  

 <system.web>
    <authentication mode="Windows" />
    <!--<authentication mode="Windows">
      <forms >
        <credentials passwordFormat="Clear">
          <user name="administrator" password="password1"></user>
        </credentials>
      </forms>
    </authentication>-->
    <!--<authorization>
      <allow users="domain\Administrator"/>
      <deny users="?"/>
    </authorization>-->

如果是ajax 或者http過來的請求,httpWebRequest.Credentials 把用戶名和password傳過去即可。

 public static string PostSecurityRequest(string remoteUrl, string postData, string userName, string password, string domain, int timeOut = 60000, string encode = "UTF-8", string contentType = "application/x-www-form-urlencoded")
        {
            string str = "";
            System.Net.HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(remoteUrl);
            byte[] bytes = Encoding.GetEncoding(encode).GetBytes(postData);
            httpWebRequest.Method = "Post";
            httpWebRequest.ContentType = contentType;
            httpWebRequest.ContentLength = (long)bytes.Length;
            httpWebRequest.Timeout = timeOut;
            httpWebRequest.UseDefaultCredentials = false;
            httpWebRequest.PreAuthenticate = false;
            httpWebRequest.Credentials = new NetworkCredential(userName, password, domain);
            Stream requestStream = httpWebRequest.GetRequestStream();
            requestStream.Write(bytes, 0, bytes.Length);
            requestStream.Close();
            Stream responseStream = httpWebRequest.GetResponse().GetResponseStream();
            if (responseStream != null)
            {
                StreamReader streamReader = new StreamReader(responseStream, Encoding.GetEncoding(encode));
                str = streamReader.ReadToEnd();
                streamReader.Close();
                responseStream.Close();
            }
            return str;
        }


對於防止CSRF跨站請求僞造攻擊,我可以可以使用.net的自帶【ValidateAntiForgeryToken】 進行驗證,只需要加在增刪改的方法上面,在from標籤裏面加入token post到後臺即可@Html.AntiForgeryToken()。

對於ajax請求 可以直接獲取token post到後臺。

var forgeryToken = $("input[name=__RequestVerificationToken]").val(); 

data: { formId: formId, __RequestVerificationToken: forgeryToken  },


中間項目使用angular js 前端, 研究了一下如何使用該功能,我們可以自定義驗證方法,把token 在header中傳過去。 在master 全局頁面中加入token在頁面。使用ajax和anjular 封裝好的http請求。

        @functions{
           public string GetAntiForgeryToken()
           {
               string cookieToken, formToken;
               AntiForgery.GetTokens(null, out cookieToken, out formToken);
               return cookieToken + ":" + formToken;
           }
        }
    <input id="antiForgeryToken" type="hidden" value="@GetAntiForgeryToken()" />  
accept: () => {
                $.ajax({
                    url: './Request',
                    headers: {'RequestVerificationToken': $("#antiForgeryToken")[0].value},
                    data: formData,
                    type: 'POST',
                    contentType: false,
                    processData: false,
                    dataType: 'json',
                    async: false,
                    cache: false
                }).always(function (jqXHR: any, textStatus: any, errorThrown: any) {
                    var data = (jqXHR.responseJSON || jqXHR).data;
                    if (data.IsSuccess) {
                        messageFlag = "success";
                    } else {
                        messageFlag = "failure";
                        errorMessage = data.MessageInfo;
                    }                  
                });
導入jquery, declare var $: any;
    options: RequestOptions;
    constructor(private http: Http) {
        let headers = new Headers();
        headers.append('X-Requested-With', 'XMLHttpRequest');
        if ($("#antiForgeryToken")) {
            headers.append('RequestVerificationToken', $("#antiForgeryToken")[0].value);
        }
        this.options = new RequestOptions({ headers: headers });
    }

在頭文件里加入 ajax 請求,和token值。在方法上使用自定義方法驗證。

[MyValidateAntiForgeryToken]

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public class MyValidateAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
    {
        private void ValidateRequestHeader(HttpRequestBase request)
        {
            string cookieToken = String.Empty;
            string formToken = String.Empty;
            string tokenValue = request.Headers["RequestVerificationToken"] ?? "";
            if (!String.IsNullOrEmpty(tokenValue))
            {
                string[] tokens = tokenValue.Split(':');
                if (tokens.Length == 2)
                {
                    cookieToken = tokens[0].Trim();
                    formToken = tokens[1].Trim();
                }
            }
            AntiForgery.Validate(cookieToken, formToken);
        }

        public void OnAuthorization(AuthorizationContext filterContext)
        {

            try
            {
                if (filterContext.HttpContext.Request.IsAjaxRequest())
                {
                    ValidateRequestHeader(filterContext.HttpContext.Request);
                }
                else
                {
                    AntiForgery.Validate();
                }
            }
            catch (HttpAntiForgeryException e)
            {
                throw new HttpAntiForgeryException("Anti forgery token cookie not found");
            }
        }
    }



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章