zoukankan      html  css  js  c++  java
  • .NET Core IdentityServer4实战 第Ⅴ章-单点登录

      OiDc可以说是OAuth的改造版,在最初的OAuth中,我们需要先请求一下认证服务器获取下Access_token,然后根据Access_token去Get资源服务器, 况且OAuth1 和 2 完全不兼容,易用性差,而OIDC可以在登陆的时候就把信息返回给你,不需要你在请求一下资源服务器。下面我们根据Oidc来做一个单点登录。

      新建三个项目(.NET Core Mvc)两个Client(端口5001,5002),一个Server(5000),首先在Server中添加IdentityServer4的引用。

      在Server中Config.cs用于模拟配置。

        public class Config
        {
            public static IEnumerable<ApiResource> GetApiResource()
            {
                return new List<ApiResource>
                {
                    new ApiResource("api","My Api App")
                };
            }
            public static IEnumerable<Client> GetClients()
            {
                return new List<Client>
                {
                    new Client()
                    {
                        ClientId = "mvc",
                        AllowedGrantTypes = GrantTypes.Implicit,
                        ClientSecrets ={
                            new Secret("secret".Sha256())
                        },
                        RequireConsent = false,
                        RedirectUris = {"http://localhost:5001/signin-oidc",
                            "http://localhost:5002/signin-oidc" } ,
                        PostLogoutRedirectUris = {"http://localhost:5001/signout-callback-oidc" ,
                            "http://localhost:5002/signout-callback-oidc" },
                        AllowedScopes = {
                            IdentityServerConstants.StandardScopes.Profile,
                            IdentityServerConstants.StandardScopes.OpenId
                        }
                    }
                };
            }
            public static List<TestUser> GetTestUsers()
            {
                return new List<TestUser>
                {
                    new TestUser()
                    {
                        SubjectId = "10000",
                        Username = "zara",
                        Password = "112233"
                    }
                };
            }
            public static IEnumerable<IdentityResource> GetIdentityResources()
            {
                return new List<IdentityResource>
                {
                    new IdentityResources.OpenId(),
                    new IdentityResources.Profile(),
                    new IdentityResources.Email()
                };
            }
        }

    GetClient方法中字段为RedirectUris是登陆成功返回的地址,并且我们采用隐式模式(因为只是传统web中传递Access_Token),RequireConsent是否出现同意授权页面,这个我们后续再细说.写完Config.cs后,我们需要依赖注入到IdentityServer中。

    public void ConfigureServices(IServiceCollection services)
            {
                services.Configure<CookiePolicyOptions>(options =>
                {
                    // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                    options.CheckConsentNeeded = context => true;
                    options.MinimumSameSitePolicy = SameSiteMode.None;
                });
            //config to identityServer Services services.AddIdentityServer() .AddDeveloperSigningCredential() .AddInMemoryClients(Config.GetClients()) .AddTestUsers(Config.GetTestUsers()) .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryApiResources(Config.GetApiResource()); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }

     在Configure中添加代码 app.UseIdentityServer(); .我们还需要添加一个登陆页面,名为Account.cshtml.

    @{
        ViewData["Title"] = "Index";
    }
    
    <h2>Index</h2>
    
    @using mvcWebFirstSolucation.Models;
    @model LoginVM;
    
    <div class="row">
        <div class="col-md-4">
            <section>
                <form method="post" asp-controller="Account" asp-action="Login" asp-route-returnUrl="@ViewData["returnUrl"]">
                    <h4>Use a local to log in .</h4>
                    <hr />
                    <div class="from-group">
                        <label asp-for="UserName"></label>
                        <input asp-for="UserName" class="form-control">
                        <span asp-validation-for="UserName" class="text-danger"></span>
                    </div>
                    <div class="from-group">
                        <label asp-for="PassWord"></label>
                        <input asp-for="PassWord" type="password" class="form-control">
                        <span asp-validation-for="UserName" class="text-danger"></span>
                    </div>
                    <div class="from-group">
                        <button type="submit" class="btn btn-default">log in </button>
                    </div>
                </form>
            </section>
        </div>
    </div>
    @section Scripts
    {
        @await Html.PartialAsync("_ValidationScriptsPartial")
    }

    在控制器中我们写一个构造函数,用于将IdentityServer.Text给我们封装好的对象传过来,这个对象是我们在Config.cs中添加的用户信息,也就是GetClients的返回值,全都在 TestUserStore 中。其中还有一个提供好的方法,来给我们用,如果验证通过我们直接跳转到了传递过来的ReturnUrl.

        public class AccountController : Controller
        {
            private readonly TestUserStore _users;
            public AccountController(TestUserStore ussotre)
            {
                _users = ussotre;
            }
            [HttpGet]
            [Route("/Account/Login")]
            public IActionResult Index(string ReturnUrl = null)
            
    {
                ViewData["returnUrl"] = ReturnUrl;
                return View();
            }
            private IActionResult RediretToLocal(string returnUrl)
            {
                if (Url.IsLocalUrl(returnUrl))
                {
                    return Redirect(returnUrl);
                }
                return RedirectToAction(nameof(HomeController.Index),"Home");
            }
            [HttpPost]
            public async Task<IActionResult> Login(LoginVM vm,string returnUrl = null)
            {
                if (ModelState.IsValid)
                {
                    ViewData["returnUrl"] = returnUrl;
                    var user =  _users.FindByUsername(vm.UserName);
                    if (user==null)
                    {
                        ModelState.AddModelError(nameof(LoginVM.UserName), "userName is exists");
                    }
                    else
                    {
                        if(_users.ValidateCredentials(vm.UserName, vm.PassWord))
                        {
                            var props = new AuthenticationProperties
                            {
                                IsPersistent = true,
                                ExpiresUtc = DateTimeOffset.UtcNow.Add(TimeSpan.FromMinutes(30))
                            };
                            await Microsoft.AspNetCore.Http
                                .AuthenticationManagerExtensions
                                    .SignInAsync( HttpContext, user.SubjectId, user.Username, props );
    
                            return RediretToLocal(returnUrl);
                        }
    
                        ModelState.AddModelError(nameof(LoginVM.PassWord), "Wrong Password");
                    }
                }
                return View();
            }
        }

    这个时候最基本的服务端已经配置成功了,我们开始配置受保护的客户端吧。

    在客户端中我们不需要引入IdentityServer,因为我们只是去请求服务端然后看看cookies有没有在而已,所以我们只需要给受保护的客户端的Api做好安全判断就好.

    在受保护的控制器中添加 [Authorize] 标识。然后再Startup.cs中添加安全验证。并且在Configure中use下 app.UseAuthentication(); 

    public void ConfigureServices(IServiceCollection services)
            {
                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.ClientSecret = "secret";
                    options.SaveTokens = true;
                });
    
                services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
            }

    在首页中最好遍历下Claims对象,这个是通过OIDC直接给我们返回回来的.(最后另一个客户端也这么干!)

    <div>
        @foreach (var claim in User.Claims)
        {
            <dl>
                <dt>@claim.Type</dt>
                <dd>@claim.Value</dd>
            </dl>
        }
    </div>

    现在我们启动项目看一下效果吧。

     

  • 相关阅读:
    帧率、码流与分辩率之间关系
    谈谈RGB、YUY2、YUYV、YVYU、UYVY、AYUV
    YUV422 to YUV420sp color invert
    mysql——数据库备份——使用mysqldump命令备份所有数据库
    mysql——表的导出——用mysqldump命令导出 文本文件
    mysql——数据还原——使用mysql命令还原
    mysql——表的导出——用mysqldump命令导出 xml文件
    mysql——表的导出——用select……into outfile导出 xls文件和文本文件
    mysql——表的导出——用mysql命令导出 文本文件 和 xls文件
    和菜鸟一起学linux之wifi学习记录基础知识
  • 原文地址:https://www.cnblogs.com/ZaraNet/p/11136468.html
Copyright © 2011-2022 走看看