微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

ASP.NET Core - 将 Cookie 和 JWT 用于 WebAPI

如何解决ASP.NET Core - 将 Cookie 和 JWT 用于 WebAPI

我已经为我的 ASP.NET Core Web API 配置了 JWT 身份验证。它在使用 Postman 时有效。

我还构建了一个 MVC 管理部分,我想登录该部分。我遵循的创建管理部分的指南使用 cookie 而不是 JWT 身份验证用于登录页面

它不起作用,我在登录后收到 401 身份验证错误。它会将我重定向到正确的页面,您可以在浏览器中看到身份 cookie,但我没有通过身份验证。

Login Page

Admin Page

在这里的深度不够哈哈

我还可以使用 cookie 和 JWT 身份验证吗? JWT 适用于任何想要访问 WebAPI 但需要通过 WebAPI 的管理页面登录的 Cookie 和会话的手机应用程序?

我的中间件 Startup.cs

public void ConfigureServices(IServiceCollection services)
{
        // Tell Entity how to connect to the sql Server
        services.AddDbContext<ApplicationDbContext>(options => 
        {
            options.UsesqlServer(Configuration.GetConnectionString("DefaultConnection"));
        });

        // Configure Identity
        services.Configure<IdentityOptions>(options =>
        {
            options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(10);
            options.Lockout.MaxFailedAccessAttempts = 5;
            options.Lockout.AllowedForNewUsers = true;
            options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMnopQRSTUVWXYZ0123456789-._@+";
            options.SignIn.RequireConfirmedEmail = false;                   // Set to true for production,test it
            options.User.RequireUniqueEmail = false;                        // Set to true for production
        });

        services.Configure<PasswordHasherOptions>(options =>
        {
            // First byte of the hashed password is 0x00 = V2 and 0x01 = V3
            options.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV3;        // Default IdentityV2 is used,it uses SHA1 for hashing,1000 iterations.
            options.IterationCount = 12000;                                                // With IdentityV3 we can use SHA256 and 12000 iterations.
        });

        // We need to add the IdentityUser to Entity and create a token for authentication.
        services.AddIdentity<User,IdentityRole>(options =>
        {
            options.Password.requiredigit = true;
            options.Password.RequireLowercase = true;
            options.Password.RequireUppercase = true;
            options.Password.requiredLength = 6;

        }).AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();


        // JWT Authentication Tokens
        services.AddAuthentication(auth =>
       {
           // This will stop Identity using Cookies and make it use JWT tokens by default.
           auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
           auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
       }).AddJwtBearer(options =>
       {
           options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
           {
               ValidateIssuer = true,ValidateAudience = true,ValidAudience = "http://mywebsite.com",Validissuer = "http://mywebsite.com",ValidateLifetime = true,RequireExpirationTime = true,ValidateIssuerSigningKey = true,IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("rsvgy555262gthsdfrthga"))
           };
           options.RequireHttpsMetadata = true;                    // Use HTTPS to transmit the token.
       });

        // Admin Login Cookie
        services.ConfigureApplicationCookie(options =>
        {
            options.LoginPath = "/Admin/Login";                             // Url for users to login to the app
            options.Cookie.Name = ".AspNetCore.Identity.Application";
            options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
            options.SlidingExpiration = true;
        });

        services.AddControllers();
        services.AddControllersWithViews();

}

我的AdminController

public class AdminController : Controller
{
    private UserManager<User> userManager;                  // Manage user accounts in DB
    private IPasswordHasher<User> passwordHasher;           // Hash user passwords
    private SignInManager<User> signInManager;              // Login

    // Constructor
    public AdminController(UserManager<User> usrMgr,IPasswordHasher<User> passwordHash,SignInManager<User> signinMgr)
    {
        userManager = usrMgr;
        passwordHasher = passwordHash;
        signInManager = signinMgr;
    }

    // Admin Login Page
    [AllowAnonymous]
    public IActionResult Login(string returnUrl)
    {
        Login login = new Login();
        return View(login);
    }

    // Admin Login Module
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Login(Login login)
    {
        if (ModelState.IsValid)
        {
            User loginUser = await userManager.FindByEmailAsync(login.Email);

            if (loginUser != null)
            {
                // Sign out any user already signed in
                await signInManager.SignOutAsync();

                // Sign in the new user
                Microsoft.AspNetCore.Identity.SignInResult result = await signInManager.PasswordSignInAsync(loginUser,login.Password,false,false);
                if (result.Succeeded)
                {
                    return Redirect("/Admin"); // Send user to localhost/Admin after login
                }
            }

            ModelState.AddModelError(nameof(login.Email),"Login Failed: Invalid Email or password");
        }

        return View(login);
    }

    // Admin logout
    public async Task<IActionResult> logout()
    {
        await signInManager.SignOutAsync();
        return RedirectToAction("Index");
    }

    // Admin Index Page
    [Authorize]
    public IActionResult Index()
    {
        return View(userManager.Users);
    }
}

谢谢,任何帮助使 cookie 工作将不胜感激。

解决方法

我发现了两种方法来实现这一点:Token Biased 和 Cookie Biased(首选)。

顺便说一下,我使用的是 ASP.NET Core 5.0,这可能不适用于 3.1。

代币偏向解决方案

services.AddAuthentication(x => {
    // Set Jwt Bearer as default auth scheme.
    // Token found in Authorization header by default (Authorization: Bearer <JWT_TOKEN>)
    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x => {
    x.SaveToken = true;

    // Tap into the event lifecycle
    x.Events = new JwtBearerEvents {

        // Called when default authentication fails
        // This is where we validate add cookie authentication
        OnAuthenticationFailed = ctx => {
            string token = ctx.HttpContext.Request.Cookies["auth"];
            
            if (string.IsNullOrEmpty(token)) {
                // Tells ASP.NET that authentication failed
                ctx.Fail("Invalid token");

            } else {

                // Validate token
                if (JwtManager.ValidateToken(token,config)) {

                    // Set the principal
                    // Will throw error if not set
                    ctx.Principal = JwtManager.GetPrincipal(token);

                    // Tells ASP.NET that the authentication was successful
                    ctx.Success();

                    // Add the principal to the HttpContext for easy access in any the controllers (only routes that are authenticated)
                    ctx.HttpContext.Items.Add("claims",principal);
                } else {
                    ctx.Fail("Invalid Token");
                }
            }
            return Task.CompletedTask;
        }
    };

    ...
});

JwtManager 只是一个处理 JWT 操作的类。本质上,它包含用于生成、验证和解码 JWT 令牌的静态方法。每种方法都有不同的方法,具体取决于图书馆。

此事件仅在默认令牌验证失败时调用。

注意 1: 使用此解决方案,您仍然需要在标题中包含 Authoritization: Bearer <SOMETHING>

Cookie 偏向解决方案 本质上是一样的,但在 OnMessageRecieved 事件

services.AddAuthentication(x => {
    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x => {
    x.SaveToken = true;
    x.Events = new JwtBearerEvents {
        // Same as before,but called before the default JWT bearer authentication
        OnMessageReceived = ctx => {
            string token = ctx.HttpContext.Request.Cookies["auth"];
            
            if (!string.IsNullOrEmpty(token)) {
                if (JwtManager.ValidateToken(token,config)) {
                    var principal = JwtManager.GetClaims(token);
                    ctx.Principal = principal;
                    ctx.Success();
                    ctx.HttpContext.Items.Add("claims",principal);
                }
            }
            
            return Task.CompletedTask;
        },};
});

每个请求都会调用它,所以它可能会有一些开销,但它仍然有效。

注意 2:这些解决方案可能不是生产就绪的,甚至不是最好的方法。与一粒盐一起使用。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。