You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在.NET Core Blazor应用中结合JWT实现Magic Link功能

.NET Core Blazor 对接现有JWT体系实现Magic Link登录方案

整套方案不需要改动你现有的JWT认证逻辑,只需要新增4个模块的代码即可落地:

1. 存储层设计

新增Magic Link令牌专用表,对应实体字段如下:

public class MagicLinkToken
{
    public long Id { get; set; }
    // 关联你现有用户表的主键
    public string UserId { get; set; } = string.Empty;
    // 登录令牌值
    public string TokenValue { get; set; } = string.Empty;
    // 过期时间,固定设为创建后10-15分钟
    public DateTime ExpiresAt { get; set; }
    // 是否已使用标记,防重放
    public bool IsUsed { get; set; }
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    // 可选:记录请求IP、UserAgent做安全审计
    public string? RequestIp { get; set; }
}

记得给TokenValue字段加唯一索引,提升查询效率。

2. 令牌生成与邮件发送接口

新增匿名访问的接口,接收用户输入的邮箱地址,逻辑如下:

  • 校验邮箱格式,查询邮箱是否对应用户,无论是否存在都返回统一提示:如果邮箱已注册,登录链接将发送至你的邮箱,避免邮箱枚举漏洞
  • 生成密码学安全的随机令牌,禁止使用Guid、可预测字符串生成:
    var tokenBytes = RandomNumberGenerator.GetBytes(32);
    var magicToken = Convert.ToBase64UrlString(tokenBytes);
    
  • 将令牌、关联UserId、过期时间写入数据库,初始IsUsed为false
  • 拼接登录回调地址:{你的Blazor应用域名}/auth/magic-login?token={magicToken}
  • 调用现有邮件服务将链接发送给用户,邮件中标明链接有效期、仅本人可使用的提示
  • 接口必须加限流规则:同IP1分钟最多请求5次,同邮箱10分钟最多接收3封邮件,防轰炸和暴力枚举

3. Blazor侧回调登录页

新增匿名可访问的Razor页面,路由设置为/auth/magic-login,核心逻辑:

@page "/auth/magic-login"
@inject NavigationManager NavManager
@inject ILocalStorageService LocalStorage
@attribute [AllowAnonymous]

<p>正在验证登录信息,请稍候...</p>

@code {
    [SupplyParameterFromQuery]
    public string? token { get; set; }

    protected override async Task OnInitializedAsync()
    {
        if (string.IsNullOrWhiteSpace(token))
        {
            NavManager.NavigateTo("/login", forceLoad: true);
            return;
        }

        // 调用后端验证接口
        var httpClient = new HttpClient();
        var response = await httpClient.PostAsJsonAsync("你的api地址/api/auth/magic-verify", new { Token = token });
        if (response.IsSuccessStatusCode)
        {
            var result = await response.Content.ReadFromJsonAsync<LoginResult>();
            // 完全复用你现有JWT存储逻辑,和账号密码登录的存储方式保持一致
            await LocalStorage.SetItemAsync("access_token", result!.AccessToken);
            NavManager.NavigateTo("/", forceLoad: true);
        }
        else
        {
            NavManager.NavigateTo("/login?error=link_expired", forceLoad: true);
        }
    }
}

不要在前端做任何令牌有效性校验,所有校验逻辑全部放服务端。

4. 后端令牌校验与JWT签发接口

新增匿名访问的校验接口,核心逻辑必须保证原子性,避免并发重放:

[HttpPost("magic-verify")]
public async Task<IActionResult> VerifyMagicToken([FromBody] MagicVerifyRequest request)
{
    // 原子操作:匹配有效令牌+直接标记为已使用,避免先查后改的并发漏洞
    var affectedRows = await _dbContext.MagicLinkTokens
        .Where(t => t.TokenValue == request.Token 
                    && !t.IsUsed 
                    && t.ExpiresAt > DateTime.UtcNow)
        .ExecuteUpdateAsync(setters => setters.SetProperty(p => p.IsUsed, true));

    if (affectedRows == 0)
    {
        return Unauthorized("登录链接已失效,请重新申请");
    }

    // 查询令牌关联的用户信息
    var validToken = await _dbContext.MagicLinkTokens.AsNoTracking()
        .FirstAsync(t => t.TokenValue == request.Token);
    
    // 完全复用你现有的JWT生成逻辑,和账号密码登录签发的JWT权限、有效期保持一致
    var jwtToken = _jwtService.GenerateToken(validToken.UserId);
    return Ok(new LoginResult { AccessToken = jwtToken });
}

接口同样需要加限流,单IP1分钟最多请求10次,防暴力破解令牌。

必做安全加固项

  • 令牌有效期严格控制在15分钟以内,建议10分钟,一次使用立刻失效
  • 绝对不要把用户信息直接编码在Magic Link的token里,所有token必须存数据库做状态校验
  • 不要在日志、前端控制台打印完整的token值,避免泄露
  • 可选:登录成功后给用户发送登录提醒邮件,告知登录时间、IP,方便用户感知异常登录
  • 生产环境回调地址必须用HTTPS,防止令牌在传输过程中被窃取

内容的提问来源于stack exchange,提问作者Nikhil Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 00:24:21