IdentityServer4 使用Authorization Code Flow保护ASP.NET Core MVC客户端
1.Authorization Code基本概念
Authorization Code
OAuth2.0 - Authorization Code Grant(授权码授权)
OpenId Connect - Authorization Code Flow(授权码流程)
使用于保密客户端(Confidential Client)
服务器端的Web应用
对用户和客户端进行身份验证
OAuth2.0 Authorization Code Grant
请求流程
身份认证请求
身份认证请求的响应
Token请求
Token请求的响应
2.ASP.NET Core MVC 客户端认证
2.1创建ASP.NET Core MVC 客户端
2.2在Startup中修改认证
这里只在Privacy页面标签进行验证

public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); //JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); JwtSecurityTokenHandler.DefaultMapInboundClaims = false; //dotnet add package Microsoft.AspNetCore.Authentication.OpenIdConnect services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;//"Cookie" options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;//"oidc"; }) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme) .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options => { options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.Authority = "https://localhost:5001"; options.RequireHttpsMetadata = false; options.ClientId = "mvc client"; options.ClientSecret = "mvc secret"; options.ResponseType = "code"; options.SaveTokens = true; options.Scope.Clear(); options.Scope.Add("scope1"); options.Scope.Add(OidcConstants.StandardScopes.OpenId); options.Scope.Add(OidcConstants.StandardScopes.Profile); options.Scope.Add(OidcConstants.StandardScopes.OfflineAccess); options.Scope.Add(OidcConstants.StandardScopes.Email); options.Scope.Add(OidcConstants.StandardScopes.Phone); options.Scope.Add(OidcConstants.StandardScopes.Address); }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { //endpoints.MapDefaultControllerRoute() // .RequireAuthorization(); endpoints.MapDefaultControllerRoute(); }); //if (env.IsDevelopment()) //{ // app.UseDeveloperExceptionPage(); //} //else //{ // app.UseExceptionHandler("/Home/Error"); //} //app.UseAuthentication(); //app.UseStaticFiles(); //app.UseCookiePolicy(); //app.UseMvc(routes => //{ // routes.MapRoute( // name: "default", // template: "{controller=Home}/{action=Index}/{id?}"); //}); } }
修改认证地址及需要访问的被保护资源
2.3修改HomeController代码 获取Access Token

//[Authorize] public class HomeController : Controller { private readonly ILogger<HomeController> _logger; public HomeController(ILogger<HomeController> logger) { _logger = logger; } /// <summary> /// 使用AccessToken访问Api资源 /// </summary> /// <returns></returns> /// <exception cref="Exception"></exception> public async Task<IActionResult> Index() { //// discovery endpoint //var client = new HttpClient(); //var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001"); //if (disco.IsError) //{ // throw new Exception(disco.Error); //} //var accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken); //client.SetBearerToken(accessToken); //var response = await client.GetAsync("https://localhost:6001/identity"); //if (!response.IsSuccessStatusCode) //{ // throw new Exception(response.ReasonPhrase); //} //var content = await response.Content.ReadAsStringAsync(); //return View("Index", content); return View(); } [Authorize] public async Task<IActionResult> Privacy() { var accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken); var idToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.IdToken); var refreshToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.RefreshToken); var code = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.Code); ViewData["accessToken"] = accessToken; ViewData["idToken"] = idToken; ViewData["refreshToken"] = refreshToken; return View(); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } public async Task Logout() { await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme); } }
3修改Idp认证服务的配置
3.1增加Mvc客户端

public static class Config { public static IEnumerable<IdentityResource> IdentityResources => new IdentityResource[] { new IdentityResources.OpenId(), new IdentityResources.Profile(), new IdentityResources.Email(), new IdentityResources.Phone(), new IdentityResources.Address(), }; public static IEnumerable<ApiScope> ApiScopes => new ApiScope[] { new ApiScope("scope1"), new ApiScope("scope2"), }; public static IEnumerable<Client> Clients => new Client[] { //client credentials flow client new Client(){ ClientId = "console client", ClientName = "Client Credentials Client", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("511536EF-F270-4058-80CA-1C89C192F69A".Sha256()) }, AllowedScopes ={ "scope1 openid profile"} }, //wpf client password grant new Client(){ ClientId = "wpf client", ClientName = "ResourceOwnerPassword Client", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, ClientSecrets = { new Secret("wpf secret".Sha256()) }, //允许请求api资源 APIs //允许请求身份认证的数据 identiy data AllowedScopes ={ "scope1", IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.Email, IdentityServerConstants.StandardScopes.Phone, IdentityServerConstants.StandardScopes.Address }, }, //mvc client password grant new Client(){ ClientId = "mvc client", ClientName = "CodeAndClientCredentials Client", AllowedGrantTypes = GrantTypes.CodeAndClientCredentials, ClientSecrets = { new Secret("mvc secret".Sha256()) }, RedirectUris = { "https://localhost:5003/signin-oidc" }, FrontChannelLogoutUri = "https://localhost:5003/signin-oidc", // where to redirect to after logout PostLogoutRedirectUris = { "https://localhost:5003/signout-callback-oidc" }, //请求配置的Identity资源 AlwaysIncludeUserClaimsInIdToken=true, //允许refreshtoken AllowOfflineAccess = true, //允许请求api资源 APIs //允许请求身份认证的数据 identiy data AllowedScopes ={ "scope1", IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.Email, IdentityServerConstants.StandardScopes.Phone, IdentityServerConstants.StandardScopes.Address }, } #region // // m2m client credentials flow client //new Client //{ // ClientId = "m2m.client", // ClientName = "Client Credentials Client", // AllowedGrantTypes = GrantTypes.ClientCredentials, // ClientSecrets = { new Secret("511536EF-F270-4058-80CA-1C89C192F69A".Sha256()) }, // AllowedScopes = { "scope1" } //}, //// interactive client using code flow + pkce //new Client //{ // ClientId = "interactive", // ClientSecrets = { new Secret("49C1A7E1-0C79-4A89-A3D6-A37998FB86B0".Sha256()) }, // AllowedGrantTypes = GrantTypes.Code, // RedirectUris = { "https://localhost:44300/signin-oidc" }, // FrontChannelLogoutUri = "https://localhost:44300/signout-oidc", // PostLogoutRedirectUris = { "https://localhost:44300/signout-callback-oidc" }, // AllowOfflineAccess = true, // AllowedScopes = { "openid", "profile", "scope2" } //}, #endregion }; }
3.2退出登录后跳到主页面(Home/Index)
在Account/AccountOptions下
这里我进行修改了但是登出后没有跳到Home/Index,暂时未找到原因
4.运行
4.1运行认证服务idp和被保护的资源Scope1Resource
把之前的idp和Scope1Resource都运行起来
直接在项目根目录运行bin/debug/exe文件即可
4.2运行MvcClient
4.3登录
点击Privacy直接跳到了identityserver登录界面
4.4登录成功
登录成功后idp认证资源和受保护的资源都可以进行访问
4.3登出
登录后没有跳转到Home/Index,后面学习深入后再想办法解决
5.为MVC客户端刷新Token
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!