在Apollo订书机服务中为远程模式添加授权指令

时间:2019-06-09 02:52:41

标签: graphql microservices graphql-js apollo-server graphql-java

我需要在apollo订书机服务器中添加一个授权层,但是找不到任何解决方案。

架构:-有一个中央apollo Graphql网关服务器(缝合服务器),该服务器与其他微服务进行通信并将其架构缝合在一起。我希望将授权和身份验证层从每个微服务(以不同的语言编写)移动到网关服务器,这样任何未经授权的请求都将无法通过网关服务器,并且我们不必以每种语言编写授权层。

因此,为了使授权有效,我们决定使用@auth(role: ADMIN)之类的指令,但是问题在于它不适用于缝合的模式。

微服务A(java)

schema.graqhql

directive @auth(requires: String) on FIELD | FIELD_DEFINITION | OBJECT | QUERY

type Campaign {
  name: String
  status: Status
} 

// I have added a dummy auth directive resolver in java microservice such that it doesn't fail schema validation at server startup.
type Query {
  campaigns : [Camapign] @auth(role: ADMIN) 
}

Apollo网关服务器

AuthDirective.js

// @flow

import {SchemaDirectiveVisitor} from "apollo-server";

class AuthDirective extends SchemaDirectiveVisitor {
    visitObject(object) {
        console.log("in visitObject");
        this.ensureFieldsWrapped(object);
    }

    visitFieldDefinition(field) {
        const {resolve} = field;

        console.log("in visitFieldDefinition");

        field.resolve = async function (...args) {
            console.log("in auth resolver");
            const context = args[2];
            const requiredRole = "ADMIN";

            const user = await getUser(context.headers.authToken);
            if (!user.hasRole(requiredRole)) {
                throw new Error("not authorized");
            }

            return resolve.apply(this, args);
        };
    }

    ensureFieldsWrapped(objectType) {

        const fields = objectType.getFields();

        Object.keys(fields).forEach(fieldName => {
            const field = fields[fieldName];
            const {resolve} = field;
            field.resolve = async function (...args) {

                console.log("in auth resolver");

                const context = args[2];
                const requiredRole = "ADMIN";

                const user = await getUser(context.headers.authToken);
                if (!user.hasRole(requiredRole)) {
                    throw new Error("not authorized");
                }

                return resolve.apply(this, args);
            };
        });
    }
}

export default AuthDirective;
// language=GraphQL

// will this directive override the directive in Microservice A ?
const RootQuery= `
    directive @auth(
        requires: String
    ) on FIELD | FIELD_DEFINITION | OBJECT | QUERY
`; 

async function getMergedSchema() {
    const remoteSchema = await makeMergedRemoteSchema(); // introspects microservice schemas using makeRemoteExecutableSchema

    const schema = makeExecutableSchema({
        typeDefs: RootQuery,
        schemaDirectives: {
            auth: AuthDirective
        }
    });

    return [
        ...remoteSchema,
        schema
    ];
}

//app.js
 const schemas = await getMergedSchema();
 const mergedSchema = mergeSchemas({
        schemas,
        resolvers: resolvers,
        mergeDirectives: true
 }});


 const server = new ApolloServer({
            schema: mergedSchema,
            debug: true,
            introspection: true,
            tracing: true,
            engine: {
                apiKey: config.get("engine.apiKey"),
                schemaTag: process.env.NODE_CONFIG_ENV,
                generateClientInfo: ({request}) => {
                    const headers = request.http && request.http.headers;
                    if (headers) {
                        return {
                            clientName: headers.get("X-clientId"),
                            clientVersion: headers.get("X-AppVersion"),
                        };
                    } else {
                        return {
                            clientName: "Unknown",
                            clientVersion: "Unknown"
                        };
                    }
                }
            },
            cacheControl: true,
            playground: {
                settings: {
                    "editor.theme": "dark",
                },
            },
            persistedQueries: {
                cache: new MemcachedCache(
                    ['memcached-server'],
                    {retries: 10, retry: 10000, ttl: 10000},
                ),
            },
            subscriptions: {
                path: "/sub"
            },
            context: ({req}) => ({
                request: req
            })
        });

        server.applyMiddleware({
            app,
            gui: true,
            bodyParserConfig: {limit: "50mb"},
            cors: {
                origin: "*"
            },
            onHealthCheck: () => new Promise((resolve) => {
                //database check or other asynchronous action
                resolve();
            })
        });

        const httpServer = http.createServer(app);
        server.installSubscriptionHandlers(httpServer);

        httpServer.listen({
                port: ((port: any): number)
            }, () => {
                Logger.info(`? Server ready at http://localhost:${port}${server.graphqlPath}`);
                Logger.info(`Try your health check at: http://localhost:${port}/.well-known/apollo/server-health`);
            }
        )

查询到Apollo服务器

 {
     campaigns {
         name
     }
 }

我希望在打此查询时调用订书机中的@auth directive,以便我可以控制它并检查传入请求是否得到授权。如果不是,则抛出UNAUTHORIZED错误,并且不会将请求转发给微服务。

但是它会在微服务A中调用auth指令:(

有什么方法可以实现这一目标,或者如果您有更好的解决方案或体系结构,请告诉我。我被困住需要帮助。

我正在使用以下软件包

  {
     ...,
     "dependencies": {
       "@types/graphql": "14.2.0",
       "apollo-engine": "1.1.2",
       "apollo-fetch": "0.7.0",
       "apollo-link-context": "1.0.17",
       "apollo-link-http": "1.5.14",
       "apollo-server": "2.6.0-alpha.0",
       "apollo-server-cache-memcached": "0.4.0",
       "apollo-server-express": "2.6.0-alpha.0",
       "apollo-server-memcached": "0.1.0-rc.10",
       "express": "4.16.4",
       "graphql": "14.3.1",
       "graphql-resolve-batch": "1.0.2",
       "graphql-subscriptions": "1.1.0",
       "graphql-tag": "2.10.1",
       "graphql-tools": "5.0.0-rc.1",
       "node-fetch": "2.5.0",
       "request": "2.88.0",
       "subscriptions-transport-ws": "0.9.16",
        "ws": "5.2.1",
        ...
     }
  }

0 个答案:

没有答案
相关问题