<del id="nnjnj"></del><track id="nnjnj"></track>

<p id="nnjnj"></p>

<address id="nnjnj"></address>

    <pre id="nnjnj"><pre id="nnjnj"></pre></pre>

      <noframes id="nnjnj"><ruby id="nnjnj"><ruby id="nnjnj"></ruby></ruby>

      • 自動秒收錄
      • 軟件:1973
      • 資訊:57811|
      • 收錄網站:279872|

      IT精英團

      談ASP.NET核心認證與授權

      談ASP.NET核心認證與授權

      瀏覽次數:
      評論次數:
      編輯: 樂詠
      信息來源: ITPUB
      更新日期: 2022-09-10 08:52:28
      摘要

      使用asp.netcore開發應用系統過程中,基本上都會涉及到用戶身份的認證,及授權訪問控制,因此了解認證和授權流程也相當重要,下面通過分析asp.netcore框架中的認證和授權的源碼來分析

      • 正文開始
      • 相關閱讀
      • 推薦作品

      在使用ASP.NET核心開發應用系統的過程中,基本都會涉及到用戶身份的認證和授權訪問控制,所以了解認證和授權的過程也是非常重要的。這里,通過分析ASP.NET核心框架中認證和授權的源代碼,分析認證和授權的原理以及認證和授權的關系。

      00-1010認證是應用系統識別當前訪問者身份的過程。應用系統收到瀏覽器的請求時,通常會根據請求中攜帶的部分用戶的關鍵信息來識別當前登錄用戶的身份。通過分析這些信息,驗證并解密用戶的合法性。如果驗證通過,則意味著認證通過。應用程序系統會將經過身份驗證的用戶信息存儲在Http請求上下文中,供后續業務使用和授權過程使用。

      在ASP.NET,認證信息通常被加密并存儲在cookie中,每次訪問需要認證的頁面時,這些cookie都會被發送到應用系統,以便應用系統識別訪問者的身份,這就是經典的cookie認證。

      注:認證僅僅只是識別當前訪問用戶的身份,并不負責具體的訪問權限控制邏輯,如不具備某個資源的訪問權限返回403,未登錄返回401等,這些均由授權流程來控制。

      ASP.NET核心中負責認證過程的中間件是AuthenticationMiddleware 類,.以下是ASP.NET核心3.1的源代碼。如您所見,首先遍歷所有實現了IAuthenticationRequestHandler接口的身份驗證方案。并調用IAuthenticationRequestHandler接口的HandleRequestAsync方法。如果認證通過,執行將不會繼續。這時,HttpContext。用戶已經包含經過身份驗證的用戶信息。如果所有實現IAuthenticationRequestHandler接口的認證方案,如果對當前訪問用戶認證失敗,則使用默認認證方案(即:GetDefaultAuthenticateSchemeAsync)返回的認證方案,可以看到認證流程即使沒能識別當前訪問者的用戶身份,也會繼續執行下一個流程,(tail:await _next(context);)

      公共類認證中間件

      {

      private只讀RequestDelegate _ next

      公共認證計劃供應商計劃

      {

      get

      設置;

      }

      公共認證中間件(RequestDelegate next,IAuthenticationSchemeProvider schemes)

      {

      if (next==null)

      {

      引發新的ArgumentNullException(“next”);

      }

      if (schemes==null)

      {

      拋出新的ArgumentNullException(' schemes ');

      }

      _next=下一個;

      方案=方案;

      }

      公共異步任務調用(HttpContext上下文)

      {

      語境。Set((IAuthenticationFeature)new AuthenticationFeature

      {

      OriginalPath=上下文。請求路徑,

      OriginalPathBase=上下文。請求.路徑庫

      });

      IAuthenticationHandlerProvider handlers=context。request services . GetRequiredServiceIAuthenticationHandlerProvider();

      await方案中的foreach (AuthenticationScheme項目。GetRequestHandlerSchemesAsync())

      {

      IAuthenticationRequestHandler authenticationRequestHandler=(await handlers。GetHandlerAsync(上下文,項目。Name))作為IAuthenticationRequest

      Handler; bool flag = authenticationRequestHandler != null; if (flag) { flag = await authenticationRequestHandler.HandleRequestAsync(); } if (flag) { return; } } AuthenticationScheme authenticationScheme = await Schemes.GetDefaultAuthenticateSchemeAsync(); if (authenticationScheme != null) { //內部調用IAuthenticationService進行認證。 AuthenticateResult authenticateResult = await context.AuthenticateAsync(authenticationScheme.Name); if (authenticateResult?.Principal != null) { context.User = authenticateResult.Principal; } } await _next(context); }}

      授權是什么?

      授權是確定當前訪問用戶是否具備訪問某個系統資源權限的過程,對于需要授權才能訪問的系統資源,通常通過[Authorize]特性來標識,通過該特性,可以指定該資源需要哪個用戶角色才能訪問、必須符合哪個授權策略才能訪問,以及訪問該資源時采用的用戶認證方案是什么,當用戶訪問系統的某個API或者頁面時,授權流程會檢查當前用戶是否具備該API或者頁面的訪問權限,如果授權檢查失敗,那么會判斷當前用戶是否已經認證通過,如果認證通過,但無訪問該資源的權限,那么返回403(禁止訪問),如果未認證,那么直接返回401(未認證),表示需要用戶登錄認證后在進行訪問,需要注意的是:檢查是否具備訪問權限之前會先進行用戶身份的認證,至于用什么認證方案就看AuthorizeAttribute有沒有指定特定的認證方案,如果沒有,則直接采用認證流程的認證成功的身份信息。

      asp.net core 中,授權流程的執行是通過AuthorizationMiddleware類來完成的,以下是asp.net core 3.1中的源碼。

      // Microsoft.AspNetCore.Authorization.AuthorizationMiddlewareusing System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using Microsoft.AspNetCore.Authentication;using Microsoft.AspNetCore.Authorization;using Microsoft.AspNetCore.Authorization.Policy;using Microsoft.AspNetCore.Http;using Microsoft.Extensions.DependencyInjection;public class AuthorizationMiddleware{	private const string AuthorizationMiddlewareInvokedWithEndpointKey = "__AuthorizationMiddlewareWithEndpointInvoked";	private static readonly object AuthorizationMiddlewareWithEndpointInvokedValue = new object();	private readonly RequestDelegate _next;	private readonly IAuthorizationPolicyProvider _policyProvider;	public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvider policyProvider)	{		_next = next ?? throw new ArgumentNullException("next");		_policyProvider = policyProvider ?? throw new ArgumentNullException("policyProvider");	}	public async Task Invoke(HttpContext context)	{		if (context == null)		{			throw new ArgumentNullException("context");		}		Endpoint endpoint = context.GetEndpoint();		if (endpoint != null)		{			context.Items["__AuthorizationMiddlewareWithEndpointInvoked"] = AuthorizationMiddlewareWithEndpointInvokedValue;		}                //獲取訪問當前資源所需要的所有角色權限,及授權策略,以及訪問該資源時需要使用的認證方案列表,并統一合并到一個AuthorizationPolicy對象中。		IReadOnlyList<IAuthorizeData> authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();		AuthorizationPolicy policy = await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData);		if (policy == null)		{			await _next(context);			return;		}		IPolicyEvaluator policyEvaluator = context.RequestServices.GetRequiredService<IPolicyEvaluator>();                //通過IPolicyEvaluator.AuthenticateAsync()方法,對當前訪問者進行認證,至于使用哪種方案認證,根據該資源要求使用的認證方案來,如果沒有指定,                //則使用默認認證方案進行認證。		AuthenticateResult authenticationResult = await policyEvaluator.AuthenticateAsync(policy, context);                //如果包含實現了IAllowAnonymous接口的特性,則不進行授權檢查。		if (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null)		{			await _next(context);			return;		}                //這里調用AuthorizeAsync進行授權檢查,注意,這里將上一步認證結果authenticationResult也傳到了授權檢查方法內部。		PolicyAuthorizationResult policyAuthorizationResult = await policyEvaluator.AuthorizeAsync(policy, authenticationResult, context, endpoint);		//檢查授權結果,如果是未登錄,則返回401未認證,讓用戶進行登錄,如果該資源指定了特定的認證方案,則調用特定認證方案的Challenge方法,                //否則調用默認認證方案的Challenge方法,通常Challenge做的事情就是重定向用戶的瀏覽器到登錄頁面或者對于ajax異步請求返回401.                if (policyAuthorizationResult.Challenged)		{			if (policy.AuthenticationSchemes.Any())			{				foreach (string authenticationScheme in policy.AuthenticationSchemes)				{					await context.ChallengeAsync(authenticationScheme);				}			}			else			{				await context.ChallengeAsync();			}		}                //如果當前訪問者用戶身份認證通過,但是不被允許訪問該資源的權限,那么默認返回401(禁止訪問)給瀏覽器端,通常對于未授權的訪問請求,應用常常的做法是將用戶的瀏覽器重定向到禁止訪問的提示頁面,或者對于ajax異步請求來說,通常返回403狀態碼,和上面未認證情況一樣,如果該資源指定了特定的認證方案,那么會調用特定認證方案的Forbid方法,否則調用默認認證方案的Forbid方法。		else if (policyAuthorizationResult.Forbidden)		{			if (policy.AuthenticationSchemes.Any())			{				foreach (string authenticationScheme2 in policy.AuthenticationSchemes)				{					await context.ForbidAsync(authenticationScheme2);				}			}			else			{				await context.ForbidAsync();			}		}		else		{			await _next(context);		}	}}

      IPolicyEvaluator接口實現類 PolicyEvaluator類代碼如下,該類主要是負責授權流程中的認證和授權。

      // Microsoft.AspNetCore.Authorization.Policy.PolicyEvaluatorusing System;using System.Security.Claims;using System.Threading.Tasks;using Microsoft.AspNetCore.Authentication;using Microsoft.AspNetCore.Authorization;using Microsoft.AspNetCore.Authorization.Policy;using Microsoft.AspNetCore.Http;using Microsoft.Extensions.Internal;public class PolicyEvaluator : IPolicyEvaluator{	private readonly IAuthorizationService _authorization;	public PolicyEvaluator(IAuthorizationService authorization)	{		_authorization = authorization;	}	public virtual async Task<AuthenticateResult> AuthenticateAsync(AuthorizationPolicy policy, HttpContext context)	{                //這里去判斷當前資源是否有要求特定的認證方案進行認證,如果有指定特定的認證方案,則分別對每個認證方案進行認證,并把認證后的用戶信息進行合并                //最終存儲到HttpContext.User屬性中,并返回認證成功,如果沒有指定認證方案,則使用認證流程中已經認證的用戶信息作為認證結果返回,                //從這里可以看出,認證流程還是很有必要的,在資源沒有指定認證方案的前提下,認證流程為授權流程提供當前訪問者的身份信息,以便執行是否具備相應資源的訪問權限檢查,否則就直接進入Challenge流程將要求用戶先進行身份認證了		if (policy.AuthenticationSchemes != null && policy.AuthenticationSchemes.Count > )		{			ClaimsPrincipal newPrincipal = null;			foreach (string authenticationScheme in policy.AuthenticationSchemes)			{				AuthenticateResult authenticateResult = await context.AuthenticateAsync(authenticationScheme);				if (authenticateResult != null && authenticateResult.Succeeded)				{					newPrincipal = SecurityHelper.MergeUserPrincipal(newPrincipal, authenticateResult.Principal);				}			}			if (newPrincipal != null)			{				context.User = newPrincipal;				return AuthenticateResult.Success(new AuthenticationTicket(newPrincipal, string.Join(";", policy.AuthenticationSchemes)));			}			context.User = new ClaimsPrincipal(new ClaimsIdentity());			return AuthenticateResult.NoResult();		}		return (context.User?.Identity?.IsAuthenticated).GetValueOrDefault() ? AuthenticateResult.Success(new AuthenticationTicket(context.User, "context.User")) : AuthenticateResult.NoResult();	}        //resource為EndPoint對象。	public virtual async Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource)	{		if (policy == null)		{			throw new ArgumentNullException("policy");		}                //這里調用IAuthorizationService.AuthorizeAsync方法進行授權檢查,默認實現類為:DefaultAuthorizationService。		if ((await _authorization.AuthorizeAsync(context.User, resource, policy)).Succeeded)		{			return PolicyAuthorizationResult.Success();		}                //下面這句表示如果授權檢查失敗的情況下是進入Forbid流程還是進入Challenge流程,可以看到如果認證成功,那么表示無權限訪問進入Forbid流程。                //如果未認證,則進入Challenge流程,引導用戶登錄認證。		return authenticationResult.Succeeded ? PolicyAuthorizationResult.Forbid() : PolicyAuthorizationResult.Challenge();	}}

       

      認證和授權的關系?

      授權檢查之前都會先執行用戶身份的認證,不過這里的認證流程只有在被訪問的資源有指定特定的認證方案時才會執行,否則直接采用統一認證流程中的產生的認證信息。

      可以理解為認證流程一方面是為了告訴應用系統當前訪問者的身份,一方面是為了給授權檢查時識別用戶的身份信息,當資源沒有指定采用何種認證方案時,授權流程將會采用統一認證流程里認證通過產生的用戶信息,如果不啟用認證流程,并且被訪問的資源也沒有指定特定的認證方案對訪問者身份進行認證時,那么最終訪問該資源時還是會被要求先登錄認證,因此認證流程的另外一個用途就是為授權流程提供默認的用戶認證信息。

       

      總結起來說,

      認證流程主要有如下幾個作用:

      1. 識別系統訪問者的身份信息,認證通過后提供給后續業務使用。
      2. 給授權流程提供訪問者身份信息(資源沒有指定特定認證方案時,采用默認認證方案認證通過的用戶信息)。
      3. 實現授權失敗后的處理邏輯,比如授權檢查失敗后返回的 401(未認證),403(禁止訪問)等最終都是認證方案的 ChallegeAsync方法以及ForbidAsync方法來處理,這些方法是IAuthenticationHandler里面定義的,這些流程在授權失敗為401/403的時候分別被授權流程調用。

      授權流程主要如下幾個作用:

      1. 授權流程主要是檢查當前用戶是否具備指定資源的訪問權限,如果授權檢查失敗,如401(未認證),403(禁止訪問),那么最終會分別調用認證方案的ChallegenAsync和ForbidAsync方法,也就是說,授權流程側重于授權失敗后的流程控制。
      2. 授權流程另外一個主要的任務是檢查授權策略是否均能檢驗通過,如果一個資源通過AuthorizeAttribute的Policy屬性指定了一個或者多個授權策略,那么必須所有授權策略都驗證通過才算授權成功,如果未指定授權策略,那么就驗證默認的授權策略是否能檢驗通過,默認的授權策略則是要求必須用戶認證通過才允許訪問資源。

      授權流程本質上就是遍歷所有注入到容器中的IAuthorizationHandler(微軟默認在AddAuthorization的時候向容器注入了:PassThroughAuthorizationHandler,這個授權處理程序遍歷AuthorizationHandlerContext.Requirements中所有實現了IAuthorizationHandler的Requirement類,并調用其HandleAsync方法來檢查當前Requirement是否能校驗通過),并對訪問指定資源所要滿足的所有策略中包含的Requirement進行驗證,如果所有策略包含的Requirement都驗證通過,那么表示授權成功,這里的Requirement是指實現了IAuthorizationRequirement的類,這個接口是一個空接口,用于標記Requirement使用。

      標簽:流程 方案 用戶
      python的環境,你再也不用愁康達
      ? 上一篇 2022-09-09
      開發者如何在應用后臺直接控制用戶的運動狀態?
      下一篇 ? 2022-09-10
      • 如何在Ubuntu中保留文件系統并備份當前開發板鏡像
        0閱讀 0條評論 個贊
        在Ubuntu保留文件系統或者說備份當前開發板鏡像的需求在不斷增加。比如Ubuntu文件系統需要安裝庫文件的話直接使用apt-get工具就可以下載,但由于需要下載的核心板較多,比較費時間,這時需要將安……
      • 國產核心板全志T507助力消防系統升級
        0閱讀 0條評論 個贊
        9月16日下午,位于湖南長沙市區內的中國電信大樓發生火災,建筑高度218米,現場濃煙滾滾,數十層樓體燃燒劇烈。消防救援人員趕到現場后很快將火勢控制住,目前大樓火勢已被撲滅,所幸未發現人員傷亡。湖南電信……
      • 教大家如何處理Spring Boot易流中的用戶和群體!
        0閱讀 0條評論 個贊
        1.準備工作2.用戶操作2.1添加用戶2.2修改用戶2.3刪除用戶2.4查詢用戶3.組操作3.1添加組3.2修改組3.3刪除組3.4查詢組4.查看表詳情雖然說我們在實際開發中,……
      • 從PG15開始WAL壓縮優化
        0閱讀 0條評論 個贊
        PG15傳聞中的超級令人激動的功能大多數跳票了,年初我也寫過一個關于PG15新功能跳票的文章。PG15BETA已經發出幾個月了,似乎PG15里令人激動人心的功能不多,不過從長長的新功能列表里,……
      • 深入了解美團葉子發射器開源方案
        0閱讀 0條評論 個贊
        大家好,我是樹哥。之前我們有聊過「如何設計一個分布式ID發號器」,其中有講過4種解決方案,分別是:UUID類雪花算法數據庫自增主鍵Redis原子自增美團以第2、3種解決方案為基礎,開發出……
      發表評論 共有條評論
      用戶名: 密碼:
      驗證碼: 匿名發表
      • 基于 網絡6的輕量級Webapi框架緊固點
        0閱讀 0條評論 個贊
        大家好,我是等天黑。FastEndpoints是一個基于.NET6開發的開源webapi框架,它可以很好地替代.NETMinimalAPIs和MVC,專門為開發效率而生,帶來了全……
      • 大促銷活動如何抵御高流量DDoS攻擊?
        0閱讀 0條評論 個贊
        大促活動如何抵御大流量DDoS攻擊?每一次活動大促帶來的迅猛流量,對技術人而言都是一次嚴峻考驗。如果在活動期間遭受黑產惡意DDoS攻擊,無疑是雪上加霜。電商的特性是業務常態下通常不會遭受大流量DD……
      • MySQL中的存儲過程(詳細文章)
        0閱讀 0條評論 個贊
        文章目錄概述優點缺點MySQL存儲過程的定義存儲過程的基本語句格式存儲過程的使用定義一個存儲過程定義一個有參數的存儲過程定義一個流程控制語句IFELSE定義一個條件控制語句CASE定義一個循環語……
      • k8s網絡持久存儲的StorageClass(如何一步步實現動態持久存儲)
        0閱讀 0條評論 個贊
        StorageClass的作用:創建pv時,先要創建各種固定大小的PV,而這些PV都是手動創建的,當業務量上來時,需要創建很多的PV,過程非常麻煩。而且開發人員在申請PVC資源時,還不一定有匹配條件的……
      • 開發者如何在應用后臺直接控制用戶的運動狀態?
        18閱讀 0條評論 個贊
        酷暑終于過去,很多人伴著涼爽的秋風開啟了新一輪的健身計劃。當用戶進行戶外運動或使用跑步機、橢圓機等器械時,他們會希望在運動健康類App里點擊即可開啟運動并記錄運動數據。而對于開發者自己開發的應用來說,……
      • 碼頭工人日常工作的常用命令
        0閱讀 0條評論 個贊
        容器生命周期管理Docker創建新容器并運行[run]語法:dockerrun[OPTIONS]IMAGE[COMMAND][ARG...]OPTIONS說明:-astdin:指定標準輸入……
      • Oracle服務器遷移的一些經驗
        3閱讀 0條評論 個贊
        前言通過此文章來分享一下Oracle服務器遷移過程中的一些經驗,希望對大家有些許幫助。本文旨在幫助更多的同學,會提及一些基本命令或技巧,但不贅述,后續有機會再進一步分享各個細節。背景之前因機房遷移……
      • :自古以來 特工程序就是兵家必敗之地
        0閱讀 0條評論 個贊
        正向代理的血案前幾天打算使用golang做一個代理程序,golang標準庫net/http/httputil已經提供了這樣的能力。一把梭之后發現必然返回403Forbidden,我直接在targe……
      • 數據庫發展史II-數據倉庫
        6閱讀 0條評論 個贊
        回顧數據倉庫的發展歷程,大致可以將其分為幾個階段:萌芽探索到全企業集成時代、企業數據集成時代、混亂時代--"數據倉庫之父"間的論戰、理論模型確認時代以及數據倉庫產品百家爭鳴時代。數據倉庫理論發展歷程上……
      • 讓自己更有價值的5種能力
        0閱讀 0條評論 個贊
        如何讓自己更值錢?回答這個問題,需要用到黃金圈理論。什么是黃金圈理論?黃金圈理論,是國際知名營銷專家、作家SimonSinek在2011年提出的,這是一種由內向外的思維模式。黃金圈理論提倡由Why、……
      • Java接口自動測試框架系列(1)自動測試框架
        0閱讀 0條評論 個贊
        一、什么是自動化測試自動化測試是把以人為驅動的測試行為轉化為機器執行的一種過程。通常,在設計了測試用例并通過評審之后,由測試人員根據測試用例一步步執行測試,得到實際結果與期望結果的比較。為了節省人力、……
      • 公司排班系統遇到瓶頸?我自己造一個!
        10閱讀 0條評論 個贊
        本期名人堂我們有幸邀請到了白鯨開源的聯合創始人代立冬先生。代立冬先生與我們分享了自己社區開源的經歷,詳細介紹了一個開源項目是如何加入Apache孵化器,如何在導師的輔導下慢慢孵化、成長的過程,以及自己……
      • 能夠在JAVA中自定義和擴展Swagger 自動生成參數值的含義 提高開發效率
        0閱讀 0條評論 個贊
        大家好,又見面了。在JAVA做前后端分離的項目開發的時候,服務端需要提供接口文檔供周邊人員做接口的對接指導。越來越多的項目都在嘗試使用一些基于代碼自動生成接口文檔的工具來替代由開發人員手動編寫接口文檔……
      • SQL Server:觸發器的詳細說明
        0閱讀 0條評論 個贊
        1.概述2.觸發器的分類3.Inserted和Deleted表4.觸發器的執行過程5.創建觸發器6.修改觸發器:7.刪除觸發器:8.查看數據庫中已有觸發器:9.“Insteadof……
      • 在頭腦中扎根泛型的思想3354深刻理解泛型
        0閱讀 0條評論 個贊
        1.前言往往一些剛接觸C#編程的初學者,對于泛型的認識就是直接跳到對泛型集合的使用上,雖然微軟為我們提供了很多內置的泛型類型,但是如果我們只是片面的了解調用方式,這會導致我們對泛型盲目的使用。至于為什……
      • 如何使用helm優雅地安裝prometheus-operator并監控k8s集群微服務
        0閱讀 0條評論 個贊
        前言:隨著云原生概念盛行,對于容器、服務、節點以及集群的監控變得越來越重要。Prometheus作為Kubernetes監控的事實標準,有著強大的功能和良好的生態。但是它不支持分布式,不支持數據……
      • 圖系列中推薦算法的召回與粗排實踐
        3閱讀 0條評論 個贊
        一、轉轉推薦算法介紹1.1什么是推薦系統?1.2轉轉推薦主要場景及流程二、圖算法原理介紹及轉轉實踐2.1經典GraphEmbedding方法:DeepWalk2.2結構性與同質性:node2……
      • 用戶自定義注釋 AOP實現的日志保存(數據庫) 所有代碼都可以粘貼復制
        0閱讀 0條評論 個贊
        前言1,在一些特定的場景我們往往需要看一下接口的入參,特別是跨系統的接口調用(下發,推送),這個時候的接口入參就很重要,我們保存入參入庫,如果出問題就可以馬上定位是上游還是下游的問題(方便扯皮)2,還……
      • springboot集成docsify實現可移植文檔
        0閱讀 0條評論 個贊
        需求分析文檔可以和項目一起進行版本管理文檔可以在線訪問文檔可以與springboot項目集成,不需要分開部署MarkDown支持文檔跟隨,打包jar也可以訪問技術選型對于網上已有的方案,大致分為如下幾……
      • [設計模式] Java設計模式-工廠模式
        3閱讀 0條評論 個贊
        目錄【設計模式】Java設計模式-工廠模式簡介1、普通工廠(SimpleFactory)模式①、定義類②、定義簡單的工廠類③、實例2、抽象工廠(AbstractFactory)模式①、定義類②、……
      最近發布資訊
      更多
      警花高潮嗷嗷叫
      <del id="nnjnj"></del><track id="nnjnj"></track>

      <p id="nnjnj"></p>

      <address id="nnjnj"></address>

        <pre id="nnjnj"><pre id="nnjnj"></pre></pre>

          <noframes id="nnjnj"><ruby id="nnjnj"><ruby id="nnjnj"></ruby></ruby>