private String OnLogin(OAuthClient client, String state, String returnUrl) { var prov = Provider; var redirect = prov.GetRedirect(Request, "~/Sso/LoginInfo/" + client.Name); // 请求来源,前后端分离时传front-end,重定向会带上token放到锚点 var source = GetRequest("source"); //if (state.IsNullOrEmpty() && !returnUrl.IsNullOrEmpty()) state = $"r={returnUrl}"; //if (!source.IsNullOrEmpty()) //{ // state += (state.IsNullOrEmpty() ? "" : "&") + $"s={source}"; //} //state = HttpUtility.UrlEncode(state); var log = new OAuthLog { Provider = client.Name, Action = "Login", Success = false, ResponseType = client.ResponseType, Scope = client.Scope, State = state, RedirectUri = returnUrl, Source = source }; log.Insert(); return(client.Authorize(redirect, log.Id + "")); }
/// <summary>绑定</summary> /// <param name="id"></param> /// <returns></returns> public virtual ActionResult Bind(String id) { var prov = Provider; var user = prov.Current; if (user == null) { #if __CORE__ var returnUrl = Request.GetEncodedPathAndQuery(); #else var returnUrl = Request.Url?.PathAndQuery; #endif var rurl = "~/Admin/User/Login".AppendReturn(returnUrl); return(Redirect(rurl)); } var url = prov.GetReturnUrl(Request, true); var client = prov.GetClient(id); client.Init(GetUserAgent()); var redirect = prov.GetRedirect(Request, "~/Sso/LoginInfo/" + client.Name); var log = new OAuthLog { Provider = client.Name, Action = "Bind", Success = false, ResponseType = client.ResponseType, Scope = client.Scope, State = null, RedirectUri = url, }; log.Insert(); url = client.Authorize(redirect, log.Id + ""); return(Redirect(url)); }
/// <summary>登录后绑定当前用户</summary> public virtual OAuthLog BindAfterLogin(Int64 oauthId) { var prv = Provider; var mode = nameof(BindAfterLogin); var user = prv.Current; if (user == null) { return(null); } var log = OAuthLog.FindById(oauthId); if (log == null) { return(null); } var uc = UserConnect.FindByID(log.ConnectId); if (uc == null) { return(null); } uc.UserID = user.ID; uc.Enable = true; uc.UpdateTime = DateTime.Now; uc.Update(); log.UserId = user.ID; log.SaveAsync(); // 写日志 LogProvider.Provider?.WriteLog(typeof(User), "绑定", true, $"[{user}]依据[{mode}]绑定到[{uc.Provider}]的[{uc.NickName}]", user.ID, user + ""); return(log); }
public virtual ActionResult LoginInfo(String id, String code, String state) { if (id.IsNullOrEmpty()) { throw new ArgumentNullException(nameof(id)); } var name = id; var prov = Provider; var client = prov.GetClient(name); client.Init(GetUserAgent()); client.WriteLog("LoginInfo name={0} code={1} state={2} {3}", name, code, state, Request.GetRawUrl()); //var ds = state.SplitAsDictionary("=", "&"); var log = OAuthLog.FindById(state.ToLong()); if (log == null) { throw new InvalidOperationException("无效state=" + state); } //// 无法拿到code时,跳回去再来 //if (code.IsNullOrEmpty()) //{ // if (state == "refresh") throw new Exception("非法请求,无法取得code"); // return Redirect(OnLogin(client, $"{name}_refresh", null)); //} //// 短期内用过的code也跳回 //if (!_codeCache.TryAdd(code, code, false, out _)) //{ // return Redirect(OnLogin(client, $"{name}_refresh", null)); //} // 构造redirect_uri,部分提供商(百度)要求获取AccessToken的时候也要传递 var redirect = prov.GetRedirect(Request, "~/Sso/LoginInfo/" + client.Name); client.Authorize(redirect); //var returnUrl = prov.GetReturnUrl(Request, false); //var returnUrl = ds["r"]; var returnUrl = log.RedirectUri; try { // 获取访问令牌 if (!client.AccessUrl.IsNullOrEmpty()) { var html = client.GetAccessToken(code); // 如果拿不到访问令牌或用户信息,则重新跳转 if (client.AccessToken.IsNullOrEmpty() && client.OpenID.IsNullOrEmpty() && client.UserID == 0 && client.UserName.IsNullOrEmpty()) { XTrace.WriteLine("[{2}]拿不到访问令牌 code={0} state={1}", code, state, id); XTrace.WriteLine(Request.GetRawUrl() + ""); if (!html.IsNullOrEmpty()) { XTrace.WriteLine(html); } log.Success = false; log.Remark = html; throw new InvalidOperationException($"内部错误,无法获取令牌 code={code}"); } log.AccessToken = client.AccessToken; log.RefreshToken = client.RefreshToken; } //// 特殊处理钉钉 //if (client is DingTalkClient ding) DoDingDing(ding); // 获取OpenID。部分提供商不需要 if (!client.OpenIDUrl.IsNullOrEmpty()) { client.GetOpenID(); } // 短时间内不要重复拉取用户信息 // 注意,这里可能因为没有OpenID和UserName,无法判断得到用户链接,需要GetUserInfo后方能匹配UserConnect var set = Setting.Current; var uc = prov.GetConnect(client); if (uc.UpdateTime.AddSeconds(set.RefreshUserPeriod) < DateTime.Now) { // 获取用户信息 if (!client.UserUrl.IsNullOrEmpty()) { client.GetUserInfo(); } } // 如果前面没有取得用户链接,需要再次查询,因为GetUserInfo可能取得了UserName,而前面只有OpenId if (uc.ID == 0) { uc = prov.GetConnect(client); } uc.Fill(client); #if __CORE__ var url = prov.OnLogin(client, HttpContext.RequestServices, uc, log.Action == "Bind"); #else var url = prov.OnLogin(client, HttpContext, uc, log.Action == "Bind"); #endif log.ConnectId = uc.ID; log.UserId = uc.UserID; log.Success = true; log.Update(); // 标记登录提供商 Session["Cube_Sso"] = client.Name; // 如果验证成功但登录失败,直接跳走 if (url.IsNullOrEmpty()) { Session["Cube_OAuthId"] = log.Id; return(Redirect("/Admin/User/Login?autologin=0".AppendReturn(returnUrl))); } // 登录后自动绑定 var logId = Session["Cube_OAuthId"].ToLong(); if (logId > 0 && logId != log.Id) { Session["Cube_OAuthId"] = null; var log2 = Cube.Controllers.SsoController.Provider.BindAfterLogin(logId); if (log2 != null && log2.Success && !log2.RedirectUri.IsNullOrEmpty()) { return(Redirect(log2.RedirectUri)); } } if (!returnUrl.IsNullOrEmpty()) { url = returnUrl; } // 子系统颁发token给前端 if (log.Source == "front-end") { var jwt = ManagerProviderHelper.GetJwt(); jwt.Expire = DateTime.Now.Add(TimeSpan.FromHours(2)); jwt.Subject = uc.User.Name; var token = jwt.Encode(null); url += $"#token={token}"; } return(Redirect(url)); } catch (Exception ex) { if (log.Remark.IsNullOrEmpty()) { log.Remark = ex.ToString(); } log.Success = false; log.SaveAsync(); XTrace.WriteException(ex); throw; } }