NET Core【IdentityServer4单点登录】+【退出登录】
第一天接触NetCore,感觉坑很多,其他都还良好
比如说我们现在有三个系统,一个是商品系统,一个是订单系统,另外一个就是单独的登录系统,
当我们要访问商品系统或者订单系统的时候,我们首先得判断一下我们的本地客户端是否有登录缓存,
如果没有的话就会再到统一的认证中心去找,如果还是没有,就会自动跳转到登录页面,登录成功后,
我们要访问订单系统或者商品系统时,就直接在自己本地客户端取出登录缓存即可,
但是得做一下相关的操作,在订单和商品服务的StartUp.cs里面都要添加一下服务记录登录状态
services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies") .AddOpenIdConnect("oidc", options => { options.SignInScheme = "Cookies"; options.Authority = "http://localhost:56772"; //登陆中心的地址 options.RequireHttpsMetadata = false; options.ClientId = ".net58_Order"; options.SaveTokens = true; });
以及添加身份验证和session中间件
//身份验证 app.UseAuthentication(); //启用session中间件 app.UseSession();
这几样都是必不可少的,另外我们还得在登录系统里面加一个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 = ".net58_Order", //订单系统 ClientName = "MVC Client", AllowedGrantTypes = GrantTypes.Implicit,//隐式方式 RequireConsent=false,//如果不需要显示否同意授权 页面 这里就设置为false RedirectUris = { "http://localhost:56574/signin-oidc" },//登录成功后返回的订单系统客户端地址 //PostLogoutRedirectUris = { "http://localhost:56574/signout-callback-oidc" },//注销登录后返回的客户端地址 AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile } }, new Client { ClientId = ".net58_product", //商品系统 ClientName = "MVC Client", AllowedGrantTypes = GrantTypes.Implicit,//隐式方式 RequireConsent=false,//如果不需要显示否同意授权 页面 这里就设置为false RedirectUris = { "http://localhost:55447/signin-oidc" },//登录成功后返回的商品系统客户端地址 //PostLogoutRedirectUris = { "http://localhost:55447/signout-callback-oidc" },//注销登录后返回的客户端地址 AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile } } }; } }
一定要记得在订单和商品将属性[Authorize]置于整个Controller之上。当用户有操作时,进入控制器前都会先验证用户是否登录,或者存储用户信息过期从而返回登录界面。
[Authorize]
//查询用户名和密码
public class UserDAL : IUserDAL { private readonly MyContext myContext; //构造函数注入 public UserDAL(MyContext myContext2) { myContext = myContext2; } public Users Login(string UserName, string Userpwd) { Users users = myContext.Users.Where(a => a.UserName == UserName && a.UserPwd == Userpwd).FirstOrDefault(); return users; } //异步 public Task<Users> LoginAsync(string UserName, string Userpwd) { Task.Run(() => { Users users = myContext.Users.Where(a => a.UserName == UserName && a.UserPwd == Userpwd).FirstOrDefault(); return users; }); return null; } }
IUserDAL接口
namespace LoginSystem.DAL.Interface { public interface IUserDAL { Users Login(string UserName, string Userpwd); Task<Users> LoginAsync(string UserName, string Userpwd); } }
在登录系统的StartUp.cs类里面的这个ConfigureServices方法里面添加点配置
//配置依赖注入关系 services.AddTransient<IUserDAL, UserDAL>(); services.AddDbContext<MyContext>(option => { option.UseSqlServer("Data Source =.; Initial Catalog =My; User ID = sa; Password = 123456"); }); services.AddIdentityServer()//Ids4服务 .AddDeveloperSigningCredential()//添加开发人员签名凭据 .AddInMemoryIdentityResources(Config.GetIdentityResources()) //添加内存apiresource .AddInMemoryClients(Config.GetClients());//把配置文件的Client配置资源放到内存
另外在登录系统里面启用一下id这个服务,这个服务也需要我们自己下载,版本根据个人需求下就可以啦
下载包:install-package identityServer4 -version 2.1.1
//启用id4 app.UseIdentityServer();
再来说一下关于登录系统里面的登录,
NetCore里面是没有Session,我们要使用的话,得自己下载一下,需要哪种版本根据自己需求下
下载包:Install-package Microsoft.AspNetCore.Session -Version 2.1.1
public class AccountController : Controller { /// <summary> /// 登录页面 /// </summary> [HttpGet] public IActionResult Login(string returnUrl = null) { ViewData["returnUrl"] = returnUrl; return View(); } //依赖注入 private readonly IIdentityServerInteractionService _interaction; private readonly IUserDAL _userDAL; public AccountController(IIdentityServerInteractionService interaction, IUserDAL userDAL ) { _interaction = interaction; _userDAL = userDAL; } //内部跳转 private IActionResult RedirectToLocal(string returnUrl) { if (Url.IsLocalUrl(returnUrl)) { //如果是本地 return Redirect(returnUrl); } return RedirectToAction(nameof(HomeController.Index), "Home"); } /// <summary> /// 登录post回发处理 /// </summary> [HttpPost] public async Task<IActionResult> Login(string userName, string password, string returnUrl = null) { ViewData["returnUrl"] = returnUrl; Users users = _userDAL.Login(userName, password); //判断是否为空 if (users != null) { AuthenticationProperties props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.Add(TimeSpan.FromDays(1)), }; //注意这里应该引用Microsoft.AspNetCore.Http这个下面的 await HttpContext.SignInAsync("2123", userName, props); //HttpContext.SignOutAsync(); if (returnUrl != null) { return RedirectToLocal(returnUrl);//登陆成功跳转原地址 //return Redirect("http://localhost:44396/home/index"); } return View(); } else { return Content("登录失败"); } } }
登录页面的代码
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Login</title> <script> new Oidc.UserManager().signinRedirectCallback().then(function (user) { alert("登录成功后跳转"); alert(user.state); window.location = user.state; }).catch(function (e) { console.error(e); }); </script> </head> <body> <div align="center"> <h1>登录认证中心</h1> <form method="post" action="/Account/Login"> 用户名:<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>
如果没有登录,订单系统和商品系统都会自动跳转回到登录界面
登录成功后的订单页面和商品页面
再来说一下退出登录
首先在商品系统的index.cshtml页面,写一个退出登录的a标签事件
<a href="/account/Logout" style="float:right;color:#ff0000;font-size:30px">退出登录</a>
接下来就是在登录系统里边写相关操作
//退出登录 private readonly IIdentityServerInteractionService _interaction; public AccountController(IIdentityServerInteractionService interaction) { _interaction = interaction; } [HttpGet] public async Task<IActionResult> Logout(string logoutId) { #region IdentityServer4 退出登录后,默认会跳转到Config.Client配置的PostLogoutRedirectUris地址, //做如下改动,则会动态的跳转到原来的地址 var logout = await _interaction.GetLogoutContextAsync(logoutId); await HttpContext.SignOutAsync(); if (logout.PostLogoutRedirectUri != null) { return Redirect(logout.PostLogoutRedirectUri); } var refererUrl = Request.Headers["Referer"].ToString(); return Redirect(refererUrl); #endregion }
再到Home控制器里边向IdentityServer进行往返,目的清除单点登录会话
public IActionResult Logout() { return SignOut("Cookies", "oidc"); }
步骤有点多,会被绕晕,得慢慢来,特别是那些地址,千万不能填错,坑也很多,得踩过之后才知道!