现Katana已经不更新了,新的版本是已经是asp.net core 1.0的一部分,实现OAUTH2好的选择可以考虑使用新版或IdentityServer3对应asp.net core 1.0版本为IdentityServer4。
Startup类
public partial class Startup { private readonly ConcurrentDictionary<string, string> _authenticationCodes = new ConcurrentDictionary<string, string>(StringComparer.Ordinal); public void ConfigureAuth(IAppBuilder app) { app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = Paths.AuthenticationType, AuthenticationMode = AuthenticationMode.Passive, LoginPath = new PathString(Paths.LoginPath), LogoutPath = new PathString(Paths.LogoutPath), }); app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions { AuthorizeEndpointPath = new PathString(Paths.AuthorizePath), TokenEndpointPath = new PathString(Paths.TokenPath), AccessTokenExpireTimeSpan = TimeSpan.FromHours(2), Provider = new OAuthAuthorizationServerProvider { OnValidateClientRedirectUri = ValidateClientRedirectUri, OnValidateClientAuthentication = ValidateClientAuthentication, OnGrantResourceOwnerCredentials = GrantResourceOwnerCredentials, OnGrantClientCredentials = GrantClientCredetails }, //Provider = new ApplicationOAuthProvider(), AuthorizationCodeProvider = new AuthenticationTokenProvider { OnCreate = CreateAuthenticationCode, OnReceive = ReceiveAuthenticationCode, }, RefreshTokenProvider = new AuthenticationTokenProvider { OnCreate = CreateRefreshToken, OnReceive = ReceiveRefreshToken, }, ApplicationCanDisplayErrors = true, #if DEBUG //HTTPS is allowed only AllowInsecureHttp = false AllowInsecureHttp = true, #endif }); } private void CreateAuthenticationCode(AuthenticationTokenCreateContext context) { context.SetToken(Guid.NewGuid().ToString("n") + Guid.NewGuid().ToString("n")); _authenticationCodes[context.Token] = context.SerializeTicket(); } private void ReceiveAuthenticationCode(AuthenticationTokenReceiveContext context) { string value; if (_authenticationCodes.TryRemove(context.Token, out value)) { context.DeserializeTicket(value); } } private Task GrantClientCredetails(OAuthGrantClientCredentialsContext context) { var identity = new ClaimsIdentity(new GenericIdentity(context.ClientId, OAuthDefaults.AuthenticationType), context.Scope.Select(x => new Claim("urn:oauth:scope", x))); context.Validated(identity); return Task.FromResult(0); } private Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { var identity = new ClaimsIdentity(new GenericIdentity(context.UserName, OAuthDefaults.AuthenticationType), context.Scope.Select(x => new Claim("urn:oauth:scope", x))); context.Validated(identity); return Task.FromResult(0); } private Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { string clientId; string clientSecret; if (context.TryGetBasicCredentials(out clientId, out clientSecret) || context.TryGetFormCredentials(out clientId, out clientSecret)) { context.Validated(); } return Task.FromResult(0); } private Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context) { if (context.ClientId == Clients.Client1.Id) { context.Validated(); //context.Validated(Clients.Client1.RedirectUrl); } else if (context.ClientId == Clients.Client2.Id) { context.Validated(Clients.Client2.RedirectUrl); } return Task.FromResult(0); } private void CreateRefreshToken(AuthenticationTokenCreateContext context) { context.SetToken(context.SerializeTicket()); } private void ReceiveRefreshToken(AuthenticationTokenReceiveContext context) { context.DeserializeTicket(context.Token); } }
OAuthController
/// <summary> /// 授权码模式授权 /// </summary> /// <returns></returns> public ActionResult Authorize() { var authentication = HttpContext.GetOwinContext().Authentication; var ticket = authentication.AuthenticateAsync(Paths.AuthenticationType).Result; var identity = ticket != null ? ticket.Identity : null; if (identity == null) { authentication.Challenge(Paths.AuthenticationType); return new HttpUnauthorizedResult(); } var scopes = (Request.QueryString.Get("scope") ?? "").Split(' '); if (Request.HttpMethod == "POST") { if (!string.IsNullOrEmpty(Request.Form.Get("submit.Grant"))) { identity = new ClaimsIdentity(identity.Claims, "Bearer", identity.NameClaimType, identity.RoleClaimType); foreach (var scope in scopes) { identity.AddClaim(new Claim("urn:oauth:scope", scope)); } authentication.SignIn(identity); } if (!string.IsNullOrEmpty(Request.Form.Get("submit.Login"))) { authentication.SignOut("Application"); authentication.Challenge("Application"); return new HttpUnauthorizedResult(); } } return View(); }
AccountController
/// <summary> /// 用户登录 /// </summary> /// <returns></returns> 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(); } /// <summary> /// 退出 /// </summary> /// <returns></returns> public ActionResult Logout() { return View(); }
调试
获得授权码
GET /oauth/authorize?client_id=irving&redirect_uri=http://localhost:38500/oauth_callback&state=LYUVwcuaGuKeRTjxhdFzhQ&scope=bio%20notes&response_type=code HTTP/1.1
http://localhost:56889/oauth/authorize?
client_id=irving&redirect_uri=http://localhost:38500/oauth_callback&state=LYUVwcuaGuKeRTjxhdFzhQ&scope=bio%20notes&response_type=code
页面重定向
HTTP/1.1 302 Found
Location: /oauth_callback?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz
获得令牌
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencodedgrant_type=authorization_code&code=929dc42905a24e79bdf028f7043769acddd90056ca8141708b3834ec174e8083&state=LYUVwcuaGuKeRTjxhdFzhQ&redirect_uri=http://localhost:38500/oauth_callback
源码:https://github.com/zhouyongtao/SecuringForWebAPI/tree/master/KatanaForWebAPI
注意事项
资源服务器识别认证服务器颁发的令牌, 需要配置相同的machinekey
<machineKey decryptionKey="B7EFF1C5839A624E3F97D0268917EDE82F408D2ECBFAC817" validation="SHA1" validationKey="C2B8DF31AB9624D69428066DFDA1A479542825F3B48865C4E47AF6A026F22D853DEC2B3248DF268599BF89EF78B9E86CA05AC73577E0D5A14C45E0267588850B" />
REFER:
http://beginor.github.io/2015/01/24/oauth2-server-with-owin.html
http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server