我正在开发一个ASP.NET 6 Restful作为拼贴作业。我想在我的API中引入AAA。我有很多允许的选项,我选择了核心身份+cookie。我尽可能多地学习教程,但没有结果。
我没有任何具体的客户端(我的任务不包括一个),我使用邮递员和Telerik发送请求。
我的主要问题是:在我使用我的端点登录之后,我仍然不能对用户进行身份验证并访问非匿名端点。如果我尝试使用它们,我会得到一个重定向:
[15:35:24 INF] Authorization failed. These requirements were not met:
DenyAnonymousAuthorizationRequirement: Requires an authenticated user.
[15:35:24 INF] AuthenticationScheme: Identity.Application was challenged.
[15:35:24 INF] HTTP GET /api/User/getDemoAuthorized responded 302 in 48.2047 ms
[15:35:24 INF] Request finished HTTP/2 GET https://localhost:7166/api/User/getDemoAuthorized - - - 302 0 - 98.8841ms
[15:35:24 INF] Request starting HTTP/2 GET https://localhost:7166/Identity/Account/Login?ReturnUrl=%2Fapi%2FUser%2FgetDemoAuthorized - -
[15:35:24 DBG] No candidates found for the request path '/Identity/Account/Login'
[15:35:24 DBG] Request did not match any endpoints
[15:35:24 DBG] The request path does not match the path filter
[15:35:24 DBG] The request path /Identity/Account/Login does not match a supported file type
[15:35:24 DBG] AuthenticationScheme: Identity.Application was not authenticated.
[15:35:24 DBG] List of registered output formatters, in the following order: ["Microsoft.AspNetCore.Mvc.Formatters.HttpNoContentOutputFormatter", "Microsoft.AspNetCore.Mvc.Formatters.StringOutputFormatter", "Microsoft.AspNetCore.Mvc.Formatters.StreamOutputFormatter", "Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter"]
[15:35:24 DBG] No information found on request to perform content negotiation.
[15:35:24 DBG] Attempting to select the first output formatter in the output formatters list which supports a content type from the explicitly specified content types '["application/problem+json", "application/problem+xml", "application/problem+json", "application/problem+xml"]'.
[15:35:24 DBG] Selected output formatter 'Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter' and content type 'application/problem+json' to write the response.
[15:35:24 INF] Executing ObjectResult, writing value of type 'Microsoft.AspNetCore.Mvc.ProblemDetails'.
[15:35:24 INF] HTTP GET /Identity/Account/Login responded 404 in 36.7039 ms
注意:我不能使用Razor网页,只是一个简单的restful。
当使用匿名端点时,我可以看到我的用户没有经过身份验证:用户未验证图片。
当我尝试登录时的日志:
[15:53:37 DBG] Context 'AppDbContext' started tracking 'IdentityUser' entity. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see key values.
[15:53:37 DBG] A data reader was disposed.
[15:53:37 DBG] Closing connection to database 'caloryDb' on server '(localdb)\MSSQLLocalDB'.
[15:53:37 DBG] Closed connection to database 'caloryDb' on server '(localdb)\MSSQLLocalDB'.
[15:53:40 INF] AuthenticationScheme: caloryCookieAuth signed in.
[15:53:40 INF] Executing StatusCodeResult, setting HTTP status code 204
[15:53:40 INF] Executed action CaloryWebApi.Api.Controllers.UserController.LoginAsync (CaloryWebApi.Api) in 7301.0998ms
[15:53:40 INF] Executed endpoint 'CaloryWebApi.Api.Controllers.UserController.LoginAsync (CaloryWebApi.Api)'
[15:53:40 INF] HTTP POST /api/User/login responded 204 in 7407.4312 ms
用户控制器:
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class UserController : ControllerBase
{
private readonly IUserService _userService;
private readonly SignInManager<IdentityUser> _signInManager;
public UserController(IUserService userService, SignInManager<IdentityUser> signInManager)
{
_userService = userService;
_signInManager = signInManager;
}
[HttpGet("getDemoAnonymous")]
[AllowAnonymous]
public ActionResult GetDemo()
{
List<string> demo = new List<string>() { "Authorization demo - anonymous" , "test" };
return Ok(demo);
}
[HttpGet("getDemoAuthorized")]
public ActionResult GetDemoAuthentitaced()
{
List<string> demo = new List<string>() { "Authorization demo - auth ok" , "test" };
return Ok(demo);
}
[AllowAnonymous]
[HttpPost("login")]
public async Task<ActionResult> LoginAsync([FromBody] LoginModelType loginModelType)
{
var user = await _userService.GetUser(loginModelType.Email);
if (user != null) // We found a user mathing this email address
{
var signInResult = await _signInManager.CheckPasswordSignInAsync(user,loginModelType.Password,false);
if (signInResult.Succeeded)
{
var identity = new ClaimsIdentity(claims : new Claim[]
{
new Claim(ClaimTypes.Name, loginModelType.Email)
},
authenticationType : "caloryCookieAuth")
{
};
var claimsPrincipal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync("caloryCookieAuth", claimsPrincipal);
return NoContent();
}
else
{
return BadRequest("Failed to authenticate user. Try again!");
}
}
return BadRequest("Failed to authenticate user. Try again!");
}
[HttpPost("logout")]
public async Task<ActionResult> LogoutAsync()
{
await HttpContext.SignOutAsync();
return NoContent();
}
[HttpPost("register")]
[AllowAnonymous]
public async Task<ActionResult> RegisterAsync([FromBody] LoginModelType loginModelType)
{
var result = await _userService.RegisterUser(loginModelType.Email, loginModelType.Password);
if(result.Succeeded)
{
return NoContent();
}
else
{
List<string> errorList = new();
errorList.Add("Failed to register...");
foreach(var error in result.Errors)
{
errorList.Add($"Error code: {error.Code} - {error.Description}");
}
return BadRequest(new { Result = errorList });
}
}
}
我的Program.cs:
...
var builder = WebApplication.CreateBuilder(args);
...
builder.Services.AddDbContext<AppDbContext>(o =>
o.UseSqlServer(builder.Configuration["ConnectionStrings:DefaultConnection"]));
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = "caloryCookieAuth";
})
.AddCookie("caloryCookieAuth", options =>
{
options.Cookie.Name = "caloryCookieAuth";
options.Cookie.SameSite = SameSiteMode.None; //TODO is this important?
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromDays(1);
options.SlidingExpiration = true;
options.LoginPath = "/api/User/login";
options.LogoutPath = "/api/User/logout";
});
builder.Services.AddDefaultIdentity<IdentityUser>(options => {
// Configure user identity options here
options.User.RequireUniqueEmail = true;
})
.AddEntityFrameworkStores<AppDbContext>();
...
var app = builder.Build();
```...```
app.UseHttpsRedirection();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
编辑:我更新了问题,包括整个用户控制器
发布于 2022-06-17 05:18:40
如果您想在Cookie.SameSite = SameSiteMode.None
中使用AddCookie()
,也需要设置Secure = true
,如果不设置Secure
,Cookie.SameSite = SameSiteMode.None
将无法工作,并且项目将不会发送自定义cookie,请参阅以下代码:
.AddCookie("caloryCookieAuth", options =>
{
options.Cookie.Name = "caloryCookieAuth";
//add this to configure Secure
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.None; //TODO is this important?
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromDays(1);
options.SlidingExpiration = true;
options.LoginPath = "/api/User/login";
options.LogoutPath = "/api/User/logout";
});
发布于 2022-06-18 15:53:24
在检查了所有的建议后,我尝试了最后一件事。我用[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
标记为控制器指定了授权方案。现在看来起作用了。
https://stackoverflow.com/questions/72647199
复制相似问题