IdentityServer4 for ASP.NET WEB Applications (单点登录统一认证)

服务端

  1. 新建ASP.NET Core Web MVC 项目

SSOServer

  1. NuGet安装IdentityServer4
  2. 新建Config.cs类
    public class Config
    {
        // scopes define the resources in your system
        public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
            };
        }

        // clients want to access resources (aka scopes)
        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
            {
                // OpenID Connect隐式流客户端(MVC)
                new Client
                {
                    ClientId = "mvc",
                    ClientName = "MVC Client",
                    AllowedGrantTypes = GrantTypes.Implicit,//隐式方式
                    RequireConsent=false,//如果不需要显示否同意授权 页面 这里就设置为false
                    RedirectUris = { "http://localhost:5002/signin-oidc" },//登录成功后返回的客户端地址
                    PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },//注销登录后返回的客户端地址

                    AllowedScopes =//下面这两个必须要加吧 不太明白啥意思
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile
                    }
                }
            };
        }
    }
  1. Startup.cs中注入IdentityServer4
    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();
            // 注入IdentityServer
            services.AddIdentityServer()
                .AddDeveloperSigningCredential()
                .AddInMemoryIdentityResources(Config.GetIdentityResources())
                .AddInMemoryClients(Config.GetClients());
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();

            app.UseRouting();

            // 注入IdentityServer
            app.UseIdentityServer();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }

  1. 项目属性中将调试端口改为5000
  2. 调试访问 https://localhost:5000/.well-known/openid-configuration

能正常访问JSON即可.

  1. 增加登陆控制

AccountController

using Microsoft.AspNetCore.Mvc;
using System;
using System.Threading.Tasks;
using IdentityServer4;
using Microsoft.AspNetCore.Http;

namespace SSOServer.Controllers
{
    public class AccountController : Controller
    {
        /// <summary>
        /// 登录页面
        /// </summary>
        [HttpGet]
        public async Task<IActionResult> Login(string returnUrl = null)
        {
            ViewData["returnUrl"] = returnUrl;
            return View();
        }

        /// <summary>
        /// 登录post回发处理
        /// </summary>
        [HttpPost]
        public async Task<IActionResult> Login(string userName, string password, string returnUrl = null)
        {
            ViewData["returnUrl"] = returnUrl;
            if (userName=="test" && password=="test")
            {
                Microsoft.AspNetCore.Authentication.AuthenticationProperties props = new Microsoft.AspNetCore.Authentication.AuthenticationProperties
                {
                    IsPersistent = true,
                    ExpiresUtc = DateTimeOffset.UtcNow.Add(TimeSpan.FromDays(1))
                };
                await HttpContext.SignInAsync(new IdentityServerUser(Guid.NewGuid().ToString()){DisplayName = userName},props); // 来源于Microsoft.AspNetCore.Http扩展
                if (returnUrl != null)
                {
                    return Redirect(returnUrl);
                }
                return View();
            }
            else
            {
                return View();
            }
        }
    }
}

  1. 登陆视图
@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Login</title>
</head>
<body>
<div align="center">
    <h1>统一认证登录中心</h1>
    <form action="/Account/Login" method="post">
        用户名:<input type="text" name="userName" /><br />
        密 码:<input type="password" name="password" /><input type="hidden" name="returnUrl" value="@ViewData["returnUrl"]" /> <br />
        <input type="submit" value="登录" />
    </form>
</div>
</body>
</html>
  1. 测试登陆

http://localhost:5000/Account/Login

  1. 可以安装Swashbuckle.AspNetCore以备调试

swgger配置方式参考这里:
https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-5.0&tabs=visual-studio




客户端(Core)

  1. 新建ASP.NET Core Web MVC项目

Core.Client

  1. NuGet安装IdentityServer4.AccessTokenValidation

  2. NuGet安装Microsoft.AspNetCore.Authentication.OpenIdConnect

  3. Startup配置

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.IdentityModel.Tokens.Jwt;

namespace Core.Client
{
    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)
        {
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); // System.IdentityModel.Tokens.Jwt

            services.AddAuthentication(options =>
                {
                    options.DefaultScheme = "Cookies";
                    options.DefaultChallengeScheme = "oidc";
                })
                .AddCookie("Cookies")
                .AddOpenIdConnect("oidc", options =>
                {
                    options.SignInScheme = "Cookies";

                    options.Authority = "http://localhost:5000";
                    options.RequireHttpsMetadata = false;

                    options.ClientId = "mvc";
                    options.SaveTokens = true;
                });

            services.AddControllersWithViews();

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthentication(); 

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

  1. Home控制器中增加认证标识

[Microsoft.AspNetCore.Authorization.Authorize]

  1. 调试

这一步骤中,坑最多.
首先,大部分新浏览器(IE除外),已经不支持SameSite=None & Secure=null的用法
因此试用了网上的SameSiteCookiesServiceCollectionExtensions等方法都行不通
将所有站点改为HTTPS站点后,问题解决




客户端(Non Core)

  1. 新建ASP.NET Web MVC项目

Web.Client

站点属性端口改为44301

  1. 安装依赖程序包

NuGet安装Microsoft.Owin.Host.SystemWeb
NuGet安装Microsoft.Owin.Security.OpenIdConnect
NuGet安装Microsoft.Owin.Security.Cookies

  1. SSOServer项目的Cofig.GetClients增加站点配置
            new Client
                {
                    ClientName = ".NET 4 MVC website",
                    ClientId = "net4mvcclient",
                    ClientSecrets =
                    {
                        new Secret("secret3".Sha256())
                    },

                    AllowedGrantTypes = GrantTypes.Implicit, //隐式流
                    RequireConsent = false,
                    AllowOfflineAccess = true,

                    AllowAccessTokensViaBrowser = true, //To be able to get the token via browser

                    RedirectUris = { "https://localhost:44301/signin-oidc" },
                    PostLogoutRedirectUris = { "https://localhost:44301" }, //An existing route on MVC client

                    AllowedScopes = {"openid", "profile", "offline_access", "api1", "api2" }
                },
  1. 配置cookie中间件

更改 ASP.NET MVC项目的Startup类代码,将OpenID Connect中间件指向IdentityServer4实例。

using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;

[assembly: OwinStartup(typeof(Web.Client.Startup))]

namespace Web.Client
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // 有关如何配置应用程序的详细信息,请访问 https://go.microsoft.com/fwlink/?LinkID=316888
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = "Cookies"
            });

            app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
            {
                Authority = "https://localhost:5000",
                ClientId = "net4mvcclient",
                ClientSecret = "secret3",
                RedirectUri = "https://localhost:44301/signin-oidc",
                PostLogoutRedirectUri = "https://localhost:44301", //Same value as set on Config.cs on IdentityServer 
                ResponseType = "id_token token", // to get id_token + access_token 
                RequireHttpsMetadata = false,
                TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                {
                    NameClaimType = "name"
                }, // This is to set Identity.Name 
                SignInAsAuthenticationType = "Cookies"
            });
        }
    }
}
  1. HomeController.Contact增加认证
        [Authorize]
        public ActionResult Contact()
        {
            ViewBag.Message = "Your contact page.";

            return View();
        }
  1. 调试测试

测试登录跳转以及认证访问正常




客户端(Wisej)

  1. 创建项目

Wisej.Client
创建文件夹Controllers & Views
添加MVC5依赖项

Visual Studio 已向项目“Wisej.Client”添加 ASP.NET MVC 5 的 全部集合 个依赖项。

项目中的 Global.asax.cs 文件可能需要其他更改才能启用 ASP.NET MVC。

1. 添加以下命名空间引用:

    using System.Web.Mvc;
    using System.Web.Routing;
    using System.Web.Optimization;

2. 如果代码尚未定义 Application_Start 方法,请添加以下方法:

    protected void Application_Start()
    {
    }

3. 在 Application_Start 方法的末尾添加以下行:

    AreaRegistration.RegisterAllAreas();
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    BundleConfig.RegisterBundles(BundleTable.Bundles);
  1. 安装依赖程序包

NuGet安装Microsoft.AspNet.Mvc (MVC基架)
NuGet安装Microsoft.AspNet.Web.Optimization (MVC基架)
NuGet安装bootstrap (MVC基架)
NuGet安装Microsoft.Owin.Host.SystemWeb
NuGet安装Microsoft.Owin.Security.OpenIdConnect
NuGet安装Microsoft.Owin.Security.Cookies

  1. SSOServer项目的Cofig.GetClients增加站点配置
new Client
                {
                    ClientName = "Wisej Client",
                    ClientId = "wisejclient",
                    ClientSecrets =
                    {
                        new Secret("secret3".Sha256())
                    },

                    AllowedGrantTypes = GrantTypes.Implicit, //隐式流
                    RequireConsent = false,
                    AllowOfflineAccess = true,

                    AllowAccessTokensViaBrowser = true, //To be able to get the token via browser

                    RedirectUris = {"https://localhost:44302/signin-oidc"},
                    PostLogoutRedirectUris = {"https://localhost:44302"}, //An existing route on MVC client

                    AllowedScopes = {"openid", "profile", "offline_access", "api1", "api2"}
                },
  1. 配置cookie中间件
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;

[assembly: OwinStartup(typeof(Wisej.Client.Startup))]

namespace Wisej.Client
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // 有关如何配置应用程序的详细信息,请访问 https://go.microsoft.com/fwlink/?LinkID=316888
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = "Cookies"
            });

            app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
            {
                Authority = "https://localhost:5000",
                ClientId = "wisejclient",
                ClientSecret = "secret3",
                RedirectUri = "https://localhost:44302/signin-oidc",
                PostLogoutRedirectUri = "https://localhost:44302", //Same value as set on Config.cs on IdentityServer 
                ResponseType = "id_token token", // to get id_token + access_token 
                RequireHttpsMetadata = false,
                TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                {
                    NameClaimType = "name"
                }, // This is to set Identity.Name 
                SignInAsAuthenticationType = "Cookies"
            });
        }
    }
}

  1. RouteConfig兼容配置
using System.Web.Mvc;
using System.Web.Routing;

namespace Wisej.Client
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.IgnoreRoute("{resource}.html/{*pathInfo}");
            routes.IgnoreRoute("{resource}.wx/{*pathInfo}");
            routes.IgnoreRoute("{resource}.json");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}

  1. 实现统一登录认证

如果SSOServer没启动,将出现下以下错误.

“/”应用程序中的服务器错误。 
由于目标计算机积极拒绝,无法连接。 127.0.0.1:5000
说明: 执行当前 Web 请求期间,出现未经处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。

异常详细信息: System.Net.Sockets.SocketException: 由于目标计算机积极拒绝,无法连接。 127.0.0.1:5000

或者

“/”应用程序中的服务器错误。 
由于意外的数据包格式,握手失败。
说明: 执行当前 Web 请求期间,出现未经处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。

异常详细信息: System.IO.IOException: 由于意外的数据包格式,握手失败。

源错误:
执行当前 Web 请求期间生成了未经处理的异常。可以使用下面的异常堆栈跟踪信息确定有关异常原因和发生位置的信息。
  1. 调试测试

登录认证成功

posted @ 2021-03-12 18:49  devs  阅读(343)  评论(0编辑  收藏  举报