1 public class CrmAuth 2 { 3 ///<summary> 4 /// Token 5 /// </summary> 6 public string access_token { get; set; } 7 8 ///<summary> 9 /// 類型 10 /// </summary> 11 public string token_type { get; set; } 12 13 ///<summary> 14 /// 有效期 秒 15 /// </summary> 16 public int expires_in { get; set; } 17 18 ///<summary> 19 /// refresh_Token 用於刷新Token 20 /// </summary> 21 public string refresh_token { get; set; } 22 23 /// <summary> 24 /// 請求Token 25 /// </summary> 26 /// <param name="adfsUri">ADFS地址</param> 27 /// <param name="resource">CRM地址</param> 28 /// <param name="clientId">客戶端ID</param> 29 /// <param name="redirectUri">跳轉地址</param> 30 /// <param name="userName">用戶名</param> 31 /// <param name="password">密碼</param> 32 /// <returns></returns> 33 public static CrmAuth AcquireToken(string adfsUri, string resource, string clientId, string redirectUri, string userName, string password) 34 { 35 VerifyParams(adfsUri, resource, clientId, redirectUri, userName, password); 36 37 var url = BuildCodeUrl(adfsUri, resource, clientId, redirectUri); 38 39 var tokenUrl = BuildTokenUrl(adfsUri); 40 41 var list = BuildCodeParams(userName, password); 42 43 //包含上下文的 用WebRequest也能夠 44 using (var handler = new WebRequestHandler { AllowAutoRedirect = false }) 45 { 46 using (var httpClient = new HttpClient(handler)) 47 { 48 //第1次請求 49 using (var response = httpClient.PostAsync(url, new FormUrlEncodedContent(list)).Result) 50 { 51 // 第2次請求 httpClient 還要用以前的 包含了第一次返回的Cookies 52 using (var response2 = httpClient.GetAsync(response.Headers.Location).Result) 53 { 54 // 獲取返回的Code 55 var query = response2.Headers.Location.Query; 56 var col = Utils.GetQueryString(query); 57 var code = col["code"]; 58 if (string.IsNullOrWhiteSpace(code)) 59 { 60 throw new Exception(query); 61 } 62 // 第3次請求 請求Token 63 var tokenParams = BuildTokenParams(clientId, code, resource, redirectUri); 64 using (var response3 = httpClient.PostAsync(tokenUrl, new FormUrlEncodedContent(tokenParams)).Result) 65 { 66 var json = response3.Content.ReadAsStringAsync().Result; 67 var auth = JsonConvert.DeserializeObject<CrmAuth>(json); 68 if (auth == null) 69 throw new Exception(json); 70 return auth; 71 } 72 73 } 74 } 75 } 76 } 77 } 78 79 /// <summary> 80 /// 刷新Token 81 /// </summary> 82 /// <param name="adfsUri"></param> 83 /// <param name="refresh_token"></param> 84 /// <returns></returns> 85 public static CrmAuth CrmRefreshToken(string adfsUri, string refresh_token) 86 { 87 var tokenUrl = BuildTokenUrl(adfsUri); 88 using (var httpClient = new HttpClient()) 89 { 90 // 請求 請求Token 91 var tokenParams = BuildRefreshTokenParams(refresh_token); 92 using (var response3 = httpClient.PostAsync(tokenUrl, new FormUrlEncodedContent(tokenParams)).Result) 93 { 94 var json = response3.Content.ReadAsStringAsync().Result; 95 var auth = JsonConvert.DeserializeObject<CrmAuth>(json); 96 if (auth == null) 97 throw new Exception(json); 98 return auth; 99 } 100 } 101 } 102 103 /// <summary> 104 /// 驗證參數 105 /// </summary> 106 /// <param name="adfsUri"></param> 107 /// <param name="resource"></param> 108 /// <param name="clientId"></param> 109 /// <param name="redirectUri"></param> 110 /// <param name="userName"></param> 111 /// <param name="password"></param> 112 private static void VerifyParams(string adfsUri, string resource, string clientId, string redirectUri, string userName, string password) 113 { 114 if (string.IsNullOrWhiteSpace(adfsUri)) 115 throw new ArgumentNullException(nameof(adfsUri)); 116 if (string.IsNullOrWhiteSpace(nameof(resource))) 117 throw new ArgumentNullException(resource); 118 if (string.IsNullOrWhiteSpace(clientId)) 119 throw new ArgumentNullException(nameof(clientId)); 120 if (string.IsNullOrWhiteSpace(redirectUri)) 121 throw new ArgumentNullException(nameof(redirectUri)); 122 if (string.IsNullOrWhiteSpace(userName)) 123 throw new ArgumentNullException(nameof(userName)); 124 if (string.IsNullOrWhiteSpace(password)) 125 throw new ArgumentNullException(nameof(password)); 126 } 127 128 /// <summary> 129 /// 構建ADFS CODE地址 130 /// </summary> 131 /// <param name="adfsUri"></param> 132 /// <param name="resource"></param> 133 /// <param name="clientId"></param> 134 /// <param name="redirectUri"></param> 135 /// <returns></returns> 136 private static string BuildCodeUrl(string adfsUri, string resource, string clientId, string redirectUri) 137 { 138 139 if (!adfsUri.EndsWith("/")) 140 adfsUri = adfsUri + "/"; 141 var url = $"{adfsUri}adfs/oauth2/authorize"; 142 143 var response_type = "code"; 144 145 var sb = new StringBuilder(); 146 sb.Append(url); 147 sb.Append("?"); 148 sb.Append($"response_type={response_type}"); 149 sb.Append("&"); 150 sb.Append($"client_id={clientId}"); 151 sb.Append("&"); 152 sb.Append($"redirect_uri={redirectUri}"); 153 sb.Append("&"); 154 sb.Append($"resource={resource}"); 155 url = sb.ToString(); 156 return url; 157 } 158 159 /// <summary> 160 /// 構建ADFS TOKEN地址 161 /// </summary> 162 /// <param name="adfsUri"></param> 163 /// <returns></returns> 164 private static string BuildTokenUrl(string adfsUri) 165 { 166 return $"{adfsUri}adfs/oauth2/token"; 167 } 168 169 /// <summary> 170 /// 構建請求Code參數 171 /// </summary> 172 /// <param name="userName"></param> 173 /// <param name="password"></param> 174 /// <returns></returns> 175 private static List<KeyValuePair<string, string>> BuildCodeParams(string userName, string password) 176 { 177 var list = new List<KeyValuePair<string, string>>() 178 { 179 new KeyValuePair<string, string>("UserName",userName), 180 new KeyValuePair<string, string>("Password",password), 181 new KeyValuePair<string, string>("Kmsi","true"), 182 new KeyValuePair<string, string>("AuthMethod","FormsAuthentication") 183 }; 184 return list; 185 } 186 187 /// <summary> 188 /// 構建請求Token參數 189 /// </summary> 190 /// <param name="clientId"></param> 191 /// <param name="code"></param> 192 /// <param name="resource"></param> 193 /// <param name="redirectUri"></param> 194 /// <returns></returns> 195 private static List<KeyValuePair<string, string>> BuildTokenParams(string clientId, string code, string resource, string redirectUri) 196 { 197 var list = new List<KeyValuePair<string, string>> 198 { 199 new KeyValuePair<string, string>("grant_type", "authorization_code"), 200 new KeyValuePair<string, string>("client_id", clientId), 201 new KeyValuePair<string, string>("code", code), 202 new KeyValuePair<string, string>("resource", resource), 203 new KeyValuePair<string, string>("redirect_uri", redirectUri) 204 }; 205 return list; 206 } 207 208 /// <summary> 209 /// 構建請求刷新Token參數 210 /// </summary> 211 /// <param name="refresh_token"></param> 212 /// <returns></returns> 213 private static List<KeyValuePair<string, string>> BuildRefreshTokenParams(string refresh_token) 214 { 215 var list = new List<KeyValuePair<string, string>> 216 { 217 new KeyValuePair<string, string>("grant_type", "refresh_token"), 218 new KeyValuePair<string, string>("refresh_token", refresh_token), 219 }; 220 return list; 221 } 222 223 }
只對ADFS有效,Azure AD沒用過應該不適用吧html
主要就是模擬表單請求git
redirectUri沒有實際用戶只是取返回的code,能夠AllowAutoRedirect=true,返回在redirectUri內作code解析Token再返回
OAuthMessageHandler 單例HttpClient使用
RefreshToken請求返回來的沒有 refresh_token 不知道爲何?
代碼 : github
Configure Windows Server 2012 R2 for Dynamics 365 applications that use OAuthjson
參考api
https://technet.microsoft.com/en-us/library/hh699726.aspx?f=255&MSPPError=-2147217396app
http://www.cloudriven.fi/en/cloud-9-en/how-to-make-dynamics-365-web-api-queries-using-oauth/ui
https://msdn.microsoft.com/zh-cn/library/gg334767.aspx#bkmk_includeFormattedValuesurl
流程spa
https://blog.scottlogic.com/2015/03/09/OAUTH2-Authentication-with-ADFS-3.0.html