基于WsFederation实现MVC与WebAPI控制器的Cookie+Bearer Token双重认证
完全可以实现你的需求,核心思路是在WsFederation完成用户认证后,额外生成JWT格式的Bearer Token,并将其传递给前端SPA。下面是具体的分步实现:
1. 配置JWT认证参数
首先在appsettings.json里添加JWT相关配置:
"JwtSettings": { "SecretKey": "your-strong-secret-key-here-make-it-long-and-random", "Issuer": "your-app-domain", "Audience": "your-api-audience", "ExpiresInMinutes": 60 }
然后在Program.cs(或.NET Core 3.1及更早版本的Startup.cs)里注册并配置认证服务:
// 读取JWT配置 var jwtSettings = builder.Configuration.GetSection("JwtSettings").Get<JwtSettings>(); builder.Services.AddSingleton(jwtSettings); // 配置认证:同时启用WsFederation和JWT builder.Services.AddAuthentication() .AddWsFederation(options => { // 保留你原有的WsFederation配置 options.MetadataAddress = "your-idp-metadata-url"; options.Wtrealm = "your-app-realm"; // 关键:添加认证成功后的事件处理 options.Events = new WsFederationEvents { OnSecurityTokenValidated = async context => { // 基于已验证的用户身份生成JWT Token var jwtHandler = new JwtSecurityTokenHandler(); var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey)); var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var tokenDescriptor = new SecurityTokenDescriptor { Subject = context.Principal.Identity as ClaimsIdentity, Expires = DateTime.UtcNow.AddMinutes(jwtSettings.ExpiresInMinutes), Issuer = jwtSettings.Issuer, Audience = jwtSettings.Audience, SigningCredentials = credentials }; var token = jwtHandler.CreateToken(tokenDescriptor); var tokenString = jwtHandler.WriteToken(token); // 将Token存入用户Claims,方便后续MVC控制器获取 context.Principal.Identities.First().AddClaim(new Claim("BearerToken", tokenString)); } }; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = jwtSettings.Issuer, ValidAudience = jwtSettings.Audience, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey)) }; });
需要创建对应配置类绑定JWT参数:
public class JwtSettings { public string SecretKey { get; set; } public string Issuer { get; set; } public string Audience { get; set; } public int ExpiresInMinutes { get; set; } }
2. 在MVC控制器中传递Token到SPA
当用户点击主页的应用链接时,对应的MVC控制器可以从当前用户Claims中取出Token,再传递给SPA视图:
public class SpaLaunchController : Controller { public IActionResult App1() { // 从用户Claims中获取Bearer Token var bearerToken = User.FindFirst("BearerToken")?.Value; // 通过ViewBag将Token传递给视图 ViewBag.BearerToken = bearerToken; // 返回SPA的入口视图 return View(); } }
在对应的视图(比如App1.cshtml)中,把Token注入到SPA初始化逻辑里:
<script> // 将Token暴露给SPA应用 window.appConfig = { bearerToken: '@Html.Raw(ViewBag.BearerToken)' }; // 加载你的SPA入口脚本(React/Vue等) // ... </script>
3. WebAPI端验证Bearer Token
确保WebAPI控制器启用JWT认证:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [ApiController] [Route("api/[controller]")] public class DataController : ControllerBase { [HttpGet] public IActionResult GetUserInfo() { // 获取当前用户的ClaimsIdentity var identity = User.Identity as ClaimsIdentity; return Ok(new { UserName = identity.Name, Claims = identity.Claims.Select(c => new { c.Type, c.Value }) }); } }
关键注意事项
- Token安全:务必使用HTTPS传输Token,避免明文泄露;同时设置合理的过期时间,必要时实现Token刷新机制。
- Cookie大小限制:如果用户Claims较多,加上Token可能超出Cookie上限,这种情况可以考虑将Token存储在Redis等服务器端,传递一个引用ID给前端,再通过ID换取Token(会增加复杂度)。
- 事件时机:
OnSecurityTokenValidated是生成Token的最佳时机,此时用户身份已完成验证,确保Token基于合法身份生成。
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

