经过客户端的后台服务器,与“服务提供商”的认证服务器进行认证。html
一、用户访问客户端,后者将前者导向认证服务器。
二、用户选择是否给予客户端受权。
三、假设用户给予受权,认证服务器首先生成一个受权码,并返回给用户,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个受权码。
四、客户端收到受权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
五、认证服务器核对了受权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。
六、Client拿着access token去访问Resource资源api
图 1 (网上搜到的受权码工做流程图说明)服务器
以前看上边的流程图,看了不下10遍,仍是搞不懂,这个图真心画的很差理解!asp.net
咱们一步步来,AuthorizationServer与ResourceServer仍是用以前的项目ide
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(); }
<!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>
运行项目 post
点击“向认证服务索要受权”,根据HomeController.cs文件的设置,页面预计会跳转到"http://localhost:8270/OAuth/Authorize"url
因此咱们须要在认证服务中新增处理受权码模式的处理逻辑spa
在项目AuthorizationServer中新增OAuthController.cs、Authorize.cshtml.net
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(); } }
http://localhost:8270/OAuth/Authorize?client_id=123456&redirect_uri=http%3A%2F%2Flocalhost%3A4825%2F&state=IUKeWFTR1HKi4hlzKOOPgw&scope=scopes1%20scopes2&response_type=code3d
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来交换
这个就是受权码模式的特点的地方了
自此,整个受权码模式已经完毕了哦