.Net Core Web Api_筆記32_採用cookie的身分驗證與授權
認證方式基本上
可以分為
Basic 認證
也就是將帳號:密碼進行Base64 編碼
Digest認證
也就是MD5
Bearer認證
->JWT採用
這裡新建好一個api專案後
目前這個資源請求是可被存取的 回傳200 OK
這邊可以嘗試在此模板的Controller上加上這個修飾[Authorize]
主要代表針對當前這個控制器下所有Action預設都是被套用權限授權控管的
都需要登入後判定有無權限才可存取
範例程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace CookieAuth.Controllers { [ApiController] [Route("[controller]")] [Authorize] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger) { _logger = logger; } [HttpGet(Name = "GetWeatherForecast")] public IEnumerable<WeatherForecast> Get() { return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); } } } |
再次運行時候會達到500的錯誤
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | System.InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions). at Microsoft.AspNetCore.Authentication.AuthenticationService.ChallengeAsync(HttpContext context, String scheme, AuthenticationProperties properties) at Microsoft.AspNetCore.Authorization.Policy.AuthorizationMiddlewareResultHandler.HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context) HEADERS ======= Accept: text/plain Connection: keep-alive Host: localhost:5018 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.69 Accept-Encoding: gzip, deflate, br Accept-Language: zh-TW,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6 Referer: http://localhost:5018/swagger/index.html sec-ch-ua: "Not_A Brand";v="99", "Microsoft Edge";v="109", "Chromium";v="109" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows" Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty |
No authenticationScheme was specified, and there was no DefaultChallengeScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions)
這裡可到github稍微去查找參考範例
到微軟的github
搜尋DefaultChallengeScheme
找到官方.net6版本的範例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | #define FIRST // FIRST SECOND NAME NS #if NEVER #elif FIRST #region snippet1 using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorPages(); builder.Services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie() .AddOpenIdConnect(options => { options.SignInScheme = "Cookies"; options.Authority = "-your-identity-provider-"; options.RequireHttpsMetadata = true; options.ClientId = "-your-clientid-"; options.ClientSecret = "-your-client-secret-from-user-secrets-or-keyvault"; options.ResponseType = "code"; options.UsePkce = true; options.Scope.Add("profile"); options.SaveTokens = true; }); var app = builder.Build(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseAuthentication(); app.UseAuthorization(); app.MapRazorPages(); app.Run(); #endregion #elif SECOND #region snippet2 using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorPages(); builder.Services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie() .AddOpenIdConnect(options => { options.SignInScheme = "Cookies"; options.Authority = "-your-identity-provider-"; options.RequireHttpsMetadata = true; options.ClientId = "-your-clientid-"; options.ClientSecret = "-client-secret-from-user-secrets-or-keyvault"; options.ResponseType = "code"; options.UsePkce = true; options.Scope.Add("profile"); options.SaveTokens = true; options.GetClaimsFromUserInfoEndpoint = true; options.ClaimActions.MapUniqueJsonKey("preferred_username", "preferred_username"); options.ClaimActions.MapUniqueJsonKey("gender", "gender"); }); var app = builder.Build(); // Code removed for brevity. #endregion if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseAuthentication(); app.UseAuthorization(); app.MapRazorPages(); app.Run(); #elif NAME using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.IdentityModel.Tokens; var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorPages(); #region snippet_name builder.Services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie() .AddOpenIdConnect(options => { // Other options... options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "email" //, RoleClaimType = "role" }; }); #endregion var app = builder.Build(); // Code removed for brevity. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseAuthentication(); app.UseAuthorization(); app.MapRazorPages(); app.Run(); #elif NS using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using System.IdentityModel.Tokens.Jwt; #region snippet_NS var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorPages(); JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); builder.Services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie() .AddOpenIdConnect(options => { options.SignInScheme = "Cookies"; options.Authority = "-your-identity-provider-"; options.RequireHttpsMetadata = true; options.ClientId = "-your-clientid-"; options.ClientSecret = "-your-client-secret-from-user-secrets-or-keyvault"; options.ResponseType = "code"; options.UsePkce = true; options.Scope.Add("profile"); options.SaveTokens = true; }); var app = builder.Build(); // Code removed for brevity. #endregion if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseAuthentication(); app.UseAuthorization(); app.MapRazorPages(); app.Run(); #endif |
在github預覽推薦安裝chrome插件(Octotree - GitHub code tree)增加左側樹狀倒覽
這邊我們切換到cookie的範例
官方.net6 cookie認證範例
我們可以看到需要到
Program.cs去補上這段配置
引入Microsoft.AspNetCore.Authentication.Cookies
並且服務註冊補上AddAuthentication以及AddCookie
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie();
配置認證的Middleware
app.UseAuthentication();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | #define FIRST // FIRST SECOND CC #if NEVER #elif FIRST #region snippet1 using Microsoft.AspNetCore.Authentication.Cookies; var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorPages(); builder.Services.AddControllersWithViews(); builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(); builder.Services.AddHttpContextAccessor(); var app = builder.Build(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseAuthentication(); app.UseAuthorization(); app.MapRazorPages(); app.MapDefaultControllerRoute(); app.Run(); #endregion #elif SECOND #region snippet2 using Microsoft.AspNetCore.Authentication.Cookies; var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorPages(); builder.Services.AddControllersWithViews(); builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.ExpireTimeSpan = TimeSpan.FromMinutes(20); options.SlidingExpiration = true; options.AccessDeniedPath = "/Forbidden/"; }); builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); var app = builder.Build(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseAuthentication(); app.UseAuthorization(); app.MapRazorPages(); app.MapDefaultControllerRoute(); app.Run(); #endregion #elif CC #region snippet_cc using Microsoft.AspNetCore.Authentication.Cookies; var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorPages(); builder.Services.AddControllersWithViews(); builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.EventsType = typeof(CustomCookieAuthenticationEvents); }); builder.Services.AddScoped<CustomCookieAuthenticationEvents>(); var app = builder.Build(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseAuthentication(); app.UseAuthorization(); app.MapRazorPages(); app.MapDefaultControllerRoute(); app.Run(); #endregion #endif |
當補完程式後再次訪問出現404
這個時候若想要得知更仔細的問題點
可以cmd開啟用curl來運行請求
可以到curl for Windows來補安裝這個CLI
https://curl.se/windows/
這裡可以用
curl -I webapi路由
curl -I http://localhost:5018/WeatherForecast
(-i 代表header + body ,只要 header使用大寫的 I 。)
那就可以更明確得知問題點
在回到github切到別支程式檔案來看
在aspnetcore/security/authentication/cookie/samples/6.x/CookieSample/Pages/Account/Login.cshtml.cs
當中我們主要需用到的程式碼就是
post提交登入表單的函數
if (ModelState.IsValid) 裡面整段block當中部分程式碼
都是我們有需要的
該有的namespace引入記得
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using System.Security.Claims;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | #region snippet1 public async Task<IActionResult> OnPostAsync(string returnUrl = null) { ReturnUrl = returnUrl; if (ModelState.IsValid) { // Use Input.Email and Input.Password to authenticate the user // with your custom authentication logic. // // For demonstration purposes, the sample validates the user // on the email address maria.rodriguez@contoso.com with // any password that passes model validation. var user = await AuthenticateUser(Input.Email, Input.Password); if (user == null) { ModelState.AddModelError(string.Empty, "Invalid login attempt."); return Page(); } var claims = new List<Claim> { new Claim(ClaimTypes.Name, user.Email), new Claim("FullName", user.FullName), new Claim(ClaimTypes.Role, "Administrator"), }; var claimsIdentity = new ClaimsIdentity( claims, CookieAuthenticationDefaults.AuthenticationScheme); var authProperties = new AuthenticationProperties { //AllowRefresh = <bool>, // Refreshing the authentication session should be allowed. //ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(10), // The time at which the authentication ticket expires. A // value set here overrides the ExpireTimeSpan option of // CookieAuthenticationOptions set with AddCookie. //IsPersistent = true, // Whether the authentication session is persisted across // multiple requests. When used with cookies, controls // whether the cookie's lifetime is absolute (matching the // lifetime of the authentication ticket) or session-based. //IssuedUtc = <DateTimeOffset>, // The time at which the authentication ticket was issued. //RedirectUri = <string> // The full path or absolute URI to be used as an http // redirect response value. }; await HttpContext.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), authProperties); _logger.LogInformation("User {Email} logged in at {Time}.", user.Email, DateTime.UtcNow); return LocalRedirect(Url.GetLocalUrl(returnUrl)); } // Something failed. Redisplay the form. return Page(); } #endregion |
在來還有登出的功能
可看到對應試跑至Index路由
這時就要回到Account 控制器當中搜尋
這邊我們回到Program.cs去補上這段配置(加入登入登出功能)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using System.Security.Claims; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.MapGet("/login",async context => { var claims = new List<Claim> { new Claim(ClaimTypes.Name, "test@gmail.com"), new Claim("FullName", "Kevin"), new Claim(ClaimTypes.Role, "Administrator"), }; var claimsIdentity = new ClaimsIdentity( claims, CookieAuthenticationDefaults.AuthenticationScheme); var authProperties = new AuthenticationProperties { //AllowRefresh = <bool>, // Refreshing the authentication session should be allowed. //ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(10), // The time at which the authentication ticket expires. A // value set here overrides the ExpireTimeSpan option of // CookieAuthenticationOptions set with AddCookie. //IsPersistent = true, // Whether the authentication session is persisted across // multiple requests. When used with cookies, controls // whether the cookie's lifetime is absolute (matching the // lifetime of the authentication ticket) or session-based. //IssuedUtc = <DateTimeOffset>, // The time at which the authentication ticket was issued. //RedirectUri = <string> // The full path or absolute URI to be used as an http // redirect response value. }; await context.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), authProperties); await context.Response.WriteAsync("login is ok"); }); app.MapGet("/logout", async context => { await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); }); app.Run(); |
在這裡我們可用cmd curl來觸發
先執行 login
curl -v http://localhost:5018/login
(透過 -v 或 –verbose 讓 curl 輸出更詳細的訊息)
可以看到拿到一串set-cookie
此時若回到swag去做api訪問仍舊404回傳
這邊可以複製curl指令並補上 cookie的參數
curl -v http://localhost:5018/WeatherForecast --cookie "Cookie: .AspNetCore.Cookies=CfDJ8OE0_gjsOQpHlb96kRjU9LOl6kd1QFZwwHDubN2gdNe0uhBOu7sTpOUf2b1ygD9shHyfkB1OL6KgXeKK6usnSwKQhWZOqf7w9usGlDDJr-mgWjwUsbx12lqOJFXwrZxAvzn7QL7xYSc3EGdkCjkIAxQNX-RdCdBEWqn_FCSSN6jY8js07vVMg76JnqVD-cfUlRUPvupwKgCWwk5KF1KfjlYgXt_Un-mSYsdVMJgBBR0lvHxvxG8zHCUfw562Hdyd09RiEuatp2E_nrotT3YUHf7ALH6OwmF-prgMX67nsGprcPWYDUqcLkAuBR1E36Qqrv8Zgmljzfx2nHkhgEQnxCNJcn9yjyjzrbghNyq2vX3rQJJ0tq9rrguGrmSFznYdPynGRiC1muQmkihXUnDKGo6r93sDQ1cPhCltOQDA3-FkrBX0zBdcTVlpEQI8SSVAwCENaU21pckF0xVYtN2ZX8o"
Set-之後的到分號結尾
那可以再嘗試登出
並重新再次訪問存取資源(帶cookie)會發現還是存取到資源!!!
可換成browser操作
一樣先存取資源 (沒辦法)
做登入 OK後 再次存取資源(成功)
做登出 OK後 再次存取資源(失敗)
原因在於
.net6 API 登出這段會將browser 的cookie清空
但cmd中curl已經記憶起來因此就算執行登出後再次同樣的cookie附帶後資源訪問仍然可以訪問的到
如何得知cookie認證一些失效時間以及相關資訊
可以擴充一個action於WeatherForecastController
進行測試
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Text; namespace CookieAuth.Controllers { [ApiController] [Route("[controller]")] [Authorize] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger) { _logger = logger; } [HttpGet(Name = "GetWeatherForecast")] public IEnumerable<WeatherForecast> Get() { return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); } [HttpGet] [Route("get1")] public string GetCookieInfo() { StringBuilder sb = new StringBuilder(); sb.AppendLine(User.Identity!.Name); sb.AppendLine(User.Identity!.AuthenticationType); sb.AppendLine(User.Identity!.IsAuthenticated.ToString()); sb.AppendLine(User.Identity!.ToString()); var items = Request.HttpContext.AuthenticateAsync().Result; foreach (var item in items.Properties!.Items) { sb.AppendLine(item.Key + "," + item.Value); } return sb.ToString(); } } } |
一樣先login完再做action訪問
Response body
Download
test@gmail.com
Cookies
True
System.Security.Claims.ClaimsIdentity
.issued,Fri, 27 Jan 2023 18:32:07 GMT
.expires,Fri, 10 Feb 2023 18:32:07 GMT
可看到1/27 ~ 到有效日2/10 預設是兩周時間(14天有效)
若針對這些配置想要客製化則可以參考到官方範例程式碼
Program.cs當中的#region snippet2
1 2 3 4 5 6 7 | builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.ExpireTimeSpan = TimeSpan.FromMinutes(20); options.SlidingExpiration = true; options.AccessDeniedPath = "/Forbidden/"; }); |
控制 cookie 要多少時間才能從其建立點起保持有效。 過期資訊位於受保護的 cookie 票券中。 因此,即使在瀏覽器應該加以清除之後會傳送至伺服器,會忽略過期的 cookie。
用戶頁面停留太久,登入逾期有效時長。
SlidingExpiration 設為 true,以指示中介軟體重新簽發處理時具有新過期時間的 cookie,其比透過過期視窗能省下一半的時間。(換言之,在cookie快到期的時候會重新頒發個cookie。)
資安建議false,白箱弱掃軟體會被要求cookie不能延展效期。
設false變成絕對逾期時間
如果用戶反應明明一直有在使用系統卻容易被自動登出的話,再設為true。
AccessDeniedPath 屬性會通知中介軟體應該將傳出 403 禁止狀態碼變更為 302 重新導向至指定的路徑。
若不是每一個action都想要被受權限控管(換言之存取特定某api action不需先登入)
則可以加上[AllowAnonymous]修飾詞
1 2 3 4 5 6 7 8 9 10 11 12 | [AllowAnonymous] [HttpGet(Name = "GetWeatherForecast")] public IEnumerable<WeatherForecast> Get() { return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); } |
就可直接存取到資源不顯示404 not found
Ref:
留言
張貼留言