前言
接着上篇的《.net core实践系列之SSO-同域实现》,这次来聊聊SSO跨域的实现方式。这次虽说是.net core实践,但是核心点使用jquery居多。
建议看这篇文章的朋友可以先看上篇《.net core实践系列之SSO-同域实现》做一个SSO大概了解。
源码地址:https://github.com/SkyChenSky/Core.SSO.git
效果图
知识点回顾
实现原则
只要统一Token的产生和校验方式,无论授权与认证的在哪(认证系统或业务系统),也无论用户信息存储在哪(浏览器、服务器),其实都可以实现单点登录的效果
实现关键点
- Token的生成
- Token的共享
- Token校验
Token共享复杂度
- 同域
- 跨域
Token认证方式
- 业务系统自认证
- 转发给认证中心认证
同源策略
所有支持JavaScript 的浏览器,都必须遵守的安全策略,也是浏览器最基本的安全功能。
如果没有处理过发起跨域请求,就算服务器接收到了,响应成功了浏览器也是会拦截的。
同源
指域名,协议,端口相同
目的
浏览器为了阻止恶意脚本获取不同源上的的敏感信息。
跨域请求
然而在实际情况下跨域请求的场景也是存在的,解决方案有两种:
- JSONP
- 响应头设置“Access-Control-Allow-Origin”
Cookie
Cookie的读取和发送也是必须遵循同源策略的。
虽说请求共享可以设置响应头Access-Control-Allow-Credentials、Access-Control-Allow-Origin与Ajax请求属性xhrFields: {withCredentials: true}进行解决,但是!
就算响应头有set-cookie浏览器也是无法正常保存的。
SSO跨域解决方式
针对cookie认证,我唯一能找到的解决方案就是跳转页面。
具体步骤:
- 认证中心登录成功后,请求登录中心接口获得token
- 携带token逐个跳转到业务系统的中转页面。
- 跳转完成后,返回到认证中心登录页面进行引导。
PS:如果哪位朋友有更加好的方案,可以及时与我沟通,非常感谢
实现方式
登录中心授权
<script> ???$(function () { ???????$("#submit").click(function () { ???????????$("#postForm").ajaxSubmit(function (result) { ???????????????if (result.success) { ???????????????????var token = getToken(); ???????????????????if (token) { ???????????????????????var authorizeHostArray = new Array( ???????????????????????????"http://www.web1.com/Token/Authorization", ???????????????????????????"http://www.web2.com/Token/Authorization" ???????????????????????); ???????????????????????var authorizeHostParams = ""; ???????????????????????authorizeHostArray.forEach(function (item) { ???????????????????????????authorizeHostParams += "&hostAuthorization=" + item; ???????????????????????}); ???????????????????????window.location.href = authorizeHostArray[0] + "?token=" + token + authorizeHostParams; ???????????????????} ???????????????} else { ???????????????????alert(result.msg); ???????????????} ???????????}); ???????}); ???????function getToken() { ???????????var token = null; ???????????$.ajax({ ???????????????url: "/api/Token", ???????????????type: "GET", ???????????????async: false, ???????????????success: function (d) { ???????????????????token = d.token; ???????????????} ???????????}); ???????????return token; ???????} ???});</script>
业务系统Token保存与注销
public class TokenController : Controller ???{ ???????public static TokenCookieOptions CookieOptions { get; set; } ???????public IActionResult Authorization(string token, List<string> hostAuthorization = null) ???????{ ???????????if (CookieOptions == null || string.IsNullOrEmpty(token)) ???????????????return BadRequest(); ???????????HttpContext.Response.Cookies.Append(CookieOptions.Name, token, new CookieOptions ???????????{ ???????????????Domain = CookieOptions.Domain, ???????????????Expires = DateTimeOffset.UtcNow.Add(CookieOptions.Expires), ???????????????HttpOnly = CookieOptions.HttpOnly, ???????????????IsEssential = CookieOptions.IsEssential, ???????????????MaxAge = CookieOptions.MaxAge, ???????????????Path = CookieOptions.Path, ???????????????SameSite = CookieOptions.SameSite ???????????}); ???????????if (hostAuthorization.Any()) ???????????????hostAuthorization = hostAuthorization.Where(a => !a.Contains(HttpContext.Request.Host.Host)).ToList(); ???????????if (!hostAuthorization.Any()) ???????????????hostAuthorization = new List<string> { "http://www.sso.com" }; ???????????return View(new TokenViewData ???????????{ ???????????????Token = token, ???????????????HostAuthorization = hostAuthorization ???????????}); ???????} ???????public IActionResult Logout(List<string> hostAuthorization = null) ???????{ ???????????HttpContext.Response.Cookies.Delete(CookieOptions.Name); ???????????if (hostAuthorization.Any()) ???????????????hostAuthorization = hostAuthorization.Where(a => !a.Contains(HttpContext.Request.Host.Host)).ToList(); ???????????if (!hostAuthorization.Any()) ???????????????hostAuthorization = new List<string> { "http://www.sso.com" }; ???????????return View(new TokenViewData ???????????{ ???????????????HostAuthorization = hostAuthorization ???????????}); ???????} ???}
Token生成与认证
与同域的实现的方式一致。
生成与认证是一对的,与之对应的就是AES的加密与解密。
public void ConfigureServices(IServiceCollection services) ???????{ ???????????services.AddMvc(); ???????????services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) ???????????????.AddCookie(options => ??????????????{ ??????????????????options.Cookie.Name = "Token"; ??????????????????options.Cookie.HttpOnly = true; ??????????????????options.ExpireTimeSpan = TimeSpan.FromMinutes(30); ??????????????????options.LoginPath = "/Account/Login"; ??????????????????options.LogoutPath = "/Account/Logout"; ??????????????????options.SlidingExpiration = true; ??????????????????//options.DataProtectionProvider = DataProtectionProvider.Create(new DirectoryInfo(@"D:\sso\key")); ??????????????????options.TicketDataFormat = new TicketDataFormat(new AesDataProtector()); ??????????????????TokenController.CookieName = options.Cookie.Name; ??????????????}); ???????}
internal class AesDataProtector : IDataProtector ???{ ???????private const string Key = "!@#13487"; ???????public IDataProtector CreateProtector(string purpose) ???????{ ???????????return this; ???????} ???????public byte[] Protect(byte[] plaintext) ???????{ ???????????return AESHelper.Encrypt(plaintext, Key); ???????} ???????public byte[] Unprotect(byte[] protectedData) ???????{ ???????????return AESHelper.Decrypt(protectedData, Key); ???????} ???}
业务系统自主认证的方式,对于系统的代码复用率与维护性都很低。如果想进行转发到认证系统进行认证,可以对[Authorize]进行重写。
大致思路是:
访问业务系统时,由自定义的[Authorize]进行拦截
获取到Token设置到请求头进行HttpPost到认证系统提供的/api/token/Authentication接口
响应给业务系统如果是成功则继续访问,如果是失败则401或者跳转到登录页。
结尾
最近事情比较多,demo与文章写的比较仓促,如果朋友们有更好的实现方式与建议,麻烦在下面评论反馈给我,先在此感谢。
.net core实践系列之SSO-跨域实现
原文地址:https://www.cnblogs.com/skychen1218/p/9805995.html