보안
기본적으로 모든 RPC Function은 어떤 클라이언트든 호출할 수 있으며, peer-to-peer 통신 기능이 활성화되어 있습니다. 어떤 클라이언트가 무엇을 할 수 있는지 세밀하게 제어하려면 RpcKernelSecurity
Class를 override 할 수 있습니다.
import { RpcKernelSecurity, Session, RpcControllerAccess } from '@deepkit/type'; //기본 구현이 포함되어 있습니다 class MyKernelSecurity extends RpcKernelSecurity { async hasControllerAccess(session: Session, controllerAccess: RpcControllerAccess): Promise<boolean> { return true; } async isAllowedToRegisterAsPeer(session: Session, peerId: string): Promise<boolean> { return true; } async isAllowedToSendToPeer(session: Session, peerId: string): Promise<boolean> { return true; } async authenticate(token: any): Promise<Session> { throw new Error('Authentication not implemented'); } transformError(err: Error) { return err; } }
이를 사용하려면 RpcKernel
에 provider를 전달하세요:
const kernel = new RpcKernel([{provide: RpcKernelSecurity, useClass: MyKernelSecurity, scope: 'rpc'}]);
또는 Deepkit app의 경우, app에서 provider로 RpcKernelSecurity
Class를 override 합니다:
import { App } from '@deepkit/type'; import { RpcKernelSecurity } from '@deepkit/rpc'; import { FrameworkModule } from '@deepkit/framework'; new App({ controllers: [MyRpcController], providers: [ {provide: RpcKernelSecurity, useClass: MyRpcKernelSecurity, scope: 'rpc'} ], imports: [new FrameworkModule] }).run();
인증 / 세션
기본적으로 Session
객체는 익명 세션이며, 이는 클라이언트가 인증되지 않았음을 의미합니다. 클라이언트가 인증을 원할 때 authenticate
Method가 호출됩니다. authenticate
Method가 받는 token은 클라이언트로부터 오며 어떤 값이든 될 수 있습니다.
클라이언트가 token을 설정하면, 첫 번째 RPC Function이 호출될 때 또는 client.connect()
가 수동으로 호출될 때 인증이 수행됩니다.
const client = new RpcWebSocketClient('localhost:8081'); client.token.set('123456789'); const controller = client.controller<Controller>('/main');
이 경우 RpcKernelSecurity.authenticate
는 token 123456789
를 받아 그에 따라 다른 세션을 반환할 수 있습니다. 반환된 세션은 hasControllerAccess
와 같은 다른 모든 Method로 전달됩니다.
import { Session, RpcKernelSecurity } from '@deepkit/rpc'; class UserSession extends Session { } class MyKernelSecurity extends RpcKernelSecurity { async hasControllerAccess(session: Session, controllerAccess: RpcControllerAccess): Promise<boolean> { if (controllerAccess.controllerClassType instanceof MySecureController) { //MySecureController는 UserSession이 필요합니다 return session instanceof UserSession; } return true; } async authenticate(token: any): Promise<Session> { if (token === '123456789') { //username은 ID 또는 사용자명일 수 있습니다 return new UserSession('username', token); } throw new Error('Authentication failed'); } }
컨트롤러 접근
hasControllerAccess
Method는 클라이언트가 특정 RPC Function을 실행할 수 있는지를 결정합니다. 이 Method는 모든 RPC Function 호출마다 실행됩니다. false
를 반환하면 접근이 거부되고, 클라이언트에서 Error가 발생합니다.
RpcControllerAccess
에는 해당 RPC Function에 대한 유용한 정보가 포함됩니다:
interface RpcControllerAccess { controllerName: string; controllerClassType: ClassType; actionName: string; actionGroups: string[]; actionData: { [name: string]: any }; }
Group과 추가 data는 decorator @rpc.action()
을 통해 변경할 수 있습니다:
class Controller { @rpc.action().group('secret').data('role', 'admin') saveUser(user: User): void { } } class MyKernelSecurity extends RpcKernelSecurity { async hasControllerAccess(session: Session, controllerAccess: RpcControllerAccess): Promise<boolean> { if (controllerAccess.actionGroups.includes('secret')) { if (session instanceof UserSession) { //todo: 확인 return session.username === 'admin'; } return false; } return true; } }