SPA - Firebase 和 .Net WebApi 2 身份验证

Posted

技术标签:

【中文标题】SPA - Firebase 和 .Net WebApi 2 身份验证【英文标题】:SPA - Firebase and .Net WebApi 2 authentication 【发布时间】:2014-09-04 21:21:58 【问题描述】:

我有一个用 AngularJs 编写的单页应用程序(此时框架无关紧要)该应用程序托管在 IIS 中,它由 index.html 和一堆客户端资产组成。

在后端,我有 WebApi 2,也作为单独的应用程序托管在 IIS 中。

对于客户端身份验证,我使用 Firebase(简单登录)并启用了多个社交网络,例如 Facebook、Twitter 或 Google。

到目前为止一切顺利。我喜欢启用 twitter 身份验证是多么容易,例如使用 firebase。

在使用社交网络登录时,我从 firebase、firebaseAuthToken 和提供者访问令牌返回。

现在我想使用 firebaseAuthToken 或提供者访问令牌来通过我的 WebApi 进行身份验证。

问题是:在给定条件下使用 WebApi 进行身份验证的最佳方式是什么?

由于我在服务器上设置了复杂的业务逻辑,因此无法仅使用 firebase 来存储我的数据并摆脱 web api。

到目前为止,我的一个愚蠢想法是将社交提供者访问令牌传递给服务器,针对提供者验证令牌,然后使用 Owin -Katana 发出安全令牌。

由于缺乏文档、复杂性以及与单页应用程序的不良集成,我没有使用来自 katana 的内置社交提供程序支持。我发现 SPA 的 Visual Studio 模板也特定于 mvc。但那是我:)

【问题讨论】:

【参考方案1】:

tl;博士-Demo Project on GitHub

以下步骤可能看起来很长,但实际上非常简单。我只用了一个小时左右就创建了我的演示项目。


我同意你关于使用 Owin 和 Katana 的观点。我以前经历过这个过程,这并不是一个很好的经历。使用 Firebase 要容易得多。

这一切都可以通过 JWT 完成!

当您通过 Firebase 和任何社交服务提供商进行身份验证时,您会得到一个 JSON Web 令牌 (JWT) - firebaseAuthToken

从仪表板获取您的 Firebase 机密

JWT 的工作方式是我们有一个秘密令牌和一个客户端令牌。客户端令牌是我们登录后收到的 firebaseAuthToken。秘密令牌是在 Firebase Dashboard 中为我们生成的。

将您的 Firebase Secret 存储在 Web.config 的 appSettings 部分中

我们需要将此密钥存储在 Web.config 中,以便以后访问。

<add key="FirebaseSecret" value="<Firebase-Secret-Token-Goes-Here" />

创建一个操作过滤器以检查来自授权标头的 JWT

我们可以通过在 Authorization 标头中传递客户端令牌来验证请求是否有效。在服务器上,我们可以存储从 Firebase 仪表板获得的密钥。当 Web API 检查请求时,我们可以使用 JWT 库 (available from NuGet) 解码 JWT。如果解码成功,那么我们可以检查令牌以确保它没有过期。

public class DecodeJWT: ActionFilterAttribute 


    public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) 
    
        string firebaseAuthToken = string.Empty;
        if (actionContext.Request.Headers.Authorization != null) 
            firebaseAuthToken = actionContext.Request.Headers.Authorization.Scheme;
         else 
            throw new HttpException((int) HttpStatusCode.Unauthorized, "Unauthorized");
        

        string secretKey = WebConfigurationManager.AppSettings["FirebaseSecret"];
        try 
            string jsonPayload = JWT.JsonWebToken.Decode(firebaseAuthToken, secretKey);
            DecodedToken decodedToken = JsonConvert.DeserializeObject < DecodedToken > (jsonPayload);
            // TODO: Check expiry of decoded token
         catch (JWT.SignatureVerificationException jwtEx) 
            throw new HttpException((int) HttpStatusCode.Unauthorized, "Unauthorized");
         catch (Exception ex) 
            throw new HttpException((int) HttpStatusCode.Unauthorized, "Unauthorized");
        

        base.OnActionExecuting(actionContext);
    


创建一个 $httpInterceptor 将 firebaseAuthToken 添加到每个请求的标头中

在客户端,诀窍是每次都必须传递令牌。为了使这更容易,我们需要使用 Angular 创建一个 $httpInterceptor 来检查 sessionStorage 上的 firebaseAuthToken

.factory('authInterceptor', function ($rootScope, $q, $window) 
    return 
        request: function (config) 
            config.headers = config.headers || ;
            if ($window.sessionStorage.firebaseAuthToken) 
                config.headers.Authorization = $window.sessionStorage.firebaseAuthToken;
            
            return config;
        ,
        response: function (response) 
            if (response.status === 401) 
                // TODO: User is not authed
            
            return response || $q.when(response);
        
    ;
)

在成功登录时将 firebaseAuthToken 设置为 sessionStorage

每当用户登录时,我们都可以将值设置为sessionStorage

$rootScope.$on('$firebaseSimpleLogin:login',
    function (e, user) 

        // add a cookie for the auth token
        if (user) 
            $window.sessionStorage.firebaseAuthToken = user.firebaseAuthToken;
        

        cb(e, user);
    );

全局注册 DecodeJWT 过滤器

WebApiConfig.cs Register 方法中,我们可以设置 DecodeJWT 过滤器以应用于我们所有的 ApiController。

config.Filters.Add(new DecodeJWT());

现在,每当我们向 ApiController 发出请求时,它都会拒绝它,除非有有效的 JWT。因此,在用户登录后,如果 ApiController 不存在,我们可以将其数据保存到。

// globally uses DecodeJWT
public class UsersController: ApiController 

    // POST api/users
    public void Post([FromBody] FbUser user) // See GitHub for this Model
    
        // Save user if we do not already have it
    

【讨论】:

用户的身份是如何绑定到会话的? HttpContext.Current.User? 嗨! .. 抱歉,如何设置 [AllowAnonymous] API? ..所以我的意思是...让一些api对未登录的用户开放? .. 非常感谢! 不幸的是,数据库机密已过时 firebase 版本 3 不再支持这些键

以上是关于SPA - Firebase 和 .Net WebApi 2 身份验证的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 React 和 Firebase 托管在 SPA 上提供 robots.txt?

带有反应打字稿的 SPA Cors .net 核心

用于没有 CORS 的 SPA 的 ASP.NET WebApi

Firebase 托管:如何防止对 SPA 的 index.html 进行缓存

如何使用 Firebase 托管为 React SPA 提供 404 页面?

在单个 ASP.NET Core 3.1 Web API 中管理两个 SPA;一个在根目录中,另一个在“管理员”路由中