c#web api使用auth1.0a和OAuthAuthorizationServerProvider

时间:2017-08-17 14:02:15

标签: c# authentication asp.net-web-api oauth asp.net-web-api2

我之前使用 OAuthAuthorizationServerProvider 设置了基于JWT令牌的身份验证。提供者看起来像这样:

public class OAuthProvider : OAuthAuthorizationServerProvider
{

    // Private properties
    private readonly IAdvancedEncryptionStandardProvider _helper;
    private readonly IUserProvider _userProvider;

    // Optional fields
    private readonly Lazy<IClientService> _clientService;

    /// <summary>
    /// Default constructor
    /// </summary>
    /// <param name="helper"></param>
    public OAuthProvider(IAdvancedEncryptionStandardProvider helper, IUserProvider userProvider, Lazy<IClientService> clientService)
    {
        _helper = helper;
        _userProvider = userProvider;
        _clientService = clientService;
    }

    /// <summary>
    /// Always validate the client because we are using angular
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext  context)
    {

        // Set up our variables
        var clientId = string.Empty;
        var clientSecret = string.Empty;
        Client client = null;

        // Try to get our credentials if basic authentication has been used
        if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
            context.TryGetFormCredentials(out clientId, out clientSecret);

        // If we have no client id
        if (context.ClientId == null)
        {
            //Remove the comments from the below line context.SetError, and invalidate context 
            //if you want to force sending clientId/secrects once obtain access tokens. 
            context.Validated();
            //context.SetError("invalid_clientId", "ClientId should be sent.");
            return;
        }

        // Get our client 
        client = await _clientService.Value.GetAsync(context.ClientId);

        // If we have no client, throw an error
        if (client == null)
        {
            context.SetError("invalid_clientId", $"Client '{ context.ClientId }' is not registered in the system.");
            return;
        }

        // Get the application type
        if (client.ApplicationType == ApplicationTypes.NativeConfidential)
        {

            // If we have a client secret
            if (string.IsNullOrWhiteSpace(clientSecret))
            {
                context.SetError("invalid_clientId", "Client secret shoud be sent.");
                return;
            }

            if (client.Secret != _helper.Encrypt(clientSecret))
            {
                context.SetError("invalid_clientId", "Client secret is invalid.");
                return;
            }
        }

        // If the client is inactive, throw an error
        if (!client.Active)
        {
            context.SetError("invalid_clientId", "Client is inactive.");
            return;
        }

        // Set our allowed origin and token expiration
        context.OwinContext.Set<string>("as:clientAllowedOrigin", client.AllowedOrigin);
        context.OwinContext.Set<string>("as:clientRefreshTokenLifeTime", client.RefreshTokenLifeTime.ToString());

        // Validate our request
        context.Validated();
        return;
    }

    /// <summary>
    /// Authorize the request
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        // Set our allowed origin
        var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
        if (string.IsNullOrEmpty(allowedOrigin))
            allowedOrigin = "*";

        // Add our CORS
        context.OwinContext.Response.Headers.Remove("Access-Control-Allow-Origin");
        context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });

        // Find user by username first
        var user = await _userProvider.FindByNameAsync(context.UserName);

        // If our user actually exists
        if (user != null)
        {

            // Validate the users credentials
            var validCredentials = await _userProvider.FindAsync(context.UserName, context.Password);
            var lockoutEnabled = await _userProvider.GetLockoutEnabledAsync(user.Id);

            // If lockout is enabled
            if (lockoutEnabled)
            {

                // If the user entered invalid credentials
                if (validCredentials == null)
                {

                    // Record the failure which also may cause the user to be locked out
                    await _userProvider.AccessFailedAsync(user);

                    // Find out how many attempts are left
                    var accessFailedCount = await _userProvider.GetAccessFailedCountAsync(user.Id);
                    var attemptsLeft = Convert.ToInt32(ConfigurationManager.AppSettings["MaxFailedAccessAttemptsBeforeLockout"].ToString()) - accessFailedCount;

                    // Inform the user of the error
                    context.SetError("invalid_grant", string.Format(Resources.PasswordInvalid, attemptsLeft));
                    return;
                }

                // Check to see if the user is already locked out
                var lockedOut = await _userProvider.IsLockedOutAsync(user.Id);

                // If the user is lockted out
                if (lockedOut)
                {

                    // Inform the user
                    context.SetError("invalid_grant", string.Format(Resources.UserLocked, ConfigurationManager.AppSettings["DefaultAccountLockoutTimeSpan"].ToString()));
                    return;
                }

                // If we get this far, reset the access attempts
                await _userProvider.ResetAccessFailedCountAsync(validCredentials);
            }

            // If the user entered the correct details
            if (validCredentials != null)
            {

                // If the user has not confirmed their account
                if (!validCredentials.EmailConfirmed)
                {

                    // Inform the user
                    context.SetError("invalid_grant", Resources.UserHasNotConfirmed);
                    return;
                }

                // Generate our identity
                var oAuthIdentity = await _userProvider.CreateIdentityAsync(validCredentials, "JWT");
                oAuthIdentity.AddClaims(ExtendedClaimsProvider.GetClaims(validCredentials));

                // Create our properties
                var properties = new AuthenticationProperties(new Dictionary<string, string>
                {
                    {"as:client_id", string.IsNullOrEmpty(context.ClientId) ? string.Empty : context.ClientId},
                    {"userName", context.UserName}
                });

                // Create our ticket and authenticate the user
                var ticket = new AuthenticationTicket(oAuthIdentity, properties);
                context.Validated(ticket);
                return;
            }
        }

        // Failsafe
        context.SetError("invalid_grant", Resources.UserOrPasswordNotFound);
        return;
    }

    /// <summary>
    /// Adds additional properties to the response
    /// </summary>
    /// <param name="context">The current 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>
    /// Grants a refresh token for the current context
    /// </summary>
    /// <param name="context">The current context</param>
    /// <returns></returns>
    public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
    {

        // Get our client ids
        var originalClient = context.Ticket.Properties.Dictionary["as:client_id"];
        var currentClient = context.ClientId;

        // If we are not the same client
        if (originalClient != currentClient)
        {

            // Set the error and exit the function
            context.SetError("invalid_clientId", "Refresh token is issued to a different clientId.");
            return Task.FromResult<object>(null);
        }

        // Change auth ticket for refresh token requests
        var newIdentity = new ClaimsIdentity(context.Ticket.Identity);

        newIdentity.AddClaim(new Claim("newClaim", "newValue"));

        var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
        context.Validated(newTicket);

        return Task.FromResult<object>(null);
    }
}

这很好用,大部分都很好。我的经理现在要求我使用密钥密钥将身份验证用于其他应用程序。 我想使用 OAuthAuthorizationServerProvider 来执行此操作,但我无法找到任何有关如何设置此文档的文档。

我已阅读并找到了一个可以覆盖的方法: GrantCustomExtension 并认为我可以使用它来设置身份验证,但就像我提到的那样,我不知道如何设置它起来。

有没有人有这方面的经验?如果他们有,他们可以通过提供代码示例或给我链接到我可以阅读的资源来帮助我吗? 任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

我建议您远离Asp.net身份并使用IdentityServer

IdentityServer4是ASP.NET Core的OpenID Connect和OAuth 2.0框架。 IdentityServer3适用于Asp.Net Classic(尽管您可以将IdentityServer4与Asp.net classic一起使用)

配置和正在进行的项目非常容易。

它有几个功能,如

  1. 身份验证即服务
  2. 单点登录/注销
  3. API的访问控制
  4. 联盟网关
  5. 而对于最重要的部分,它是免费和开源的。