基于Owin Oauth2构建授权服务器

时间:2021-03-29 13:27:46

---恢复内容开始---

我们简单的描述怎么通过owin和asp.net mvc创建一个授权服务器,首先创建一个空的MVC网站名为AuthorizationServer并且安装如下包:

  • Microsoft.AspNet.Mvc
  • Microsoft.Owin.Host.SystemWeb
  • Microsoft.Owin.Security.OAuth
  • Microsoft.Owin.Security.Cookies

在项目的根目录创建一个名为Startup的类:

using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(
typeof(AuthorizationServer.Startup))]

namespace AuthorizationServer
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}

创建一个App_Start文件夹,选中App_Start添加类文件Startup.Auth.cs

public void ConfigureAuth(IAppBuilder app)
{
// 启用登录应用使用Cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType
= "Application",
AuthenticationMode
= AuthenticationMode.Passive,
LoginPath
= new PathString(Paths.LoginPath),
LogoutPath
= new PathString(Paths.LogoutPath),
});

// 启用外部登录使用Cookie
app.SetDefaultSignInAsAuthenticationType("External");
app.UseCookieAuthentication(
new CookieAuthenticationOptions
{
AuthenticationType
= "External",
AuthenticationMode
= AuthenticationMode.Passive,
CookieName
= CookieAuthenticationDefaults.CookiePrefix + "External",
ExpireTimeSpan
= TimeSpan.FromMinutes(5),
});

// 启用google身份验证
app.UseGoogleAuthentication();

// 配置授权服务
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
AuthorizeEndpointPath
= new PathString(Paths.AuthorizePath),
TokenEndpointPath
= new PathString(Paths.TokenPath),
ApplicationCanDisplayErrors
= true,
#if DEBUG
AllowInsecureHttp
= true,
#endif
// 授权服务提供者控制授权服务的生命周期
Provider = new OAuthAuthorizationServerProvider
{
OnValidateClientRedirectUri
= ValidateClientRedirectUri,
OnValidateClientAuthentication
= ValidateClientAuthentication,
OnGrantResourceOwnerCredentials
= GrantResourceOwnerCredentials,
OnGrantClientCredentials
= GrantClientCredetails
},

// 授权码提供者用来创建和接受授权码
AuthorizationCodeProvider = new AuthenticationTokenProvider
{
OnCreate
= CreateAuthenticationCode,
OnReceive
= ReceiveAuthenticationCode,
},

// 刷新令牌提供这用来创建和接受令牌
RefreshTokenProvider = new AuthenticationTokenProvider
{
OnCreate
= CreateRefreshToken,
OnReceive
= ReceiveRefreshToken,
}
});
}

上面的代码启用了应用/外部登陆使用Cookie并且可以使用谷歌身份验证,由授权服务器本身管理账户。

 

UseCookieAuthentication扩展方法是用来配置授权服务的,配置选项是:

AuthorizeEndpointPath:客户端应用程序登录授权的请求地址,它必须以前导斜杠开始,例如:“/Authorize”

TokenEndpointPath:客户端应用程序直接获得访问令牌的请求地址,它也同样是以前导斜杠开始,例如:“/Token”

ApplicationCanDisplayErrors:如果Web应用程序想要在/Authorize地址为客户端验证生成一个自定义的错误页那么设置为true,浏览器不重定向到客户端应用。例如当client_id或者redirect_uri是错误的,/Authorize可能希望看到“oauth.Error”、“oauth.ErrorDescription”和“oauth.ErrorUri”属性被添加到Owin环境中。

AllowInsecureHttp:如果设置为true,代表授权和令牌地址允许不安全的Http协议。

Provider:配置授权服务中间件处理认证和授权的事件。

AuthorizationCodeProvider:产生一个一次性使用的验证码给客户端应用程序,OnCreate创建授权码,OnReceive收到授权码。

RefreshTokenProvider:产生一个刷新Token在需要的时候用来获得新的访问Token。

 

Oauth不关心在哪儿或怎么去管理你的账号信息,它是有Asp.Net Indentity来负责的,在本教程我们将简化账户管理的代码只确保用户可以使用Owin cookie中间件登录,如下在AccountControl中的简单代码:

public class AccountController : Controller
{
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();
}

public ActionResult Logout()
{
return View();
}

public ActionResult External()
{
var authentication = HttpContext.GetOwinContext().Authentication;
if (Request.HttpMethod == "POST")
{
foreach (var key in Request.Form.AllKeys)
{
if (key.StartsWith("submit.External.") && !string.IsNullOrEmpty(Request.Form.Get(key)))
{
var authType = key.Substring("submit.External.".Length);
authentication.Challenge(authType);
return new HttpUnauthorizedResult();
}
}
}
var identity = authentication.AuthenticateAsync("External").Result.Identity;
if (identity != null)
{
authentication.SignOut(
"External");
authentication.SignIn(
new AuthenticationProperties { IsPersistent = true },
new ClaimsIdentity(identity.Claims, "Application", identity.NameClaimType, identity.RoleClaimType));
return Redirect(Request.QueryString["ReturnUrl"]);
}

return View();
}
}
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);
}

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);
}
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);
}

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);
}

 ValidateClientRedirectUri用于验证被注册的跳转Url。ValidateClientAuthentication 验证从Basic架构的请求头或Form表单提交过来的客户端凭证。