同一Web应用中ASP.NET Core 2 MVC与WebAPI身份认证整合问询
Got it, let's break down how to integrate your MVC Cookie authentication with WebAPI JWT auth so your Vue components in Razor views can call the API seamlessly without forcing users to log in twice. I've tackled this exact scenario before—here's the play-by-play:
First, when a user logs in via your MVC AccountController, you'll want to create a JWT token alongside setting the auth cookie. This way, the Vue frontend can grab the token and use it for API calls.
Here's how to modify your Login action:
[HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Login(LoginViewModel model) { // Your existing user validation logic here if (ModelState.IsValid && await _userManager.CheckPasswordAsync(user, model.Password)) { var claimsPrincipal = await _signInManager.CreateUserPrincipalAsync(user); // Set the MVC auth cookie (your existing code) await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal); // Generate JWT token var jwtToken = GenerateJwtToken(claimsPrincipal); // Option 1: Store token in a non-HttpOnly cookie (Vue can read this) Response.Cookies.Append("JwtToken", jwtToken, new CookieOptions { HttpOnly = false, // Allow JS access SameSite = SameSiteMode.Strict, Secure = Environment.IsProduction(), // Use HTTPS in prod Expires = DateTime.UtcNow.AddHours(1) }); // Option 2: Pass token to Razor view, then save to localStorage in Vue ViewData["JwtToken"] = jwtToken; return RedirectToAction("Index", "Home"); } // Handle invalid login ModelState.AddModelError("", "Invalid login attempt"); return View(model); } // Helper method to generate JWT private string GenerateJwtToken(ClaimsPrincipal principal) { var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:SecretKey"])); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: Configuration["Jwt:Issuer"], audience: Configuration["Jwt:Audience"], claims: principal.Claims, expires: DateTime.UtcNow.AddHours(1), signingCredentials: creds); return new JwtSecurityTokenHandler().WriteToken(token); }
Your WebAPI currently only accepts JWT, but we need it to recognize either JWT (for external clients) or MVC cookies (for Vue calls, if you go that route) — or just enforce JWT but let Vue pass the token we generated.
Update your Startup.cs to register both auth schemes:
public void ConfigureServices(IServiceCollection services) { // Existing MVC setup services.AddMvc(); // Configure authentication services.AddAuthentication(options => { // Default to cookie auth for MVC, but we'll specify JWT for API when needed options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddCookie(options => { options.LoginPath = "/Account/Login"; options.SlidingExpiration = true; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = Configuration["Jwt:Issuer"], ValidAudience = Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:SecretKey"])) }; }); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // Other middleware (error handling, static files, etc.) // Make sure authentication runs before MVC app.UseAuthentication(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); routes.MapRoute(name: "api", template: "api/{controller}/{action}/{id?}"); }); }
Then, on your WebAPI controllers/actions, you can allow both schemes like this:
[Authorize(AuthenticationSchemes = "Bearer,Cookies")] [Route("api/[controller]")] public class MyApiController : ControllerBase { // API actions here }
This lets the API accept either a Bearer <token> header or the MVC auth cookie.
Now, set up your Vue app to grab the JWT from either the cookie or localStorage, and attach it to every axios request.
If using the cookie approach:
Add this to your Vue main.js (or an axios config file):
import axios from 'axios'; // Helper to get cookie value function getCookie(name) { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(';').shift(); } // Attach JWT to all requests axios.interceptors.request.use(config => { const token = getCookie('JwtToken'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, error => Promise.reject(error)); // Make axios available globally in Vue Vue.prototype.$axios = axios;
If using localStorage:
In your Razor view, add this script before initializing Vue:
<script> // Save the token from ViewData to localStorage localStorage.setItem('JwtToken', '@ViewData["JwtToken"]'); </script>
Then update the axios interceptor:
axios.interceptors.request.use(config => { const token = localStorage.getItem('JwtToken'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, error => Promise.reject(error));
To avoid forcing users to log in again when the JWT expires, add a refresh token endpoint that uses the MVC cookie to issue a new JWT:
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)] [Route("api/auth/refresh-token")] public IActionResult RefreshToken() { // User is already authenticated via cookie, so we can get their claims var newToken = GenerateJwtToken(User); return Ok(new { Token = newToken }); }
Then update your axios response interceptor to handle 401 errors:
axios.interceptors.response.use(response => response, async error => { const originalRequest = error.config; // Only retry once for 401s if (error.response.status === 401 && !originalRequest._retry) { originalRequest._retry = true; try { const refreshResponse = await axios.post('/api/auth/refresh-token'); const newToken = refreshResponse.data.Token; // Update cookie or localStorage document.cookie = `JwtToken=${newToken}; expires=${new Date(Date.now() + 3600000).toUTCString()}; path=/; secure; samesite=strict`; // localStorage.setItem('JwtToken', newToken); // Retry the original request with the new token originalRequest.headers.Authorization = `Bearer ${newToken}`; return axios(originalRequest); } catch (refreshError) { // Refresh failed — send user to login window.location.href = '/Account/Login'; return Promise.reject(refreshError); } } return Promise.reject(error); });
- XSS Protection: If using non-HttpOnly cookies or localStorage, make sure your Vue app is protected against XSS attacks (sanitize user input, use Vue's built-in escaping).
- CSRF Protection: If letting the API accept cookies, enable CSRF protection for your API endpoints (ASP.NET Core has built-in middleware for this).
- HTTPS: Always use HTTPS in production to prevent token interception.
- Token Lifespan: Keep JWT expiration short (1-2 hours) and rely on the refresh endpoint instead of long-lived tokens.
内容的提问来源于stack exchange,提问作者bryken

