前段時間由於公司架構服務層向WebApi轉換,就研究了OAuth在WebApi中的使用,這中間遇到了很多坑,在此記錄一下OAuth的正確使用方式。
1、 OAuth是做什么的?
在網上瀏覽時,大家都見過這樣的功能:網站A提供了第三方登錄服務,比如使用新浪微博、QQ賬戶登錄。用戶使用第三方賬戶登陸后,第三方返回Token給網站A,當網站A調用第三方服務請求登錄用戶信息時需傳遞該Token給第三方,第三方才允許該服務請求。之后的每次請求無需再次認證,直接使用該Token即可。這就是OAuth的典型應用。
2、 簡單使用介紹 (具體使用OAuth的方法請參考:在ASP.NET中基於Owin OAuth使用Client Credentials Grant授權發放Token)
2.1、用戶登錄的過程即是獲取Token的過程,前端用戶登錄示例代碼如下:

1 $.ajax({ 2 type: "POST", 3 url: api_address + "token", //api_address為WebApi服務地址,由於OAuth的使用中設置了屬性TokenEndpointPath = new PathString("/token"),所以請求到“token”鏈接時即可自動進入認證流程。
4 data: { grant_type: "password", username: username, password: password, ran: Math.random() },//傳遞用戶名、密碼、認證方式
5 dataType: "json", 6 success: function (result) { 7 if (result.access_token && result.access_token.length > 0) { 8 //result.access_token即是有效的服務調用憑證,可以把該值存入到Cookie中,以備下次使用。
9 callback(1, "登錄成功。"); 10 } 11 else { 12 callback(0, "未知錯誤!"); 13 } 14 }, 15 error: function (XMLHttpRequest, textStatus, errorThrown) { 16 callback(0, XMLHttpRequest.responseJSON.error); 17 } 18 });
2.2、當認證方式為password時,已下方法為認證流程中的一步。(認證通過才會返回Token)

1 public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 2 { 3 var username= context.UserName; 4 var password=context.Password; 5 if(用戶名與密碼不合法) 6 { 7 context.setError(“用戶名或密碼錯誤!”);//認證不通過
8 } 9 else
10 { 11 var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType); 12 oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, context.UserName)); 13 //可以加入用戶信息及其他必要信息到Token中,以便在api服務中使用(使用中HttpContext.Current.User.Identity即為oAuthIdentity對象,WebApi的Controller中可直接使用User.Identity)。
14 oAuthIdentity.AddClaim(new Claim("UserID", user.UserID.ToString())); 15 var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties()); 16 context.Validated(ticket);//認證通過
17 } 18 return base.GrantResourceOwnerCredentials(context); 19 }
3、 已經獲取了Token,如何使用?
網上的大部分示例都是使用HttpClient調用的方式,而前后端的完全分離作為一種發展趨勢,我們需要Jquery的調用方式。

1 $.ajax({ 2 type: “method”,//get,post,put,delete
3 url:api_address + “api/Test”,//如果調用webapi中的TestController
4 data: {data}, 5 dataType: "json", 6 headers: { 7 "Authorization": "Bearer " + “token” //把登錄獲取的Token加入到http請求頭中
8 }, 9 success: function (result) { 10 callback(result); 11 }, 12 error: function (XMLHttpRequest, textStatus, errorThrown) { 13 //。。。。。。
14 } 15 });
4、Api的訪問權限該如何做?
認證中我們把用戶登錄成功作為認證通過的標志,但不同角色的用戶具有不同的訪問權限(個人認為認證中應使用最小權限驗證,如示例中的登錄成功),如何控制有些Controller不能被低權限用戶訪問。

1 [Authorize] 2 public class TestController: ApiController 3 { 4 // GET api/<controller>
5 public HttpResponseMessage Get(int appid) 6 { 7 return null; 8 } 9 }
[Authorize]表示訪問該Controller的請求必須經過認證(請求頭中具有Token信息),這里我們可以自定義一個特性去驗證用戶權限,並替換特性AuthorizeAttribute。(這里僅提供思路,具體做法請自己摸索,不保證以下代碼的正確性)

1 public class CustomeAuthorizeAttribute:System.Web.Http.AuthorizeAttribute 2 { 3 protected override bool IsAuthorized(System.Web.Http.Controllers.HttpActionContext actionContext) 4 { 5 if(base.IsAuthorized(actionContext)) 6 { 7 //這里對用戶的權限進行驗證,actionContext可以獲得請求的是哪一個Controller
8 var user = HttpContext.Current.User.Identity;//Token中帶有的用戶信息
9 if (可以訪問) 10 { 11 return true; 12 } 13 return false; 14 } 15 return false; 16 } 17 } 18