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(); 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了。