Web Api Owin+Oauth2.0(ClientCredentials)+Jwt Token許可權認證控制
阿新 • • 發佈:2019-02-01
OAuth簡介
OAuth簡單說就是一種授權的協議,只要授權方和被授權方遵守這個協議去寫程式碼提供服務,那雙方就是實現了OAuth模式。
OAuth 2.0 四種授權模式:
- 授權碼模式(authorization code)
- 簡化模式(implicit)
- 密碼模式(resource owner password credentials)
- 客戶端模式(client credentials)
下面的實列就是 客戶端模式(client credentials)
Jwt 簡介
JSON Web Token(JWT)是一個開放式標準(RFC 7519),它定義了一種緊湊且自包含的方式,用於在各方之間以JSON物件安全傳輸資訊。這些資訊可以通過數字簽名進行驗證和信任。可以使用祕密(使用HMAC演算法)或使用RSA的公鑰/私鑰對對JWT進行簽名。
實列講解
如上圖所示引入對應得Nuget包。
在專案中建立 Startup.cs 檔案,新增如下程式碼:
/// <summary> /// Startup /// </summary> public class Startup { private readonly HttpConfiguration _httpConfig; /// <summary> /// Initializes a new instance of the <see cref="Startup" /> class. /// </summary> public Startup() { _httpConfig = new HttpConfiguration(); } /// <summary> /// Configurations the specified application. /// </summary> /// <param name="app">The application.</param> public void Configuration(IAppBuilder app) { ConfigureOAuthTokenGeneration(app); ConfigureOAuthTokenConsumption(app); } //配置token生成 private void ConfigureOAuthTokenGeneration(IAppBuilder app) { var oAuthServerOptions = new OAuthAuthorizationServerOptions { //TODO:For Dev enviroment only (on production should be AllowInsecureHttp = false) AllowInsecureHttp = true, TokenEndpointPath = new PathString("/oauth/token"),//獲取token請求地址 AccessTokenExpireTimeSpan = TimeSpan.FromDays(5),//token過期時間 Provider = new SimpleOAuthProvider(),//token生成服務 AccessTokenFormat = new SimpleJwtFormat()//token生成Jwt格式 }; app.UseOAuthAuthorizationServer(oAuthServerOptions); app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); } //配置token使用 private void ConfigureOAuthTokenConsumption(IAppBuilder app) { var issuer = ConfigurationManager.AppSettings["oauth:Issuer"]; var audienceIds = ConfigurationManager.AppSettings["oauth:Audiences"]; var audienceSecrets = ConfigurationManager.AppSettings["oauth:Secrets"]; var allowedAudiences = audienceIds.Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries); var base64Keys = audienceSecrets.Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries); var keys = base64Keys.Select(s => TextEncodings.Base64Url.Decode(s)).ToList(); app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions { AuthenticationMode = AuthenticationMode.Active, AllowedAudiences = allowedAudiences, IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[] { new SymmetricKeyIssuerSecurityTokenProvider(issuer, keys) }, Provider = new SimpleOAuthBearerAuthenticationProvider("access_token") }); } }
SimpleOAuthProvider示例程式碼:
public class SimpleOAuthProvider : OAuthAuthorizationServerProvider { public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { string clientId; string clientSecret; if (!context.TryGetBasicCredentials(out clientId, out clientSecret)) context.TryGetFormCredentials(out clientId, out clientSecret); var authDbContext = new ExternalInterfaceBaseDbContext(); ICustomerRepository customerRepository = new CustomerRepository(authDbContext); if (context.ClientId == null) { context.SetError("invalid_client", "The client_id is not set."); return Task.FromResult<object>(null); } var secretKey = customerRepository.GetSecretKey(clientId); if (secretKey==null) { context.SetError("invalid_client", $"Invalid client_id '{context.ClientId}'."); return Task.FromResult<object>(null); } if (clientSecret != secretKey) { context.SetError("invalid_client", "Invalid client_Secret."); return Task.FromResult<object>(null); } context.Validated(); return Task.FromResult<object>(null); } public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context) { context.OwinContext.Response.Headers["Access-Control-Allow-Origin"] = "*"; var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType); oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, "External Interface")); var properties = new Dictionary<string, string> { { "audience", context.ClientId ?? string.Empty } }; var authenticationProperties = new AuthenticationProperties(properties); var ticket = new AuthenticationTicket(oAuthIdentity, authenticationProperties); context.Validated(ticket); return base.GrantClientCredentials(context); } }
使用Oauth2.0 ClientCredentials 模式獲取token,授予客戶憑證 。注意:不同得模式重寫。GrantResourceOwnerCredentials 內部可以呼叫外部服務,以進行對使用者賬戶資訊的驗證。
SimpleJwtFormat 示例程式碼:
public class SimpleJwtFormat : ISecureDataFormat<AuthenticationTicket>
{
private const string AudiencePropertyKey = "audience";
private readonly string _issuer;
private readonly string _audienceSecrets;
public SimpleJwtFormat()
{
_issuer = ConfigurationManager.AppSettings["oauth:Issuer"];
_audienceSecrets = ConfigurationManager.AppSettings["oauth:Secrets"];
}
public string Protect(AuthenticationTicket data)
{
if (data == null)
throw new ArgumentNullException(nameof(data));
var properties = data.Properties;
var propertityDictionary = properties.Dictionary;
var audienceId = propertityDictionary.ContainsKey(AudiencePropertyKey)
? propertityDictionary[AudiencePropertyKey]
: null;
if (string.IsNullOrWhiteSpace(audienceId))
throw new InvalidOperationException("AuthenticationTicket.Properties does not include audience.");
if (properties.IssuedUtc == null)
throw new InvalidOperationException("AuthenticationTicket.Properties does not include issued.");
if (properties.ExpiresUtc == null)
throw new InvalidOperationException("AuthenticationTicket.Properties does not include expires.");
var issued = properties.IssuedUtc.Value.UtcDateTime;
var expires = properties.ExpiresUtc.Value.UtcDateTime;
//TODO:
//var authDbContext = new InstrumentDbContext();
//var audienceRepository = new AudienceRepository(authDbContext);
//var audience = audienceRepository.Get(audienceId);
var decodedSecret = TextEncodings.Base64Url.Decode(_audienceSecrets);
var signingCredentials = new HmacSigningCredentials(decodedSecret);
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued, expires,
signingCredentials);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
return jwt;
}
}
根據授權Id生成Jwt Token返回。
SimpleOAuthBearerAuthenticationProvider 示例程式碼:
public class SimpleOAuthBearerAuthenticationProvider : OAuthBearerAuthenticationProvider
{
private readonly string _accessTokenName;
public SimpleOAuthBearerAuthenticationProvider(string accessTokenName)
{
_accessTokenName = accessTokenName;
}
public override Task RequestToken(OAuthRequestTokenContext context)
{
var token = context.Request.Query.Get(_accessTokenName);
if (!string.IsNullOrEmpty(token))
context.Token = token;
return Task.FromResult<object>(null);
}
}
配置AccessToken使用。
到這裡許可權認證程式碼基本完成,最後就是在控制器或者方法上配置許可權控制特性 [Authorize]。
[Authorize]
[RoutePrefix("api/areas")]
public class AreaController : ApiController
這樣Web Api Owin + Oauth2.0 + Jwt Token 的許可權認證框架就已經搭好了。