最近一直在寫一些graphql方面的東西坠狡, 在做項(xiàng)目的過(guò)程中會(huì)有一些是關(guān)于權(quán)限管理的部分继找,于是發(fā)現(xiàn)了一個(gè)十分優(yōu)雅的方式來(lái)做權(quán)限方面的處理.
簡(jiǎn)而言之, 先講下需求背景逃沿, 該項(xiàng)目是一個(gè)商城婴渡,商城會(huì)有三個(gè)角色,分別為ROOT(管理員)
, MERCHANT(商戶)
感挥,CUSTOMER(用戶)
每個(gè)角色能夠訪問(wèn)和控制的資源顯然是不一樣的缩搅,在過(guò)去的項(xiàng)目中,我們可能會(huì)使用一些Validator來(lái)校驗(yàn)當(dāng)前請(qǐng)求的用戶是否具備相應(yīng)的權(quán)限触幼,而在Graphql這套體系中,我們可以使用Directive 更加聲明式地來(lái)鑒權(quán)
我們先來(lái)看個(gè)Directive來(lái)鑒權(quán)的sample
"鑒權(quán)指令"
directive @auth(requires: Role = CUSTOMER) on OBJECT | FIELD_DEFINITION
extend type Mutation {
"用戶刪除"
deleteUser(userDeleteInput: UserDeleteInput!): UserActionResult!
@auth(requires: ROOT)
}
通過(guò)上面的@auth(requires: ROOT)
我們就可以很輕松地限制僅ROOT
權(quán)限才可以進(jìn)行刪除用戶的行為究飞, 這樣就能夠很聲明式地去做權(quán)限管理部分的功能置谦,同時(shí)對(duì)于開(kāi)發(fā)者而言,也能夠很清晰地看到哪個(gè)部分的資源需要什么樣子的權(quán)限才能進(jìn)行訪問(wèn)
既然我們可以這么優(yōu)雅地干權(quán)限管理這間事情亿傅, 那么怎么在Apollo Graphql中實(shí)現(xiàn)媒峡?
首先我們需要先看下如何實(shí)現(xiàn)一個(gè)指令相關(guān)文檔點(diǎn)這里
import { SchemaDirectiveVisitor } from 'graphql-tools';
import { defaultFieldResolver } from 'graphql';
import { Role } from '../status';
// 繼承Apollo Graphql提供的SchemaDirectiveVisitor
export class AuthDriective extends SchemaDirectiveVisitor {
/**
* 此處便是定義了訪問(wèn)filed的方法
*/
visitFieldDefinition(field) {
const { resolve = defaultFieldResolver } = field;
};
}
}
我們可以看到在此處可以拿到對(duì)應(yīng)的filed的resolve方法,那只需要將resolve進(jìn)行改造葵擎,在調(diào)用resolve之前進(jìn)行鑒權(quán)即可
import { SchemaDirectiveVisitor } from 'graphql-tools';
import { defaultFieldResolver } from 'graphql';
import { Role } from '../status';
export class AuthDriective extends SchemaDirectiveVisitor {
visitFieldDefinition(field) {
const { resolve = defaultFieldResolver } = field;
const { requires } = this.args;
field.resolve = function resolver(...args) {
const context = args[2];
if (!context.currentUser || context.currentUser.role < Role[requires]) {
throw new Error('賬號(hào)權(quán)限不足');
}
return resolve.apply(this, args);
};
}
}
在上面的代碼中谅阿,我們通過(guò)this.args可以獲取到指令當(dāng)前的參數(shù),也就是requires
, 然后我們通過(guò)重新定義filed.resolve酬滤, 在進(jìn)入業(yè)務(wù)流程前進(jìn)行權(quán)限校驗(yàn)签餐, context
是上下文的意思,通過(guò)context
盯串, 我們可以知道當(dāng)前的用戶的相關(guān)信息氯檐,如果校驗(yàn)鑒權(quán)失敗,就拋出對(duì)應(yīng)的鑒權(quán)錯(cuò)誤
指令部分的實(shí)現(xiàn)先到這里体捏,我們還有一件事情需要處理 將用戶信息設(shè)置到context中
import { tradeTokenForUser } from '../encode';
export const setGraphqlContext = ({ ctx: { request, session } }) => {
let authToken = null;
let currentUser = null;
try {
// 獲取token
authToken = request.headers.authorization;
if (authToken) {
// 解開(kāi)token獲取當(dāng)前請(qǐng)求的用戶信息
currentUser = tradeTokenForUser(authToken);
}
} catch (e) {
throw e;
}
return {
authToken,
currentUser,
};
};
同時(shí)要在 Apollo中注入冠摄,還有別忘了 指令
new ApolloServer({
context: setGraphqlContext糯崎,
schemaDirectives: {
auth: AuthDriective,
},
})
完成上面的步驟,最后我們還需要在聲明下指令
"鑒權(quán)指令"
directive @auth(requires: Role = CUSTOMER) on OBJECT | FIELD_DEFINITION
好的河泳,整個(gè)Apollo Graphql 到這里就完成了沃呢, 后續(xù)的話,我在更新一個(gè)demo的鏈接就完美了