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

Angular SPA与C# API:如何保障API安全性?

防止SPA用户获取JWT后直接调用API的可行方案

首先得明确一个核心事实:只要JWT存储在前端(不管是localStorage、sessionStorage还是普通Cookie),技术上用户都能拿到并直接调用你的API——因为SPA运行在浏览器环境,前端代码和存储内容对用户完全透明。我们能做的不是彻底阻止这种行为,而是通过多种手段降低滥用风险、抬高滥用成本,同时限制滥用的影响范围。

下面是针对Angular + .NET技术栈的具体落地方案:

1. 缩短Access Token有效期,配合安全存储的Refresh Token

  • 把Access Token的有效期设得极短(比如15-30分钟),即使用户拿到令牌,能滥用的时间窗口也非常有限。
  • 用HttpOnly + Secure + SameSite的Cookie存储Refresh Token,前端无法读取这个Cookie,只能由浏览器自动在请求时携带给后端。后端验证Refresh Token时,还可以绑定用户的IP、设备指纹等信息,进一步降低被盗用的概率。
  • .NET中配置Refresh Token Cookie的示例:
    services.ConfigureApplicationCookie(options =>
    {
        options.HttpOnly = true;
        options.SecurePolicy = CookieSecurePolicy.Always;
        options.SameSite = SameSiteMode.Strict;
        options.ExpireTimeSpan = TimeSpan.FromDays(7);
        options.Cookie.Name = "YourApp.RefreshToken";
    });
    

2. 验证请求来源(Origin/Referer)

  • 在.NET后端添加自定义中间件,检查请求的Origin或Referer请求头,只允许你的SPA域名发起的请求。
  • 注意:这种方法无法完全阻止技术熟练的用户(请求头可以伪造),但能挡住大部分非技术用户的直接调用,过滤掉批量的恶意请求。
  • 示例中间件代码:
    app.Use(async (context, next) =>
    {
        var allowedOrigins = new[] { "https://your-spa-domain.com" };
        var origin = context.Request.Headers["Origin"].FirstOrDefault();
        
        if (!string.IsNullOrEmpty(origin) && !allowedOrigins.Contains(origin))
        {
            context.Response.StatusCode = StatusCodes.Status403Forbidden;
            await context.Response.WriteAsync("Invalid request origin");
            return;
        }
        
        await next();
    });
    

3. 给JWT绑定会话标识,做关联验证

  • 在生成JWT时,添加一个自定义的SessionId Claim,同时把这个SessionId存储在后端的用户会话记录中(比如Redis)。
  • 后端每个API接口在验证JWT时,额外检查Token中的SessionId是否与后端存储的、当前用户的有效SessionId匹配。如果用户拿到JWT但没有对应的有效Session记录,请求会被拒绝。
  • 当用户主动登出或会话过期时,后端直接删除对应的SessionId记录,让已流出的JWT失效。

4. 严格的权限与Claims验证

  • 确保JWT中包含足够细粒度的权限Claims,后端每个API接口都严格校验用户的权限范围。就算用户拿到JWT,也只能调用自己权限内的接口,无法越权操作敏感数据。
  • .NET中可以用[Authorize(Roles = "Editor")]或者自定义Policy来实现:
    services.AddAuthorization(options =>
    {
        options.AddPolicy("CanEditPosts", policy =>
            policy.RequireClaim("Permissions", "Post.Edit"));
    });
    
    接口上直接标记:[Authorize(Policy = "CanEditPosts")]

5. 内存中存储Access Token

  • Angular端不要把Access Token存在localStorage或sessionStorage,而是只保存在内存变量中(比如服务的私有属性)。页面刷新后,通过Refresh Token重新获取新的Access Token。
  • 这样用户无法通过浏览器开发者工具直接拿到Access Token,只能通过抓包获取,但抓包得到的Token有效期很短,滥用价值极低。

内容的提问来源于stack exchange,提问作者Ben Halicki

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 16:22:37