前言
最近轮到我在小组晨会来分享知识点,突然想到单点登录,准备来分享下如何实现单点登录,所以有了下文。实现方案以及代码可能写得不是很严谨,有漏洞的地方或者错误的地方欢迎大家指正。
刚开始头脑中没有思路,直接在博客园里面看看别人是如何来实现的,看了几篇文章发现,发现解决方案有点问题,或者说不算实现了单点登录
名称定义
为了方便说明先说明几个文中出现的名词的含义:
P站:统一登录授权验证中心,demo中 域名是www.passport.com:801
A站:处于不同域名下的测试网站,demo中 域名是www.a.com:802
B站:处于不同域名下的测试网站,demo中 域名是www.b.com:803
Token:用户访问P站的秘钥
Ticket:用来保存用户信息的加密字符串
单点登录
访问A站需要登陆的就跳转P站中进行登陆,P站登陆之后跳转回至A站,用户再次访问B站需要登陆的页面,用户不需要进行登陆操作就可以正常访问。
实现思路
未登录用户访问A站,首先会重定向跳转至P站授权中心,P站首先通过检测Cookie来判断当前不是处于登陆状态,就跳转至登陆页面进行登陆操作,登陆成功之后把用户信息加密ticket附在A的请求地址上返回,A站通过解密ticket来获取用户信息,解密成功并存进Session中(这样用户在A中就处于登陆状态了),访问通过;当用户再次访问B站的时候,对于B站来说,用户是处于未登录状态,则同样会重定向跳转至P站授权中心,P站检测Cookie,判断当前用户处于登陆状态,就把当前用户信息加密成ticket附在B的请求地址上返回,后面的操作就和A站处理一样;这样都登陆之后再次访问A或者B,A和B中Session中都存储了用户信息,就不会再次请求P站了。
简单关系图
泳道流程图
主要逻辑说明
A站主要逻辑
用户首先访问A站,A站中会生成Token,并存入Cache中。Token是A访问P的钥匙,P在回调给A的时候需要携带这个Token。A请求P,P验证Token,P回调A,A检测Token是否是发送出去的Token,验证之后Token即失效,防止Token被再次使用。
Token的生成是通过取时间戳的不同字段进行MD5加密生成,当然这里可以再加个盐进行防伪。
/// <summary> /// 生成秘钥 /// </summary> /// <param name="timestamp"></param> /// <returns></returns> public static string CreateToken(DateTime timestamp) { StringBuilder securityKey = new StringBuilder(MD5Encypt(timestamp.ToString("yyyy"))); securityKey.Append(MD5Encypt(timestamp.ToString("MM"))); securityKey.Append(MD5Encypt(timestamp.ToString("dd"))); securityKey.Append(MD5Encypt(timestamp.ToString("HH"))); securityKey.Append(MD5Encypt(timestamp.ToString("mm"))); securityKey.Append(MD5Encypt(timestamp.ToString("ss"))); return MD5Encypt(securityKey.ToString()); }
P回调A的时候进行,A中对Token进行校验,校验不成功则请求P站统一授权验证。
/// <summary> /// 授权枚举 /// </summary> public enum AuthCodeEnum { Public = 1, Login = 2 } /// <summary> /// 授权过滤器 /// </summary> public class AuthAttribute : ActionFilterAttribute { /// <summary> /// 权限代码 /// </summary> public AuthCodeEnum Code { get; set; } /// <summary> /// 验证权限 /// </summary> /// <param name="filterContext"></param> public override void OnActionExecuting(ActionExecutingContext filterContext) { var request = filterContext.HttpContext.Request; var session = filterContext.HttpContext.Session; //如果存在身份信息 if (Common.CurrentUser == null) { if (Code == AuthCodeEnum.Public) { return; } string reqToken = request["Token"]; string ticket = request["Ticket"]; Cache cache = HttpContext.Current.Cache; //没有获取到Token或者Token验证不通过或者没有取到从P回调的ticket 都进行再次请求P TokenModel tokenModel= cache.Get(ConstantHelper.TOKEN_KEY)==null?null:(TokenModel)cache.Get(ConstantHelper.TOKEN_KEY); if (string.IsNullOrEmpty(reqToken) || tokenModel == null || tokenModel.Token!= reqToken || string.IsNullOrEmpty(ticket)) { DateTime timestamp = DateTime.Now; string returnUrl = request.Url.AbsoluteUri; tokenModel = new TokenModel { TimeStamp = timestamp, Token = AuthernUtil.CreateToken(timestamp) }; //Token加入缓存中,设计过期时间为20分钟 cache.Add(ConstantHelper.TOKEN_KEY, tokenModel, null, DateTime.Now.AddMinutes(20),Cache.NoSlidingExpiration,CacheItemPriority.Default, null); filterContext.Result = new ContentResult { Content = GetAuthernScript(AuthernUtil.GetAutherUrl(tokenModel.Token, timestamp), returnUrl) }; return; } LoginService service = new LoginService(); var userinfo = service.GetUserInfo(ticket); session[ConstantHelper.USER_SESSION_KEY] = userinfo; //验证通过,cache中去掉Token,保证每个token只能使用一次 cache.Remove(ConstantHelper.TOKEN_KEY); } } /// <summary> /// 生成跳转脚本 /// </summary> /// <param name="authernUrl">统一授权地址</param> /// <param name="returnUrl">回调地址</param> /// <returns></returns> private string GetAuthernScript(string authernUrl, string returnUrl) { StringBuilder sbScript = new StringBuilder(); sbScript.Append("<script type='text/html" target="_blank">javascript'>"); sbScript.AppendFormat("window.location.href='{0}&returnUrl=' + encodeURIComponent('{1}');", authernUrl, returnUrl); sbScript.Append("</script>"); return sbScript.ToString(); } }
代码说明:这里为了方便设置Token的过期时间,所以使用Cache来存取Token,设定Token的失效时间为两分钟,当验证成功则从cache中移除Token。
调取过滤器
[Auth(Code = AuthCodeEnum.Login)] public ActionResult Index() { return View(); }
P站主要逻辑
P站收到授权请求,P站首先通过Coookie来判断是否登陆,未登录则跳转至登陆页面进行登陆操作。
/// <summary> /// 授权登陆验证 /// </summary> /// <returns></returns> [HttpPost] public ActionResult PassportVertify() { var cookie=Request.Cookies[ConstantHelper.USER_COOKIE_KEY]; if (cookie == null ||string.IsNullOrEmpty(cookie.ToString())) { return RedirectToAction("Login", new { ReturnUrl = Request["ReturnUrl"] ,Token= Request["Token"] }); } string userinfo = cookie.ToString(); var success= passportservice.AuthernVertify(Request["Token"], Convert.ToDateTime(Request["TimeStamp"])); if (!success) { return RedirectToAction("Login", new { ReturnUrl = Request["ReturnUrl"], Token = Request["Token"] }); } return Redirect(passportservice.GetReturnUrl(userinfo, Request["Token"],Request["ReturnUrl"])); }
已登陆则验证Token
/// <summary> /// 验证令牌 /// </summary> /// <param name="token">令牌</param> /// <param name="timestamp">时间戳</param> /// <returns></returns> public bool AuthernVertify(string token,DateTime timestamp) { return AuthernUtil.CreateToken(timestamp) == token; }
测试说明
1、修改host
127.0.0.1 www.passport.com
127.0.0.1 www.a.com
127.0.0.1 www.b.com
2、部署IIS
P www.passport.com:801
A www.a.com:802
B www.b.com:803
3、测试账号和webconfig
<add key="PassportCenterUrl" value="http://www.passport.com:801"/>
用户名:admin 密码:123
demo
下载地址:源码下载地址
原文链接:http://www.cnblogs.com/minesnil-forfaith/p/6062943.html
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
我的任务是为我们的客户实施单点登录,作为我们下一个版本的一部分。流程如下: 用户使用学校提供给他/她的学生ID/密码登录学校的主要门户系统。 用户点击我公司产品的链接。 用户会自动进入仪表板页面,就好像他们刚刚通过我们网站上的登录表单登录一样。 因此,有两种机制可以将用户验证到我们的站点: 访问我们产品的主页,并使用我们存储在本地系统中的电子邮件/密码登录。 使用单点登录,学生已经使用学生id和密
注意 所有OAuth2 SSO和资源服务器功能在版本1.3中移动到Spring Boot。您可以在Spring Boot用户指南中找到文档 。 该项目提供从CloudFoundry服务凭据到Spring Boot功能的自动绑定。如果您有一个称为“sso”的CloudFoundry服务,例如,使用包含“client_id”,“client_secret”和“auth_domain”的凭据,它将自动绑
地址URL https://api.es.xiaojukeji.com/river/Login/getLoginEncryptStr 返回数据格式 JSON 请求方式 GET 是否需要登录 是 关于登录授权,参见 如何登录授权 访问授权限制 暂无 请求参数 名称 类型 必选 描述 client_id string yes 申请应用时分配的AppKey access_token string yes
接口说明: 接口类型:主动调用接口。 接口作用:可通过调用该接口来获取一个临时的URL,通过访问该URL即可直接进入某账号的智齿客服后台,从而实现单点登录的业务。 请求方式: POST 请求地址: https(http)://www.sobot.com/api/oss/5/direct_url 注:使用https请求返回的登录链接是https协议的,使用http请求,返回的登录链接就是http协
【注意】下列选项和选项卡会根据服务器版本和验证类型而有所不同。 常规属性 登录名 定义登录的名。 验证类型 选择登录的验证类型。 密码 指定登录的密码。 确认密码 重新输入登录密码。 指定旧密码 如果你想在编辑登录时输入此帐号使用的旧密码,可勾选这个选项。 实施密码策略 如果你想强制密码遵循 SQL Server 的密码原则,可勾选这个选项。 实施密码过期 如果你想强制密码具有到期日,可勾选这个选
【注意】下列选项和选项卡会根据服务器版本和验证类型而有所不同。 常规属性 用户名 定义登录的名。 验证类型 选择登录的验证类型。 密码 指定登录的密码。 确认密码 重新输入登录密码。 指定旧密码 如果你想在编辑登录时输入此帐号使用的旧密码,可勾选这个选项。 实施密码策略 如果你想强制密码遵循 SQL Server 的密码策略,可勾选这个选项。 实施密码过期 如果你想强制密码具有到期日,可勾选这个选