如何在成为旧版ASP.NET MVC应用程序的一部分时在Web API 2中对用户进行身份验证?

时间:2021-05-21 03:36:28

I have a Web API which is currently used by AngularJS apps in an ASP.NET MVC web application. The MVC application is utilizing ASP.NET Forms Authentication as authentication mechanism. How should I authenticate a user of the Web API when the client is not the web client but e.g. a stand-alone service. What I've done right now is adding a login method to the Web API which gives anyone with right credentials access:

我有一个Web API,目前AngularJS应用程序在ASP.NET MVC Web应用程序中使用。 MVC应用程序使用ASP.NET Forms Authentication作为身份验证机制。当客户端不是Web客户端时,我应该如何验证Web API的用户,例如一个独立的服务。我现在所做的是为Web API添加一个登录方法,它为任何人提供了正确的凭据访问权限:

[Route("api/v2/login"), HttpPost]
[AllowAnonymous]
public IHttpActionResult Post([FromBody]Credentials credentials)
{
    var principal = FindPrincipal(credentials);
    if (principal != null)
    {
        FormsAuthentication.SetAuthCookie(principal.Identity.Name, false);
        return Ok();
    }
    return Unauthorized();
}

My question is if this is how this should be solved or if there's a better way?

我的问题是,如果这应该如何解决或者是否有更好的方法?

1 个解决方案

#1


3  

You can use the token authentication mechanism for WebApi2.

您可以使用WebApi2的令牌认证机制。

The flow will be some thing like this:

流程将是这样的事情:

users send you an https request on:

用户向您发送https请求:

https://yourApiUrl/Token

content type of request should be:

请求的内容类型应为:

application/x-www-form-urlencoded

body should include:

身体应包括:

grant_type=password&username=yourWebsFormsUsername&password=yourWebFormsPassword

your OWIN startup class will look some thing like this:

你的OWIN启动类看起来像这样:

public partial class Startup
{
    public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

    public static string PublicClientId { get; private set; }

    public void ConfigureAuth(IAppBuilder app)
    {
        app.UseCookieAuthentication(new CookieAuthenticationOptions());
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

        PublicClientId = "self";
        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/Token"),
            Provider = new YourOAuthProvider(),
            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            AllowInsecureHttp = true,
        };

        app.UseOAuthBearerTokens(OAuthOptions);
    }
}

Notice YourOAuthProvider above, thats the important part. This is your custom provider that will validate your username/password against what ever credential storage you have. In your case aspnet_membership table. This validation is done below in method RequestHasValidCredentials:

注意上面的YourOAuthProvider,这是重要的部分。这是您的自定义提供程序,它将根据您拥有的凭据存储来验证您的用户名/密码。在你的情况下aspnet_membership表。此验证在RequestHasValidCredentials方法中完成:

public class YourOAuthProvider : OAuthAuthorizationServerProvider
{
    public string apikey = string.Empty;

    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        context.Validated();
    }

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {

        if (RequestHasValidCredentials(context.UserName, context.Password))
        {
            var id = new ClaimsIdentity(context.Options.AuthenticationType);
            id.AddClaim(new Claim("username", context.UserName));

            context.Validated(id);
        }
        else
        {
            context.SetError("invalid_grant", "The user name or password is incorrect.");
            return;
        }            
    }
}

the response your user will get to the above will be a token, which will include the username, or any other information you added into the context in above method:

您的用户将获得上述响应将是一个令牌,其中包括用户名或您在上述方法中添加到上下文中的任何其他信息:

id.AddClaim(new Claim("username", context.UserName));

the reponse from the above token api call will be something like this:

来自上述令牌api调用的响应将是这样的:

{
    "access_token": "9TIpW2m2rUbB_Bmb7kKAQ9GH4hgfnKF8g3fL0tAre2gcFjI45fajmG6qdOJe-A",
    "token_type": "bearer",
    "expires_in": 1209599
}

your user will then have to pass this token as Http Authorization header for all the API calls. They need to pass this in using Bearer scheme, e.g.:

然后,您的用户必须将此标记作为所有API调用的Http Authorization标头传递。他们需要使用承载方案,例如:

Bearer 9TIpW2m2rUbB_Bmb7kKAQ9GH4hgfnKF8g3fL0tAre2gcFjI45fajmG6qdOJe-A

as this token contains username, you will be able to know who the user is. Last thing is now to read this token and retrieve the username. For that you need to create a custom Authorize attribute, and decorate your controller or methods with that.

由于此令牌包含用户名,您将能够知道该用户是谁。现在最后一件事是阅读此令牌并检索用户名。为此,您需要创建自定义Authorize属性,并使用它来装饰您的控制器或方法。

public class YourAuthorizeAttribute : AuthorizationFilterAttribute
{
    public override void OnAuthorization(HttpActionContext actionContext)
    {
        var ticket = Startup.OAuthOptions.AccessTokenFormat.Unprotect(actionContext.Request.Headers.Authorization.Parameter);

        string username = claims.Where(x => x.Type == "username").FirstOrDefault();

        base.OnAuthorization(actionContext);
    }
}

all other custom authorization logic can be added in here, once you have the username.

一旦您拥有用户名,就可以在此处添加所有其他自定义授权逻辑。

You can pass in other custom information at the time of generating token, and read it in here. (in case you need that for any other special authorization logic.)

您可以在生成令牌时传递其他自定义信息,并在此处阅读。 (如果您需要任何其他特殊授权逻辑。)

Its lengthy approach, but will work will any credentials storage.

它冗长的方法,但将工作将任何凭据存储。

#1


3  

You can use the token authentication mechanism for WebApi2.

您可以使用WebApi2的令牌认证机制。

The flow will be some thing like this:

流程将是这样的事情:

users send you an https request on:

用户向您发送https请求:

https://yourApiUrl/Token

content type of request should be:

请求的内容类型应为:

application/x-www-form-urlencoded

body should include:

身体应包括:

grant_type=password&username=yourWebsFormsUsername&password=yourWebFormsPassword

your OWIN startup class will look some thing like this:

你的OWIN启动类看起来像这样:

public partial class Startup
{
    public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

    public static string PublicClientId { get; private set; }

    public void ConfigureAuth(IAppBuilder app)
    {
        app.UseCookieAuthentication(new CookieAuthenticationOptions());
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

        PublicClientId = "self";
        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/Token"),
            Provider = new YourOAuthProvider(),
            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            AllowInsecureHttp = true,
        };

        app.UseOAuthBearerTokens(OAuthOptions);
    }
}

Notice YourOAuthProvider above, thats the important part. This is your custom provider that will validate your username/password against what ever credential storage you have. In your case aspnet_membership table. This validation is done below in method RequestHasValidCredentials:

注意上面的YourOAuthProvider,这是重要的部分。这是您的自定义提供程序,它将根据您拥有的凭据存储来验证您的用户名/密码。在你的情况下aspnet_membership表。此验证在RequestHasValidCredentials方法中完成:

public class YourOAuthProvider : OAuthAuthorizationServerProvider
{
    public string apikey = string.Empty;

    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        context.Validated();
    }

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {

        if (RequestHasValidCredentials(context.UserName, context.Password))
        {
            var id = new ClaimsIdentity(context.Options.AuthenticationType);
            id.AddClaim(new Claim("username", context.UserName));

            context.Validated(id);
        }
        else
        {
            context.SetError("invalid_grant", "The user name or password is incorrect.");
            return;
        }            
    }
}

the response your user will get to the above will be a token, which will include the username, or any other information you added into the context in above method:

您的用户将获得上述响应将是一个令牌,其中包括用户名或您在上述方法中添加到上下文中的任何其他信息:

id.AddClaim(new Claim("username", context.UserName));

the reponse from the above token api call will be something like this:

来自上述令牌api调用的响应将是这样的:

{
    "access_token": "9TIpW2m2rUbB_Bmb7kKAQ9GH4hgfnKF8g3fL0tAre2gcFjI45fajmG6qdOJe-A",
    "token_type": "bearer",
    "expires_in": 1209599
}

your user will then have to pass this token as Http Authorization header for all the API calls. They need to pass this in using Bearer scheme, e.g.:

然后,您的用户必须将此标记作为所有API调用的Http Authorization标头传递。他们需要使用承载方案,例如:

Bearer 9TIpW2m2rUbB_Bmb7kKAQ9GH4hgfnKF8g3fL0tAre2gcFjI45fajmG6qdOJe-A

as this token contains username, you will be able to know who the user is. Last thing is now to read this token and retrieve the username. For that you need to create a custom Authorize attribute, and decorate your controller or methods with that.

由于此令牌包含用户名,您将能够知道该用户是谁。现在最后一件事是阅读此令牌并检索用户名。为此,您需要创建自定义Authorize属性,并使用它来装饰您的控制器或方法。

public class YourAuthorizeAttribute : AuthorizationFilterAttribute
{
    public override void OnAuthorization(HttpActionContext actionContext)
    {
        var ticket = Startup.OAuthOptions.AccessTokenFormat.Unprotect(actionContext.Request.Headers.Authorization.Parameter);

        string username = claims.Where(x => x.Type == "username").FirstOrDefault();

        base.OnAuthorization(actionContext);
    }
}

all other custom authorization logic can be added in here, once you have the username.

一旦您拥有用户名,就可以在此处添加所有其他自定义授权逻辑。

You can pass in other custom information at the time of generating token, and read it in here. (in case you need that for any other special authorization logic.)

您可以在生成令牌时传递其他自定义信息,并在此处阅读。 (如果您需要任何其他特殊授权逻辑。)

Its lengthy approach, but will work will any credentials storage.

它冗长的方法,但将工作将任何凭据存储。