源碼在上面的地址中能夠下載 web
打開客戶端頁面http://localhost:38500/服務器
客戶端代碼引用了DotNetOpenAuth.OAuth2app
public class HomeController : Controller { private WebServerClient _webServerClient; public ActionResult Index() { ViewBag.AccessToken = Request.Form["AccessToken"] ?? ""; ViewBag.RefreshToken = Request.Form["RefreshToken"] ?? ""; ViewBag.Action = ""; ViewBag.ApiResponse = ""; InitializeWebServerClient(); var accessToken = Request.Form["AccessToken"]; if (string.IsNullOrEmpty(accessToken)) { var authorizationState = _webServerClient.ProcessUserAuthorization(Request); if (authorizationState != null) { ViewBag.AccessToken = authorizationState.AccessToken; ViewBag.RefreshToken = authorizationState.RefreshToken; ViewBag.Action = Request.Path; } } if (!string.IsNullOrEmpty(Request.Form.Get("submit.Authorize"))) { var userAuthorization = _webServerClient.PrepareRequestUserAuthorization(new[] { "bio", "notes" }); userAuthorization.Send(HttpContext); Response.End(); } else if (!string.IsNullOrEmpty(Request.Form.Get("submit.Refresh"))) { var state = new AuthorizationState { AccessToken = Request.Form["AccessToken"], RefreshToken = Request.Form["RefreshToken"] }; if (_webServerClient.RefreshAuthorization(state)) { ViewBag.AccessToken = state.AccessToken; ViewBag.RefreshToken = state.RefreshToken; } } else if (!string.IsNullOrEmpty(Request.Form.Get("submit.CallApi"))) { var resourceServerUri = new Uri(Paths.ResourceServerBaseAddress); var client = new HttpClient(_webServerClient.CreateAuthorizingHandler(accessToken)); var body = client.GetStringAsync(new Uri(resourceServerUri, Paths.MePath)).Result; ViewBag.ApiResponse = body; } return View(); } private void InitializeWebServerClient() { var authorizationServerUri = new Uri(Paths.AuthorizationServerBaseAddress); var authorizationServer = new AuthorizationServerDescription { AuthorizationEndpoint = new Uri(authorizationServerUri, Paths.AuthorizePath), TokenEndpoint = new Uri(authorizationServerUri, Paths.TokenPath) }; _webServerClient = new WebServerClient(authorizationServer, Clients.Client1.Id, Clients.Client1.Secret); } }
2.點擊Authorize按鈕,在客戶端添加兩個Scopeide
var userAuthorization = _webServerClient.PrepareRequestUserAuthorization(new[] { "bio", "notes" });
調用以下ui
public OutgoingWebResponse PrepareRequestUserAuthorization(IEnumerable<string> scopes = null, Uri returnTo = null);
此時會到服務端進行客戶驗證this
private Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context) { if (context.ClientId == Clients.Client1.Id) { context.Validated(Clients.Client1.RedirectUrl); } else if (context.ClientId == Clients.Client2.Id) { context.Validated(Clients.Client2.RedirectUrl); } return Task.FromResult(0); }
驗證後跳轉到Authorize,首先判斷是否登錄spa
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(); }
沒有登錄的話跳轉到Login頁面debug
http://localhost:11625/Account/Login?ReturnUrl=%2FOAuth%2FAuthorize%3Fclient_id%3D123456%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A38500%252F%26state%3D4PH8wxITy6KAVSpnt4YpAA%26scope%3Dbio%2520notes%26response_type%3Dcode
使用UrlDecode能夠看得更明白3d
http://localhost:11625/Account/Login?ReturnUrl=/OAuth/Authorize?client_id=123456&redirect_uri=http%3A%2F%2Flocalhost%3A38500%2F&state=4PH8wxITy6KAVSpnt4YpAA&scope=bio%20notes&response_type=code
再次解碼code
http://localhost:11625/Account/Login?ReturnUrl=/OAuth/Authorize?client_id=123456&redirect_uri=http://localhost:38500/&state=4PH8wxITy6KAVSpnt4YpAA&scope=bio notes&response_type=code
這裏隨便輸入一個用戶名123456,點擊Sign In按鈕
public ActionResult Login() { var authentication = HttpContext.GetOwinContext().Authentication; if (Request.HttpMethod == "POST") { var isPersistent = !string.IsNullOrEmpty(Request.Form.Get("isPersistent")); if (!string.IsNullOrEmpty(Request.Form.Get("submit.Signin"))) { authentication.SignIn( new AuthenticationProperties { IsPersistent = isPersistent }, new ClaimsIdentity(new[] { new Claim(ClaimsIdentity.DefaultNameClaimType, Request.Form["username"]) }, "Application")); } } return View(); }
會經過OWIN寫入身份,再次到服務端進行驗證
驗證後進行受權確認
http://localhost:11625/OAuth/Authorize?client_id=123456&redirect_uri=http%3A%2F%2Flocalhost%3A38500%2F&state=4PH8wxITy6KAVSpnt4YpAA&scope=bio%20notes&response_type=code
解碼後
http://localhost:11625/OAuth/Authorize?client_id=123456&redirect_uri=http://localhost:38500/&state=4PH8wxITy6KAVSpnt4YpAA&scope=bio notes&response_type=code
點擊Grant按鈕,一樣會先進行驗證,而後寫入Scope,注意這裏寫入的Bearer,在後面的資源服務器驗證時使用
經過登錄後會生成token
private void CreateAuthenticationCode(AuthenticationTokenCreateContext context) { context.SetToken(Guid.NewGuid().ToString("n") + Guid.NewGuid().ToString("n")); _authenticationCodes[context.Token] = context.SerializeTicket(); }
返回到客戶端,請求token
服務端先驗證受權
private Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { string clientId; string clientSecret; if (context.TryGetBasicCredentials(out clientId, out clientSecret) || context.TryGetFormCredentials(out clientId, out clientSecret)) { if (clientId == Clients.Client1.Id && clientSecret == Clients.Client1.Secret) { context.Validated(); } else if (clientId == Clients.Client2.Id && clientSecret == Clients.Client2.Secret) { context.Validated(); } } return Task.FromResult(0); }
而後移除前面生成的Code
private void ReceiveAuthenticationCode(AuthenticationTokenReceiveContext context) { string value; if (_authenticationCodes.TryRemove(context.Token, out value)) { context.DeserializeTicket(value); } }
再生成RefreshToken
回到客戶端
頁面
http://localhost:38500/?code=cd086e2e868e4899ac8a15c332036cd4e52f344dee804351a0789eb20bb1ead5&state=m54g6dYyfMUfYNf7F9Qbcw
點擊Refresh按鈕
會從新到服務器先作受權驗證ValidateClientAuthentication,驗證成功後從新生成token
點擊訪問受保護資源API,由於受保護資源一樣是使用OWIN驗證的
using System; using System.Threading.Tasks; using Microsoft.Owin; using Owin; [assembly: OwinStartup(typeof(ResourceServer.Startup))] namespace ResourceServer { public partial class Startup { public void Configuration(IAppBuilder app) { ConfigureAuth(app); } } }
using Owin; using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace ResourceServer { public partial class Startup { public void ConfigureAuth(IAppBuilder app) { app.UseOAuthBearerAuthentication(new Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationOptions()); } } }
namespace ResourceServer.Controllers { [Authorize] public class MeController : ApiController { public string Get() { return this.User.Identity.Name; } } }
結果以下
這裏從受權Server到資源Server都是使用OWIN,關鍵就在這兩個項目中有相同的配置
<system.web> <compilation debug="true" targetFramework="4.5" /> <httpRuntime targetFramework="4.5" /> <machineKey decryptionKey="B7EFF1C5839A624E3F97D0268917EDE82F408D2ECBFAC817" validation="SHA1" validationKey="C2B8DF31AB9624D69428066DFDA1A479542825F3B48865C4E47AF6A026F22D853DEC2B3248DF268599BF89EF78B9E86CA05AC73577E0D5A14C45E0267588850B" /> </system.web>