ASP.NET Core MVC 使用 JWT 令牌使用 API。 User.Identity.IsAuthenticated 返回 false

问题描述 投票:0回答:1

我使用 .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">
            &copy; 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。我做错了什么?

c# authentication jwt asp.net-core-mvc asp.net-core-webapi
1个回答
0
投票

所以 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";
})

User.Identity.IsAuthenticated 可以正确返回:

© www.soinside.com 2019 - 2024. All rights reserved.