Denna handledning visar hur du implementerar schemabaserad autentisering och auktorisation i ASP.NET Core. ASP.NET Core 3.0 är väldigt strikt när det gäller hur autentisering och behörighetshantering ska implementeras.
Schemabaserad autentisering och auktorisation är mycket smidig att använda, du kan använda inbyggda scheman och skapa dina egna autentiseringshanterare. Du kan också lägga till flera scheman och skydda olika delar av din webbplats med olika scheman. Du kan också använda flera scheman för en controller.
Autentisering är en process för att bekräfta en användares identitet, ett inloggningsformulär används vanligen på webbplatser för att matcha en användare och en session. Auktorisation är en process för att ge tillgång till resurser beroende på användarrättigheter.
Jag kommer att implementera cookie-baserad autentisering, grundläggande autentisering och rollbaserad auktorisation i denna handledning.
Konfigurera tjänster
Autentisering läggs till i metoden ConfigureServices i klassen StartUp. Mellanprogram för autentisering och behörigheter läggs till i metoden Configure i klassen StartUp.
public void ConfigureServices(IServiceCollection services)
{
// Add the mvc framework
services.AddRazorPages();
// Add memory cache
services.AddDistributedMemoryCache();
// Add redis distributed cache
if (configuration.GetSection("AppSettings")["RedisConnectionString"] != "")
{
services.AddDistributedRedisCache(options =>
{
options.Configuration = configuration.GetSection("AppSettings")["RedisConnectionString"];
options.InstanceName = "Fotbollstabeller:";
});
}
// Add the session service
services.AddSession(options =>
{
// Set session options
options.IdleTimeout = TimeSpan.FromMinutes(20d);
options.Cookie.Name = ".Fotbollstabeller";
options.Cookie.Path = "/";
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = SameSiteMode.Lax;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
});
// Create database options
services.Configure<DatabaseOptions>(options =>
{
options.connection_string = configuration.GetSection("AppSettings")["ConnectionString"];
options.sql_retry_count = 1;
});
// Create cache options
services.Configure<CacheOptions>(options =>
{
options.expiration_in_minutes = 240d;
});
// Add Authentication
services.AddAuthentication()
.AddCookie("Administrator", options =>
{
options.ExpireTimeSpan = TimeSpan.FromDays(10);
options.Cookie.MaxAge = TimeSpan.FromDays(10);
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = SameSiteMode.Lax;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Events.OnRedirectToLogin = (context) =>
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
context.Response.Redirect("/admin_login");
return Task.CompletedTask;
};
})
.AddCookie("Member", options =>
{
options.ExpireTimeSpan = TimeSpan.FromHours(4);
options.Cookie.MaxAge = TimeSpan.FromHours(4);
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = SameSiteMode.Lax;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Events.OnRedirectToLogin = (context) =>
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
context.Response.Redirect("/member_login");
return Task.CompletedTask;
};
})
.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("ApiAuthentication", null);
// Add clients
services.AddHttpClient();
// Add repositories
services.AddSingleton<IDatabaseRepository, MsSqlRepository>();
services.AddSingleton<IWebsiteSettingRepository, WebsiteSettingRepository>();
services.AddSingleton<IAdministratorRepository, AdministratorRepository>();
services.AddSingleton<IFinalRepository, FinalRepository>();
services.AddSingleton<IGroupRepository, GroupRepository>();
services.AddSingleton<IStaticPageRepository, StaticPageRepository>();
services.AddSingleton<IXslTemplateRepository, XslTemplateRepository>();
services.AddSingleton<ISitemapRepository, SitemapRepository>();
services.AddSingleton<IXslProcessorRepository, XslProcessorRepository>();
services.AddSingleton<ICommonServices, CommonServices>();
} // End of the ConfigureServices method
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// Use redirection
app.UseMiddleware<RedirectMiddleware>();
// Use error handling
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseStatusCodePagesWithReExecute("/home/error/{0}");
}
// Use static files
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
// Cache static files for 30 days
ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=25920000");
ctx.Context.Response.Headers.Append("Expires", DateTime.UtcNow.AddDays(300).ToString("R", CultureInfo.InvariantCulture));
}
});
// Use sessions
app.UseSession();
// For most apps, calls to UseAuthentication, UseAuthorization, and UseCors must
// appear between the calls to UseRouting and UseEndpoints to be effective.
app.UseRouting();
// Use authentication and authorization middlewares
app.UseAuthentication();
app.UseAuthorization();
// Routing endpoints
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
"default",
"{controller=home}/{action=index}/{id?}");
});
} // End of the Configure method
Grundläggande autentisering
Jag har skapat en anpassad hanterare för grundläggande autentisering. Jag använder den här hanteraren i schemat ApiAuthentication.
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
#region Variables
private readonly IAdministratorRepository administrator_repository;
#endregion
#region Constructors
public BasicAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger,
UrlEncoder encoder, ISystemClock clock, IAdministratorRepository administrator_repository)
: base(options, logger, encoder, clock)
{
// Set instance variables
this.administrator_repository = administrator_repository;
} // End of the constructor
#endregion
#region Methods
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
// Make sure that there is an Authorization header
if (Request.Headers.ContainsKey("Authorization") == false)
{
// Return failure
return AuthenticateResult.Fail("No Authorization header");
}
// Get the authorization header
string authHeader = Request.Headers["Authorization"];
// Get tokens
string authToken = authHeader.Substring("Basic ".Length).Trim();
string decodedToken = Encoding.UTF8.GetString(Convert.FromBase64String(authToken));
// Get the separator index
Int32 seperatorIndex = decodedToken.IndexOf(":");
// Get the username and password
string username = decodedToken.Substring(0, seperatorIndex);
string password = decodedToken.Substring(seperatorIndex + 1);
// Get a api user, username must be unique
Administrator api_user = await this.administrator_repository.GetApiUser(username, password);
// Make sure that the username and password is correct
if(api_user != null)
{
// Create claims
ClaimsIdentity identity = new ClaimsIdentity(Scheme.Name);
identity.AddClaim(new Claim("user", JsonConvert.SerializeObject(api_user)));
ClaimsPrincipal principal = new ClaimsPrincipal(identity);
AuthenticationTicket ticket = new AuthenticationTicket(principal, Scheme.Name);
// Return success
return AuthenticateResult.Success(ticket);
}
else
{
// Return failure
return AuthenticateResult.Fail("Incorrect username or password");
}
} // End of the HandleAuthenticateAsync method
#endregion
} // End of the class
Kräv autentisering och auktorisation
Lägg till ett auktoriseringsattribut till controllers och/eller metoder som kräver autentisering. Du kan ange ett eller flera scheman som ska användas. Standardschemat används om inget schema anges. Lägg till roller om du vill begränsa åtkomst till användare med vissa roller.
// One scheme
[Authorize(AuthenticationSchemes = "Administrator")]
public class admin_xsl_templatesController : Controller
// Multiple schemes
[Authorize(AuthenticationSchemes = "Administrator,Member")]
public class admin_xsl_templatesController : Controller
// Protect a method by only allowing some roles
[HttpGet]
[Authorize(Roles = "Administrator,Editor")]
public async Task<IActionResult> index()
// Api authentication
[Route("api/jobs/[action]")]
[Authorize(AuthenticationSchemes = "ApiAuthentication")]
public class JobsController : Controller
Inloggning och utloggning
Jag har ett inloggningsformulär på min webbplats som anropar metoden login. Jag lägger till anspråk och loggar in användaren med schemat Administrator. Spara inte för mycket information i anspråk avseende cookie-baserad autentisering, anspråk sparas i cookien. Spara en identifierare och använd denna identifierare för att hämta information om en användare.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> login(IFormCollection collection)
{
// Get the data from the form
string username = collection["txtUsername"];
string password = collection["txtPassword"];
// Get the administrator
Administrator administrator = this.administrator_repository.GetOneByUsername(username);
// Create response data
ResponseData data = null;
// Check if the user name exists and if the password is correct
if (administrator != null && this.administrator_repository.ValidatePassword(administrator.id, password) == true)
{
// Create claims
ClaimsIdentity identity = new ClaimsIdentity("Administrator");
//identity.AddClaim(new Claim("administrator", JsonConvert.SerializeObject(administrator)));
identity.AddClaim(new Claim(ClaimTypes.Name, administrator.admin_user_name));
identity.AddClaim(new Claim(ClaimTypes.Role, administrator.admin_role));
ClaimsPrincipal principal = new ClaimsPrincipal(identity);
// Sign in the administrator
await HttpContext.SignInAsync("Administrator", principal);
// Add success data
data = new ResponseData(true, username, $"Du har nu loggats in!");
}
else
{
// Add error data
data = new ResponseData(false, username, $"Användarnamnet eller lösenordet är felaktigt!");
}
// Return the data
return Json(data: data);
} // End of the post login method
[HttpGet]
public async Task<IActionResult> logout()
{
// Sign out the administrator
await HttpContext.SignOutAsync("Administrator");
// Redirect the user to the login page
return RedirectToAction("index", "admin_login");
} // End of the logout method
Erhåll användarinformation
Vi kan enkelt få information om en inloggad användare i våra metoder från HttpContext eftersom vi sparat denna information som anspråk.
// Get Json document
Claim claim = HttpContext.User.FindFirst("administrator");
Administrator user = JsonConvert.DeserializeObject<Administrator>(claim.Value);
// Get administrator from username
Administrator user = this.administrator_repository.GetOneByUsername(HttpContext.User.Identity.Name);