aspnet门户网站源码分享,asp.net cms 源码

很多朋友对于aspnet门户网站源码分享和asp.net cms 源码不太懂,今天就由小编来为大家分享,希望可以帮助到大家,下面一起来看看吧!

前言

SSO的系列还是以.NetCore作为实践例子与大家分享,SSO在Web方面复杂度分同域与跨域。本篇先分享同域的设计与实现,跨域将在下篇与大家分享。

如有需要调试demo的,可把SSO项目部署为域名http://sso.cg.com/,Web1项目部署为http://web1.cg.com,http://web2.cg.com,可以减少配置修改量

源码地址:https://github.com/SkyChenSky/Core.SSO

效果图

SSO简介

单点登录,全称为SingleSignOn,在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

它是一个解决方案,目的是为了整合企业内多个应用系统,仅由一组账号只需进行一次登录,就可被授权访问多个应用系统。

流程描述

未登录状态访问业务Web应用会引导到认证中心。

用户在认证中心输入账号信息通过登录后,认证中心会根据用户信息生成一个具有安全性的token,将以任何方式持久化在浏览器。

此后访问其他Web应用的时候,必须携带此token进行访问,业务Web应用会通过本地认证或者转发认证而对token进行校验。

从上图可以简单的分析出三个关键点:

Token的生成Token的共享Token校验

Token的生成

方式有多种:

可以通过Web框架对用户信息加密成Token。

Token编码方式也可以为JSONWEBTOKEN(JWT)

也可以是一段MD5,通过字典匹配保存在服务器用户信息与MD5值

Token的共享

浏览器存储有三种方式:

作为拥有会失效的会话状态,更因选择Cookie存储。那么Cookie的使用是可以在同域共享的,因此在实现SSO的时候复杂度又分为同域与跨域。

同域的共享比较简单,在应用设置Cookie的Domain属性进行设置,就可以完美的解决。

Token校验

校验分两种情况:

转发给认证中心认证由谁授权,就由谁进行身份认证。授权与认证是成对的。如果是以Cookie认证,那就是服务端对token进行解密。如果是服务端保存用户信息,则匹配token值。业务应用自身认证不需要转发,那就意味着业务应用认证规则与认证中心的认证规则必须是一致的。

设计要点

原则上来讲,只要统一Token的产生和校验方式,无论授权与认证的在哪(认证系统或业务系统),也无论用户信息存储在哪(浏览器、服务器),其实都可以实现单点登录的效果。

此次使用.NETCoreMVC框架,以Cookie认证通过业务应用自身认证的方式进行同父域的SSO实现。

为什么要使用Cookie认证方式?

1、会话状态分布在客户浏览器,避免大量用户同时在线对服务端内存容量的压力。

2、横向扩展良好性,可按需增减节点。

统一应用授权认证

将以Core的Cookie认证进行实现,那么意味着每个应用对用户信息的加解密方式需要一致。

因此对AddCookie的设置属性DataProtectionProvider或者TicketDataFormat的加密方式进行重写实现。

.NETCore的SSO实现

Cookie认证

认证中心AddCookie的设置

publicvoidConfigureServices(IServiceCollectionservices){services.AddMvc();services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options=>{options.Cookie.Name=”Token”;options.Cookie.Domain=”.cg.com”;options.Cookie.HttpOnly=true;options.ExpireTimeSpan=TimeSpan.FromMinutes(30);options.LoginPath=”/Account/Login”;options.LogoutPath=”/Account/Logout”;options.SlidingExpiration=true;//options.DataProtectionProvider=DataProtectionProvider.Create(newDirectoryInfo(@”D:ssokey”));options.TicketDataFormat=newTicketDataFormat(newAesDataProtector());});}

业务应用AddCookie的设置

publicvoidConfigureServices(IServiceCollectionservices){services.AddMvc();services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options=>{options.Cookie.Name=”Token”;options.Cookie.Domain=”.cg.com”;options.Events.OnRedirectToLogin=BuildRedirectToLogin;options.Events.OnSigningOut=BuildSigningOut;options.Cookie.HttpOnly=true;options.ExpireTimeSpan=TimeSpan.FromMinutes(30);options.LoginPath=”/Account/Login”;options.LogoutPath=”/Account/Logout”;options.SlidingExpiration=true;options.TicketDataFormat=newTicketDataFormat(newAesDataProtector());});}

基于设计要点的“统一应用授权认证”这一点,两者的区别不大,ticket的加密方式统一使用了AES,都指定Cookie.Domain=”.cg.com”,保证了Cookie同域共享,设置了HttpOnly避免XSS攻击。

两者区别在于:

options.Events.OnRedirectToLogin=BuildRedirectToLogin;options.Events.OnSigningOut=BuildSigningOut;

这是为了让业务应用引导跳转到认证中心登录页面。

OnRedirectToLogin是认证失败跳转。

OnSigningOut是注销跳转。

///<summary>///未登录下,引导跳转认证中心登录页面///</summary>///<paramname=”context”></param>///<returns></returns>privatestaticTaskBuildRedirectToLogin(RedirectContext<CookieAuthenticationOptions>context){varcurrentUrl=newUriBuilder(context.RedirectUri);varreturnUrl=newUriBuilder{Host=currentUrl.Host,Port=currentUrl.Port,Path=context.Request.Path};varredirectUrl=newUriBuilder{Host=”sso.cg.com”,Path=currentUrl.Path,Query=QueryString.Create(context.Options.ReturnUrlParameter,returnUrl.Uri.ToString()).Value};context.Response.Redirect(redirectUrl.Uri.ToString());returnTask.CompletedTask;}///<summary>///注销,引导跳转认证中心登录页面///</summary>///<paramname=”context”></param>///<returns></returns>privatestaticTaskBuildSigningOut(CookieSigningOutContextcontext){varreturnUrl=newUriBuilder{Host=context.Request.Host.Host,Port=context.Request.Host.Port??80,};varredirectUrl=newUriBuilder{Host=”sso.cg.com”,Path=context.Options.LoginPath,Query=QueryString.Create(context.Options.ReturnUrlParameter,returnUrl.Uri.ToString()).Value};context.Response.Redirect(redirectUrl.Uri.ToString());returnTask.CompletedTask;}

登录注销

认证中心与业务应用两者的登录注册基本一致。

privateasyncTask<IActionResult>SignIn(Useruser){varclaims=newList<Claim>{newClaim(JwtClaimTypes.Id,user.UserId),newClaim(JwtClaimTypes.Name,user.UserName),newClaim(JwtClaimTypes.NickName,user.RealName),};varuserPrincipal=newClaimsPrincipal(newClaimsIdentity(claims,”Basic”));varreturnUrl=HttpContext.Request.Cookies[ReturnUrlKey];awaitHttpContext.SignInAsync(userPrincipal,newAuthenticationProperties{IsPersistent=true,RedirectUri=returnUrl});HttpContext.Response.Cookies.Delete(ReturnUrlKey);returnRedirect(returnUrl??”/”);}privateasyncTaskSignOut(){awaitHttpContext.SignOutAsync();}

HttpContext.SignInAsync的原理

使用的是Cookie认证那么就是通过Microsoft.AspNetCore.Authentication.Cookies库的CookieAuthenticationHandler类的HandleSignInAsync方法进行处理的。

源码地址:https://github.com/aspnet/Security/blob/master/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs

protectedasyncoverrideTaskHandleSignInAsync(ClaimsPrincipaluser,AuthenticationPropertiesproperties){if(user==null){thrownewArgumentNullException(nameof(user));}properties=properties??newAuthenticationProperties();_signInCalled=true;//Processtherequestcookietoinitializememberslike_sessionKey.awaitEnsureCookieTicket();varcookieOptions=BuildCookieOptions();varsignInContext=newCookieSigningInContext(Context,Scheme,Options,user,properties,cookieOptions);DateTimeOffsetissuedUtc;if(signInContext.Properties.IssuedUtc.HasValue){issuedUtc=signInContext.Properties.IssuedUtc.Value;}else{issuedUtc=Clock.UtcNow;signInContext.Properties.IssuedUtc=issuedUtc;}if(!signInContext.Properties.ExpiresUtc.HasValue){signInContext.Properties.ExpiresUtc=issuedUtc.Add(Options.ExpireTimeSpan);}awaitEvents.SigningIn(signInContext);if(signInContext.Properties.IsPersistent){varexpiresUtc=signInContext.Properties.ExpiresUtc??issuedUtc.Add(Options.ExpireTimeSpan);signInContext.CookieOptions.Expires=expiresUtc.ToUniversalTime();}varticket=newAuthenticationTicket(signInContext.Principal,signInContext.Properties,signInContext.Scheme.Name);if(Options.SessionStore!=null){if(_sessionKey!=null){awaitOptions.SessionStore.RemoveAsync(_sessionKey);}_sessionKey=awaitOptions.SessionStore.StoreAsync(ticket);varprincipal=newClaimsPrincipal(newClaimsIdentity(new[]{newClaim(SessionIdClaim,_sessionKey,ClaimValueTypes.String,Options.ClaimsIssuer)},Options.ClaimsIssuer));ticket=newAuthenticationTicket(principal,null,Scheme.Name);}varcookieValue=Options.TicketDataFormat.Protect(ticket,GetTlsTokenBinding());Options.CookieManager.AppendResponseCookie(Context,Options.Cookie.Name,cookieValue,signInContext.CookieOptions);varsignedInContext=newCookieSignedInContext(Context,Scheme,signInContext.Principal,signInContext.Properties,Options);awaitEvents.SignedIn(signedInContext);//OnlyredirectontheloginpathvarshouldRedirect=Options.LoginPath.HasValue&&OriginalPath==Options.LoginPath;awaitApplyHeaders(shouldRedirect,signedInContext.Properties);Logger.SignedIn(Scheme.Name);}

从源码我们可以分析出流程:

根据ClaimsPrincipal的用户信息序列化后通过加密方式进行加密获得ticket。(默认加密方式是的KeyRingBasedDataProtecto。

源码地址:https://github.com/aspnet/DataProtection)

再通过之前的初始化好的CookieOption再AppendResponseCookie方法进行设置Cookie

最后通过Events.RedirectToReturnUrl进行重定向到ReturnUrl。

Ticket加密

两种设置方式

CookieAuthenticationOptions.DataProtectionProviderCookieAuthenticationOptions.TicketDataFormat

DataProtectionProvider

如果做了集群可以设置到共享文件夹,在第一个启动的应用则会创建如下图的文件

options.DataProtectionProvider=DataProtectionProvider.Create(newDirectoryInfo(@”D:ssokey”));

TicketDataFormat

重写数据加密方式,本次demo使用了是AES.

options.TicketDataFormat=newTicketDataFormat(newAesDataProtector());internalclassAesDataProtector:IDataProtector{privateconststringKey=”!@#13487″;publicIDataProtectorCreateProtector(stringpurpose){returnthis;}publicbyte[]Protect(byte[]plaintext){returnAESHelper.Encrypt(plaintext,Key);}publicbyte[]Unprotect(byte[]protectedData){returnAESHelper.Decrypt(protectedData,Key);}}

结尾

以上为.NETCoreMVC的同域SSO实现思路与细节。因编写demo的原因代码复用率并不好,冗余代码比较多,大家可以根据情况进行抽离封装。

好了,本文到此结束,如果可以帮助到大家,还望关注本站哦!

Published by

风君子

独自遨游何稽首 揭天掀地慰生平