我使用 .NET 7 创建了一个 API。该 API 使用 JWT 令牌,并有一个负责用户注册和登录的控制器。这在 Swagger 中运行得很好。
这是我的 API
AuthController
:
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using WildlifeLogAPI.Models.DTO;
using WildlifeLogAPI.Repositories;
namespace WildlifeLogAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private readonly UserManager<IdentityUser> userManager;
private readonly ITokenRepository tokenRepository;
public AuthController(UserManager<IdentityUser> userManager, ITokenRepository tokenRepository)
{
this.userManager = userManager;
this.tokenRepository = tokenRepository;
}
//POST: api/Auth/Register
[HttpPost]
[Route("Register")]
public async Task<IActionResult> Register([FromBody] RegisterRequestDto registerRequestDto)
{
// create a new instance of IdentityUser (which includes the username and email they submit)
var identityUser = new IdentityUser
{
UserName = registerRequestDto.Username,
Email = registerRequestDto.Email
};
// then use the userManager's built-in CreateAsync (pass in the identityUser we just created, and the password passed into the dto)
var identityResult = await userManager.CreateAsync(identityUser, registerRequestDto.Password);
// Add roles to the user
// check to see if the user was successfully created
if (identityResult.Succeeded)
{
// Add the User Role by default when they register
var roleIdentityResult = await userManager.AddToRoleAsync(identityUser, "User");
// check if the userrole was successfully added, if so display message
if (roleIdentityResult.Succeeded)
{
return Ok("User was registered. Please login.");
}
}
// if it didn't succeed
return BadRequest("something went wrong. Please try again. ");
}
//POST: api/auth/login
[HttpPost]
[Route("Login")]
public async Task<IActionResult> Login([FromBody] LoginRequestDto loginRequestDto)
{
// get the user by their email and store the user in the user variable
var user = await userManager.FindByEmailAsync(loginRequestDto.Email);
// check if the email is associated with a user
// if it is filled in and we find it in the database then do the code required to log in
if (user != null)
{
// check that password matches the email using built in CheckPasswordAsync
// pass in the email and password
var checkPasswordResult = await userManager.CheckPasswordAsync(user, loginRequestDto.Password);
// if the password matches the email, then create a token
if (checkPasswordResult)
{
// Create token here
// Get roles for this user
var roles = await userManager.GetRolesAsync(user);
// if there are roles then create the token
if (roles != null)
{
// create jwt token
var jwtToken = tokenRepository.CreateJWTToken(user, roles.ToList()); // this is our token
// put that token into a dto
var response = new LoginResponseDto
{
jwtToken = jwtToken
};
return Ok(response);
}
}
}
return BadRequest("Username or password is incorrect");
}
}
}
然后我创建了一个 ASP.NET Core MVC 应用程序,其中包含一个名为
AuthsController
的控制器,该控制器使用该 API 并处理 JWT 令牌。
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Text.Json;
using WildlifeLog.UI.Models.DTO;
using WildlifeLog.UI.Models.ViewModels;
using WildlifeLogAPI.Models.DTO;
namespace WildlifeLog.UI.Controllers
{
public class AuthsController : Controller
{
private readonly IHttpClientFactory httpClientFactory;
private readonly IHttpContextAccessor httpContextAccessor;
private readonly SignInManager<IdentityUser> signInManager;
private readonly ILogger<AuthsController> logger;
public AuthsController(IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor,
SignInManager<IdentityUser> signInManager, ILogger<AuthsController> logger)
{
this.httpClientFactory = httpClientFactory;
this.httpContextAccessor = httpContextAccessor;
this.signInManager = signInManager;
this.logger = logger;
}
[HttpGet]
public IActionResult Register()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Register(RegisterViewModel registerViewModel)
{
// Created client
var client = httpClientFactory.CreateClient();
// create httpRequestMessage
var httpRequestMessage = new HttpRequestMessage()
{
Method = HttpMethod.Post,
RequestUri = new Uri("https://localhost:7075/api/auth/register"),
Content = new StringContent(JsonSerializer.Serialize(registerViewModel), Encoding.UTF8, "application/json")
};
// use client to send httpRequestMessage to api and we get a json response abck
var httpResponseMessage = await client.SendAsync(httpRequestMessage);
// Ensure success
httpResponseMessage.EnsureSuccessStatusCode();
// "Read the body" as a string DONT convert form JSON to Dto
var response = await httpResponseMessage.Content.ReadAsStringAsync();
// If successful, redirect to ?
if (response != null)
{
return RedirectToAction("Index", "Home");
}
// else just return to view
return View();
}
[HttpGet]
public IActionResult Login()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Login(LoginViewModel loginViewModel)
{
try
{
// create the client
var client = httpClientFactory.CreateClient();
// create httpRequestMessage
var httpRequestMessage = new HttpRequestMessage()
{
Method = HttpMethod.Post,
RequestUri = new Uri("https://localhost:7075/api/auth/login"),
Content = new StringContent(JsonSerializer.Serialize(loginViewModel), Encoding.UTF8, "application/json")
};
// use client to send httpRequestMessage to api and we get a json response back
var httpResponseMessage = await client.SendAsync(httpRequestMessage);
// Ensure success
httpResponseMessage.EnsureSuccessStatusCode();
// "Read the body" as a string
var response = await httpResponseMessage.Content.ReadAsStringAsync();
// Deserialize the JSON response to a DTO (assuming LoginResponseDto is your DTO class)
var loginResponseDto = JsonSerializer.Deserialize<Models.DTO.LoginResponseDto>(response);
// Extract the JWT token from the response
var jwtToken = loginResponseDto.jwtToken;
// Store the token for subsequent requests (consider more secure storage options)
HttpContext.Session.SetString("JwtToken", jwtToken);
// Include the token in the Authorization header for subsequent requests
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken);
// Specify the authentication type when creating ClaimsIdentity
var userIdentity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Email, loginViewModel.Email),
new Claim(ClaimTypes.AuthenticationMethod, "MyCookieMiddlewareInstance"),
}, "MyCookieMiddlewareInstance");
// Use ClaimsPrincipal with the specified ClaimsIdentity
var user = new ClaimsPrincipal(userIdentity);
// Use SignInAsync to sign in the user
await HttpContext.SignInAsync("MyCookieMiddlewareInstance", user, new AuthenticationProperties
{
ExpiresUtc = DateTime.UtcNow.AddMinutes(20),
IsPersistent = false, // You can change this based on your requirements
AllowRefresh = false
});
// Log successful login
logger.LogInformation("User successfully logged in.");
return RedirectToAction("Index", "Home");
}
catch (HttpRequestException)
{
// Handle request exception (e.g., log, display error message)
logger.LogError("Login failed due to HttpRequestException.");
return View();
}
catch (Exception ex)
{
// Handle other exceptions
logger.LogError(ex, "An unexpected error occurred during login.");
return View();
}
}
}
}
这是我的 ASP.NET Core MVC 应用程序的
Program.cs
:
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using WildlifeLogAPI.Data;
using WildlifeLogAPI;
using WildlifeLog.UI.Repositories;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Inject HttpClient
builder.Services.AddHttpClient();
// inject logging
builder.Services.AddLogging(builder =>
{
builder.AddConsole(); // Add other logging providers if needed
});
builder.Services.AddControllersWithViews();
// Inject Session Configuration
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
// Configure session options as needed
options.IdleTimeout = TimeSpan.FromMinutes(30);
options.Cookie.Name = "SessionCookie";
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
// Inject dbContext
builder.Services.AddDbContext<WildlifeLogDbContext>();
builder.Services.AddDbContext<WildlifeLogAuthDbContext>();
// inject identity
builder.Services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<WildlifeLogAuthDbContext>()
.AddDefaultTokenProviders();
// inject cloudinary
builder.Services.AddScoped<IImageRepository, CloudinaryImageRepository>();
// Configure authentication
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = "MyCookieMiddlewareInstance";
options.DefaultSignInScheme = "MyCookieMiddlewareInstance";
options.DefaultChallengeScheme = "MyCookieMiddlewareInstance";
})
.AddCookie("MyCookieMiddlewareInstance", options =>
{
options.ExpireTimeSpan = TimeSpan.FromMinutes(30); // Set your desired expiration time
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
options.SlidingExpiration = true;
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseSession();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
当我在用户界面上登录时,我可以告诉我已登录,因为与未登录时相比,我可以访问受限内容。因此登录有效,但是在我的 _layout 视图中,我想隐藏“登录”和“注册”按钮当我登录并显示用户的用户名时。即使我知道我已经登录,这种情况也不会发生。
这是我的
_layout.cshtml
:
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> signInManager
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - WildlifeLog.UI</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/WildlifeLog.UI.styles.css" asp-append-version="true" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">WildlifeLog.UI</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Parks" asp-action="Index">Parks</a>
</li>
<li>
<a class="nav-link text-dark" asp-area="" asp-controller="Logs" asp-action="Index">Your Logs</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="AdminUsers" asp-action="Index">Users</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
</ul>
<div class="d-flex align-items-center">
@if (User.Identity.IsAuthenticated)
{
<div class="me-3 text-light">
@User?.Identity?.Name
</div>
}
else
{
<p>IsAuthenticated: @User.Identity.IsAuthenticated</p>
<p> User is not signed in </p>
<a class="btn me-3 bg-light text-dark"
asp-area=""
asp-controller="Auths"
asp-action="Register">Register</a>
<a class="btn me-3 bg-light text-dark"
asp-area=""
asp-controller="Auths"
asp-action="Login">Login</a>
}
</div>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
© 2023 - WildlifeLog.UI - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>
因此,即使我已登录并且可以访问只能由登录用户访问的控制器,
User.Identity.IsAuthenticated
也会返回 false。我做错了什么?
所以 User.Identity.IsAuthenticated 返回 false,即使我 已登录并且可以访问只能由以下人员访问的控制器 已登录的用户。
身份验证方案是一组负责验证用户身份的规则和处理程序。在您提供的代码程序中,您使用了 cookie 身份验证以及配置的身份,但这自定义了 MyCookieMiddlewareInstance 身份验证方案。 在 ASP.NET Core 中,身份验证方案通常被认为是默认方案。如果未将自定义方案设置为默认身份验证方案,则 ASP.NET Core 使用 Identity 方案而不是自定义方案,这可能会导致 User.Identity.IsAuthenticated 返回错误。因此,即使用户登录并授权,也只是使用默认的身份认证方案,而不是自定义的认证方案。如果您想确保使用自定义 cookie 身份验证方案而不是默认的 Identity 方案,您可以将自定义方案设置为默认身份验证方案:
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "MyCookieMiddlewareInstance";
options.DefaultScheme = "MyCookieMiddlewareInstance";
options.DefaultSignInScheme = "MyCookieMiddlewareInstance";
options.DefaultChallengeScheme = "MyCookieMiddlewareInstance";
})