<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          .NET Core 中的鑒權授權正確方式

          共 15760字,需瀏覽 32分鐘

           ·

          2021-12-01 17:08


          ?一、簡介

          前后端分離的站點一般都會用jwt或IdentityServer4之類的生成token的方式進行登錄鑒權。這里要說的是小項目沒有做前后端分離的時站點登錄授權的正確方式。

          二、傳統(tǒng)的授權方式

          這里說一下傳統(tǒng)授權方式,傳統(tǒng)授權方式用session或cookies來完成。

          1、在請求某個Action之前去做校驗,驗證當前操作者是否登錄過,登錄過就有權限

          2、如果沒有權限就跳轉到登錄頁中去

          3、傳統(tǒng)登錄授權用的AOP-Filter:ActionFilter。

          具體實現(xiàn)為:

          1、增加一個類CurrentUser.cs 保存用戶登錄信息

          ///?
          ///?登錄用戶的信息
          ///?

          public?class?CurrentUser
          {
          ????///?
          ????///?用戶Id
          ????///?

          ????public?int?Id?{?get;?set;?}
          ????///?
          ????///?用戶名稱
          ????///?

          ????public?string?Name?{?get;?set;?}
          ????///?
          ????///?賬號
          ????///?

          ????public?string?Account?{?get;?set;?}
          }

          2、建一個Cookice/Session幫助類CookieSessionHelper.cs

          public?static?class?CookieSessionHelper
          {
          ????public?static?void?SetCookies(this?HttpContext?httpContext,?string?key,?string?value,?int?minutes?=?30)
          ????{
          ????????httpContext.Response.Cookies.Append(key,?value,?new?CookieOptions
          ????????{
          ????????????Expires?=?DateTime.Now.AddMinutes(minutes)
          ????????});
          ????}
          ????public?static?void?DeleteCookies(this?HttpContext?httpContext,?string?key)
          ????{
          ????????httpContext.Response.Cookies.Delete(key);
          ????}

          ????public?static?string?GetCookiesValue(this?HttpContext?httpContext,?string?key)
          ????{
          ????????httpContext.Request.Cookies.TryGetValue(key,?out?string?value);
          ????????return?value;
          ????}
          ????public?static?CurrentUser?GetCurrentUserByCookie(this?HttpContext?httpContext)
          ????{
          ????????httpContext.Request.Cookies.TryGetValue("CurrentUser",?out?string?sUser);
          ????????if?(sUser?==?null)
          ????????{
          ????????????return?null;
          ????????}
          ????????else
          ????????{
          ????????????CurrentUser?currentUser?=?Newtonsoft.Json.JsonConvert.DeserializeObject(sUser);
          ????????????return?currentUser;
          ????????}
          ????}

          ????public?static?CurrentUser?GetCurrentUserBySession(this?HttpContext?context)
          ????{
          ????????string?sUser?=?context.Session.GetString("CurrentUser");
          ????????if?(sUser?==?null)
          ????????{
          ????????????return?null;
          ????????}
          ????????else
          ????????{
          ????????????CurrentUser?currentUser?=?Newtonsoft.Json.JsonConvert.DeserializeObject(sUser);
          ????????????return?currentUser;
          ????????}
          ????}
          }

          3、建一個登錄控制器AccountController.cs

          public?class?AccountController?:?Controller
          {
          ????//登錄頁面
          ????public?IActionResult?Login()
          ????{
          ????????return?View();
          ????}
          ????//登錄提交
          ????[HttpPost]
          ????public?IActionResult?LoginSub(IFormCollection?fromData)
          ????{
          ????????string?userName?=?fromData["userName"].ToString();
          ????????string?passWord?=?fromData["password"].ToString();
          ????????//真正寫法是讀數(shù)據(jù)庫驗證
          ????????if?(userName?==?"test"?&&?passWord?==?"123456")
          ????????{
          ????????????#region?傳統(tǒng)session/cookies
          ????????????//登錄成功,記錄用戶登錄信息
          ????????????CurrentUser?currentUser?=?new?CurrentUser()
          ????????????{
          ????????????????Id?=?123,
          ????????????????Name?=?"測試賬號",
          ????????????????Account?=?userName
          ????????????};

          ????????????//寫sessin
          ???????????//?HttpContext.Session.SetString("CurrentUser",?JsonConvert.SerializeObject(currentUser));
          ????????????//寫cookies
          ????????????HttpContext.SetCookies("CurrentUser",?JsonConvert.SerializeObject(currentUser));
          ????????????#endregion

          ????????????//跳轉到首頁
          ????????????return?RedirectToAction("Index",?"Home");

          ????????}
          ????????else
          ????????{
          ????????????TempData["err"]?=?"賬號或密碼不正確";
          ????????????//賬號密碼不對,跳回登錄頁
          ????????????return?RedirectToAction("Login",?"Account");
          ????????}
          ????}
          ????///?
          ????///?退出登錄
          ????///?

          ????///?
          ????public?IActionResult?LogOut()
          ????{
          ????????HttpContext.DeleteCookies("CurrentUser");
          ????????//Session方式
          ????????//?HttpContext.Session.Remove("CurrentUser");
          ????????return?RedirectToAction("Login",?"Account");
          ????}
          }

          4、登錄頁Login.cshtml 內容

          <form?action="/Account/LoginSub"?method="post">
          ????<div>
          ????????賬號:<input?type="text"?name="userName"?/>
          ????div>
          ????<div>
          ????????賬號:<input?type="password"?name="passWord"?/>
          ????div>
          ????<div>
          ???????<input?type="submit"?value="登錄"?/>?<span?style="color:#ff0000">@TempData["err"]span>
          ????div>
          form>

          5、建一個登錄成功跳轉到主頁控制器HomeController.cs

          public?class?HomeController?:?Controller
          {
          ????public?IActionResult?Index()
          ????{
          ????????//從cookie獲取用戶信息
          ?????????CurrentUser?user?=?HttpContext.GetCurrentUserByCookie();
          ????????//CurrentUser?user?=?HttpContext.GetCurrentUserBySession();
          ????????return?View(user);
          ????}
          }

          6、頁面 Index.cshtml

          @{
          ????ViewData["Title"]?=?"Index";
          }

          @model?SessionAuthorized.Demo.Models.CurrentUser

          <h1>歡迎[email protected]?來到主頁h1>
          <div><a?href="/Account/Logout">退出登錄a>div>

          7、增加鑒權過濾器MyActionAuthrizaFilterAttribute.cs,實現(xiàn)IActinFilter,在OnActionExecuting中寫鑒權邏輯

          public?class?MyActionAuthrizaFilterAttribute?:?Attribute,?IActionFilter
          ?{
          ?????public?void?OnActionExecuted(ActionExecutedContext?context)
          ?????{
          ?????????//throw?new?NotImplementedException();
          ?????}
          ?????///?
          ?????///?進入action前
          ?????///?

          ?????///?
          ?????public?void?OnActionExecuting(ActionExecutingContext?context)
          ?????{
          ?????????//throw?new?NotImplementedException();
          ?????????Console.WriteLine("開始驗證權限...");
          ????????//?CurrentUser?currentUser?=?context.HttpContext.GetCurrentUserBySession();
          ?????????CurrentUser?currentUser?=?context.HttpContext.GetCurrentUserByCookie();
          ?????????if?(currentUser?==?null)
          ?????????{
          ?????????????Console.WriteLine("沒有權限...");
          ?????????????if?(this.IsAjaxRequest(context.HttpContext.Request))
          ?????????????{
          ?????????????????context.Result?=?new?JsonResult(new
          ?????????????????{
          ?????????????????????Success?=?false,
          ?????????????????????Message?=?"沒有權限"
          ?????????????????});
          ?????????????}
          ?????????????context.Result?=?new?RedirectResult("/Account/Login");??????????return;
          ?????????}
          ?????????Console.WriteLine("權限驗證成功...");
          ?????}
          ?????private?bool?IsAjaxRequest(HttpRequest?request)
          ?????{
          ?????????string?header?=?request.Headers["X-Requested-With"];
          ?????????return?"XMLHttpRequest".Equals(header);
          ?????}
          ?}

          在需要鑒權的控制器或方法上加上這個Filter即可完成鑒權,這里在主頁中加入鑒權,登錄成功的用戶才能訪問

          8、如果要用Session,還要在startup.cs中加入Session

          public?void?ConfigureServices(IServiceCollection?services)
          {
          ????services.AddControllersWithViews();
          ????services.AddSession();

          }
          public?void?Configure(IApplicationBuilder?app,?IWebHostEnvironment?env)
          ?{
          ?????if?(env.IsDevelopment())
          ?????{
          ?????????app.UseDeveloperExceptionPage();
          ?????}
          ?????else
          ?????{
          ?????????app.UseExceptionHandler("/Error");
          ?????????//?The?default?HSTS?value?is?30?days.?You?may?want?to?change?this?for?production?scenarios,?see?https://aka.ms/aspnetcore-hsts.
          ?????????app.UseHsts();
          ?????}

          ?????app.UseHttpsRedirection();
          ?????app.UseStaticFiles();
          ?????app.UseSession();
          ?????app.UseRouting();

          ?????app.UseAuthorization();

          ?????app.UseEndpoints(endpoints?=>
          ?????{
          ?????????endpoints.MapDefaultControllerRoute();
          ?????});
          ?}

          到這里,傳統(tǒng)的鑒權就完成了,下面驗證一下效果。

          三、.NET5中正確的鑒權方式

          傳統(tǒng)的授權方式是通過Action Filter(before)來完成的,上圖.Net Core的filter順序可以發(fā)現(xiàn),Action filter(befre)之前還有很多個filter,如果可以在前把鑒權做了,就能少跑了幾步冤枉路,所以,正確的鑒權應該是在Authorization filter中做,Authorization filter是.NET5里面專門做鑒權授權用的。

          怎么做呢,鑒權授權通過中間件支持。

          1、在staup.cs的Configure方法里面的app.UseRouting();之后,在app.UseEndpoints()之前,增加鑒權授權;

          public?void?Configure(IApplicationBuilder?app,?IWebHostEnvironment?env)
          {
          ????if?(env.IsDevelopment())
          ????{
          ????????app.UseDeveloperExceptionPage();
          ????}
          ????else
          ????{
          ????????app.UseExceptionHandler("/Error");
          ????????//?The?default?HSTS?value?is?30?days.?You?may?want?to?change?this?for?production?scenarios,?see?https://aka.ms/aspnetcore-hsts.
          ????????app.UseHsts();
          ????}

          ????app.UseHttpsRedirection();
          ????app.UseStaticFiles();
          ????app.UseSession();
          ????app.UseRouting();
          ????app.UseAuthentication();//檢測用戶是否登錄
          ????app.UseAuthorization();?//授權,檢測有沒有權限,是否能夠訪問功能
          ???
          ????app.UseEndpoints(endpoints?=>
          ????{
          ????????endpoints.MapDefaultControllerRoute();
          ????});
          }

          2、在ConfigureServices中增加

          public?void?ConfigureServices(IServiceCollection?services)
          {
          ????services.AddControllersWithViews();
          ????//services.AddSession();?傳統(tǒng)鑒權

          ????services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
          ????????.AddCookie(options?=>?{
          ????????????options.LoginPath?=?new?PathString("/Account/Login");//沒登錄跳到這個路徑
          ????????});

          }

          3、標記哪些控制器或方法需要登錄認證,在控制器或方法頭標記特性[Authorize],如果里面有方法不需要登錄驗證的,加上匿名訪問標識 [AllowAnonymousAttribute]

          //?[MyActionAuthrizaFilterAttribute]?傳統(tǒng)授權
          [Authorize]
          public?class?HomeController?:?Controller
          {
          ????public?IActionResult?Index()
          ????{
          ????????//從cookie獲取用戶信息
          ????????//?CurrentUser?user?=?HttpContext.GetCurrentUserByCookie();
          ????????//CurrentUser?user?=?HttpContext.GetCurrentUserBySession();

          ????????var?userInfo?=?HttpContext.User;
          ????????CurrentUser?user?=?new?CurrentUser()
          ????????{
          ????????????Id?=?Convert.ToInt32(userInfo.FindFirst("id").Value),
          ????????????Name?=?userInfo.Identity.Name,
          ????????????Account=userInfo.FindFirst("account").Value
          ????????};
          ????????return?View(user);
          ????}

          ????///?
          ????///?無需登錄,匿名訪問
          ????///?

          ????///?
          ????[AllowAnonymousAttribute]
          ????public?IActionResult?About()
          ????{
          ????????return?Content("歡迎來到關于頁面");
          ????}
          }

          4、登錄處AccountController.cs的代碼

          public?class?AccountController?:?Controller
          {
          ????//登錄頁面
          ????public?IActionResult?Login()
          ????{
          ????????return?View();
          ????}
          ????//登錄提交
          ????[HttpPost]
          ????public?IActionResult?LoginSub(IFormCollection?fromData)
          ????{
          ????????string?userName?=?fromData["userName"].ToString();
          ????????string?passWord?=?fromData["password"].ToString();
          ????????//真正寫法是讀數(shù)據(jù)庫驗證
          ????????if?(userName?==?"test"?&&?passWord?==?"123456")
          ????????{
          ????????????#region?傳統(tǒng)session/cookies
          ????????????//登錄成功,記錄用戶登錄信息
          ????????????//CurrentUser?currentUser?=?new?CurrentUser()
          ????????????//{
          ????????????//????Id?=?123,
          ????????????//????Name?=?"測試賬號",
          ????????????//????Account?=?userName
          ????????????//};

          ????????????//寫sessin
          ????????????//?HttpContext.Session.SetString("CurrentUser",?JsonConvert.SerializeObject(currentUser));
          ????????????//寫cookies
          ????????????//HttpContext.SetCookies("CurrentUser",?JsonConvert.SerializeObject(currentUser));
          ????????????#endregion

          ????????????//用戶角色列表,實際操作是讀數(shù)據(jù)庫
          ????????????var?roleList?=?new?List<string>()
          ????????????{
          ????????????????"Admin",
          ????????????????"Test"
          ????????????};
          ????????????var?claims?=?new?List()?//用Claim保存用戶信息
          ????????????{
          ????????????????new?Claim(ClaimTypes.Name,"測試賬號"),
          ????????????????new?Claim("id","1"),
          ????????????????new?Claim("account",userName),//...可以增加任意信息
          ????????????};
          ????????????//填充角色
          ????????????foreach(var?role?in?roleList)
          ????????????{
          ????????????????claims.Add(new?Claim(ClaimTypes.Role,?role));
          ????????????}
          ????????????//把用戶信息裝到ClaimsPrincipal
          ????????????ClaimsPrincipal?claimsPrincipal?=?new?ClaimsPrincipal(new?ClaimsIdentity(claims,?"Customer"));
          ????????????//登錄,把用戶信息寫入到cookie
          ????????????HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,?claimsPrincipal,
          ????????????????new?AuthenticationProperties
          ????????????????{
          ????????????????????ExpiresUtc?=?DateTime.Now.AddMinutes(30)//過期時間30分鐘
          ????????????????}).Wait();

          ????????????//跳轉到首頁
          ????????????return?RedirectToAction("Index",?"Home");

          ????????}
          ????????else
          ????????{
          ????????????TempData["err"]?=?"賬號或密碼不正確";
          ????????????//賬號密碼不對,跳回登錄頁
          ????????????return?RedirectToAction("Login",?"Account");
          ????????}
          ????}
          ????///?
          ????///?退出登錄
          ????///?

          ????///?
          ????public?IActionResult?LogOut()
          ????{
          ????????//?HttpContext.DeleteCookies("CurrentUser");
          ????????//Session方式
          ????????//?HttpContext.Session.Remove("CurrentUser");

          ????????HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
          ????????return?RedirectToAction("Login",?"Account");
          ????}
          }

          5、驗證結果:

          可以看到,一開始沒登錄狀態(tài),訪問/Home/Index會跳轉到登錄頁面,訪問/Home/About能成功訪問,證明匿名訪問ok,后面的登錄,顯示用戶信息,退出登錄也沒問題,證明功能沒問題,鑒權到這里就完成了。

          四、.NET5中角色授權

          上面的claims中已經記錄了用戶角色,這個角色就可以用來做授權了。

          在startup.cs中修改沒權限時跳轉頁面路徑

          public?void?ConfigureServices(IServiceCollection?services)
          {
          ????services.AddControllersWithViews();
          ????//services.AddSession();?傳統(tǒng)鑒權

          ????services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
          ????????.AddCookie(options?=>?{
          ????????????options.LoginPath?=?new?PathString("/Account/Login");//沒登錄跳到這個路徑
          ????????????options.AccessDeniedPath?=?new?PathString("/Account/AccessDenied");//沒權限跳到這個路徑
          ????????});

          }

          AccountController.cs增加方法

          public?IActionResult?AccessDenied()
          {
          ????return?View();
          }

          視圖內容

          沒有權限訪問-401

          1、單個角色訪問權限

          在方法頭加上特性 [Authorize(Roles ="角色代碼")]

          在HomeController.cs中增加一個方法

          ///?
          ///?角色為Admin能訪問
          ///?

          ///?
          [Authorize(Roles?="Admin")]
          public?IActionResult?roleData1()?{
          ????return?Content("Admin能訪問");
          }

          驗證。

          開始角色為

          訪問roleData1數(shù)據(jù):

          訪問成功,然后把角色Admin去掉

          ?var?roleList?=?new?List<string>(){ //"Admin","Test"};

          重新登錄,在訪問rleData1數(shù)據(jù):

          訪問不成功,跳轉到預設的沒權限的頁面了。

          2、多個角色包含一個權限

          [Authorize(Roles?=?"Admin,Test")]//多個角色用逗號隔開,角色包含有其中一個就能訪問
          ?public?IActionResult?roleData2()
          ?{
          ?????return?Content("roleData2訪問成功");
          ?}

          3、多個角色組合權限

          ///?
          ///?同時擁有標記的全部角色才能訪問
          ///?

          ///?
          [Authorize(Roles?=?"Admin")]
          [Authorize(Roles?=?"Test")]
          public?IActionResult?roleData3()
          {
          ????return?Content("roleData3訪問成功");
          }

          五、自定義策略授權

          上面的角色授權的缺點在哪里呢,最大的缺點就是角色要提前寫死到方法上,如果要修改只能改代碼,明顯很麻煩,實際項目中權限都是根據(jù)配置修改的,

          所以就要用到自定義策略授權了。

          第一步:

          增加一個CustomAuthorizatinRequirement.cs,要求實現(xiàn)接口:IAuthorizationRequirement

          ///?
          ///?策略授權參數(shù)
          ///?

          public?class?CustomAuthorizationRequirement:?IAuthorizationRequirement
          {
          ????///?
          ????///?
          ????///?

          ????public?CustomAuthorizationRequirement(string?policyname)
          ????{
          ????????this.Name?=?policyname;
          ????}

          ????public?string?Name?{?get;?set;?}
          }

          增加CustomAuthorizationHandler.cs------專門做檢驗邏輯的;要求繼承自AuthorizationHandler<>泛型抽象類;

          ///?
          ///?自定義授權策略
          ///?

          public?class?CustomAuthorizationHandler:?AuthorizationHandler<CustomAuthorizationRequirement>
          {
          ????public?CustomAuthorizationHandler()
          ????{

          ????}
          ???protected?override?Task?HandleRequirementAsync(AuthorizationHandlerContext?context,?CustomAuthorizationRequirement?requirement)
          ????{
          ???????
          ????????bool?flag?=?false;
          ????????if?(requirement.Name?==?"Policy01")
          ????????{
          ????????????Console.WriteLine("進入自定義策略授權01...");
          ????????????///策略1的邏輯
          ????????}

          ????????if?(requirement.Name?==?"Policy02")
          ????????{
          ????????????Console.WriteLine("進入自定義策略授權02...");
          ????????????///策略2的邏輯
          ????????}

          ????????if(flag)
          ????????{
          ????????????context.Succeed(requirement);?//驗證通過了
          ????????}

          ????????return?Task.CompletedTask;?//驗證不同過
          ????}
          }

          第二步,讓自定義的邏輯生效。

          starup.cs的ConfigureServices方法中注冊進來

          ?public?void?ConfigureServices(IServiceCollection?services)
          ?{
          ?????services.AddControllersWithViews();
          ?????//services.AddSession();?傳統(tǒng)鑒權
          ?????services.AddSingleton();

          ?????services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
          ?????????.AddCookie(options?=>
          ?????????{
          ?????????????options.LoginPath?=?new?PathString("/Account/Login");//沒登錄跳到這個路徑
          ?????????????options.AccessDeniedPath?=?new?PathString("/Account/AccessDenied");//沒權限跳到這個路徑
          ?????????});
          ?????services.AddAuthorization(optins?=>
          ?????{
          ?????????//增加授權策略
          ?????????optins.AddPolicy("customPolicy",?polic?=>
          ?????????{
          ?????????????polic.AddRequirements(new?CustomAuthorizationRequirement("Policy01")
          ????????????????//?,new?CustomAuthorizationRequirement("Policy02")
          ?????????????????);
          ?????????});
          ?????});

          ?}

          第三步,把要進授權策略的控制器或方法增加標識

          HomeContrller.cs增加測試方法

          ///?
          ///?進入授權策略
          ///?

          ///?
          [Authorize(policy:?"customPolicy")]
          public?IActionResult?roleData4()
          {
          ????return?Content("自定義授權策略");
          }

          訪問roleData4,看是否進到自定義授權策略邏輯

          可以看到自定義授權策略生效了,授權策略就可以在這里做了,下面加上授權邏輯。

          我這里的權限用路徑和角色關聯(lián)授權,加上授權邏輯后的校驗代碼。

          ///?
          ///?自定義授權策略
          ///?

          public?class?CustomAuthorizationHandler?:?AuthorizationHandler<CustomAuthorizationRequirement>
          {
          ????public?CustomAuthorizationHandler()
          ????{

          ????}
          ????protected?override?Task?HandleRequirementAsync(AuthorizationHandlerContext?context,?CustomAuthorizationRequirement?requirement)
          ????{

          ????????bool?flag?=?false;

          ????????//把context轉換到httpConext,方便取上下文
          ????????HttpContext?httpContext?=?context.Resource?as?HttpContext;
          ????????string?path?=?httpContext.Request.Path;//當前訪問路徑,例:"/Home/roleData4"

          ????????var?user?=?httpContext.User;
          ????????//用戶id
          ????????string?userId?=?user.FindFirst("id")?.Value;
          ????????if?(userId?==?null)
          ????????{
          ????????????//沒登錄,直接結束
          ????????????return?Task.CompletedTask;
          ????????}

          ????????//登錄成功時根據(jù)角色查出來這個用戶的權限存到redis,這里實際是根據(jù)用戶id從redis查詢出來
          ????????List<string>?paths?=?new?List<string>()
          ????????{
          ????????????"/Home/roleData4",
          ????????????"/Home/roleData3"
          ????????};

          ????????if?(requirement.Name?==?"Policy01")
          ????????{
          ????????????Console.WriteLine("進入自定義策略授權01...");
          ????????????///策略1的邏輯
          ????????????if?(paths.Contains(path))
          ????????????{
          ????????????????flag?=?true;
          ????????????}
          ????????}

          ????????if?(requirement.Name?==?"Policy02")
          ????????{
          ????????????Console.WriteLine("進入自定義策略授權02...");
          ????????????///策略2的邏輯
          ????????}

          ????????if?(flag)
          ????????{
          ????????????context.Succeed(requirement);?//驗證通過了
          ????????}

          ????????return?Task.CompletedTask;?//驗證不同過
          ????}
          }

          加上邏輯后再訪問。

          訪問成功,自定義授權策略完成。

          源代碼:https://github.com/weixiaolong325/SessionAuthorized.Demo

          轉自:包子wxl

          鏈接:cnblogs.com/wei325/p/15575141.html

          瀏覽 82
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产激情片| 91久久久久无码精品国产麻豆 | 伊人AV在线观看 | 日韩毛片儿 | 青娱乐在线观看人人 |