Используйте JWT (Authorization: Bearer) в Swagger в ASP.NET Core
Я создаю REST api в ASP.NET Core 1.0. Я использовал Swagger для тестирования, но теперь я добавил авторизацию JWT для некоторых маршрутов. (с UseJwtBearerAuthentication
)
Можно ли изменить заголовок запросов Swagger, чтобы маршруты с атрибутом [Authorize]
могли быть протестированы?
Ответы
Ответ 1
Я боролся с той же проблемой и нашел рабочее решение в этом блоге:
http://blog.sluijsveld.com/28/01/2016/CustomSwaggerUIField
Это сводится к добавлению этого в ваши настройки
services.ConfigureSwaggerGen(options =>
{
options.OperationFilter<AuthorizationHeaderParameterOperationFilter>();
});
и код для операции
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"
});
}
}
}
Затем вы увидите дополнительный текст авторизации TextBox в своем swagger, где вы можете добавить свой токен в формате "Bearer {jwttoken}", и вы должны быть авторизованы в своих запросах swagger.
Ответ 2
В настоящее время Swagger имеет функциональность для аутентификации с помощью JWT-токена и может автоматически добавлять токен в заголовок (я использую Swashbuckle.AspNetCore 1.1.0).
![enter image description here]()
Следующий код должен помочь достичь этого.
В 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" },
//}
});
});
Проверьте и настройте TokenUrl, если ваша конечная точка отличается.
В 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");
});
Если ваша конечная точка для аутентификации по токену соответствует стандарту OAuth2, все должно работать. Но на всякий случай я добавил образец этой конечной точки:
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; }
}
Ответ 3
Чтобы расширить ответ HansVG, который работал у меня (спасибо), и поскольку у меня недостаточно очков для вклада, я не могу ответить на вопрос emseetea напрямую. После того, как у вас есть текстовое поле авторизации, вам нужно будет вызвать конечную точку, которая генерирует токен, который будет находиться за пределами вашей конечной точки [Авторизовать].
Как только вы вызвали эту конечную точку для создания маркера с конечной точки, вы можете скопировать ее из результатов для этой конечной точки. Затем у вас есть токен для использования в других областях, которые должны [Авторизовать]. Просто вставьте его в текстовое поле. Удостоверьтесь, что, как отметил HansVG, добавить его в правильном формате, который должен включать "носитель". Формат = "предъявитель {токен}".
Ответ 4
Благодаря ответу Павла К. я наконец решил эту проблему в ASP.NET Core 2.2 с Swagger 4.0.1.
В 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[] { }}
});
});
.
.
.
}
В Startup.cs Configure():
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("...");
});
.
.
.
}
И вот как я сделал конечную точку для выдачи токена JWT:
[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));
}
}
Все ваши правила и логика для проверки имени пользователя и пароля (и/или client_id и clinet_secret) будут в ValidateCredentialAndGenerateClaims()
.
Если вам просто интересно, вот мои модели запросов и ответов:
/// <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; }
}
Ответ 5
Вы можете добавить любой дополнительный заголовок с помощью вызова API с помощью этой конфигурации чванства.
// 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]()