简体   繁体   中英

Firebase Service Account Credentials Json Read permission denied

I downloaded my service account credential json file from Firebase console placed it earlier in the main directory of GAE endpoint project when I run my backed locally it gives Security exception.

java.security.AccessControlException: access denied ("java.io.FilePermission" "\src\main\secret.json" "read")

I tried placing the .json file under the src directory also but no help.

您应该将json文件放在src/main/resources

I found a couple ways to approach this. First is by getting it from a file over an internet stream. The other is locally.

INTERNET WAY

My first method involved storing the file on my public dropbox folder. I got the shareable link (make sure it ends in .json ) and pasted it in the string example "https://dl.dropboxusercontent.com/..EXAMPLE-CREDENTIALS"

/** A simple endpoint method that takes a name and says Hi back */
    @ApiMethod(name = "sayHi")
    public MyBean sayHi(@Named("name") String name) {

        MyBean mModelClassObject = null;

        String text = "";

        try {
            String line = "";
            StringBuilder builder = new StringBuilder();
            URL url = new URL("https://dl.dropboxusercontent.com/..EXAMPLE-CREDENTIALS");
            BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));


            while ((line = reader.readLine()) != null) {
                // ...
                builder.append(line);
            }
            reader.close();

            text = builder.toString();
        } catch (MalformedURLException e) {
            // ...
        } catch (IOException e) {
            // ...
        }

        InputStream stream = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));

        FirebaseOptions options = null;
        options = new FirebaseOptions.Builder()
                .setServiceAccount(stream)
                .setDatabaseUrl("https://[PROJECT-ID].firebaseio.com/")
                .build();
        FirebaseApp.initializeApp(options);

        DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
        final TaskCompletionSource<MyBean> tcs = new TaskCompletionSource<>();


        Task<MyBean> tcsTask = tcs.getTask();

        ref.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                MyBean result = dataSnapshot.getValue(MyBean.class);
                if(result != null){
                    tcs.setResult(result);
                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError){
                //handle error
            }
        });

        try {
            mModelClassObject = Tasks.await(tcsTask);
        }catch(ExecutionException e){
            //handle exception
        }catch (InterruptedException e){
            //handle exception
        }

        return mModelClassObject;
    }

LOCAL WAY

The other way is taking the version above and skipping something like dropbox

/** A simple endpoint method that takes a name and says Hi back */
        @ApiMethod(name = "sayHi")
    public MyBean sayHi(@Named("name") String name) {

        MyBean mModelClassObject = null;

        String text = "JUST PASTE YOUR JSON CONTENTS HERE";

        InputStream stream = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));

        FirebaseOptions options = null;
        options = new FirebaseOptions.Builder()
                .setServiceAccount(stream)
                .setDatabaseUrl("https://[PROJECT-ID].firebaseio.com/")
                .build();
        FirebaseApp.initializeApp(options);

        DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
        final TaskCompletionSource<MyBean> tcs = new TaskCompletionSource<>();


        Task<MyBean> tcsTask = tcs.getTask();

        ref.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                MyBean result = dataSnapshot.getValue(MyBean.class);
                if(result != null){
                    tcs.setResult(result);
                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError){
                //handle error
            }
        });

        try {
            mModelClassObject = Tasks.await(tcsTask);
        }catch(ExecutionException e){
            //handle exception
        }catch (InterruptedException e){
            //handle exception
        }

        return mModelClassObject;
    }

I don't know if this follows best practice but my project is working now. I also included firebase's code for getting info. check out this answer to a question i asked recently on reading and writing to firebase.

EDIT

cleaned up version which doesnt throw errors

public class MyEndpoint {

    private FirebaseOptions options;
    private DatabaseReference ref;
    private String serviceAccountJSON = "i took mine out for security reasons";

    // create firebase instance if need be
    private void connectToFirebase(){
        if (options == null) {
            options = null;
            options = new FirebaseOptions.Builder()
                    .setServiceAccount(new ByteArrayInputStream(serviceAccountJSON.getBytes(StandardCharsets.UTF_8)))
                    .setDatabaseUrl("https://[PROJECT-ID].firebaseio.com/")
                    .build();
            FirebaseApp.initializeApp(options);
        }
        if(ref == null) {
            ref = FirebaseDatabase.getInstance().getReference();
        }
    }

    /** A simple endpoint method that takes a name and says Hi back */
    @ApiMethod(name = "sayHi")
    public MyBean sayHi(@Named("name") String name) {

        // always do this first
        connectToFirebase();

        MyBean mModelClassObject = null;

        final TaskCompletionSource<MyBean> tcs = new TaskCompletionSource<>();
        Task<MyBean> tcsTask = tcs.getTask();

        // get the info
        ref.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                MyBean result = dataSnapshot.getValue(MyBean.class);
                if(result != null){
                    tcs.setResult(result);
                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError){
                //handle error
            }
        });

        // wait for it
        try {
            mModelClassObject = Tasks.await(tcsTask);
        }catch(ExecutionException e){
            //handle exception
        }catch (InterruptedException e){
            //handle exception
        }

        mModelClassObject.setData(mModelClassObject.getData() + name);

        return mModelClassObject;
    }
}

Finally, I found the solution, Its written under the APIs and references section of Google App Engine in this link , that we need to add such files in the appengine-web.xml file under the <resource-files> tag, using <include path=""/> property. After doing so its works for me. I placed the .json file containing project credentials in the WEB-INF directory and then entered its relative path in <resource-files> tag.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM