首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >云端点+ Firebase Admin :通过FirebaseUser令牌对用户进行身份验证

云端点+ Firebase Admin :通过FirebaseUser令牌对用户进行身份验证
EN

Stack Overflow用户
提问于 2016-11-26 23:32:30
回答 2查看 609关注 0票数 0

我使用google云端点为我的android应用程序构建了一个移动后端(version 1,使用android )。我希望通过电子邮件/密码对我的用户进行身份验证,所以我使用Firebase身份验证。Firebase身份验证sdk允许我在客户端(在android中)获取每个用户的令牌,而firebase admin sdk允许我检查后端令牌的有效性。我了解到,在云端点中,我可以提供自己的自定义身份验证器(请参阅:Google Cloud Endpoints and user's authentication),并计划在自定义验证器中调用firebase,以验证用户提供的令牌。

我的问题是,由于我使用google云端点构建我的后端,我不知道在哪里插入代码来初始化所需的firebase管理对象,然后才能验证任何令牌。在常规的应用程序引擎环境中,您可以在HTTPServlet的init()方法中进行初始化(请参阅https://github.com/GoogleCloudPlatform/firebase-appengine-backend/blob/master/src/main/java/com/google/cloud/solutions/flexenv/backend/MessageProcessorServlet.java ),但是云端点通过自动提供"SystemServiceServlet“作为HTTPServlet来隐藏这一点。我尝试过对SystemServiceServlet进行子类化并重写init()方法,但随后将端点部署到app引擎失败了,因为很明显,创建SystemServiceServlet客户端库需要使用一个SystemServiceServlet(并且必须将其命名为"SystemServiceServlet")。

我可以在云端点提供的每个api方法(例如,在我的api的插入方法中)中对firebase管理应用程序进行初始化,但这似乎是非常低效率的。我将如何在使用google云端点构建的后端中使用Firebase admin sdk?

非常感谢你抽出时间

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-11-28 20:18:57

由于找不到合适的位置来初始化云端点内的Firebase管理代码,所以我编写了自己的服务器端Java代码,以按照library验证Firebase令牌

下面是一个帮助类,您可以使用它来验证用户的Firebase令牌并获取他们的Firebase用户uid (此代码使用c/jose4j/wiki/Home中的jose.4.j库来执行JWT操作):

代码语言:javascript
复制
public class TokenManager {
    private final static String PROJECT_ID = "your_firebase_project_id";
    private final static String AUDIENCE = PROJECT_ID;
    private final static String ISSUER = "https://securetoken.google.com/" + PROJECT_ID;
    private final static String KEYS_URL = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com";

    /**
     * Parses and verifies a FirebaseUser ID token (JWT) and returns the associated user's uid
     *
     * @param token the firebase user's token
     * @return the firebase user UID
     * @throws UnauthorizedException if the token is invalid.
     */
    public static String verfiyToken(String token) throws UnauthorizedException{
        JwtConsumer firstPassJwtConsumer = new JwtConsumerBuilder()
                .setSkipAllValidators()
                .setDisableRequireSignature()
                .setSkipSignatureVerification()
                .build();

        //The first JwtConsumer is basically just used to parse the JWT into a JwtContext object.
        JwtContext jwtContext;
        try {
            jwtContext = firstPassJwtConsumer.process(token);
        } catch (InvalidJwtException e) {
            throw new UnauthorizedException(e.getMessage());
        }

        // get the key id from the header of the JWT
        List<JsonWebStructure> list = jwtContext.getJoseObjects();
        String kid = list.get(0).getKeyIdHeaderValue();
        String keyAsString;
        try {
            keyAsString = getPublicKey(kid);
        } catch (IOException e) {
            throw new UnauthorizedException(e.getMessage());
        }

        // decode the key into proper format
        InputStream certIs = new ByteArrayInputStream(Charset.forName("UTF-8").encode(keyAsString).array());

        CertificateFactory certificateFactory;
        try {
            certificateFactory = CertificateFactory.getInstance("X.509");
        } catch (CertificateException e) {
            throw new UnauthorizedException(e.getMessage());
        }

        X509Certificate cert;
        try {
            cert = (X509Certificate) certificateFactory.generateCertificate(certIs);
        } catch (CertificateException e) {
            throw new UnauthorizedException(e.getMessage());
        }
        PublicKey key = cert.getPublicKey();

        // now that we have the public key, we can verify the JWT
        JwtConsumer jwtConsumer = new JwtConsumerBuilder()
                .setRequireExpirationTime() // the JWT must have an expiration time
                .setMaxFutureValidityInMinutes(300) // but the  expiration time can't be too crazy
                .setAllowedClockSkewInSeconds(30) // allow some leeway in validating time based claims to account for clock skew
                .setRequireSubject() // the JWT must have a subject claim
                .setExpectedIssuer(ISSUER) // whom the JWT needs to have been issued by
                .setExpectedAudience(AUDIENCE) // to whom the JWT is intended for
                .setVerificationKey(key) // verify the signature with the public key
                .build(); // create the JwtConsumer instance

        JwtClaims jwtClaims;
        try {
            //  Validate the JWT and process it to the Claims
            jwtClaims = jwtConsumer.processToClaims(token);
        } catch (InvalidJwtException e)  {
            throw new UnauthorizedException(e.getMessage());
        }

        String userUid;

        try {
            userUid = jwtClaims.getSubject();
        } catch(MalformedClaimException e) {
            throw new UnauthorizedException(e.getMessage());
        }
        return userUid;
    }


    /**
     * Grab the certificate corresponding to the keyid specified in the JWT
     *
     * @param kid key id corresponding to one of the public keys listed at public keys listed at
     *            https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com
     * @return the certificate
     * @throws IOException if the process fails
     */
    private static String getPublicKey(String kid) throws IOException {
        URL url = new URL(KEYS_URL);
        HttpURLConnection request = (HttpURLConnection) url.openConnection();
        request.connect();

        JsonParser jp = new JsonParser(); //from gson
        JsonElement root = jp.parse(new InputStreamReader((InputStream) request.getContent()));
        JsonObject rootobj = root.getAsJsonObject();
        String publicKey = rootobj.get(kid).getAsString();

        return publicKey;
    }
}
票数 5
EN

Stack Overflow用户

发布于 2017-03-23 03:23:08

@Dan7620已经提出了另一种替代方法,但并没有解决问题。下面是一个简单的解决方案,它使用Firebase Admin,并在Cloud模块中正确配置和初始化。我将在此总结以下步骤:

  1. 将您的serviceAccountKey.json放在应用程序的WEB文件夹中。
  2. 将其插入您的appengineweb.xml中:
  3. 定义一个类似于下面的类。强制使用独立的init()方法: 公共类FirebaseService { public静态void (){ FileInputStream serviceAccount =新FileInputStream(新文件(“WEB/serviceAccountKey.json”));FirebaseOptions选项=新FirebaseOptions.Builder() FirebaseOptions.Builder(“NAME].firebaseio.com/") .build();FirebaseApp.initializeApp(选项);System.out.print("In Firebase Init模块.!!“);}catch(忽略FileNotFoundException) {}
  4. 在您定义的任何端点中的任何静态{}代码中调用此方法。例如: 静态{ ObjectifyService.register(FCMTokenMap.class);FirebaseService.init();}
代码语言:javascript
复制
1. You can invoke other Firebase methods which involve Database, FCM, etc from anywhere..!

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40824248

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档