ASP.NET Core分布式项目实战(oauth2 + oidc 实现 server部分)--学习笔记

ASP.NET Core分布式项目实战(oauth2 + oidc 实现 server部分)--学习笔记

任务15:oauth2 + oidc 实现 server部分

基于之前快速入门的项目(MvcCookieAuthSample):
https://www.cnblogs.com/MingsonZheng/p/11614686.html
https://www.cnblogs.com/MingsonZheng/p/11623815.html

mvcCookieAuthSample2下载地址:
http://video.jessetalk.cn/course/5/material/217/download

把这个 MVC 注册登录的网站变成一个单点登录,现在它是自己登录自己使用,我们需要把它的登录信息返回给第三方

添加 identityserver4 引用

在 startup 中

using IdentityServer4;

按照之前的文章添加 Config.cs

using System.Collections;
using System.Collections.Generic;
using IdentityServer4.Models;
using IdentityServer4.Test;

namespace mvcCookieAuthSample
{
    public class Config
    {
        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
            {
                new Client()
                {
                    ClientId = "client",
                    AllowedGrantTypes = GrantTypes.Implicit,// 隐式模式
                    ClientSecrets = 
                    {
                        new Secret("secret".Sha256())
                    },
                    AllowedScopes = {"api"},
                }
            };
        }

        public static IEnumerable<ApiResource> GetApiResource()
        {
            return new List<ApiResource>
            {
                new ApiResource("api", "My Api")
            };
        }

        public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
                new IdentityResources.Email(),
            };
        }

        public static List<TestUser> GetTestUsers()
        {
            return new List<TestUser>
            {
                new TestUser
                {
                    SubjectId = "1",
                    Username = "mingsonzheng",
                    Password = "123456"
                }
            };
        }
    }
}

startup 的 ConfigureServices

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddIdentityServer()
            .AddDeveloperSigningCredential()
            .AddInMemoryClients(Config.GetClients())
            .AddInMemoryApiResources(Config.GetApiResource())
            .AddInMemoryIdentityResources(Config.GetIdentityResources())
            .AddTestUsers(Config.GetTestUsers());

    //services.AddDbContext<ApplicationDbContext>(options =>
    //{
    //    options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
    //});

    //services.AddIdentity<ApplicationUser, ApplicationUserRole>()
    //    .AddEntityFrameworkStores<ApplicationDbContext>()
    //    .AddDefaultTokenProviders();

    //services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    //    .AddCookie(options => {
    //        options.LoginPath = "/Account/Login";
    //    });

    //services.Configure<IdentityOptions>(options =>
    //{
    //    options.Password.RequireLowercase = true;
    //    options.Password.RequireNonAlphanumeric = true;
    //    options.Password.RequireUppercase = true;
    //    options.Password.RequiredLength = 12;
    //});

    services.AddMvc();
}

startup 的 Configure 中 UseIdentityServer

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

    app.UseStaticFiles();
    
    //app.UseAuthentication();
    app.UseIdentityServer();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

我们已经把 IdentityServer4 添加到 MVC 程序中,接着需要在 Controller 中实现这个逻辑

首先注释 AccountController 原先的登录逻辑

//private UserManager<ApplicationUser> _userManager;
//private SignInManager<ApplicationUser> _signInManager;

Logout 中使用 HttpContext.SignOutAsync 替换

public async Task<IActionResult> Logout()
{
    //await _signInManager.SignOutAsync();
    await HttpContext.SignOutAsync();
    return RedirectToAction("Index", "Home");
}

接着改造登录的逻辑,我们需要验证用户名和密码,前面我们在 Config 中添加了 TestUser,它被放在 TestUserStore 中,可以通过依赖注入引用进来,有了它之后就可以在登录的时候拿到用户名和密码

private readonly TestUserStore _users;

public AccountController(TestUserStore users)
{
    _users = users;
}

因为 TestUser 本身不提供 Email 登录,所以我们需要修改 LoginViewModel 以及 Login.cshtml

LoginViewModel

[Required]
//[DataType(DataType.EmailAddress)]
//public string Email { get; set; }
public string UserName { get; set; }

Login.cshtml

<div class="form-group">
    <label asp-for="UserName"></label>
    <input asp-for="UserName" class="form-control" />
    <span asp-validation-for="UserName" class="text-danger"></span>
</div>

改造登录的逻辑

public async Task<IActionResult> Login(LoginViewModel loginViewModel,string returnUrl)
{
    if (ModelState.IsValid)
    {
        //ViewData["ReturnUrl"] = returnUrl;
        //var user = await _userManager.FindByEmailAsync(loginViewModel.Email);
        //if (user == null)
        //{
        //    ModelState.AddModelError(nameof(loginViewModel.Email), "Email not exists");
        //}
        //else
        //{
        //    await _signInManager.SignInAsync(user, new AuthenticationProperties { IsPersistent = true });
        //    return RedirectToLoacl(returnUrl);
        //}

        ViewData["ReturnUrl"] = returnUrl;
        var user = _users.FindByUsername(loginViewModel.UserName);
        if (user == null)
        {
            ModelState.AddModelError(nameof(loginViewModel.UserName), "UserName not exists");
        }
        else
        {
            if (_users.ValidateCredentials(loginViewModel.UserName, loginViewModel.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 RedirectToLoacl(returnUrl);
            }

            ModelState.AddModelError(nameof(loginViewModel.Password), "Wrong Password");
        }
    }

    return View();
}

这样,我们就实现了一个通过 IdentityServer4 下的方法来实现了一个登录逻辑,然后做了一个跳转,下一节再把客户端加进来

课程链接

http://video.jessetalk.cn/course/explore

ASP.NET Core分布式项目实战(oauth2 + oidc 实现 server部分)--学习笔记

ASP.NET Core分布式项目实战(oauth2 + oidc 实现 server部分)--学习笔记

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接: http://www.cnblogs.com/MingsonZheng/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

如有任何疑问,请与我联系 (MingsonZheng@outlook.com) 。