0

我使用Google雲端點(版本1,使用android studio)爲我的android應用程序構建了一個移動後端。我希望通過電子郵件/密碼對我的用戶進行身份驗證,因此我正在使用Firebase身份驗證來執行此操作。 Firebase身份驗證SDK允許我在客戶端(在android中)獲取每個用戶的令牌,並且firebase admin sdk允許我檢查後端令牌的有效性。我明白,在雲端點上,我可以提供自己的自定義身份驗證器(請參閱:Google Cloud Endpoints and user's authentication),並且我打算在自定義身份驗證器中調用Firebase管理員sdk來驗證用戶提供的令牌。Cloud Endpoints + Firebase Admin SDK:通過FirebaseUser令牌對用戶進行身份驗證

我的問題是,因爲我使用谷歌雲端點來構建我的後端,所以我不知道在驗證任何標記之前插入代碼以執行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()方法,但隨後將端點部署到應用程序引擎失敗,因爲顯然,創建android客戶端庫要求必須使用SystemServiceServlet(並且必須將其命名爲「SystemServiceServlet 「)。

我可以在雲端點提供的每個api方法(例如,在我的api的插入方法中)中對Firebase管理應用程序進行初始化,但這看起來效率極低。我將如何在使用谷歌雲端點構建的後端中使用Firebase管理員sdk?

非常感謝您的時間

+0

如果你想要做的就是生成自定義標記,使用這個庫生成自定義的令牌 - > https://github.com/jwtk/jjwt。使用起來很簡單,而且您不用使用此庫進行初始化時遇到問題。 –

回答

5

因爲我無法找到一個合適的地方來初始化雲端點內我寫我​​自己的服務器端Java代碼來驗證令牌火力地堡按照https://firebase.google.com/docs/auth/admin/verify-id-tokens#verify_id_tokens_using_a_third-party_jwt_library

的火力地堡管理員密碼

這裏是一個輔助類,你可以用它來驗證用戶的火力地堡令牌,並得到他們的火力地堡用戶的UID(此代碼使用jose.4.j庫從https://bitbucket.org/b_c/jose4j/wiki/Home做JWT操作):

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/[email protected]"; 

    /** 
    * 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/[email protected] 
    * @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; 
    } 
} 
+0

這對我的情況非常合適,試圖從Firebase同步驗證令牌,而不是像Firebase Admin APK部隊那樣異步驗證。感謝這個美好的例子! – DoctorD

0

@ Dan7620,提出了另一種替代方法,但它並沒有解決問題。這是一個簡單的解決方案,它使用Firebase Admin SDK,在Cloud Endpoints模塊中正確配置並初始化。我將在這裏總結一下步驟:

  1. 將您serviceAccountKey.json在您的應用程序WEB-INF文件夾中。在appengineweb.xml
  2. 插入這樣的:

    <resource-files> 
        <include path="/**.json" /> 
    </resource-files> 
    
  3. 定義一個類像下面的一個地方。有一個單獨的init()方法強制:

    public class FirebaseService { 
    
    public static void init() { 
        try { 
         FileInputStream serviceAccount = new FileInputStream(new File("WEB-INF/serviceAccountKey.json")); 
         FirebaseOptions options = new FirebaseOptions.Builder() 
          .setCredential(FirebaseCredentials.fromCertificate(serviceAccount)) 
          .setDatabaseUrl("https://[YOUR_APP_NAME].firebaseio.com/") 
          .build(); 
         FirebaseApp.initializeApp(options); 
         System.out.print("In Firebase Init module...!!"); 
        } catch(FileNotFoundException ignored) {} 
    } 
    
  4. 在任何靜態{}在任何你所定義的端點的代碼調用此方法。例如:

    static { 
        ObjectifyService.register(FCMTokenMap.class); 
        FirebaseService.init(); 
    } 
    
  5. 您可以調用其中涉及數據庫,FCM等從任何地方的其他火力地堡方法..!
相關問題