Инфраструктура:
- 1 Серверы авторизации
- ~ Серверы ресурсов
Технология:
- ASP.NET MVC Owin
- ASP.NET WebAPI Owin
- ASP.NET Identity
- Маркер носителя с использованием JWT
- .Net Framework 4.7.1
Сценарий: У меня есть служба для обработки всех действий, связанных с доступом пользователей, в центральном месте (служба аутентификации) и 4 службы ресурсов (3 веб-сайта).и 1 Web.API).Эта настройка работает правильно, и я могу успешно передать сгенерированный токен на серверы ресурсов.В рамках службы аутентификации у меня есть дополнительный контроллер для обработки других запросов, используемых службами ресурсов, таких как регистрация, сброс пароля, смена пароля, изменение адреса электронной почты и т. Д. Однако в настоящий момент эти действия контроллера установлены на анонимность, ноМне нужно добавить атрибут [Authorize] на них.
AUTHSERVICE:
Startup.cs
/// <summary>
///
/// </summary>
/// <param name="app">Application Builder</param>
/// <param name="provider">Authorization Server Provider</param>
public void ConfigureOAuth(IAppBuilder app, IOAuthAuthorizationServerProvider provider)
{
var oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
TokenEndpointPath = new PathString("/oauth2/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = provider,
RefreshTokenProvider = new RefreshTokenProvider(),
ApplicationCanDisplayErrors = true,
AllowInsecureHttp = true,
AccessTokenFormat = new CustomJwtFormat("http://devurl")
};
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(oAuthServerOptions);
}
ApplicationOAuthProvider.cs - внедрен в метод ConfigureOAuth
public class ApplicationOAuthProvider
: OAuthAuthorizationServerProvider
{
private readonly ApplicationUserManager _userManager;
private readonly string _publicClientId;
/// <summary>
/// Ctor
/// </summary>
/// <param name="userManager"></param>
/// <param name="publicClientId"></param>
public ApplicationOAuthProvider(ApplicationUserManager userManager, string publicClientId = "self")
{
_userManager = userManager;
_publicClientId = publicClientId;
}
/// <summary>
/// Validate a client's Id
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId = context.ClientId;
string clientSecret = string.Empty;
string symmetricKeyAsBase64 = string.Empty;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (context.ClientId == null)
{
context.SetError("invalid_clientId", "client_Id is not set");
return Task.FromResult<object>(null);
}
var audience = ClientStore.FindClient(context.ClientId);
if (audience == null)
{
context.SetError("invalid_clientId", $"Invalid client_id '{context.ClientId}'");
return Task.FromResult<object>(null);
}
context.Validated();
return Task.FromResult<object>(null);
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
User user = await _userManager.FindByEmailAsync(context.UserName);
if (user == null)
{
context.SetError("invalid_grant", "The email address does not exist!");
return;
}
user = await _userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The email address or password is incorrect.");
return;
}
if (!await _userManager.IsEmailConfirmedAsync(user.Id))
{
context.SetError("invalid_grant", "The email address has not been confirmed.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(_userManager, OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(_userManager, CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(context, user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
/// <summary>
/// Token Endpoint
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
/// <summary>
/// Validate the client redirect URI
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
Uri expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
{
context.Validated();
}
}
return Task.FromResult<object>(null);
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <param name="userName"></param>
/// <returns></returns>
private static AuthenticationProperties CreateProperties(OAuthGrantResourceOwnerCredentialsContext context, string userName)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "username", userName },
{ "client", context.ClientId }
};
return new AuthenticationProperties(data);
}
/// <summary>
/// Validate a token request
/// </summary>
/// <param name="context">The current request context</param>
/// <returns></returns>
public override Task ValidateTokenRequest(OAuthValidateTokenRequestContext context)
{
return base.ValidateTokenRequest(context);
}
}
RefreshTokenProvider.cs
public class RefreshTokenProvider : IAuthenticationTokenProvider
{
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
Create(context);
}
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
Receive(context);
}
public void Create(AuthenticationTokenCreateContext context)
{
object inputs;
context.OwinContext.Environment.TryGetValue("Microsoft.Owin.Form#collection", out inputs);
var grantType = ((FormCollection)inputs)?.GetValues("grant_type");
var grant = grantType.FirstOrDefault();
if (grant == null || grant.Equals("refresh_token")) return;
context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(1);
context.SetToken(context.SerializeTicket());
}
public void Receive(AuthenticationTokenReceiveContext context)
{
context.DeserializeTicket(context.Token);
if (context.Ticket == null)
{
context.Response.StatusCode = 400;
context.Response.ContentType = "application/json";
context.Response.ReasonPhrase = "invalid token";
return;
}
if (context.Ticket.Properties.ExpiresUtc <= DateTime.UtcNow)
{
context.Response.StatusCode = 401;
context.Response.ContentType = "application/json";
context.Response.ReasonPhrase = "unauthorized";
return;
}
context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(1);
context.SetTicket(context.Ticket);
}
}
РЕСУРСНАЯ СЛУЖБА:
Startup.cs
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
LogoutPath = new PathString("/Account/LogOff"),
CookieName = "access_token",
Provider = new CookieAuthenticationProvider
{
OnResponseSignedIn = RememberMeTokenMiddleware.CheckAndCreateRememberMeToken,
OnResponseSignOut = ctx =>
{
RememberMeTokenMiddleware.Logout(ctx.OwinContext);
},
}
});
app.Use<RememberMeTokenMiddleware>();
ConfigureOAuth(app);
}
/// <summary>
///
/// </summary>
/// <param name="app"></param>
public void ConfigureOAuth(IAppBuilder app)
{
ISettings settings = new Settings();
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { settings.AuthClientId },
IssuerSecurityKeyProviders = new IIssuerSecurityKeyProvider[]
{
new SymmetricKeyIssuerSecurityKeyProvider(settings.AuthServiceUrl, TextEncodings.Base64Url.Decode("clientsecret"))
},
});
}
public class ProtectedController : Controller
{
// GET: Protected
[Authorize]
public ActionResult Index()
{
var claimsIdentity = User.Identity as ClaimsIdentity;
var claims = claimsIdentity.Claims.Select(x => new { type = x.Type, value = x.Value });
return View(claims);
}
}
Вопросы:
- Если я добавлю атрибут [Authorize] поверх действий, которые ему нужны, смените пароль, какили что разрешает этот вызов службе аутентификации, т. е. нужен ли мне специальный фильтр аутентификации для проверки токена носителя?
- Где я могу проверить, разрешено ли клиенту, вызывающему службу, и прекращать вызовы?от других клиентов отдыха?
- Я включил refresh_token при первой аутентификации, когда и где это вступает в силу или как я могу его использовать?
Не так многодокументации для полной распределенной архитектуры, подобной этой, и как сервисы должны взаимодействовать, пожалуйста, еслиВы можете предоставить мне демонстрационный проект.Безопасность так запутанно: - (