首页 文章

如何限制Google App Engine endpoints API只能访问我的Android应用程序?

提问于
浏览
22

我是一名Android开发人员,为我的应用构建了我的第一个Google App Engine(java)后端 . 除了我的应用程序之外,我不希望任何其他人访问此API . (我计划在我的Android应用程序中使用App引擎验证InApp购买) . 我的数据与用户无关,因此我不希望用户即使使用他们的Google帐户(在网络或Android设备上)登录也能访问我的API .

我遵循-"Specifying authorized clients in the API backend"(https://developers.google.com/appengine/docs/java/endpoints/auth)中提到的步骤,比如生成客户端ID并将它们添加到@Api(clientIds和受众)中,除了"Add a User parameter" - 因为我不需要用户身份验证 .

然后我部署了App引擎,我仍然可以通过API资源管理器访问API(https://your_app_id.appspot.com/_ah/api/explorer)(我还没有添加API_EXPLORER客户端ID)

我在添加客户端ID之前使用使用 endpoints 库构建的APK进行了测试,并且仍然可以访问API .

  • 是否必须向所有 endpoints API添加“用户参数”?实现我的目的(限制API到我的Android应用程序) .

  • 我可以从Android客户端传递null作为userAccount名称并忽略服务器上的用户参数值(因为它将为null)?这是否确保API只能从我的Android应用程序访问(因为客户端ID是为我的包名和APK的SHA1生成的?)

  • 我是否应该为此目的使用类似服务帐户的东西?

文档说,对于Android,必须添加Android和Web客户端ID,并且受众必须与Web客户端ID相同 . 这是否可以访问任何其他Web客户端?我可以跳过提及Web客户端ID并仍然达到我的目的吗?

感谢您的时间和帮助 .

......随着我的进一步调查而更新......

我做了以下事情:

  • 在后端的API中添加了User参数 - 但没有检查空值 . 无需传递任何凭据即可访问API(来自Android调试APK和API资源管理器)

  • 然后,我试过了

mCredential = GoogleAccountCredential.usingAudience(this,“server:client_id:”WEB_CLIENT_ID); mCredential.setSelectedAccountName(NULL);

并将此凭据传递给API构建器(如其他一些帖子中所建议的)导致致命异常 . 所以,我们不能传递null帐户名 .

  • 我可以在没有OAuth的情况下使用API资源管理器调用API . 但是当我启用OAuth时,它提示错误,说不允许此客户端ID! (我还没有在client_ids {}中添加com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID)

  • 然后我添加了代码,如果用户为null,则在后端抛出OAuthRequestException . 这导致API资源管理器在没有OAuth的情况下收到错误 . 将API_EXPLORER_CLIENT_ID添加到client_ids后,它可以启用OAuth

  • 添加了从我的Android应用程序传递有效用户帐户名(电子邮件)的代码 . 然后,我只能使用我的发布APK访问API . 即使是调试APK也会有异常! - 这是我的预期 . 所以,我认为没有其他Android应用程序能够访问此API .

因此,不检查后端API上的null用户是个坏主意(如其他帖子所示) . 它没有提到任何client_ids而没有User param .

我现在唯一的问题是:如果有人可以从APK中找出WEB_CLIENT_ID,他们是否可以使用它来构建一个Web客户端来访问我的API(我没有在代码中的任何地方提到过客户端秘密 . 所以我认为这是不可能的) .


我确实搜索了Google群组和Stackoverflow,但目前尚不清楚 .

5 回答

  • 3

    我有类似的问题,不是在Android和App Engine之间,而是在单独的服务器和App Engine之间 . 我处理它的方法是将签名哈希字段作为参数添加到每个API调用 . 如果请求有不正确的签名,则会被拒绝 .

    例如,假设您的API endpoints 为 example.com/api/do_thing?param1=foo . 我将散列整个url以及一个密钥,然后将哈希的结果附加到请求: example.com/api/do_thing?param1=foo&hash=[some long hex value] .

    然后,在服务器端,我首先从url请求中删除哈希,然后对剩余的所有内容运行哈希 . 最后,检查计算的散列是否与随请求一起发送的散列匹配,如果不匹配,则可以拒绝该请求 .

    这个很但重要的是,你的密钥仍然保密 . 您必须在Android上小心这一点,因为有人可能会尝试反编译您的APK .

  • 8

    面对同样的问题比你!在没有Google用户帐户的情况下验证Android endpoints 是不可能的!

    所以这是我解决这个问题的方法,没有任何用户交互(也许不是正确的但是有效,并且你有强大的身份验证(SHA1 Google帐户)):

    HERE IS MY ANDROID CODE

    Get and Build Valid Credential

    //Get all accounts from my Android Phone
             String validGoogleAccount = null;
             Pattern emailPattern = Patterns.EMAIL_ADDRESS; // API level 8+
             Account[] accounts = AccountManager.get(context).getAccounts();
             for (Account account : accounts) {
                 if (emailPattern.matcher(account.name).matches()) {
                     //Just store mail if countain gmail.com
                     if (account.name.toString().contains("gmail.com")&&account.type.toString().contains("com.google")){
                         validGoogleAccount=account.name.toString();
                     }
    
                 }
             }
    
            //Build Credential with valid google account
            GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(this,"server:client_id:301991144702-5qkqclsogd0b4fnkhrja7hppshrvp4kh.apps.googleusercontent.com");
            credential.setSelectedAccountName(validGoogleAccount);
    

    Use this credential for secure calls

    Campagneendpoint.Builder endpointBuilder = new Campagneendpoint.Builder(AndroidHttp.newCompatibleTransport(), new JacksonFactory(), credential);
    

    HERE IS MY API BACKEND CODE: API Annotation

    @Api(
            scopes=CONSTANTES.EMAIL_SCOPE,
            clientIds = {CONSTANTES.ANDROID_CLIENT_ID, 
                         CONSTANTES.WEB_CLIENT_ID,
                         com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID},
            audiences = {CONSTANTES.ANDROID_AUDIENCE},      
            name = "campagneendpoint",
            version = "v1"
         )
    

    Method code:

    public Collection<Campagne> getCampagnes(@Named("NumPortable")String NumPortable, User user) throws  UnauthorizedException {
            if (user == null) throw new UnauthorizedException("User is Not Valid");
    
          return CampagneCRUD.getInstance().findCampagne(NumPortable);
         }
    

    目前,它只适用于Android(我不知道我们将如何做IOS ..)..

    希望它会对你有所帮助!

  • 0

    Google为 AndroidwebiOS 提供了执行此操作的方法 . 这些步骤涉及:

    • 为要允许向API发出请求的应用指定客户端ID

    • 将User参数添加到要受授权保护的所有公开方法 .

    • 再次为任何Android客户端生成客户端库

    • 重新部署后端API .

    • 将重新生成的jar文件更新为Android客户端的Android项目 .

    这些步骤在Google的Using Auth with Endpoints以及此blog上详细列出 .

  • 3

    面对同样的问题,这是我研究的结果:

    • 在Google控制台中添加了带有SHA1指纹的Android cliend id

    • 在API注释中使用它

    但是:

    • 如果我不向方法添加用户参数:检查Android应用程序客户端ID不起作用

    • 如果我添加USER参数但不要求用户选择其谷歌帐户来创建凭证...也不起作用...

    结论:似乎必须连接用户帐户以检查要执行的应用程序客户端ID ...我真的不明白为什么因为2个进程之间不存在链接

  • 1

    Access this site

    选择您的项目,转到凭证部分

    创建一个新的api密钥

    创建一个新的Android密钥

    单击“编辑允许的Android应用程序”并输入您的SHA1密钥;你的android包名

    如果这解决了问题,请告诉我 .

相关问题