授權碼模式定義
通過客戶端的后台服務器,與“服務提供商”的認證服務器進行認證。
1、用戶訪問客戶端,后者將前者導向認證服務器。
2、用戶選擇是否給予客戶端授權。
3、假設用戶給予授權,認證服務器首先生成一個授權碼,並返回給用戶,認證服務器將用戶導向客戶端事先指定的"重定向URI"(redirection URI),同時附上一個授權碼。
4、客戶端收到授權碼,附上早先的"重定向URI",向認證服務器申請令牌。這一步是在客戶端的后台的服務器上完成的,對用戶不可見。
5、認證服務器核對了授權碼和重定向URI,確認無誤后,向客戶端發送訪問令牌(access token)和更新令牌(refresh token)。
6、Client拿着access token去訪問Resource資源
授權碼模式的工作流程圖

圖 1 (網上搜到的授權碼工作流程圖說明)
之前看上邊的流程圖,看了不下10遍,還是搞不懂,這個圖真心畫的不好理解!
我們一步步來,AuthorizationServer與ResourceServer還是用之前的項目
新建項目:AuthorizationCodeGrant

HomeController.cs也簡單
public ActionResult Index()
{
ViewBag.AccessToken = Request.Form["AccessToken"] ?? "";
ViewBag.RefreshToken = Request.Form["RefreshToken"] ?? "";
ViewBag.Action = "";
ViewBag.ResourceResponse = "";
var authorizationServerUri = new Uri("http://localhost:8270/");
var authorizationServer = new AuthorizationServerDescription
{
AuthorizationEndpoint = new Uri(authorizationServerUri, "OAuth/Authorize"),
TokenEndpoint = new Uri(authorizationServerUri, "OAuth/Token")
};
// 刷新AccessToken
var client = new WebServerClient(authorizationServer, "123456", "abcdef");
if (string.IsNullOrEmpty(ViewBag.AccessToken))
{
var authorizationState = client.ProcessUserAuthorization(Request);
if (authorizationState != null)
{
ViewBag.AccessToken = authorizationState.AccessToken;
ViewBag.RefreshToken = authorizationState.RefreshToken;
ViewBag.Action = Request.Path;
}
}
// 授權申請
if (!string.IsNullOrEmpty(Request.Form.Get("btnRequestAuthorize")))
{
var grantRequest = client.PrepareRequestUserAuthorization(new[] { "scopes1", "scopes2" });
grantRequest.Send(HttpContext);
Response.End();
}
// 申請資源
if (!string.IsNullOrEmpty(Request.Form.Get("btnRequestResource")))
{
var resourceServerUri = new Uri("http://localhost:8001/");
var resourceRequest = new HttpClient(client.CreateAuthorizingHandler(ViewBag.AccessToken));
ViewBag.ResourceResponse = resourceRequest.GetStringAsync(new Uri(resourceServerUri, "api/Values")).Result;
}
return View();
}
Index.cshtml
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Authorization Code Grant Client</title>
</head>
<body>
<form id="form1" action="@ViewBag.Action" method="POST">
<div>
<input id="AccessToken" name="AccessToken" value="@ViewBag.AccessToken" type="hidden" />
<input id="Authorize" name="btnRequestAuthorize" value="向認證服務器索要授權" type="submit" />
<input id="Resource" name="btnRequestResource" value="訪問資源(Resource)" type="submit" />
</div>
<div>@ViewBag.ResourceResponse</div>
</form>
</body>
</html>
運行項目

授權過程
點擊“向認證服務索要授權”,根據HomeController.cs文件的設置,頁面預計會跳轉到"http://localhost:8270/OAuth/Authorize"
所以我們需要在認證服務中新增處理授權碼模式的處理邏輯
在項目AuthorizationServer中新增OAuthController.cs、Authorize.cshtml
public class OAuthController : Controller
{
public ActionResult Authorize()
{
if (Response.StatusCode != 200)
{
return View("AuthorizeError");
}
var authentication = HttpContext.GetOwinContext().Authentication;
var ticket = authentication.AuthenticateAsync("Application").Result;
var identity = ticket != null ? ticket.Identity : null;
if (identity == null)
{
authentication.Challenge("Application");
return new HttpUnauthorizedResult(); //用戶登錄憑證失效就報401錯誤,並且跳轉至AccountController中的Login中
}
ViewBag.IdentityName = identity.Name;
ViewBag.Scopes = (Request.QueryString.Get("scope") ?? "").Split(' ');
if (Request.HttpMethod == "POST")
{
// 點擊btnGrant就確認授權,返回token等信息
if (!string.IsNullOrEmpty(Request.Form.Get("btnGrant")))
{
identity = new ClaimsIdentity(identity.Claims, "Bearer", identity.NameClaimType, identity.RoleClaimType);
foreach (var scope in ViewBag.Scopes)
{
identity.AddClaim(new Claim("urn:oauth:scope", scope));
}
authentication.SignIn(identity);
}
if (!string.IsNullOrEmpty(Request.Form.Get("btnOtherLogin")))
{
authentication.SignOut("Application");
authentication.Challenge("Application");
return new HttpUnauthorizedResult();
}
}
return View();
}
}
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Authorize</title>
</head>
<body>
<h1>認證頁面</h1>
<form method="POST">
<p>登錄用戶:@ViewBag.IdentityName</p>
<p>第三方應用需要你給他開放以下權限</p>
<ul>
@foreach (var scope in ViewBag.Scopes)
{
<li>@scope</li>
}
</ul>
<p>
<input type="submit" name="btnGrant" value="確認授權" />
<input type="submit" name="btnOtherLogin" value="以不同用戶登錄" />
</p>
</form>
</body>
</html>
public class AccountController : Controller
{
public ActionResult Login()
{
var authentication = HttpContext.GetOwinContext().Authentication;
if (Request.HttpMethod == "POST")
{
// 默認用戶登錄成功
// 生產環境需要單獨整合第三方登錄信息
var username = Request.Form["username"];
authentication.SignIn(
new AuthenticationProperties { IsPersistent = true },
new ClaimsIdentity(
new[] { new Claim(ClaimsIdentity.DefaultNameClaimType, username) }, "Application"));
}
return View();
}
public ActionResult Logout()
{
return View();
}
}
運行項目,成功跳轉至認證登錄頁面

點擊登錄,此時url地址為:
http://localhost:8270/OAuth/Authorize?client_id=123456&redirect_uri=http%3A%2F%2Flocalhost%3A4825%2F&state=IUKeWFTR1HKi4hlzKOOPgw&scope=scopes1%20scopes2&response_type=code
7.1 client_id為客戶端ID,即之前我們在AuthorizationCodeGrant項目設置的clientID
7.2 redirect_uri、state為之前登錄時就確定的值
7.3 scope為用戶確定授權的范圍
7.4 response_type=code,即指定為授權碼模式

確認授權
此時url有變化:http://localhost:4825/?code=efab38fc30c741a198b20663ec60869a36c6b25ff21f4c9986bcb9c9ae8d20eb&state=tjB9jXhNiHvIr4Ko9VhEkw

注意:這一步會會默認獲取Token


點擊訪問資源

完全能夠對上;
url中的code即認證服務返回的授權碼,之后Client請求Token會用這個code來交換
這個就是授權碼模式的特色的地方了

自此,整個授權碼模式已經完畢了哦
asp.net權限認證系列
- asp.net權限認證:Forms認證
- asp.net權限認證:HTTP基本認證(http basic)
- asp.net權限認證:Windows認證
- asp.net權限認證:摘要認證(digest authentication)
- asp.net權限認證:OWIN實現OAuth 2.0 之客戶端模式(Client Credential)
- asp.net權限認證:OWIN實現OAuth 2.0 之密碼模式(Resource Owner Password Credential)
- asp.net權限認證:OWIN實現OAuth 2.0 之授權碼模式(Authorization Code)
- asp.net權限認證:OWIN實現OAuth 2.0 之簡化模式(Implicit)
