Heim  >  Artikel  >  Backend-Entwicklung  >  Ausführliche Erläuterung des ASP.NET MVC SSO Single Sign-On-Designbeispiels

Ausführliche Erläuterung des ASP.NET MVC SSO Single Sign-On-Designbeispiels

零下一度
零下一度Original
2017-07-03 17:28:552865Durchsuche

Dieser Artikel stellt hauptsächlich das Design und die Implementierung von ASP.NET MVC SSO Single Sign-On vor. Interessierte können mehr darüber erfahren.

Experimentelle Umgebungskonfiguration

Die HOST-Dateikonfiguration ist wie folgt:

127.0.0.1 app.com
127.0.0.1 sso.com

IIS-Konfiguration ist wie folgt:

Der Anwendungspool verwendet .Net Framework 4.0

Achten Sie auf die an IIS gebundenen Domänennamen, zwei völlig unterschiedliche Domänennamen.

Die Website-Konfiguration von app.com ist wie folgt:

Die Website-Konfiguration von sso.com ist wie folgt:

memcachedCache:

Datenbankkonfiguration:

Die Datenbank verwendet EntityFramework 6.0, die entsprechende Datenbank- und Tabellenstruktur wird beim ersten Durchlauf automatisch erstellt.

Demonstration des Autorisierungsüberprüfungsprozesses:

Besuchen Sie: http://app.com in der Adressleiste des Browsers. Wenn sich der Benutzer nicht angemeldet hat, wird die Website automatisch geöffnet Umleiten Gehen Sie zu: http://sso.com/passport und übergeben Sie die entsprechende AppKey-Anwendungs-ID über die QueryString-Parameter. Der laufende Screenshot lautet wie folgt:

URL-Adresse: http://sso.com/passport ?appkey=670b14728ad9902aecba32e22fa4f6bd&username=

Nachdem Sie das richtige Anmeldekonto und Passwort eingegeben haben, klicken Sie auf die Anmeldeschaltfläche. Das System leitet dann automatisch zur Anwendung weiter und die Startseite wird gelöscht Die Zerstörung ist erfolgreich, wie unten gezeigt:

Da die SSO-Autorisierungsanmeldung unter verschiedenen Domänen durchgeführt wird, wird die QueryString-Methode verwendet, um die Autorisierungs-ID zurückzugeben. Cookies können auf Websites derselben Domain verwendet werden. Da die 301-Umleitungsanforderung vom Browser gesendet wird, geht die Autorisierungskennung bei der Umleitung durch den Browser verloren, wenn sie in Handers platziert wird. Nach erfolgreicher Umleitung schreibt das Programm die Autorisierungsmarkierung automatisch in das Cookie. Wenn Sie auf eine andere Seitenadresse klicken, werden die Informationen zur Autorisierungsmarkierung nicht mehr in der URL-Adressleiste angezeigt. Die Cookie-Einstellungen lauten wie folgt:

Nachträgliche Berechtigungsprüfung nach erfolgreichem Login (Zugriff auf andere Seiten, die eine Autorisierung erfordern):

Verifizierungsadresse: http:// sso.com/api/passport?sessionkey=xxxxxx&remark=xxxxxx

Ergebnisse zurückgeben: true, false

Der Client kann den Benutzer basierend auf einer Autorisierung auffordern Die tatsächliche Geschäftssituation ist verloren gegangen und muss neu autorisiert werden. Standardmäßig wird automatisch zur SSO-Anmeldeseite weitergeleitet, nämlich: http://sso.com/passport?appkey=670b14728ad9902aecba32e22fa4f6bd&username=seo@ljja.cn. Gleichzeitig wird das Textfeld für die E-Mail-Adresse auf der Anmeldeseite angezeigt Vervollständigen Sie das Anmeldekonto des Benutzers automatisch. Nach erfolgreicher Autorisierung wird die Gültigkeitsdauer der Sitzung automatisch um ein Jahr verlängert.

SSO-Datenbanküberprüfungsprotokoll:

Benutzerautorisierungsüberprüfungsprotokoll:

Benutzerautorisierungssitzungssitzung:

Datenbankbenutzerkonto und Anwendungsinformationen:

Kerncode der Anmeldebestätigungsseite für die Anwendungsautorisierung:


/// <summary>
  /// 公钥:AppKey
  /// 私钥:AppSecret
  /// 会话:SessionKey
  /// </summary>
  public class PassportController : Controller
  {
    private readonly IAppInfoService _appInfoService = new AppInfoService();
    private readonly IAppUserService _appUserService = new AppUserService();
    private readonly IUserAuthSessionService _authSessionService = new UserAuthSessionService();
    private readonly IUserAuthOperateService _userAuthOperateService = new UserAuthOperateService();

    private const string AppInfo = "AppInfo";
    private const string SessionKey = "SessionKey";
    private const string SessionUserName = "SessionUserName";

    //默认登录界面
    public ActionResult Index(string appKey = "", string username = "")
    {
      TempData[AppInfo] = _appInfoService.Get(appKey);

      var viewModel = new PassportLoginRequest
      {
        AppKey = appKey,
        UserName = username
      };

      return View(viewModel);
    }

    //授权登录
    [HttpPost]
    public ActionResult Index(PassportLoginRequest model)
    {
      //获取应用信息
      var appInfo = _appInfoService.Get(model.AppKey);
      if (appInfo == null)
      {
        //应用不存在
        return View(model);
      }

      TempData[AppInfo] = appInfo;

      if (ModelState.IsValid == false)
      {
        //实体验证失败
        return View(model);
      }

      //过滤字段无效字符
      model.Trim();

      //获取用户信息
      var userInfo = _appUserService.Get(model.UserName);
      if (userInfo == null)
      {
        //用户不存在
        return View(model);
      }

      if (userInfo.UserPwd != model.Password.ToMd5())
      {
        //密码不正确
        return View(model);
      }

      //获取当前未到期的Session
      var currentSession = _authSessionService.ExistsByValid(appInfo.AppKey, userInfo.UserName);
      if (currentSession == null)
      {
        //构建Session
        currentSession = new UserAuthSession
        {
          AppKey = appInfo.AppKey,
          CreateTime = DateTime.Now,
          InvalidTime = DateTime.Now.AddYears(1),
          IpAddress = Request.UserHostAddress,
          SessionKey = Guid.NewGuid().ToString().ToMd5(),
          UserName = userInfo.UserName
        };

        //创建Session
        _authSessionService.Create(currentSession);
      }
      else
      {
        //延长有效期,默认一年
        _authSessionService.ExtendValid(currentSession.SessionKey);
      }

      //记录用户授权日志
      _userAuthOperateService.Create(new UserAuthOperate
      {
        CreateTime = DateTime.Now,
        IpAddress = Request.UserHostAddress,
        Remark = string.Format("{0} 登录 {1} 授权成功", currentSession.UserName, appInfo.Title),
        SessionKey = currentSession.SessionKey
      }); 104 
      var redirectUrl = string.Format("{0}?SessionKey={1}&SessionUserName={2}",
        appInfo.ReturnUrl, 
        currentSession.SessionKey, 
        userInfo.UserName);

      //跳转默认回调页面
      return Redirect(redirectUrl);
    }
  }
Memcached会话标识验证核心代码:
public class PassportController : ApiController
  {
    private readonly IUserAuthSessionService _authSessionService = new UserAuthSessionService();
    private readonly IUserAuthOperateService _userAuthOperateService = new UserAuthOperateService();

    public bool Get(string sessionKey = "", string remark = "")
    {
      if (_authSessionService.GetCache(sessionKey))
      {
        _userAuthOperateService.Create(new UserAuthOperate
        {
          CreateTime = DateTime.Now,
          IpAddress = Request.RequestUri.Host,
          Remark = string.Format("验证成功-{0}", remark),
          SessionKey = sessionKey
        });

        return true;
      }

      _userAuthOperateService.Create(new UserAuthOperate
      {
        CreateTime = DateTime.Now,
        IpAddress = Request.RequestUri.Host,
        Remark = string.Format("验证失败-{0}", remark),
        SessionKey = sessionKey
      });

      return false;
    }
  }

Attribut „Client-Autorisierungsüberprüfungsfilter“


public class SSOAuthAttribute : ActionFilterAttribute
  {
    public const string SessionKey = "SessionKey";
    public const string SessionUserName = "SessionUserName";

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
      var cookieSessionkey = "";
      var cookieSessionUserName = "";

      //SessionKey by QueryString
      if (filterContext.HttpContext.Request.QueryString[SessionKey] != null)
      {
        cookieSessionkey = filterContext.HttpContext.Request.QueryString[SessionKey];
        filterContext.HttpContext.Response.Cookies.Add(new HttpCookie(SessionKey, cookieSessionkey));
      }

      //SessionUserName by QueryString
      if (filterContext.HttpContext.Request.QueryString[SessionUserName] != null)
      {
        cookieSessionUserName = filterContext.HttpContext.Request.QueryString[SessionUserName];
        filterContext.HttpContext.Response.Cookies.Add(new HttpCookie(SessionUserName, cookieSessionUserName));
      }

      //从Cookie读取SessionKey
      if (filterContext.HttpContext.Request.Cookies[SessionKey] != null)
      {
        cookieSessionkey = filterContext.HttpContext.Request.Cookies[SessionKey].Value;
      }

      //从Cookie读取SessionUserName
      if (filterContext.HttpContext.Request.Cookies[SessionUserName] != null)
      {
        cookieSessionUserName = filterContext.HttpContext.Request.Cookies[SessionUserName].Value;
      }

      if (string.IsNullOrEmpty(cookieSessionkey) || string.IsNullOrEmpty(cookieSessionUserName))
      {
        //直接登录
        filterContext.Result = SsoLoginResult(cookieSessionUserName);
      }
      else
      {
        //验证
        if (CheckLogin(cookieSessionkey, filterContext.HttpContext.Request.RawUrl) == false)
        {
          //会话丢失,跳转到登录页面
          filterContext.Result = SsoLoginResult(cookieSessionUserName);
        }
      }

      base.OnActionExecuting(filterContext);
    }

    public static bool CheckLogin(string sessionKey, string remark = "")
    {
      var httpClient = new HttpClient
      {
        BaseAddress = new Uri(ConfigurationManager.AppSettings["SSOPassport"])
      };

      var requestUri = string.Format("api/Passport?sessionKey={0}&remark={1}", sessionKey, remark);

      try
      {
        var resp = httpClient.GetAsync(requestUri).Result;

        resp.EnsureSuccessStatusCode();

        return resp.Content.ReadAsAsync<bool>().Result;
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }

    private static ActionResult SsoLoginResult(string username)
    {
      return new RedirectResult(string.Format("{0}/passport?appkey={1}&username={2}",
          ConfigurationManager.AppSettings["SSOPassport"],
          ConfigurationManager.AppSettings["SSOAppKey"],
          username));
    }
  }

Beispiel für die Verwendung der SSO-Überprüfungsfunktion:


[SSOAuth]
  public class HomeController : Controller
  {
    public ActionResult Index()
    {
      return View();
    }

    public ActionResult About()
    {
      ViewBag.Message = "Your application description page.";

      return View();
    }

    public ActionResult Contact()
    {
      ViewBag.Message = "Your contact page.";

      return View();
    }
  }

Zusammenfassung:

Aus dem Entwurf des Beispielcodes können Sie ersehen, dass es noch viele Optimierungsbereiche für Code gibt Es gibt auch eine Reihe von Meldungen, z. B. dass das Benutzerkonto auf der Anmeldeseite der SSO-Anwendungsautorisierung nicht vorhanden ist, das Kennwort falsch ist usw. In der späteren Phase, wenn der Geschäftscode grundsätzlich ordnungsgemäß ausgeführt wird, können Sie die Optimierung weiterer Sicherheitsstufen in Betracht ziehen, z. B. die Aktivierung der AppSecret-Signaturüberprüfung des privaten Schlüssels, der IP-Bereichsüberprüfung, eines Angriffs mit fester Sitzungsanforderung und des Verifizierungscodes des SSO-Autorisierungs-Anmeldeschnittstelle, automatische Rekonstruktion des Sitzungscache, horizontale Erweiterung des SSO-Servers, Cache usw.

Das obige ist der detaillierte Inhalt vonAusführliche Erläuterung des ASP.NET MVC SSO Single Sign-On-Designbeispiels. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn