【ASP.NET Core分布式項目實戰】(二)oauth2 + oidc 實現 server部分


資料

我們基於之前的MvcCookieAuthSample來做開發

MvcCookieAuthSample下載地址:https://files.cnblogs.com/files/wyt007/ASPNETCore%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8.rar

MvcCookieAuthSample教程地址:http://www.cnblogs.com/wyt007/p/8204731.html

正文

給網站設置默認地址     http://localhost:5000

 

首先,我們將之前寫的系統的Identity注釋掉,在Startup.cs中

第一步:添加Nuget包:IdentityServer4

我們可以在vscode中使用ctrl+P鍵來打開命令面板。然后輸入nuget按回車,輸入identityserver4后按回車來選擇版本進行安裝

第二步:添加Config.cs配置類

我們接下來添加一個Config.cs類,這個類是用來初始化IdentityServer的

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

namespace MvcCookieAuthSample
{
    public class Config
    {
        //所有可以訪問的Resource
        public static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>
            {
                new ApiResource("api1","API Application")
            };
        }

        //客戶端
        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
            {
                new Client()
                {
                    ClientId="mvc",
                    AllowedGrantTypes= GrantTypes.Implicit,//模式:最簡單的模式
                    ClientSecrets={//私鑰
                        new Secret("secret".Sha256())
                    },
 AllowedScopes={//可以訪問的Resource
                        IdentityServerConstants.StandardScopes.Profile,
                        IdentityServerConstants.StandardScopes.OpenId,
                    },
                    RedirectUris={"http://localhost:5001/signin-oidc"},//跳轉登錄到的客戶端的地址
                    PostLogoutRedirectUris={"http://localhost:5001/signout-callback-oidc"},//跳轉登出到的客戶端的地址
                    RequireConsent=false//是否需要用戶點擊確認進行跳轉
                }
            };
        }

        //測試用戶
        public static List<TestUser> GetTestUsers()
        {
            return new List<TestUser>{
                new TestUser{
                    SubjectId="10000",
                    Username="wyt",
                    Password="password"
                }
            };
        }

        //定義系統中的資源
        public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
                new IdentityResources.Email()
            };
        }

    }
}

 

以上使用IdentityServer4測試數據類添加數據,直接存在內存中。IdentityServer4 是支持持久化。

第三步:添加Startup配置

引用命名空間:

using IdentityServer4;

 然后打開Startup.cs 加入如下:

services.AddIdentityServer()
    .AddDeveloperSigningCredential()//添加開發人員簽名憑據
    .AddInMemoryApiResources(Config.GetApiResources())//添加內存apiresource
    .AddInMemoryClients(Config.GetClients())//添加內存client
    .AddInMemoryIdentityResources(Config.GetIdentityResources())//添加系統中的資源
    .AddTestUsers(Config.GetTestUsers());//添加測試用戶
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
 {
    ...
    //app.UseAuthentication();
    app.UseIdentityServer();
    ...

接着安裝UI,UI部分也可以自己編寫,也就是登錄 注銷 允許和錯誤。

可以到 https://github.com/IdentityServer/IdentityServer4.Quickstart.UI/tree/release 下載,然后解壓到項目目錄下。

也可以使用命令提示符快速安裝:

powershell iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI/release/get.ps1'))

在項目目錄下打開命令提示符,輸入以上命令。

更多信息,可以查看官方readme:https://github.com/IdentityServer/IdentityServer4.Quickstart.UI/blob/release/README.md

大神博客:https://www.cnblogs.com/linezero/p/identityserver4openidconnect.html

修改LoginViewModel,將Email改為UserName,並修改強類型視圖的引用部分Login.cshtml

namespace MvcCookieAuthSample.ViewModels
{
    public class LoginViewModel
    {
        //[Required]//必須的
        //[DataType(DataType.EmailAddress)]//內容檢查是否為郵箱
        //public string Email { get; set; }

        [Required]
        public string UserName { get; set; }

        [Required]//必須的
        [DataType(DataType.Password)]//內容檢查是否為密碼
        public string Password { get; set; }
    }
}

 

修改AccountController,原來的_userManager和_signInManager不再使用

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication.Cookies;
using System.Security.Claims;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using Microsoft.AspNetCore.Identity;
using MvcCookieAuthSample.Models;
using System.Threading.Tasks;
using MvcCookieAuthSample.ViewModels;
using IdentityServer4.Test;
using System;

namespace MvcCookieAuthSample.Controllers
{
    public class AccountController : Controller
    {
        // private UserManager<ApplicationUser> _userManager;//創建用戶的
        // private SignInManager<ApplicationUser> _signInManager;//用來登錄的

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

                // //依賴注入
        // public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
        // {
        //     _userManager = userManager;
        //     _signInManager = signInManager;
        // }

        //內部跳轉
        private IActionResult RedirectToLocal(string returnUrl)
        {
            if (Url.IsLocalUrl(returnUrl))
            {//如果是本地
                return Redirect(returnUrl);
            }

            return RedirectToAction(nameof(HomeController.Index), "Home");
        }

        //添加驗證錯誤
        private void AddError(IdentityResult result)
        {
            //遍歷所有的驗證錯誤
            foreach (var error in result.Errors)
            {
                //返回error到model
                ModelState.AddModelError(string.Empty, error.Description);
            }
        }




        public IActionResult Register(string returnUrl = null)
        {
            ViewData["returnUrl"] = returnUrl;
            return View();
        }

        [HttpPost]
        public async Task<IActionResult> Register(RegisterViewModel registerViewModel, string returnUrl = null)
        {
            // if (ModelState.IsValid)
            // {
            //     ViewData["returnUrl"] = returnUrl;

            //     var identityUser = new ApplicationUser
            //     {
            //         Email = registerViewModel.Email,
            //         UserName = registerViewModel.Email,
            //         NormalizedUserName = registerViewModel.Email
            //     };
            //     var identityResult = await _userManager.CreateAsync(identityUser, registerViewModel.Password);
            //     if (identityResult.Succeeded)
            //     {
            //         //注冊完成登錄生成cookies信息
            //         await _signInManager.SignInAsync(identityUser, new AuthenticationProperties { IsPersistent = true });

            //         return RedirectToLocal(returnUrl);
            //     }
            //     else//注冊失敗
            //     {
            //         //添加驗證錯誤
            //         AddError(identityResult);
            //     }
            // }

            return View();
        }

        public IActionResult Login(string returnUrl = null)
        {
            ViewData["returnUrl"] = returnUrl;
            return View();
        }

        [HttpPost]
        public async Task<IActionResult> Login(LoginViewModel  loginViewModel, string returnUrl = null)
        {
            if (ModelState.IsValid)
            {
                ViewData["returnUrl"] = returnUrl;

                //var user = await _userManager.FindByEmailAsync(loginViewModel.Email);
                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 prop = new AuthenticationProperties()
                        {
                            IsPersistent = true,
                            ExpiresUtc = DateTimeOffset.UtcNow.Add(TimeSpan.FromMinutes(30))
                        };

                        //HttpContext.SignInAsync(user.SubjectId, user.Username, prop);
                        await Microsoft.AspNetCore.Http.AuthenticationManagerExtensions.SignInAsync(HttpContext, user.SubjectId, user.Username, prop);
                    }
                }
                //賬號密碼先不做驗證,需要可以自己寫
                //await _signInManager.SignInAsync(user, new AuthenticationProperties { IsPersistent = true });

                return RedirectToLocal(returnUrl);
            }

            return View();
            
        }

        //登出
        public async Task<IActionResult> Logout()
        {

            await HttpContext.SignOutAsync();
            return RedirectToAction("Index", "Home");
        }

    }
}

接下來我們需要將原來的Program.cs中的數據庫初始化的內容注釋掉

然后我們就可以運行網站,輸入用戶名和密碼進行登錄了

 

新建客戶端 

新建一個MVC網站MvcClient 

dotnet new mvc --name MvcClient

給網站設置默認地址     http://localhost:5001

MVC的網站已經內置幫我們實現了Identity,所以我們不需要再額外添加Identity引用

添加認證

services.AddAuthentication(options =>
{
    options.DefaultScheme = "Cookies";//使用Cookies認證
    options.DefaultChallengeScheme = "oidc";//使用oidc
})
.AddCookie("Cookies")//配置Cookies認證
.AddOpenIdConnect("oidc",options=> {//配置oidc
    options.SignInScheme = "Cookies";
    options.Authority = "http://localhost:5000";
    options.RequireHttpsMetadata = false;

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

在管道中使用Authentication

app.UseAuthentication();

Login.cshtml 頁面接收returnUrl進行跳轉

<form method="post" asp-controller="Account" asp-action="Login">
    <h4>Use a local account to log in.</h4>
    <hr />
    @*用於登錄后進行跳轉*@
    <input type="hidden" name="returnUrl" value="@ViewData["returnUrl"]" />


    <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>

    <div class="form-group">
        <label asp-for="Password"></label>
        <input asp-for="Password" type="password" class="form-control" />
        <span asp-validation-for="Password" class="text-danger"></span>
    </div>
    <div class="form-group">
        <button type="submit" class="btn btn-block btn-danger">Log in</button>
    </div>

</form>
View Code

接下來我們在HomeController上打上  [Authorize]  標簽,然后啟動運行

我們這個時候訪問首頁http://localhost:5001會自動跳轉到ocalhost:5000/account/login登錄

登錄之后會自動跳轉回來

我們可以在Home/About頁面將claim的信息顯示出來

@{
    ViewData["Title"] = "About";
}
<h2>@ViewData["Title"]</h2>
<h3>@ViewData["Message"]</h3>

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

 

 這邊的內容是根據我們在IdentityServer服務中定義的返回資源決定的

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM