1.1 傳統的登錄
舉一個例子:有一個"雲沖印"的網站,可以將用戶儲存在Google的照片,沖印出來。用戶為了使用該服務,必須讓"雲沖印"讀取自己儲存在Google上的照片。
問題是只有得到用戶的授權,Google才會同意"雲沖印"讀取這些照片。那么,"雲沖印"怎樣獲得用戶的授權呢?
傳統方法是,用戶將自己的Google用戶名和密碼,告訴"雲沖印",后者就可以讀取用戶的照片了。這樣的做法有以下幾個嚴重的缺點
傳統登錄的缺點
(1)"雲沖印"為了后續的服務,會保存用戶的密碼,這樣很不安全。
(2)Google不得不部署密碼登錄,而我們知道,單純的密碼登錄並不安全。
(3)"雲沖印"擁有了獲取用戶儲存在Google所有資料的權力,用戶沒法限制"雲沖印"獲得授權的范圍和有效期。
(4)用戶只有修改密碼,才能收回賦予"雲沖印"的權力。但是這樣做,會使得其他所有獲得用戶授權的第三方應用程序全部失效。
(5)只要有一個第三方應用程序被破解,就會導致用戶密碼泄漏,以及所有被密碼保護的數據泄漏。
1.2 OAUTH
OAUTH協議為用戶資源的授權提供了一個安全的、開放而又簡易的標准。與以往的授權方式不同之處是OAUTH的授權不會使第三方觸及到用戶的帳號信息(如用戶名與密碼),
即第三方無需使用用戶的用戶名與密碼就可以申請獲得該用戶資源的授權,因此OAUTH是安全的。oAuth是Open Authorization的簡寫。
OAUTH原理

我在圖上分了四個步驟,下面是四步的講解:
第一步:用戶訪問第三方網站,比如:就是你需要使用QQ進行登錄的網站;
第二步:你點擊QQ登錄后,第三方網站將會連接並進行請求,比如:你點擊登錄后,第三方網站會跳轉到QQ平台,提示你進行登錄;
第三步:你要進行授權第三方網站對你的信息訪問的一個權限,比如:當你QQ登錄成功后,QQ會提示你,是否授權第三方Web訪問你的用戶基本信息或其他的資源信息,這時你點擊授權即可;
第四步:授權后,第三方Web即可訪問你剛才授權的資源信息,比如:你的QQ基本信息-頭像、昵稱、性別等。
OAUTH2.0 認證流程

第一步:首先直接跳轉至用戶授權地址,即圖示 Request User Url ,提示用戶進行登錄,並給予相關資源授權,得到唯一的Auth code,這里注意的是code只有10分鍾的有效期
第二步:得到授權code后,這一步就是請求access token,通過 圖示 Request access url ,生成得到數據Token;
第三步:通過Access Token請求OpenID,OpenID是用戶在此平台的唯一標識,通過圖示 Request info url 請求,然后得到OpenID;
第四步:通過第二步得到的數據Token、第三步得到的OpenID及相關API,進行請求,獲取用戶授權資源信息。
釘釘登錄的實現
釘釘登錄實現的原理其實就是用的OAUTH2.0
要讓你的app實現釘釘的登錄獲取登陸者相關的信息,你需要做一起操作:
1.首先你需要登錄釘釘管理員賬號,
(https://open-doc.dingtalk.com/doc2/detail.htm?treeId=168&articleId=104881&docType=1)
這是詳細操作獲取地址網址,
2 c#實現登錄
1 public ActionResult Index(string url) 2 { 3 var s = Request.Url.ToString(); 4 s = s.Replace("code=", "&"); 5 s = s.Replace("&state=", "&"); 6 if (s.Contains("STATE")) 7 { 8 try 9 { 10 var code = s.Split('&')[1].ToString(); 11 var access_token = ""; 12 13 #region 使用appid及appSecret訪問如下接口,獲取accesstoken 14 15 var uriString = ""; 16 17 if (s.Contains("服務器ip地址")) 18 { 19 uriString = @"https://oapi.dingtalk.com/sns/gettoken?appid=dingoaycgdqrrmcvvgde99&appsecret=eKBFq0-0yoWyg9jCWh-K6HiDVh9jP0_0lyOqGhDYfQ8HWb7ZlNUiGnP8JOOP2pSL"; 20 } 21 var uri = new Uri(uriString); 22 var req = HttpWebRequest.Create(uri) as HttpWebRequest; 23 //設置通用的請求屬性 24 req.Method = "get"; 25 req.Host = "oapi.dingtalk.com"; 26 req.ContentType = "application/Json"; 27 req.Timeout = 1000 * 30; 28 var rsp = this.RequestHttp(req); 29 using (Stream stream = rsp.GetResponseStream()) 30 { 31 using (StreamReader reader = new StreamReader(stream, Encoding.UTF8)) 32 { 33 var text = reader.ReadToEnd(); 34 access_token = text.Split(',')[1].ToString().Split(':')[1].ToString(); 35 } 36 } 37 38 #endregion 39 40 #region 根據code 和 accesstoken 獲取當前釘釘用戶授權給你的持久授權碼 41 42 var uriString2 = "https://oapi.dingtalk.com/sns/get_persistent_code?access_token=" + access_token; 43 uriString2 = uriString2.Replace("\"", ""); 44 var uri2 = new Uri(uriString2); 45 var req2 = HttpWebRequest.Create(uri2) as HttpWebRequest; 46 req2.Method = "Post"; 47 req2.Accept = "*/*"; 48 req2.Host = uri2.Authority; 49 req2.ContentType = "application/Json"; 50 51 var jsonData = "{\"tmp_auth_code\"" + ":\"" + code + "\"}"; 52 53 byte[] cont = Encoding.UTF8.GetBytes(jsonData); 54 55 56 req2.ContentLength = cont.Length; 57 Stream reqStream2 = req2.GetRequestStream(); 58 reqStream2.Write(cont, 0, cont.Length); 59 60 var rsp2 = this.RequestHttp(req2); 61 var persistentCode = ""; 62 using (Stream stream2 = rsp2.GetResponseStream()) 63 { 64 using (StreamReader reader = new StreamReader(stream2, Encoding.UTF8)) 65 { 66 var text = reader.ReadToEnd(); 67 persistentCode = text; 68 } 69 } 70 71 #endregion 72 73 #region 通過永久授權碼,獲取該用戶授權的SNS_TOKEN,此token的有效時間為2小時,重復獲取不會續期 74 75 var uriString3 = "https://oapi.dingtalk.com/sns/get_sns_token?access_token=" + access_token; 76 uriString3 = uriString3.Replace("\"", ""); 77 var uri3 = new Uri(uriString3); 78 var req3 = HttpWebRequest.Create(uri3) as HttpWebRequest; 79 req3.Method = "Post"; 80 req3.Accept = "*/*"; 81 req3.Host = uri3.Authority; 82 req3.ContentType = "application/Json"; 83 84 byte[] cont3 = Encoding.UTF8.GetBytes(persistentCode); 85 86 87 req3.ContentLength = cont3.Length; 88 Stream reqStream3 = req3.GetRequestStream(); 89 reqStream3.Write(cont3, 0, cont3.Length); 90 91 var rsp3 = this.RequestHttp(req3); 92 var sns_token = ""; 93 using (Stream stream3 = rsp3.GetResponseStream()) 94 { 95 using (StreamReader reader = new StreamReader(stream3, Encoding.UTF8)) 96 { 97 var text = reader.ReadToEnd(); 98 sns_token = text.Split(',')[2].ToString().Split(':')[1].ToString(); 99 } 100 } 101 102 #endregion 103 104 #region 在獲得釘釘用戶的SNS_TOKEN后,通過以下請求獲取該用戶的個人信息 105 106 var uriString4 = "https://oapi.dingtalk.com/sns/getuserinfo?sns_token=" + sns_token; 107 uriString4 = uriString4.Replace("\"", ""); 108 var uri4 = new Uri(uriString4); 109 var req4 = HttpWebRequest.Create(uri4) as HttpWebRequest; 110 req4.Method = "get"; 111 req4.Accept = "*/*"; 112 req4.Host = uri4.Authority; 113 req4.ContentType = "application/Json"; 114 115 var rsp4 = this.RequestHttp(req4); 116 var userName = ""; 117 using (Stream stream4 = rsp4.GetResponseStream()) 118 { 119 using (StreamReader reader = new StreamReader(stream4, Encoding.UTF8)) 120 { 121 var text = reader.ReadToEnd(); 122 userName = text.Split(':')[5].ToString().Split(',')[0].ToString(); 123 userName = userName.Substring(1, userName.Length - 2);//去掉引號 124 } 125 } 126 127 #endregion 128 129 ViewBag.UserName = userName; 130 var repo = RF.Concrete<DBMUserRepository>(); 131 var user = repo.GetByName(userName); 132 if (user.Count == 0) 133 { 134 DBMUser item = new DBM.DBMUser(); 135 item.Name = userName; 136 RF.Save(item); 137 } 138 139 ///當數據庫沒有該用戶的時候,數據庫為該用戶添加數據 140 var newUser = repo.GetByName(userName)[0]; 141 DBMContext.CurrentUser = newUser; 142 DBMContext.IsAdmin = newUser.IsAdmin; 143 return View(); 144 } 145 catch (Exception e) 146 { 147 return null; 148 } 149 } 150 else 151 { 152 ViewBag.UserName = "error1"; 153 return View(); 154 } 155 }
這樣就OK了。
