首页 > 其他分享 >OpenIddict使用教程

OpenIddict使用教程

时间:2023-06-16 14:34:06浏览次数:40  
标签:教程 return await 使用 var OpenIddict model options

@@openiddict password 模式 流程

 

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<ApplicationDbContext>(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<ApplicationDbContext>()
            .ReplaceDefaultEntities<ApplicationDbContext>();
    })
    .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<IActionResult> 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<IActionResult> 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<IActionResult> 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: <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>link</a>.");

    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<IActionResult> 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<OpenIddictServerOptions>
{
    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<string, string>
{
    ["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创建一个基本的身份验证和授权服务器。当然,在实现身份验证和授权服务器时有很多细节需要考虑,例如维护安全性、处理错误、管理用户和客户端应用程序等。希望这篇文章对您有所帮助!

标签:教程,return,await,使用,var,OpenIddict,model,options
From: https://www.cnblogs.com/wl-blog/p/17485451.html

相关文章

  • git cherry-pick 教程
    场景:部分代码变动(某几个提交),这时可以采用Cherrypick。1拿到代码变动提交的log,切换到代码变动的分支,然后查看log.==>gitcheckout变动分支名gitlog在英文状态下输入q!退出log2切换到自己的分支,输入gitcherry-pickgitlog的哈希值3gitpush提交上去下面......
  • 使用Postman的Get请求遇到:"type": "parsing_exception","reason": "Unknown
    错误如图原因postman自身的的bug问题。body里面写了json参数,结果postman将GET请求当成POST请求了。解决办法删掉json数据,或者重新建立一个连接。......
  • Apache Storm教程_编程入门自学教程_菜鸟教程-免费教程分享
    教程简介ApacheStorm是一个分布式实时大数据处理系统。Storm设计用于在容错和水平可扩展方法中处理大量数据。它是一个流数据框架,具有最高的摄取率。虽然Storm是无状态的,它通过ApacheZooKeeper管理分布式环境和集群状态。它很简单,您可以并行地对实时数据执行各种操作。ApacheS......
  • 隧道模式HTTP使用教程
    华科隧道HTTP格式为:服务器:端口账号密码隧道代理分钟2种模式:固定时间更改新IP(比如5分钟,10分钟,初次开通的时候可设定)请求一次更换一个新IP(可通过浏览器或者curl)1、使用浏览器1.1、使用浏览器切换IP(限制180次/小时,间隔>20秒)用浏览器访问 http://ip.hahado.cn/simple/switch-ip......
  • Adobe Prelude CC2022【Pl视频编辑软件】中文直装版安装教程
    dobePrelude是一个很好的视频编辑软件。该软件结合了优异的性能、优美的改进用户界面和许多奇妙的创意功能,包括WarpStabilizer、动态时间轴切割、扩展多机编辑、调整图层等。该专业视频捕获程序允许转换任何文件格式和设置标记。该应用程序提供了对文件准备过程的有效管理,并立即......
  • Adobe Media Encoder CC2022【视频与音频编码工具】安装教程
    新款AdobeMediaencoder2022正式上线,又称Me2022,该软件不仅为用户提供了转换视频、音频格式等功能,使用户能够转换各种视频或音频格式,或为不同的应用程序开发和各种格式编码音视频文件。还提供了各种专业的硬件设备编码格式设置和设计预设设置,包括视频渲染、剪切、摄取、转码等。更......
  • Adobe Lightroom Classic 2022 V11【图片后期处理软件】直装版安装教程
    Lightroom2022是一款功能强大、非常专业的图片编辑软件,由著名公司Adobe制作,可以为用户编辑照片。这个软件和我们熟悉的ps有很大的不同。它主要帮助用户简单方便地管理电脑上的照片,甚至完成照片的一些修改,比如去除不需要的物体,校正照片,增强照片的颜色。在生活中,很多人经常处理照片,但......
  • CHAT-GPT初使用
    拿chatgpt去试验了一下,一个挺小的需求,但是前后还是更改了三次,体验就是它可以不断改进之前实现的代码,但需要提需求的人比较清楚需求内的细节,差不多类似于,我有想法,它来实现,还是可以提高不少效率的。具体体验如下:我:请写一段C语言代码,将2M大小的文件填充0xff到30M,要求不能申请超过1K......
  • Adobe InDesign CC2022【ID 排版编辑软件】直装版安装教程
    AdobeInDesign2022是一款强大的编辑排版工具,它是市场上主要的数字出版应用程序之一,它为用户提供了一系列可扩展的工具,可以创造出引人注目的印刷版面和复杂的在线出版物。本软件是针对专业设计人员和初学者的需求而设计的,它拥有一个灵活的工作环境和节省时间的功能,以帮助你在保持布......
  • Apache Spark教程_编程入门自学教程_菜鸟教程-免费教程分享
    教程简介ApacheSpark是专为大规模数据处理而设计的快速通用的计算引擎。Spark是UCBerkeleyAMPlab(加州大学伯克利分校的AMP实验室)所开源的类HadoopMapReduce的通用并行框架,Spark,拥有HadoopMapReduce所具有的优点;但不同于MapReduce的是——Job中间输出结果可以保存在内存......