千家信息网

.net单点登录设计的示例分析

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,这篇文章将为大家详细讲解有关.net单点登录设计的示例分析,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。名称定义为了方便说明先说明几个文中出现的名词的
千家信息网最后更新 2026年11月01日.net单点登录设计的示例分析

这篇文章将为大家详细讲解有关.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加密生成,当然这里可以再加个盐进行防伪。

///     /// 生成秘钥    ///     ///     ///     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站统一授权验证。

///   /// 授权枚举  ///   public enum AuthCodeEnum  {    Public = 1,    Login = 2  }  ///   /// 授权过滤器  ///   public class AuthAttribute : ActionFilterAttribute  {    ///      /// 权限代码     ///      public AuthCodeEnum Code { get; set; }    ///      /// 验证权限    ///      ///      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);      }    }    ///     /// 生成跳转脚本    ///     /// 统一授权地址    /// 回调地址    ///     private string GetAuthernScript(string authernUrl, string returnUrl)    {      StringBuilder sbScript = new StringBuilder();      sbScript.Append("");      return sbScript.ToString();    }  }

代码说明:这里为了方便设置Token的过期时间,所以使用Cache来存取Token,设定Token的失效时间为两分钟,当验证成功则从cache中移除Token。

调取过滤器

[Auth(Code = AuthCodeEnum.Login)]     public ActionResult Index()     {       return View();    }

P站主要逻辑

P站收到授权请求,P站首先通过Coookie来判断是否登陆,未登录则跳转至登陆页面进行登陆操作。

///     /// 授权登陆验证    ///     ///     [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

///     /// 验证令牌    ///     /// 令牌    /// 时间戳    ///     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

用户名:admin 密码:123

关于.net单点登录设计的示例分析就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

0