it-swarm.com.de

Verwenden Sie JWT (Authorization: Bearer) in Swagger in ASP.NET Core

Ich erstelle eine REST - API in ASP.NET Core 1.0. Ich habe Swagger zum Testen verwendet, aber jetzt habe ich für einige Routen die JWT-Autorisierung hinzugefügt. (mit UseJwtBearerAuthentication)

Kann der Header der Swagger-Anforderungen geändert werden, damit die Routen mit dem [Authorize]-Attribut getestet werden können?

19
Rubanov

Ich kämpfte mit dem gleichen Problem und fand eine funktionierende Lösung in diesem Blogpost: http://blog.sluijsveld.com/28/01/2016/CustomSwaggerUIField

Es kommt darauf an, dies in Ihre Konfigurationsoptionen einzufügen

services.ConfigureSwaggerGen(options =>
{
   options.OperationFilter<AuthorizationHeaderParameterOperationFilter>();
});

und den Code für den Operationsfilter

public class AuthorizationHeaderParameterOperationFilter : IOperationFilter
{
   public void Apply(Operation operation, OperationFilterContext context)
   {
      var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors;
      var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter);
      var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter);

      if (isAuthorized && !allowAnonymous)
      {
          if (operation.Parameters == null)
             operation.Parameters = new List<IParameter>();

          operation.Parameters.Add(new NonBodyParameter
          {                    
             Name = "Authorization",
             In = "header",
             Description = "access token",
             Required = true,
             Type = "string"
         });
      }
   }
}

Dann sehen Sie in Ihrem Swagger eine zusätzliche Autorisierungstextbox, in der Sie Ihr Token im Format "Träger {jwttoken}" hinzufügen können, und Sie sollten in Ihren Swagger-Anfragen autorisiert sein.

29
HansVG

Derzeit verfügt Swagger über Funktionen zur Authentifizierung mit JWT-Token und kann Token automatisch in den Header einfügen (ich verwende Swashbuckle.AspNetCore 1.1.0).

 enter image description here

Der folgende Code sollte dazu beitragen.

In den Startup.ConfigureServices ():

services.AddSwaggerGen(c =>
{
    // Your custom configuration
    c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
    c.DescribeAllEnumsAsStrings();
    // JWT-token authentication by password
    c.AddSecurityDefinition("oauth2", new OAuth2Scheme
    {
        Type = "oauth2",
        Flow = "password",
        TokenUrl = Path.Combine(HostingEnvironment.WebRootPath, "/token"),
        // Optional scopes
        //Scopes = new Dictionary<string, string>
        //{
        //    { "api-name", "my api" },
        //}
    });
});

Überprüfen Sie und konfigurieren Sie TokenUrl, wenn sich Ihr Endpunkt unterscheidet.

In der Startup.Configure ():

app.UseSwagger();
app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "API V1");

    // Provide client ID, client secret, realm and application name (if need)

    // Swashbuckle.AspNetCore 4.0.1
    c.OAuthClientId("swagger-ui");
    c.OAuthClientSecret("swagger-ui-secret");
    c.OAuthRealm("swagger-ui-realm");
    c.OAuthAppName("Swagger UI");

    // Swashbuckle.AspNetCore 1.1.0
    // c.ConfigureOAuth2("swagger-ui", "swagger-ui-secret", "swagger-ui-realm", "Swagger UI");
});

Wenn Ihr Endpunkt für die Authentifizierung per Token dem OAuth2-Standard entspricht, sollten alle funktionieren. Nur für den Fall, dass ich ein Beispiel für diesen Endpunkt hinzugefügt habe:

public class AccountController : Controller
{
    [ProducesResponseType(typeof(AccessTokens), (int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    [ProducesResponseType((int)HttpStatusCode.Unauthorized)]
    [HttpPost("/token")]
    public async Task<IActionResult> Token([FromForm] LoginModel loginModel)
    {
        switch (loginModel.grant_type)
        {
            case "password":
                var accessTokens = // Authentication logic
                if (accessTokens == null)
                    return BadRequest("Invalid user name or password.");
                return new ObjectResult(accessTokens);

            case "refresh_token":
                var accessTokens = // Refresh token logic
                if (accessTokens == null)
                    return Unauthorized();
                return new ObjectResult(accessTokens);

            default:
                return BadRequest("Unsupported grant type");
        }
    }
}

public class LoginModel
{
    [Required]
    public string grant_type { get; set; }

    public string username { get; set; }
    public string password { get; set; }
    public string refresh_token { get; set; }
    // Optional
    //public string scope { get; set; }
}

public class AccessTokens
{
    public string access_token { get; set; }
    public string refresh_token { get; set; }
    public string token_type { get; set; }
    public int expires_in { get; set; }
}
6
Pavel K.

Um auf die Antwort von HansVG zu erweitern, die für mich funktioniert hat (danke) und da ich nicht genügend Beitragspunkte habe, kann ich die emseetea-Frage nicht direkt beantworten. Sobald Sie das Textfeld Authorization (Autorisierung) haben, müssen Sie den Endpunkt aufrufen, der das Token generiert, das sich außerhalb des Mussbereichs [Authorize] der Endpunkte befindet. 

Nachdem Sie diesen Endpunkt aufgerufen haben, um das Token vom Endpunkt zu generieren, können Sie es aus den Ergebnissen für diesen Endpunkt kopieren. Dann haben Sie das Token, das Sie in Ihren anderen Bereichen verwenden müssen, die [Authorize] sind. Fügen Sie es einfach in das Textfeld ein. Stellen Sie, wie HansVG erwähnt, sicher, dass Sie es im richtigen Format hinzufügen, das "Träger" enthalten muss. Format = "Träger {Token}".

4
Timm Hagen

Sie können jeden zusätzlichen Header mit API-Aufruf hinzufügen, indem Sie diese swagger-Konfiguration verwenden

// Register the Swagger generator, defining 1 or more Swagger documents
services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new Info
    {
        Version = "v1",
        Title = "Core API",
        Description = "ASP.NET Core API",
        TermsOfService = "None",
        Contact = new Contact
        {
            Name = "Raj Kumar",
            Email = ""
        },
        License = new License
        {
            Name = "Demo"
        }
    });
    c.AddSecurityDefinition("Bearer", new ApiKeyScheme()
    {
        Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
        Name = "Authorization",
        In = "header",
        Type = "apiKey"
    });
    c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
    {
    {"Bearer",new string[]{}}
    });
});

 enter image description here

0
Raj kumar

Dank der Antwort von Pavel K. habe ich dieses Problem in ASP.NET Core 2.2 mit Swagger 4.0.1 endgültig gelöst.

In der Startup.cs ConfigureServices ():

public void ConfigureServices(IServiceCollection services)
{
    .
    .
    .
    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new Info { Title = "...", Version = "v1" });
        .
        .
        .
        c.AddSecurityDefinition("Bearer", new OAuth2Scheme
        {
            Flow = "password",
            TokenUrl = "/token"
        });

       // It must be here so the Swagger UI works correctly (Swashbuckle.AspNetCore.SwaggerUI, Version=4.0.1.0)
       c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
       {
           {"Bearer", new string[] { }}
       });
    });
    .
    .
    .
}

In der Startup.cs-Konfiguration ():

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    .
    .
    .
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "...");
        // Provide client ID, client secret, realm and application name (if need)
        c.OAuthClientId("...");
        c.OAuthClientSecret("...");
        c.OAuthRealm("...");
        c.OAuthAppName("...");
    });
    .
    .
    .
}

Und so habe ich einen Endpunkt erstellt, um ein JWT-Token auszugeben:

[ApiController, Route("[controller]")]
public class TokenController : ControllerBase
{
    [HttpPost, AllowAnonymous]
    public async Task<ActionResult<AccessTokensResponse>> RequestToken([FromForm]LoginRequest request)
    {
        var claims = await ValidateCredentialAndGenerateClaims(request);

        var now = DateTime.UtcNow;
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_setting.SecurityKey));
        var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(
            issuer: _setting.Issuer,
            audience: _setting.Audience,
            claims: claims,
            notBefore: now,
            expires: now.AddMinutes(_setting.ValidDurationInMinute),
            signingCredentials: signingCredentials);

        return Ok(new AccessTokensResponse(token));
    }
}

Alle Ihre Regeln und Ihre Logik zur Überprüfung von Benutzername und Kennwort (und/oder client_id und clinet_secret) befinden sich in ValidateCredentialAndGenerateClaims().

Wenn Sie sich nur wundern, sind dies meine Anfrage- und Antwortmodelle:

/// <summary>
/// Encapsulates fields for login request.
/// </summary>
/// <remarks>
/// See: https://www.oauth.com/oauth2-servers/access-tokens/
/// </remarks>
public class LoginRequest
{
    [Required]
    public string grant_type { get; set; }
    public string username { get; set; }
    public string password { get; set; }
    public string refresh_token { get; set; }
    public string scope { get; set; }

    public string client_id { get; set; }
    public string client_secret { get; set; }
}

/// <summary>
/// JWT successful response.
/// </summary>
/// <remarks>
/// See: https://www.oauth.com/oauth2-servers/access-tokens/access-token-response/
/// </remarks>
public class AccessTokensResponse
{
    /// <summary>
    /// Initializes a new instance of <seealso cref="AccessTokensResponse"/>.
    /// </summary>
    /// <param name="securityToken"></param>
    public AccessTokensResponse(JwtSecurityToken securityToken)
    {
        access_token = new JwtSecurityTokenHandler().WriteToken(securityToken);
        token_type = "Bearer";
        expires_in = Math.Truncate((securityToken.ValidTo - DateTime.UtcNow).TotalSeconds);
    }

    public string access_token { get; set; }
    public string refresh_token { get; set; }
    public string token_type { get; set; }
    public double expires_in { get; set; }
}
0
Tohid