OWIN OAuth 2.0 Authorization Server
源码在上面的地址中可以下载
打开客户端页面http://localhost:38500/
客户端代码引用了DotNetOpenAuth.OAuth2
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按钮,在客户端添加两个Scope
var userAuthorization = _webServerClient.PrepareRequestUserAuthorization(new[] { "bio", "notes" });
调用如下
public OutgoingWebResponse PrepareRequestUserAuthorization(IEnumerable<string> scopes = null, Uri returnTo = null);
此时会到服务端进行客户验证
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,首先判断是否登陆
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页面
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可以看得更明白
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
再次解码
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>
时间: 2024-10-24 04:19:41