我正在开发 ASP.NET Core 8.0 Web API。您能告诉我为什么某些端点在 Swagger UI 中没有挂锁图标吗?
例如,我的 ASP.NET 本地主机:localhost ASP.NET swagger。
另一方面,editor.swagger.io 上的预览是正确的:editor.swagger.io 预览。
当我执行请求时(例如到
/authorize
路径),请求标头没有 Authentication
字段。
我认为与第一个问题相关的第二个问题是,单击挂锁后,我会获得所有身份验证选项。
例如,我单击
GET /student
端点,我将得到:本地主机上的所有选项授权。在 editor.swagger.io 中,我将获得一个正确的选项:editor.swagger.io 的一个选项授权。
这是我的 openapi.yaml:
openapi: 3.0.3
info:
title: API schema for research project
description: API schema for research project
version: 1.0.0
servers:
- url: 'localhost'
tags:
- name: general
- name: student
description: Access to student data.
paths:
/hello:
get:
tags:
- general
summary: Returns "Hello World!" phrase
description: Returns "Hello World!" phrase.
responses:
'200':
description: successful operation
content:
text/plain:
schema:
$ref: '#/components/schemas/HelloWorld'
/authorize:
post:
tags:
- general
summary: Get authorization JWT token
description: Returns JWT token for further authorization.
responses:
'200':
description: successful operation
content:
text/plain:
schema:
$ref: '#/components/schemas/JwtToken'
'401':
description: unauthorized
security:
- jwt_token_auth: []
/student:
get:
tags:
- student
summary: Find first n students
description: Find and return first n students ordered by its IDs.
parameters:
- name: results
in: query
description: Number of students to return.
schema:
type: integer
default: 1
minimum: 1
responses:
'200':
description: successful operation
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Student'
'400':
description: Invalid 'results' parameter value
'401':
description: unauthorized
security:
- student_data_auth: []
post:
tags:
- student
summary: Add a new students
description: Add a new students
requestBody:
description: Create a new students
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Student'
required: true
responses:
'201':
description: Created successfully
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Student'
'400':
description: Invalid input
'401':
description: unauthorized
'422':
description: Validation exception
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ErrorMessage'
security:
- student_data_auth: []
/student/{studentId}:
put:
tags:
- student
summary: Update student
description: Update student by given ID.
parameters:
- name: studentId
in: path
description: ID of the student to update
required: true
schema:
type: integer
requestBody:
description: Update existing student
content:
application/json:
schema:
$ref: '#/components/schemas/Student'
required: true
responses:
'200':
description: Updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Student'
'400':
description: Invalid input
'401':
description: unauthorized
'404':
description: Student not found
'422':
description: Validation exception
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ErrorMessage'
security:
- student_data_auth: []
patch:
tags:
- student
summary: Update some fields of student
description: Update some fields of student by given student ID.
parameters:
- name: studentId
in: path
description: ID of the student to update
required: true
schema:
type: integer
requestBody:
description: Update existing student
content:
application/json:
schema:
$ref: '#/components/schemas/Student'
required: true
responses:
'200':
description: Updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Student'
'400':
description: Invalid input
'401':
description: unauthorized
'404':
description: Student not found
'422':
description: Validation exception
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ErrorMessage'
security:
- student_data_auth: []
delete:
tags:
- student
summary: Delete student by ID
description: Delete student by given ID.
parameters:
- name: studentId
in: path
description: ID of the student to delete
required: true
schema:
type: integer
responses:
'204':
description: Deleted successfully
'401':
description: unauthorized
'404':
description: Student not found
security:
- student_data_auth: []
components:
schemas:
HelloWorld:
type: string
example: "Hello World!"
JwtToken:
type: string
example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Student:
type: object
properties:
id:
type: integer
readOnly: true
example: 1
gender:
type: string
maxLength: 10
example: female
title:
type: string
maxLength: 20
example: Miss
first_name:
type: string
maxLength: 50
example: Terri
last_name:
type: string
maxLength: 50
example: Lucas
email:
type: string
maxLength: 100
example: [email protected]
dob:
type: string
description: date of birth
format: date
example: 1964-11-23
registered:
type: string
description: date of registration
format: date-time
example: 2014-07-23T03:21:42.259Z
phone:
type: string
maxLength: 20
example: 03-2662-3559
id_name:
type: string
maxLength: 20
description: national identification number type
example: TFN
id_value:
type: string
maxLength: 50
description: national identification number value
example: '230000682'
nat:
type: string
maxLength: 10
description: nationality short code
example: AU
location:
$ref: '#/components/schemas/Location'
picture:
$ref: '#/components/schemas/Picture'
Location:
type: object
properties:
street_number:
type: integer
example: 2595
street_name:
type: string
maxLength: 100
example: Main Street
city:
type: string
maxLength: 100
example: Tamworth
state:
type: string
maxLength: 100
example: Queensland
country:
type: string
maxLength: 100
example: Australia
postcode:
type: string
maxLength: 20
example: '6066'
timezone:
type: string
maxLength: 10
example: +5:30
Picture:
type: object
properties:
large:
type: string
maxLength: 255
example: https://randomuser.me/api/portraits/men/75.jpg
medium:
type: string
maxLength: 255
example: https://randomuser.me/api/portraits/med/men/75.jpg
thumbnail:
type: string
maxLength: 255
example: https://randomuser.me/api/portraits/thumb/men/75.jpg
ErrorMessage:
type: object
properties:
field:
type: string
example: first_name
message:
type: string
example: Input must be string
securitySchemes:
jwt_token_auth:
type: http
scheme: basic
student_data_auth:
type: http
scheme: bearer
bearerFormat: JWT
我只是补充一下,我的 ASP.NET Core 代码是由 IntelliJ 中的 OpenAPI 生成器生成的。之后我没有任何授权,添加后
OpenApiSecurityRequirement
我得到了上述情况。
在您的 OpenAPI 规范中,您定义了两种安全方案。 JWT 令牌通常作为不记名令牌传递,而不是基本身份验证。如果
jwt_token_auth
应该是基于 JWT 的授权,您应该将其定义为 Bearer 令牌。
每个端点指定一个安全要求。确保 yaml
中的安全要求与 ASP.NET Core 后端中的身份验证逻辑一致。
components:
securitySchemes:
jwt_token_auth:
type: http
scheme: bearer
bearerFormat: JWT
以下是示例代码:
AuthController.cs:
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace StudentApi.Controllers
{
[ApiController]
[Route("Auth")]
public class AuthController : ControllerBase
{
[HttpPost("authorize")] // Maps to POST /Auth/authorize
public IActionResult Authorize()
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.UTF8.GetBytes("");
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, "Test User")
}),
Expires = DateTime.UtcNow.AddDays(1),
Issuer = "",
Audience = "",
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
return Ok(tokenString);
}
}
}
StudentController.cs:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using StudentApi.Models;
namespace StudentApi.Controllers
{
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiController]
[Route("[controller]")]
public class StudentController : ControllerBase
{
private static readonly List<Student> Students = new List<Student>
{
new Student { Id = 1, FirstName = "Terri", LastName = "Lucas", Email = "[email protected]" }
};
[HttpGet]
public ActionResult<IEnumerable<Student>> GetStudents(int results = 1)
{
return Ok(Students.Take(results));
}
[HttpPost]
public ActionResult<IEnumerable<Student>> AddStudent([FromBody] List<Student> newStudents)
{
Students.AddRange(newStudents);
return CreatedAtAction(nameof(GetStudents), newStudents);
}
[HttpPut("{studentId}")]
public ActionResult<Student> UpdateStudent(int studentId, [FromBody] Student updatedStudent)
{
var student = Students.FirstOrDefault(s => s.Id == studentId);
if (student == null)
return NotFound();
student.FirstName = updatedStudent.FirstName;
student.LastName = updatedStudent.LastName;
student.Email = updatedStudent.Email;
return Ok(student);
}
[HttpPatch("{studentId}")]
public ActionResult<Student> PatchStudent(int studentId, [FromBody] Student updatedFields)
{
var student = Students.FirstOrDefault(s => s.Id == studentId);
if (student == null)
return NotFound();
if (updatedFields.FirstName != null)
student.FirstName = updatedFields.FirstName;
if (updatedFields.LastName != null)
student.LastName = updatedFields.LastName;
if (updatedFields.Email != null)
student.Email = updatedFields.Email;
return Ok(student);
}
[HttpDelete("{studentId}")]
public IActionResult DeleteStudent(int studentId)
{
var student = Students.FirstOrDefault(s => s.Id == studentId);
if (student == null)
return NotFound();
Students.Remove(student);
return NoContent();
}
}
}
程序.cs:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "",
ValidAudience = "",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(""))
};
});
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Student API", Version = "v1" });
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Bearer {token}\"",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty<string>()
}
});
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();