微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Firebase - 验证 api 请求在 .net 5

如何解决Firebase - 验证 api 请求在 .net 5

我正在使用 Flutter 应用程序中的 firebase auth,它从前端获取 jwt,如下所示:

accesstoken = await user!.getIdToken(true);

现在我希望我的 .net 5 AspNetCore 后端应用程序验证 id 令牌。

我将 firebase admin SDK 添加到我的 .net 应用程序中,如下所示:

startup.cs中:

FirebaseApp.Create();

Firebase docs 显示如何验证 id 令牌:

Firebasetoken decodedToken = await FirebaseAuth.DefaultInstance
    .VerifyIdTokenAsync(idToken);
string uid = decodedToken.Uid;

但是这段代码去哪儿了?我不想为 AspNetCore 应用程序中的每个控制器端点手动执行此操作。这可以作为中间件在每个 api 请求上自动发生吗?

编辑:Eeek 我什至不认为 Firebase Admin 是我想要的。我只想验证使用 Firebase Auth for Flutter 在客户端上创建的 jwt id 令牌,以最可能的 firebasey 方式。它不适用于管理员,它适用于普通用户。我以为 Firebase 管理员就是这样。我应该采取什么方向?我在网上找到了各种各样关于不同策略的文章,所以我很困惑哪个是正确的。这足够了吗? https://dominique-k.medium.com/using-firebase-jwt-tokens-in-asp-net-core-net-5-834c43d4aa00 这看起来不是很火,而且似乎过于简化了?我认为下面 Dharmaraj 的答案是最火爆的方式,但我不确定 idToken 从哪里获取值(如何从请求标头中获取?否则它就是 null)。

解决方法

Firebase Admin SDK 名称中的“管理员”一词意味着您在受信任的环境中使用此 SDK,例如您的开发机器或您控制的服务器 - 例如您的 ASP.NET 代码。

>

Admin SDK 可以验证来自任何类型用户的令牌。实际上,它不知道用户的类型,它只是验证令牌是否有效。

,

您在中间件中添加验证请求的函数。我不确定 .NET 中的情况如何,但我查找了一些中间件片段并找到了这个:

public class Startup
{
    public Startup()
    {
    } 

    public void Configure(IApplicationBuilder app,IHostingEnvironment env)
    {
         app.Run(MyMiddleware);
    }

    private Task MyMiddleware(HttpContext context) 
    {
        // Add the code here and verify the IDToken
        //FirebaseToken decodedToken = await FirebaseAuth.DefaultInstance
    .VerifyIdTokenAsync(idToken); 
        //string uid = decodedToken.Uid;
        return context.Response.WriteAsync("Hello World! ");
    }
}

现在中间件将检查所有指定端点的令牌。您可以轻松读取其中的 idToken(从标题或正文),并在出现任何问题时返回错误。

如果令牌有效,您可以将用户的 UID 和您需要的任何其他信息添加到上下文对象中。

,

我基本上遵循 this SO answer 用于非 Firebase 内容和 this tutorial 用于 Firebase 内容,一切正常,我可以访问用户、访问令牌、声明等所有内容,通过extendedContext.userResolverService,并且在我所有使用 [Authorize] 的 Web 控制器端点中验证了 id 令牌。

由于我在所有控制器中都有 id 令牌,因此我也可以从任何控制器手动调用它,但这不是必需的。

FirebaseToken decodedToken = await FirebaseAuth.DefaultInstance
    .VerifyIdTokenAsync(idToken);
string uid = decodedToken.Uid;

userResolverService.cs:

using System.Security.Claims;
using Microsoft.AspNetCore.Http;

public class UserResolverService
{
    public readonly IHttpContextAccessor _context;

    public UserResolverService(IHttpContextAccessor context)
    {
        _context = context;
    }

    public string GetGivenName()
    {
        return _context.HttpContext.User.FindFirst(ClaimTypes.GivenName).Value;
    }

    public string GetSurname()
    {
        return _context.HttpContext.User.FindFirst(ClaimTypes.Surname).Value;
    }

    public string GetNameIdentifier()
    {
        return _context.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
    }

    public string GetEmails()
    {
        return _context.HttpContext.User.FindFirst("emails").Value;
    }
}

扩展 DataContext(通过组合):

namespace Vepo.DataContext {
    public class ExtendedVepoContext
    {
        public VepoContext _context;
        public UserResolverService _userResolverService;

        public ExtendedVepoContext(VepoContext context,UserResolverService userService)
        {
            _context = context;
            _userResolverService = userService;
            _context._currentUserExternalId = _userResolverService.GetNameIdentifier();
        }
    }
}

startup.cs:

public void ConfigureServices(IServiceCollection services)
        {
            ....

        services.AddHttpContextAccessor();

        services.AddTransient<UserResolverService>();

        services.AddTransient<ExtendedVepoContext>();

            FirebaseApp.Create(new AppOptions()
            {
                Credential = GoogleCredential.FromFile("firebase_admin_sdk.json"),});

            services
                .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    options.Authority = "https://securetoken.google.com/my-firebase-app-id";
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuer = true,ValidIssuer = "https://securetoken.google.com/my-firebase-app-id",ValidateAudience = true,ValidAudience = "my-firebase-app-id",ValidateLifetime = true
                    };
                });

还有startup.cs:

public void Configure(IApplicationBuilder app,IWebHostEnvironment env,VepoContext context,ISearchIndexService searchIndexService)
{ ....

    app.UseAuthentication();
    app.UseAuthorization();

然后像这样将 auth 添加到控制器端点中:

[HttpPost]
[Authorize]
public async Task<ActionResult<GroceryItemGroceryStore>> PostGroceryItemGroceryStore(GroceryItemGroceryStore groceryItemGroceryStore)
{...

您可以更进一步,在每次保存时与用户一起做一些事情,例如添加元数据:

要保存并添加元数据的实体:

public interface IDomainEntity<TId>
{
    TId Id { get; set; }    
    DateTime SysStartTime { get; set; }
    DateTime SysEndTime { get; set; }
    string CreatedById { get; set; }
    User CreatedBy { get; set; }
    string UpdatedById { get; set; }
    User UpdatedBy { get; set; }
}

我的数据上下文:

public class VepoContext : DbContext
{
    public VepoContext(DbContextOptions<VepoContext> options)
        : base(options)
    {
    }

        public DbSet<User> User { get; set; }

        public string _currentUserExternalId;
        
        public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            var user = await User.SingleAsync(x => x.Id == _currentUserExternalId);

            AddCreatedByOrUpdatedBy(user);

            return (await base.SaveChangesAsync(true,cancellationToken));
        }

        public override int SaveChanges()
        {
            var user = User.Single(x => x.Id == _currentUserExternalId);

            AddCreatedByOrUpdatedBy(user);

            return base.SaveChanges();
        }

        public void AddCreatedByOrUpdatedBy(User user)
        {
            foreach (var changedEntity in ChangeTracker.Entries())
            {
                if (changedEntity.Entity is IDomainEntity<int> entity)
                {
                    switch (changedEntity.State)
                    {
                        case EntityState.Added:
                            entity.CreatedBy = user;
                            entity.UpdatedBy = user;
                            break;
                        case EntityState.Modified:
                            Entry(entity).Reference(x => x.CreatedBy).IsModified = false;
                            entity.UpdatedBy = user;
                            break;
                    }
                }
            }
        }

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?