最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

天天百事通!OpenIddict使用教程

来源:博客园

OpenIddict是一个ASP.NET Core身份验证库,可帮助您添加OpenID Connect和OAuth 2.0支持到ASP.NET Core应用程序中。下面是OpenIddict使用教程的步骤:


(资料图)

  1. 安装OpenIddict,在项目中添加OpenIddict.Core和OpenIddict.EntityFrameworkCore Nuget包。

  2. 配置OpenIddict,在Startup.cs文件中添加OpenIddict服务的配置。您可以选择使用内存或EFCore进行配置。以下是使用EF Core进行配置的示例:

services.AddDbContext(options =>{    options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));    options.UseOpenIddict();});services.AddCustomOpenIddictApplication();services.AddCustomOpenIddictAuthorization();services.AddCustomOpenIddictScope();services.AddCustomOpenIddictToken();services.AddCustomOpenIddictValidation();services.AddCustomOpenIddictUser();services.AddOpenIddict()    .AddCore(options =>    {        options.UseEntityFrameworkCore()            .UseDbContext()            .ReplaceDefaultEntities();    })    .AddServer(options =>    {        options.UseMvc();        options.EnableAuthorizationEndpoint("/connect/authorize")            .EnableLogoutEndpoint("/connect/logout")            .EnableTokenEndpoint("/connect/token")            .EnableUserinfoEndpoint("/connect/userinfo");        options.RegisterScopes("openid", "profile", "email", "offline_access");        options.AllowImplicitFlow();        options.DisableHttpsRequirement();        options.AddSigningCertificate(File.ReadAllBytes(Configuration["Auth:Certificates:Path"]),            Configuration["Auth:Certificates:Password"]);        options.DisableAccessTokenEncryption();        options.SetAccessTokenLifetime(TimeSpan.FromHours(6));    });
  1. 添加授权策略,在Startup.cs文件添加需要的授权策略。以下是一个例子:
services.AddAuthorization(options =>{    options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));    options.AddPolicy("AdministratorOnly", policy => policy.RequireRole("Administrator"));});
  1. 在您的应用程序中使用OpenIddict,您可以使用OpenIddict来实现您的OAuth 2.0或OpenID Connect需求。以下是一些常见的用例:

4.1 登录页面

使用OpenIddict进行身份验证,您可以使用如下代码在您的控制器中。您可以使用请求重定向到触发OpenID Connect流:

[HttpGet("~/login")]public IActionResult Login(){    var request = HttpContext.GetOpenIddictServerRequest();        return View(new LoginViewModel    {        Nonce = RandomNumberGenerator.GetInt32(),        ReturnUrl = request.RedirectUri,        Ticket = request.GetOpenIddictServerTransactionId(),    });}[HttpPost("~/login")]public IActionResult Login(LoginViewModel model){    if (ModelState.IsValid)    {        var user = await _userManager.FindByNameAsync(model.Username);        if (user == null)        {            ModelState.AddModelError("Username", "Username or password is incorrect.");        }        else if (!await _userManager.IsEmailConfirmedAsync(user))        {            ModelState.AddModelError("Email", "You must have a confirmed email to log in.");        }        else if (!await _userManager.CheckPasswordAsync(user, model.Password))        {            ModelState.AddModelError("Username", "Username or password is incorrect.");        }        else        {            // 创建一个新的身份验证票据.            var ticket = await CreateTicketAsync(user);            return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);        }    }    ViewData["returnUrl"] = model.ReturnUrl;    ViewData["nonce"] = model.Nonce;    ViewData["transactionId"] = model.Ticket;    return View(model);}

4.2 注册页面

您还可以使用OpenIddict来实现您的注册页面。以下是一个例子:

[HttpGet("~/register")]public IActionResult Register(){    return View();}[HttpPost("~/register")]public async Task Register(RegisterViewModel model){    if (ModelState.IsValid)    {        var user = new ApplicationUser        {            UserName = model.Email,            Email = model.Email,            FirstName = model.FirstName,            LastName = model.LastName,        };        var result = await _userManager.CreateAsync(user, model.Password);        if (result.Succeeded)        {            var code = await _userManager.GenerateEmailConfirmationAsync(user);            var callbackUrl= Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);                        await _emailSender.SendEmailAsync(model.Email, "Confirm your email",                $"Please confirm your account by clicking this link: {callbackUrl}");            return RedirectToAction(nameof(RegisterConfirmation));        }        foreach (var error in result.Errors)        {            ModelState.AddModelError("Email", error.Description);        }    }    return View(model);}[HttpGet("~/register/confirmation")]public IActionResult RegisterConfirmation(){    return View();}

4.3 访问受保护的资源

最后,您可以使用OpenIddict来实现访问受保护资源的身份验证和授权。以下是一个例子:

[HttpGet("~/manager")][Authorize(Roles = "Manager")]public IActionResult ManagerDashboard(){    return View();}[HttpGet("~/employee")][Authorize(Policy = "EmployeeOnly")]public IActionResult EmployeeDashboard(){    return View();}[HttpGet("~/administrator")][Authorize(Policy = "AdministratorOnly")]public IActionResult AdministratorDashboard(){    return View();}
  1. 通过OpenIddict实现Token刷新

当访问受保护的API时,您可以使用OpenIddict来实现使用token刷新。以下是实现Token刷新的一个示例方法:

[HttpPost("~/api/token/refresh")]public async Task Refresh([FromForm]string refreshToken){    var info = await HttpContext.AuthenticateAsync(OpenIddictServerDefaults.AuthenticationScheme);    if (info == null)    {        return BadRequest(new        {            error = OpenIddictConstants.Errors.InvalidRequest,            error_description = "The refresh token is no longer valid."        });    }    var principal = info.Principal;    var user = await _userManager.GetUserAsync(principal);    if (user == null)    {        return BadRequest(new        {            error = OpenIddictConstants.Errors.InvalidRequest,            error_description = "The refresh token is no longer valid."        });    }    // 确保刷新令牌没有被撤销.    if (!await _tokenManager.ValidateAsync(        principal.GetId(),        principal.GetClaim(OpenIddictConstants.Claims.JwtId)))    {        return BadRequest(new        {            error = OpenIddictConstants.Errors.InvalidRequest,            error_description = "The refresh token is no longer valid."        });    }    // 从数据库得到客户端应用程序详细信息    var application = await _applicationManager.FindByClientIdAsync(        principal.GetClaim(OpenIddictConstants.Claims.ClientId));    if (application == null)    {        return BadRequest(new        {            error = OpenIddictConstants.Errors.InvalidRequest,            error_description = "The client application associated with this token is no longer valid."        });    }    var identity = await _userManager.CreateIdentityAsync(user, principal.GetScopes());    var ticket = await CreateTicketAsync(application, identity, principal);    return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);}
  1. 通过OpenIddict实现密码恢复流程OpenIddict还可以实现忘记密码流程的重置密码,以下是一个简单的示例:
[HttpPost("~/forgot-password")][AllowAnonymous]public async Task ForgotPassword([FromForm] string email){    var user = await _userManager.FindByEmailAsync(email);    if (user == null)    {        // 不要显示用户不存在,懂的都懂~        return Ok();    }    var code = await _userManager.GeneratePasswordResetTokenAsync(user);    code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));    var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Reque    await _emailSender.SendEmailAsync(        email,        "Password Reset",        $"Please reset your password by clicking here: link.");    return Ok();}[HttpGet("~/reset-password")][AllowAnonymous]public IActionResult ResetPassword(string code = null, string userId = null){    return View(new ResetPasswordViewModel { Code = code, UserId = userId });}[HttpPost("~/reset-password")][AllowAnonymous]public async Task ResetPassword([FromForm] ResetPasswordViewModel model){    if (!ModelState.IsValid)    {        return View(model);    }    var user = await _userManager.FindByIdAsync(model.UserId);    if (user == null)    {        // 不要显示用户不存在        return View("ResetPasswordConfirmation");    }    var decodedCode = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(model.Code));    var result = await _userManager.ResetPasswordAsync(user, decodedCode, model.Password);    if (result.Succeeded)    {        return RedirectToAction(nameof(ResetPasswordConfirmation));    }    foreach (var error in result.Errors)    {        ModelState.AddModelError(string.Empty, error.Description);    }    return View(model);}[HttpGet("~/reset-password-confirmation")][AllowAnonymous]public IActionResult ResetPasswordConfirmation(){    return View();}
  1. 使用OpenIddict实现自定义Token发布方案

OpenIddict支持自定义Token发布方案,以适应各种需求。在以下示例中,我们将实现自定义发布方案来控制Token的过期时间:

public class CustomTokenEndpointHandler : OpenIddictServerHandler{    public CustomTokenEndpointHandler(IServiceProvider services)        : base(services)    {    }    public override async Task HandleAsync([NotNull] OpenIddictServerHandleContext context)    {        if (context == null)        {            throw new ArgumentNullException(nameof(context));        }        // 从数据库检索客户机应用程序.        var application = await context.HttpContext.GetOpenIddictServerApplicationAsync();        if (application == null)        {            throw new InvalidOperationException("The client application cannot be retrieved.");        }        // 从授权服务器设置检索用户主体.        var principal = context.HttpContext.User;        // 确保允许应用程序使用指定的授权类型。        if (!await ValidateClientRedirectUriAsync(application, context.Request))        {            throw new InvalidOperationException("The grant type is not allowed for this application.");        }        //注意:这个自定义令牌终端点总是忽略“scopes”参数,并根据授予的scopes/roles自动定义声明。        var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(),            OpenIddictServerDefaults.AuthenticationScheme);                    // 根据请求的自定义授权类型自定义令牌生命周期.        if (string.Equals(context.Request.GrantType, "urn:custom_grant", StringComparison.OrdinalIgnoreCase))        {            // Set the token expiration to 1 hour.            ticket.Properties.ExpiresUtc = context.Options.SystemClock.UtcNow.AddHours(1);        }        else        {            // 将令牌过期时间设置为默认持续时间(5分钟)            ticket.Properties.ExpiresUtc = context.Options.SystemClock.UtcNow.Add(                context.Options.AccessTokenLifetime ?? TimeSpan.FromMinutes(5));        }        context.Logger.LogInformation("The custom token request was successfully processed.");        await context.HttpContext.SignInAsync(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);        // 将响应标记为已处理,以跳过管道的其余部分.        context.HandleRequest();    }}

您需要将其添加到OpenIddict配置中:

services.AddOpenIddict()    .AddCore(options =>    {        // ...    })    .AddServer(options =>{    // ...        options.Handlers.Add(new CustomTokenEndpointHandler(services));        // ...}).AddValidation(options =>{    // ...});

此时,您可以使用urn:custom_grant授权类型来发出过期时间为1小时的Token,这可以通过以下方式完成:

var client = new HttpClient();var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:5000/connect/token");request.Content = new FormUrlEncodedContent(new Dictionary{    ["grant_type"] = "urn:custom_grant",    ["client_id"] = "your_client_id",    ["client_secret"] = "your_client_secret",    ["scope"] = "your_scopes_separated_by_spaces"});var response = await client.SendAsync(request);var payload = await response.Content.ReadAsStringAsync();

总结

本文介绍了如何使用OpenIddict创建一个基本的身份验证和授权服务器。当然,在实现身份验证和授权服务器时有很多细节需要考虑,例如维护安全性、处理错误、管理用户和客户端应用程序等。希望这篇文章对您有所帮助!

关键词: