基于共享数据库的ASP.NET MVC(.NET Framework 4.7.2)与.NET 6 Web API跨服务身份认证方案咨询
Got it, let's tackle your scenario step by step. You've got a legacy .NET Framework 4.7.2 MVC app using Identity/Cookie auth, a new .NET 6 Web API with JWT, shared database, and need MVC to forward requests to the API securely without paid IdentityServer. Here are three practical, production-ready approaches:
1. MVC Generates JWT Tokens (Best for Standard API Workflows)
Since you already have JWT set up on the API, the cleanest approach is to have your MVC app generate valid JWT tokens using its existing Identity components, then attach the token to forwarded requests. This keeps the API stateless and aligns with standard API authentication patterns.
Steps to Implement:
- Sync JWT Configs: Make sure both MVC and API use the same JWT settings (secret key, issuer, audience). Store these in config files (not hardcoded!).
- MVC (Web.config):
<appSettings> <add key="Jwt:Secret" value="your-strong-shared-secret-32chars-min" /> <add key="Jwt:Issuer" value="https://your-mvc-app.com" /> <add key="Jwt:Audience" value="https://your-api.com" /> <add key="Jwt:ExpireMinutes" value="60" /> </appSettings>
- MVC (Web.config):
- Add JWT Generation to MVC: Use your existing
UserManagerto pull user claims and generate a token.using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using Microsoft.IdentityModel.Tokens; using Microsoft.AspNet.Identity; public class JwtTokenGenerator { private readonly UserManager<ApplicationUser> _userManager; private readonly NameValueCollection _config; public JwtTokenGenerator(UserManager<ApplicationUser> userManager, NameValueCollection config) { _userManager = userManager; _config = config; } public string GenerateToken(ApplicationUser user) { var claims = new List<Claim> { new Claim(JwtRegisteredClaimNames.Sub, user.Id), new Claim(JwtRegisteredClaimNames.Email, user.Email), new Claim(ClaimTypes.Name, user.UserName) }; // Add user roles var roles = _userManager.GetRoles(user); claims.AddRange(roles.Select(r => new Claim(ClaimTypes.Role, r))); var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Secret"])); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: _config["Jwt:Issuer"], audience: _config["Jwt:Audience"], claims: claims, expires: DateTime.UtcNow.AddMinutes(Convert.ToDouble(_config["Jwt:ExpireMinutes"])), signingCredentials: creds); return new JwtSecurityTokenHandler().WriteToken(token); } } - Attach Token to Forwarded Requests: When your MVC app calls the API, add the JWT to the
Authorizationheader.public async Task<IActionResult> ForwardRequestToApi() { var currentUser = await _userManager.GetUserAsync(User); var jwtToken = _jwtGenerator.GenerateToken(currentUser); using var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", jwtToken); var apiResponse = await httpClient.GetAsync("https://your-api.com/api/orders"); // Handle response and return to MVC view return View(); }
Pros:
- Uses standard JWT flow, compatible with future clients (mobile, SPAs)
- Stateless API, no session dependency
- Claims are embedded in the token, so API doesn't need to hit the database for basic user info
2. API Key Authentication (Simple Service-to-Service Trust)
If you want a simpler setup for strictly internal service-to-service calls, use API keys. Since both apps are under your control, you can authenticate the MVC app itself with a secret key, then pass the user's ID/claims separately if needed.
Steps to Implement:
- Add API Key Auth to .NET 6 API:
// Program.cs builder.Services.AddAuthentication("ApiKey") .AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>("ApiKey", options => { options.ApiKey = builder.Configuration["ApiKeys:MvcAppKey"]; }); // Custom handler public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions> { public ApiKeyAuthenticationHandler(IOptionsMonitor<ApiKeyAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } protected override Task<AuthenticateResult> HandleAuthenticateAsync() { if (!Request.Headers.TryGetValue("X-API-Key", out var apiKeyHeader)) return Task.FromResult(AuthenticateResult.Fail("API key missing")); if (!string.Equals(apiKeyHeader, Options.ApiKey, StringComparison.OrdinalIgnoreCase)) return Task.FromResult(AuthenticateResult.Fail("Invalid API key")); // Optional: Add user claims if MVC passes user ID in headers var userId = Request.Headers["X-User-Id"].FirstOrDefault(); var claims = new List<Claim>(); if (!string.IsNullOrEmpty(userId)) claims.Add(new Claim(ClaimTypes.NameIdentifier, userId)); var identity = new ClaimsIdentity(claims, Scheme.Name); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, Scheme.Name); return Task.FromResult(AuthenticateResult.Success(ticket)); } } // Don't forget to enable auth app.UseAuthentication(); app.UseAuthorization(); - MVC Sends API Key + User Info:
public async Task<IActionResult> CallApiWithApiKey() { using var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Add("X-API-Key", ConfigurationManager.AppSettings["ApiKeys:MvcAppKey"]); httpClient.DefaultRequestHeaders.Add("X-User-Id", User.Identity.GetUserId()); var response = await httpClient.PostAsync("https://your-api.com/api/orders", new StringContent("{}", Encoding.UTF8, "application/json")); // Handle response return View(); }
Pros:
- Extremely simple setup
- Works well for internal service calls
- No need to manage JWT tokens
Cons:
- Doesn't embed user claims by default (you'll need to fetch user data from the database in the API)
- Less flexible for external clients
3. Shared Cookie Authentication (Leverage Existing Identity Cookie)
Since both apps share the same database, you can configure them to use the same encrypted authentication cookie. This lets the API validate the MVC's cookie directly, avoiding token generation entirely.
Steps to Implement:
- Sync Data Protection Between Apps: The cookie is encrypted using ASP.NET Data Protection, so both apps need access to the same key ring.
- MVC (.NET Framework):
// Global.asax or Startup.cs using Microsoft.AspNetCore.DataProtection; using System.IO; var dataProtectionProvider = DataProtectionProvider.Create( new DirectoryInfo(@"\\your-shared-server\data-protection-keys"), config => config.SetApplicationName("YourSharedAppName")); var ticketProtector = dataProtectionProvider.CreateProtector( "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", "Identity.Application", "v2"); app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, CookieName = ".AspNetCore.Identity.Application", TicketDataFormat = new TicketDataFormat(ticketProtector), // Keep your existing cookie settings }); - .NET 6 API:
// Program.cs builder.Services.AddDataProtection() .PersistKeysToFileSystem(new DirectoryInfo(@"\\your-shared-server\data-protection-keys")) .SetApplicationName("YourSharedAppName"); builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.Cookie.Name = ".AspNetCore.Identity.Application"; options.Cookie.Domain = "your-domain.com"; // Set if apps share a domain options.Events = new CookieAuthenticationEvents { OnRedirectToLogin = ctx => { // API should return 401 instead of redirecting ctx.Response.StatusCode = 401; return Task.CompletedTask; } }; }); app.UseAuthentication(); app.UseAuthorization();
- MVC (.NET Framework):
- Forward Cookie in MVC Requests: When calling the API, ensure the MVC app includes its authentication cookie in the request.
public async Task<IActionResult> CallApiWithSharedCookie() { using var httpClient = new HttpClient(new HttpClientHandler { UseCookies = true, CookieContainer = new CookieContainer() }); // Copy the MVC's auth cookie to the HttpClient's cookie container var authCookie = Request.Cookies[".AspNetCore.Identity.Application"]; if (authCookie != null) { httpClient.CookieContainer.Add(new Uri("https://your-api.com"), new Cookie(".AspNetCore.Identity.Application", authCookie)); } var response = await httpClient.GetAsync("https://your-api.com/api/profile"); // Handle response return View(); }
Pros:
- No extra token generation code; uses existing Identity cookie
- Seamless user context between apps
Cons:
- API becomes stateful (depends on cookie sessions)
- Requires shared data protection key storage (must be secure)
- More complex to configure across domains
Key Security Notes
- Always use HTTPS for all communications to prevent token/cookie interception
- Store secrets (JWT keys, API keys) in secure configs (e.g., Azure Key Vault, environment variables) instead of plaintext files
- For JWT, use a strong secret key (32+ characters) and short expiration times to limit exposure if tokens are leaked
- For shared cookies, restrict cookie domains and use
HttpOnly/Secureflags
内容的提问来源于stack exchange,提问作者user1784297

