.net 单点登录的设计与实践

前言

最近轮到我在小组晨会来分享知识点,突然想到单点登录,准备来分享下如何实现单点登录,所以有了下文。实现方案以及代码可能写得不是很严谨,有漏洞的地方或者错误的地方欢迎大家指正。  

刚开始头脑中没有思路,直接在博客园里面看看别人是如何来实现的,看了几篇文章发现,发现解决方案有点问题,或者说不算实现了单点登录

名称定义

为了方便说明先说明几个文中出现的名词的含义:

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • ASP.NET 5中使用AzureAD实现单点登录

    题记:在ASP.NET 5中虽然继续可以沿用ASP.NET Identity来做验证授权,不过也可以很容易集成支持标准协议的第三方服务,比如Azure Active Directory. 其实,在ASP.NET 5中集成AzureAD,利用其进行验证和授权,是非常简单的.因为:首先Azure Active Directory提供了OAuth2.0.OpenId Connect 1.0.SAML和WS-Federation 1.2标准协议接口:其次微软在ASP.NET 5中移植了集成OpenId

  • asp.net简单实现单点登录(SSO)的方法

    本文实例讲述了asp.net简单实现单点登录(SSO)的方法.分享给大家供大家参考,具体如下: 单点登录(Single Sign On , 简称 SSO )是目前比较流行的服务于企业业务整合的解决方案之一, SSO 使得在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统 CAS(Central Authentication Service)是一款不错的针对 Web 应用的单点登录框架(耶鲁大学开发)主要用于Java Php 有兴趣大家可以研究下.. 下面是一个简单实现单点登录的

  • 一个简单的asp.net 单点登录实现

    以下是实现的效果图:  首先上图的点击火车订票链接,就会打开http://学生信息平台网站/LoginToTrainSite.asa页面. LoginToTrainSite.asa页面的大致代码如下: 复制代码 代码如下: <% Response.Buffer = True Response.ExpiresAbsolute = Now() - 1 Response.Expires = 0 Response.CacheControl = "no-cache" Response.Ad

  • 在ASP.NET 中实现单点登录

    出自:[孟宪会之精彩世界] 发布日期:2005年1月27日 8点48分0秒 [有删改]  由于某些原因,在我们的应用中会遇到一个用户只能在一个地方登录的情况,也就是我们通常所说的单点登录.在ASP.NET中实现单点登录其实很简单,下面就把主要的方法和全部代码进行分析.   实现思路   利用Cache的功能,我们把用户的登录信息保存在Cache中,并设置过期时间为Session失效的时间,因此,一旦Session失效,我们的Cache也过期:而Cache对所有的用户都可以访问,因此,用它保存用户

  • asp.net 简单单点登录技术分析

    代码如下: 复制代码 代码如下: ///单点登录(Single Sign On) public void SSOMethods(string username, string password) { //判断登录情况 此处方法省略-- int result = CheckLogin(username, password); if(result>0) { //唯一标识,可自行设定 string key = string.Format("{0}_{1}",username, pass

  • .net 单点登录的设计与实践

    前言 最近轮到我在小组晨会来分享知识点,突然想到单点登录,准备来分享下如何实现单点登录,所以有了下文.实现方案以及代码可能写得不是很严谨,有漏洞的地方或者错误的地方欢迎大家指正. 刚开始头脑中没有思路,直接在博客园里面看看别人是如何来实现的,看了几篇文章发现,发现解决方案有点问题,或者说不算实现了单点登录 名称定义 为了方便说明先说明几个文中出现的名词的含义: P站:统一登录授权验证中心,demo中 域名是www.passport.com:801 A站:处于不同域名下的测试网站,demo中 域名

  • 入门到精通Java SSO单点登录原理详解

    目录 1. 基础概念 2. 单点登录 3. CAS 流程 4. OAuth 流程 5. CAS和OAuth的区别 1. 基础概念 SSO单点登录(Single sign-on) 所谓单点登录就是在多个应用系统中,用户只需登录一次就可以访问所有相互信任的系统. CAS 中央认证服务(Central Authentication Service) CAS是由美国耶鲁大学发起的一个企业级开源项目,旨在为WEB应用系统提供一种可靠的单点登录解决方案(WEB SSO). OAuth2.0 开放授权(Ope

  • 基于.Net的单点登录(SSO)实现解决方案

    前些天一位朋友要我帮忙做一单点登录,其实这个概念早已耳熟能详,但实际应用很少,难得最近轻闲,于是决定通过本文来详细描述一个SSO解决方案,希望对大家有所帮助.SSO的解决方案很多,但搜索结果令人大失所望,大部分是相互转载,并且描述的也是走马观花. 闲话少叙,进入正题,我的想法是使用集中验证方式,多个站点集中Passport验证. 如下图所示:  为方便清晰描述,先定义几个名词,本文中出现之处均为如下含义. 主站:Passport集中验证服务器 http://www.passport.com/ .

  • 浅谈谁都能看懂的单点登录(SSO)实现方式(附源码)

    SSO的基本概念 SSO英文全称Single Sign On(单点登录).SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统.它包括可以将这次主要的登录映射到其他应用中用于同一个用户的登录的机制.它是目前比较流行的企业业务整合的解决方案之一.(本段内容来自百度百科) 今天这篇文章将介绍SSO的一种实现方式,代码超简单,仅用来验证我的思路是否可行,具体细节请大家来完善! 二级域名的单点登录 什么是二级域名呢?例如: site1.domain.com site2.domai

  • SpringCloud实现SSO 单点登录的示例代码

    前言 作为分布式项目,单点登录是必不可少的,文本基于之前的的博客(猛戳:SpringCloud系列--Zuul 动态路由,SpringBoot系列--Redis)记录Zuul配合Redis实现一个简单的sso单点登录实例 sso单点登录思路: 1.访问分布式系统的任意请求,被Zuul的Filter拦截过滤 2.在run方法里实现过滤规则:cookie有令牌accessToken且作为key存在于Redis,或者访问的是登录页面.登录请求则放行 3.否则,将重定向到sso-server的登录页面且

  • Spring Cloud OAuth2 实现用户认证及单点登录的示例代码

    OAuth 2 有四种授权模式,分别是授权码模式(authorization code).简化模式(implicit).密码模式(resource owner password credentials).客户端模式(client credentials),具体 OAuth2 是什么,可以参考这篇文章.(http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html) 本文我们将使用授权码模式和密码模式两种方式来实现用户认证和授权管理. OAuth2 其

  • Java spring单点登录系统

    目录 1.单点登录系统介绍 2.简单业务实现 2.1添加依赖 2.2 项目配置文件 2.3添加项目启动类 2.4 启动并访问项目 3. 优化进一步设计 3.1 定义安全配置类 SecurityConfig 3.2定义用户信息处理对象 3.3 网关中登陆路由配置 3.4基于Postman进行访问测试 3.5 定义登陆页面 3.6 构建令牌配置对象 3.7 定义认证授权核心配置 授权服务器的核心配置 Postman访问测试 4 资源服务器配置–sca-resource 4.1 构建令牌配置对象 4.

  • Spring gateway + Oauth2实现单点登录及详细配置

    场景: 按职能,鉴权系统需要划分 网关(spring gateway) + 鉴权(auth-server).本文通过实践搭建鉴权系统. spring gateway 首先引入pom依赖 1.resilience 熔断器 2.gateway 网关 3.eureka client 服务注册中心 4.lombok插件 5.actuator状态监控 <dependencies> <!-- 熔断器--> <dependency> <groupId>io.github.

  • Redis键值设计的实践

    目录 1 优雅的key结构 2 拒绝BigKey 2.1 判断BigKey 2.2 BigKey的危害 2.3 如何发现BigKey 2.4 如何删除BigKey 3 恰当的数据类型 3.1 存储对象 3.2 Hash优化 在Redis中,良好的键值设计可以达成事半功倍的效果,而不好的键值设计可能会带来Redis服务停滞,网络阻塞,CPU使用率飙升等一系列问题,今天就教大家如何设计一个良好的key-value 1 优雅的key结构 Redis的Key虽然可以自定义,但最好遵循下面的几个最佳实践约

  • Asp.net mvc 权限过滤和单点登录(禁止重复登录)

    1.权限控制使用controller和 action来实现,权限方式有很多种,最近开发项目使用控制控制器方式实现代码如下 /// <summary> /// 用户权限控制 /// </summary> public class UserAuthorize : AuthorizeAttribute { /// <summary> /// 授权失败时呈现的视图 /// </summary> public string AuthorizationFailView

随机推荐