OpenIddict 登录及详细流程解析
GitHub上实例都是集成了Identity来实现,我这里去掉了相关东西,实现自定义的登录满足自己的结构要求
服务端配置添加数据库服务以及定时任务服务
builder.Services.AddDbContext<OpenIdDbContext>(options =>
{ options.UseMySql(constr, ServerVersion.AutoDetect(constr), builder =>
{
builder.UseRelationalNulls();
builder.MigrationsAssembly("OpenIdService"); });
options.UseOpenIddict();
}).AddQuartz(options =>
{
options.UseMicrosoftDependencyInjectionJobFactory();
options.UseSimpleTypeLoader();
options.UseInMemoryStore();
}).AddQuartzHostedService(options => options.WaitForJobsToComplete = true);
OpenIddict服务配置 根据需要而定
builder.Services.AddOpenIddict()
.AddCore(options =>
{
//配置OpenIddict以使用EntityFrameworkCore存储和模型。 注意: 调用replacedefaultenentities()来替换默认的OpenIddict实体。
options.UseEntityFrameworkCore().UseDbContext<OpenIdDbContext>();
//喜欢使用MongoDB的开发人员可以删除前面的代码行并配置OpenIddict使用指定的MongoDB数据库:
// options.UseMongoDb() .UseDatabase(new MongoClient().GetDatabase("openiddict"));
options.UseQuartz();
})
.AddServer(options =>
{ //配置交互服务地址
options.SetAuthorizationEndpointUris("/connect/authorize")
.SetDeviceEndpointUris("/connect/device")
.SetIntrospectionEndpointUris("/connect/introspect")
.SetRevocationEndpointUris("/connect/revocat")
.SetUserinfoEndpointUris("/connect/userinfo")
.SetVerificationEndpointUris("/connect/verify")
.SetLogoutEndpointUris("/connect/logout")
.SetTokenEndpointUris("/connect/token") //这是允许的模式
.AllowAuthorizationCodeFlow()
.AllowClientCredentialsFlow()
.AllowDeviceCodeFlow()
.AllowHybridFlow()
.AllowImplicitFlow()
.AllowPasswordFlow()
.AllowRefreshTokenFlow() .RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles) //提供给API校验Jwt令牌使用是配置
.AddEncryptionKey(new SymmetricSecurityKey(
Convert.FromBase64String("DRjd/GnduI3Efzen9V9BvbNUfc/VKgXltV7Kbk9sMkY=")))
// 加密凭证 、注册签名
.AddDevelopmentEncryptionCertificate().AddDevelopmentSigningCertificate() //强制客户端应用程序使用 Proof Key Code Exchange (PKCE)
.RequireProofKeyForCodeExchange()
.Configure(options =>
{
options.CodeChallengeMethods.Add(CodeChallengeMethods.Plain);
})
//配置 启用通过后的后续处理
.UseAspNetCore().EnableStatusCodePagesIntegration()
.EnableAuthorizationEndpointPassthrough()
.EnableLogoutEndpointPassthrough()
.EnableTokenEndpointPassthrough()
.EnableUserinfoEndpointPassthrough()
.EnableVerificationEndpointPassthrough()
.DisableTransportSecurityRequirement(); // 禁用HTTPS 在开发测试环境 #region 禁用忽略选项配置
//禁用授权信息存储
// options.DisableAuthorizationStorage();
// options.AcceptAnonymousClients();
// options.DisableScopeValidation();
// options.IgnoreEndpointPermissions()
// .IgnoreGrantTypePermissions()
// .IgnoreResponseTypePermissions()
// .IgnoreScopePermissions();
options.DisableAccessTokenEncryption();
#endregion }).AddValidation(options =>
{
options.UseLocalServer();
//强制授权条目验证 出于性能原因,OpenIddict 3.0在接收API请求时默认不检查授权条目的状态:即使附加的授权被撤销
//,访问令牌也被认为是有效的
options.EnableAuthorizationEntryValidation();
options.UseAspNetCore(); });
准备工作基本完成,如果你需要做一个服务登录界面,这是需要提供相关页面和认证服务代码,然后去完成相关页面逻辑
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = UosoAuthenticationScheme.AuthenticationScheme; }).AddCookie(UosoAuthenticationScheme.AuthenticationScheme, options =>
{
options.AccessDeniedPath = "/Account/Login";
options.LoginPath = "/Account/Login";
options.LogoutPath = "/Account/LogOut";
});
这里需要自定义AuthorizationController 来实现认证逻辑,这里可以参考官方例子代码说明
登录页面逻辑采用自己的方式如下实现 SignInAsync 方法完成
var properties = new AuthenticationProperties { IsPersistent = model.RememberMe, ExpiresUtc= DateTimeOffset.UtcNow.AddMinutes(30) };
var principal = CreateUserPrincpal(resultdata);
await HttpContext.SignInAsync(UosoAuthenticationScheme.AuthenticationScheme, principal, properties);
这里放下AuthorizationController源码

public class AuthorizationController : Controller
{ private const string applicationname = UosoAuthenticationScheme.AuthenticationScheme; #region 注入OpenIddict相关交互接口
private readonly IOpenIddictApplicationManager _applicationManager;
private readonly IOpenIddictAuthorizationManager _authorizationManager;
private readonly IOpenIddictScopeManager _scopeManager;
IAuthenticationSchemeProvider _schemeProvider;
IUSUserLoginService _userService;
#endregion
#region 注入用户信息交互接口
//private readonly SignInManager<ApplicationUser> _signInManager;
//private readonly UserManager<ApplicationUser> _userManager;
#endregion public AuthorizationController(
IOpenIddictApplicationManager applicationManager,
IOpenIddictAuthorizationManager authorizationManager,
IOpenIddictScopeManager scopeManager, IUSUserLoginService userService)
{
_applicationManager = applicationManager;
_authorizationManager = authorizationManager;
_scopeManager = scopeManager;
_userService = userService;
} #region 授权端点的操作 指定路由 这一步自己处理 private IEnumerable<string> GetDestinations(Claim claim)
{
// Note: by default, claims are NOT automatically included in the access and identity tokens.
// To allow OpenIddict to serialize them, you must attach them a destination, that specifies
// whether they should be included in access tokens, in identity tokens or in both. return claim.Type switch
{
Claims.Name or
Claims.Subject
=> ImmutableArray.Create(Destinations.AccessToken, Destinations.IdentityToken), _ => ImmutableArray.Create(Destinations.AccessToken),
};
} private IEnumerable<string> GetDestinations(Claim claim, ClaimsPrincipal principal)
{ switch (claim.Type)
{
case Claims.Name:
yield return Destinations.AccessToken; if (principal.HasScope(Scopes.Profile))
yield return Destinations.IdentityToken; yield break; case Claims.Email:
yield return Destinations.AccessToken; if (principal.HasScope(Scopes.Email))
yield return Destinations.IdentityToken; yield break; case Claims.Role:
yield return Destinations.AccessToken; if (principal.HasScope(Scopes.Roles))
yield return Destinations.IdentityToken; yield break; case "AspNet.Identity.SecurityStamp": yield break; default:
yield return Destinations.AccessToken;
yield break;
}
}
private ClaimsPrincipal CreateUserPrincpal(USUserLoginInfo resultdata, string claimsIdentityName = "USLOGININFO")
{ //登录成功流程
ClaimsIdentity identity = new ClaimsIdentity(claimsIdentityName);
identity.AddClaim(new Claim(Claims.Subject, resultdata.Id + ""));
identity.AddClaim(new Claim(Claims.Name, resultdata.UserName));
identity.AddClaim(new Claim(Claims.Nickname, resultdata.NickName));
identity.AddClaim(new Claim("tenantid", resultdata.TenantId + ""));
identity.AddClaim(new Claim("organizes", resultdata.Organizes + ""));
identity.AddClaim(new Claim("usergroups", resultdata.UserGroups));
identity.AddClaim(new Claim("usertype", resultdata.UserType + ""));
identity.AddClaim(new Claim("userroles", resultdata.Roles + ""));
identity.AddClaim(new Claim("userposts", resultdata.Posts + ""));
return new ClaimsPrincipal(identity);
}
/// <summary>
/// 登录权限校验
/// </summary>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
/// <exception cref="Exception"></exception>
[HttpGet("~/connect/authorize")]
[HttpPost("~/connect/authorize")]
[IgnoreAntiforgeryToken]
public async Task<IActionResult> Authorize()
{
// var s=await _schemeProvider.GetAllSchemesAsync(); //通过扩展的获取自定义的参数校验
var request = HttpContext.GetOpenIddictServerRequest() ?? throw new InvalidOperationException("未获取到相关认证情况"); #region 存在登录凭证且明确了登录请求的行为
// 存在登录凭证且明确了登录请求的行为
if (request.HasPrompt(Prompts.Login))
{
//这里有个小坑,在Challenge之前必须把这个行为去掉 不然 Challenge 进入 /connect/authorize 路由陷入死循环
var prompt = string.Join(" ", request.GetPrompts().Remove(Prompts.Login));
var parameters = Request.HasFormContentType ?
Request.Form.Where(parameter => parameter.Key != Parameters.Prompt).ToList() :
Request.Query.Where(parameter => parameter.Key != Parameters.Prompt).ToList(); parameters.Add(KeyValuePair.Create(Parameters.Prompt, new StringValues(prompt))); return Challenge(
authenticationSchemes: applicationname, // IdentityConstants.ApplicationScheme,
properties: new AuthenticationProperties
{
RedirectUri = Request.PathBase + Request.Path + QueryString.Create(parameters)
});
}
#endregion
//检索本地的Cookies信息 确定重定向页面 这里都是UTC时间来设置的过期情况 这里没有用Identity 所以这里可以指定自己的应用名称
var result = await HttpContext.AuthenticateAsync(applicationname); //IdentityConstants.ApplicationScheme
#region 未获取本地Cookies信息或者 cookie过期的情况
if (request == null || !result.Succeeded || (request.MaxAge != null && result.Properties?.IssuedUtc != null &&
DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value)))
{
//是否是无效授权
if (request.HasPrompt(Prompts.None))
{
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string?>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.LoginRequired,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "用户未登录."
}));
}
return Challenge(
authenticationSchemes: applicationname,
properties: new AuthenticationProperties
{
RedirectUri = Request.PathBase + Request.Path + QueryString.Create(
Request.HasFormContentType ? Request.Form.ToList() : Request.Query.ToList())
});
}
#endregion var resultdata = _userService.GetLoginInfo(new Guid(result.Principal.GetClaim(Claims.Subject) ?? throw new Exception("用户标识存在"))) ?? throw new Exception("用户详细信息不存在"); // 获取客户端详细信息 验证其他数据
var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? throw new InvalidOperationException("未查找到该客户端的应用详细信息"); //查找当前情况客户端下请求用户的持久化授权数据信息
var authorizations = await _authorizationManager.FindAsync(
subject: resultdata.Id.ToString(),
client: await _applicationManager.GetIdAsync(application) ?? throw new Exception("没有找到客户端的应用信息"), //这里区分下 是application的Id而不是ClientId
status: Statuses.Valid,
type: AuthorizationTypes.Permanent,
scopes: request.GetScopes()).ToListAsync(); var consenttype = await _applicationManager.GetConsentTypeAsync(application);
//获取授权同意确认页面
switch (consenttype)
{
//判断授权同意的类型 //1 外部允许的且没有任何授权项
case ConsentTypes.External when !authorizations.Any():
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string?>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
"登录用户没用访问该客户端应用的权限"
})); // 隐式、外部授权、显示模式模式
case ConsentTypes.Implicit:
case ConsentTypes.External when authorizations.Any():
case ConsentTypes.Explicit when authorizations.Any() && !request.HasPrompt(Prompts.Consent): ClaimsPrincipal principal = CreateUserPrincpal(resultdata);
//设置请求的范围
principal.SetScopes(request.GetScopes()); //查找scope允许访问的资源
var resources = await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync();
//通过扩展设置不同的资源访问 其实本质都是设置Claims 只是 key 在 scope以及Resource上不同
//Resource = "oi_rsrc";
// Scope = "oi_scp"; principal.SetResources(resources); // 自动创建一个永久授权,以避免需要明确的同意 用于包含相同范围的未来授权或令牌请求
var authorization = authorizations.LastOrDefault();
if (authorization is null)
{
authorization = await _authorizationManager.CreateAsync(
principal: principal,
subject: resultdata.Id.ToString(),
client: await _applicationManager.GetIdAsync(application),
type: AuthorizationTypes.Permanent,
scopes: principal.GetScopes());
} principal.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization)); foreach (var claim in principal.Claims)
{
claim.SetDestinations(GetDestinations(claim, principal));
}
//登录 OpenIddict签发令牌
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); // At this point, no authorization was found in the database and an error must be returned
// if the client application specified prompt=none in the authorization request.
case ConsentTypes.Explicit when request.HasPrompt(Prompts.None):
case ConsentTypes.Systematic when request.HasPrompt(Prompts.None):
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string?>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
"Interactive user consent is required."
})); // In every other case, render the consent form.
default:
return View(new AuthorizeViewModel
{
ApplicationName = await _applicationManager.GetLocalizedDisplayNameAsync(application),
Scope = request.Scope
});
} } #region 同意、拒绝逻辑 [Authorize, FormValueRequired("submit.Accept")]
[HttpPost("~/connect/authorize"), ValidateAntiForgeryToken]
public async Task<IActionResult> Accept()
{
var request = HttpContext.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved."); var resultdata = _userService.GetLoginInfo(new Guid(User.GetClaim(Claims.Subject) ?? throw new Exception("用户标识存在"))) ?? throw new Exception("用户详细信息不存在"); // 获取客户端详细信息 验证其他数据
var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? throw new InvalidOperationException("未查找到该客户端的应用详细信息"); //查找当前情况客户端下请求用户的持久化授权数据信息
var authorizations = await _authorizationManager.FindAsync(
subject: resultdata.Id.ToString(),
client: await _applicationManager.GetIdAsync(application) ?? throw new Exception("没有找到客户端的应用信息"), //这里区分下 是application的Id而不是ClientId
status: Statuses.Valid,
type: AuthorizationTypes.Permanent,
scopes: request.GetScopes()).ToListAsync(); if (!authorizations.Any() && await _applicationManager.HasConsentTypeAsync(application, ConsentTypes.External))
{
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string?>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
"The logged in user is not allowed to access this client application."
}));
}
ClaimsPrincipal principal = CreateUserPrincpal(resultdata); principal.SetScopes(request.GetScopes());
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync()); var authorization = authorizations.LastOrDefault();
if (authorization is null)
{ authorization = await _authorizationManager.CreateAsync(
principal: principal,
subject: resultdata.Id.ToString(),
client: await _applicationManager.GetIdAsync(application) ?? throw new Exception("未找到客户端应用信息"),
type: AuthorizationTypes.Permanent,
scopes: principal.GetScopes());
} principal.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization)); foreach (var claim in principal.Claims)
{
claim.SetDestinations(GetDestinations(claim, principal));
} return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } [Authorize, FormValueRequired("submit.Deny")]
[HttpPost("~/connect/authorize"), ValidateAntiForgeryToken] public IActionResult Deny() => Forbid(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); #endregion #endregion #region 获取Token地址 包括所有方式 /// <summary>
/// 可以指定不同的获取Token的客户端逻辑
/// </summary>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception> [HttpPost("~/connect/token"), Produces("application/json")]
public async Task<IActionResult> Exchange()
{ var request = HttpContext.GetOpenIddictServerRequest() ?? throw new InvalidOperationException("OIDC请求不存在."); if (request.IsClientCredentialsGrantType())
{ var application = await _applicationManager.FindByClientIdAsync(request.ClientId);
if (application == null)
{
throw new InvalidOperationException("当前客户端应用不存在");
} var identity = new ClaimsIdentity(
TokenValidationParameters.DefaultAuthenticationType,
Claims.Name, Claims.Role); // Use the client_id as the subject identifier.
identity.AddClaim(Claims.Subject, await _applicationManager.GetClientIdAsync(application),
Destinations.AccessToken, Destinations.IdentityToken); identity.AddClaim(Claims.Name, await _applicationManager.GetDisplayNameAsync(application),
Destinations.AccessToken, Destinations.IdentityToken); var principal = new ClaimsPrincipal(identity);
principal.SetScopes(request.GetScopes());
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync()); foreach (var claim in principal.Claims)
{
claim.SetDestinations(GetDestinations(claim));
} return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } else if (request.IsPasswordGrantType())
{ var result = _userService.UserLogin(new LoginModel { username = request?.Username, userpwd = request.Password });
if (!result.IsSuccess)
{ throw new OpenIddictExceptions.ValidationException(result.Message);
//return Forbid(
// authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
// properties: new AuthenticationProperties(new Dictionary<string, string?>
// {
// [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
// [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = result.Message
// }));
} var resultdata = result.Data as USUserLoginInfo ?? throw new Exception("登录用户信息异常"); var principal = CreateUserPrincpal(resultdata); //这是密码模式能方位那些
principal.SetScopes(new[]
{
Scopes.OpenId,
Scopes.Email,
Scopes.Profile,
Scopes.Roles,
"user_api",
"openiddict_api" }); principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync()); foreach (var claim in principal.Claims)
{
claim.SetDestinations(GetDestinations(claim, principal));
}
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
} if (request.IsAuthorizationCodeGrantType() || request.IsDeviceCodeGrantType() || request.IsRefreshTokenGrantType())
{
var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal; // principal.Identity.IsAuthenticated var user = _userService.GetLoginInfo(new Guid(principal?.GetClaim(Claims.Subject) ?? throw new Exception("用户标识存在")));
if (user == null)
{
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string?>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "令牌已失效"
}));
} // Ensure the user is still allowed to sign in.
//if (!await _signInManager.CanSignInAsync(user))
//{
// return Forbid(
// authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
// properties: new AuthenticationProperties(new Dictionary<string, string?>
// {
// [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
// [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in."
// }));
//} foreach (var claim in principal.Claims)
{
claim.SetDestinations(GetDestinations(claim, principal));
} return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}
throw new InvalidOperationException("The specified grant type is not supported.");
} [HttpGet("~/connect/logout")]
public IActionResult Logout() => View(); [ActionName(nameof(Logout)), HttpPost("~/connect/logout"), ValidateAntiForgeryToken]
public async Task<IActionResult> LogoutPost()
{ await HttpContext.SignOutAsync();
return SignOut(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties
{
RedirectUri = "/"
});
}
#endregion
}
AuthorizationController
整个逻辑如下我画一张图结合关键代码来诠释它

先说WebSite通过 OpenIdConnect 来完成登录认证 ,最后都是交给中间件中的signin-oidc页面来完成客户端Cookie登录状态维持,如果在客户端我们注释掉会发生什么
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
// options.RequireAuthenticatedSignIn = true; })
// .AddCookie(options =>
//{
// options.LoginPath = "/";
// options.ExpireTimeSpan = TimeSpan.FromMinutes(50);
// options.SlidingExpiration = false;
//})
.AddOpenIdConnect(options =>
{
// Note: these settings must match the application details
// inserted in the database at the server level.
options.ClientId = "mvc";
options.ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654"; options.RequireHttpsMetadata = false;
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = false;
options.UsePkce=true;
options.Prompt = OpenIdConnectPrompt.Login;
options.ResponseType = OpenIdConnectResponseType.Code;
options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;
//options.Prompt = "login";
options.Authority = "http://localhost:5276";
//options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
options.Scope.Add("roles");
options.Scope.Add("user_api");
options.Scope.Add("openiddict_api");
options.SecurityTokenValidator = new JwtSecurityTokenHandler
{
InboundClaimTypeMap = new Dictionary<string, string>()
};
options.TokenValidationParameters.NameClaimType = "name";
options.TokenValidationParameters.RoleClaimType = "role";
options.AccessDeniedPath = "/";
});

这就是为什么我们写回调地址 要写 http://localhost:44381/signin-oidc 的signin-oidc ,如果要写其他的,需要我们自己处理,这里OpenIdConnect中间件帮我们处理了,这其中包括维持客户端的登录状态,类似我们用其他第三方登录一样回调后需要按某个协议来处理,如果客户端设置了
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = false;
GetClaimsFromUserInfoEndpoint=true 需要服务端准备UserInfo接口否则会报错
SaveTokens=true 客户端可以拿到 var token = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);拿到AccessToken
WebAPI 可结合服务端配置的
AddEncryptionKey(new SymmetricSecurityKey(
Convert.FromBase64String("DRjd/GnduI3Efzen9V9BvbNUfc/VKgXltV7Kbk9sMkY=")))

Token令牌的签发 /connect/token 接口 根据请求模式签发对应令牌
if (request.IsClientCredentialsGrantType())
{
//处理
} else if (request.IsPasswordGrantType())
{
//处理
}
else if (request.IsAuthorizationCodeGrantType() || request.IsDeviceCodeGrantType() || request.IsRefreshTokenGrantType())
{
//处理
}
OpenIddict 登录及详细流程解析的更多相关文章
- Session (简介、、相关方法、流程解析、登录验证)
Session简介 Session的由来 Cookie虽然在一定程度上解决了"保持状态"的需求,但是由于Cookie本身最大支持4096字节,以及Cookie本身保存在客户端,可能 ...
- TCP/IP协议三次握手与四次握手流程解析
原文链接地址:http://www.2cto.com/net/201310/251896.html TCP/IP协议三次握手与四次握手流程解析 TCP/IP协议的详细信息参看<TCP/IP协议详 ...
- iOS-申请邓白氏编码的超详细流程介绍
导读 11.22号要申请苹果公司开发者账号,所以当天下午申请了邓白氏编码,昨天邓白氏编码下来了,这里就做一下记录,分享给大家. 概览 11.22提交的邓白氏编码申请,11.28(礼拜一)华夏邓白氏发来 ...
- TCP/IP协议三次握手与四次握手流程解析(转载及总结)
原文地址:http://www.2cto.com/net/201310/251896.html,转载请注明出处: TCP/IP协议三次握手与四次握手流程解析 一.TCP报文格式 TCP/IP协议的详 ...
- Django生命周期 URL ----> CBV 源码解析-------------- 及rest_framework APIView 源码流程解析
一.一个请求来到Django 的生命周期 FBV 不讨论 CBV: 请求被代理转发到uwsgi: 开始Django的流程: 首先经过中间件process_request (session等) 然后 ...
- 微信小程序提交审核并发布详细流程
微信小程序提交审核并发布详细流程 审核在1小时到N天不等 官方7天,一般3天内 提交审核?如何发布?审核过程中注意事项? 服务器: 域名只支持 https (request.uploadFile.do ...
- virsh的详细命令解析(一)
virsh的详细命令解析 virsh 有命令模式和交互模式如果直接在vrish后面添加参数是命令模式,如果直接写virsh,就会进入交互模式 virsh list 列出所有的虚拟机,虚拟机的状态有(8 ...
- HBase - 数据写入流程解析
本文由 网易云发布. 作者:范欣欣 本篇文章仅限内部分享,如需转载,请联系网易获取授权. 众所周知,HBase默认适用于写多读少的应用,正是依赖于它相当出色的写入性能:一个100台RS的集群可以轻松 ...
- [转] electron实战开发详细流程
[From] http://www.myk3.com/arc-8856.html 很久没有更新博客,人越来越懒了,唉 说好做的electron教程距离上次玩electron已经过去了好几个月了.. 这 ...
随机推荐
- 【Linux】【Basis】Grub
GRUB(Boot Loader): 1. grub: GRand Unified Bootloader grub 0.x: grub legacy grub 1.x: grub2 2. gr ...
- 搭建mybatis开发环境
1.创建工程 <groupId>com.hope</groupId> <artifactId>day01_eesy_01mybatis</artifa ...
- 【Matlab】abs不支持复整数
需要将uint8转换成double型数据才能计算 https://blog.csdn.net/lihe4151021/article/details/89372688 图像数据格式uint8与doub ...
- java多线程4:volatile关键字
上文说到了 synchronized,那么就不得不说下 volatile关键字了,它们两者经常协同处理多线程的安全问题. volatile保证可见性 那么volatile的作用是什么呢? 在jvm运行 ...
- Python用pandas获取Excel数据
import pandas as pd df1 = pd.DataFrame(pd.read_excel(r'C:\python测试文件\我的三国啊.xlsx',sheet_name='Sheet1' ...
- [源码解析] PyTorch 分布式(17) --- 结合DDP和分布式 RPC 框架
[源码解析] PyTorch 分布式(17) --- 结合DDP和分布式 RPC 框架 目录 [源码解析] PyTorch 分布式(17) --- 结合DDP和分布式 RPC 框架 0x00 摘要 0 ...
- JAVA日记之mybatis-3一对一,一对多,多对多xml与注解配置
1.Mybatis多表查询1.1 一对一查询1.1.1 一对一查询的模型用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户 一对一查询的需求:查询一个订单,与此同时查询出该订单所属的 ...
- LuoguP7375 [COCI2018-2019#5] Jarvis 题解
Content 有 \(n\) 架无人机,每架无人机都有一个当前属性值 \(a_i\) 和出战属性值 \(b_i\).你可以给每架无人机的当前属性值同时加一个数 \(x\)(但只能做一次),使得能够出 ...
- java 多线程 线程组ThreadGroup;多线程的异常处理。interrupt批量停止组内线程;线程组异常处理
1,线程组定义: 线程组存在的意义,首要原因是安全.java默认创建的线程都是属于系统线程组,而同一个线程组的线程是可以相互修改对方的数据的.但如果在不同的线程组中,那么就不能"跨线程组&q ...
- DKT模型及其TensorFlow实现(Deep knowledge tracing with Tensorflow)
今年2月15日,谷歌举办了首届TensorFlow Dev Summit,并且发布了TensorFlow 1.0 正式版. 3月18号,上海的谷歌开发者社区(GDG)组织了针对峰会的专场回顾活动.本文 ...