通过gRPC添加删除v2ray用户

什么是gRPC?

gRPC是谷歌开源的一个远程方法调用(Remote Process Call)框架,gRPC基于CS模型,使得客户端可以像调用本地方法一样调用远程服务端的方法,使得您能够更容易地创建分布式应用和服务。

gRPC使用ProtoBuffers作为数据序列化传输工具,gRPC-java使用netty作为网络通信基础设施。

V2Ray开放基于gRPC的API接口

HandlerService

一些对于入站出站代理进行修改的 API,可用的功能如下:

  • 添加一个新的入站代理;
  • 添加一个新的出站代理;
  • 删除一个现有的入站代理;
  • 删除一个现有的出站代理;
  • 在一个入站代理中添加一个用户(仅支持 VMess);
  • 在一个入站代理中删除一个用户(仅支持 VMess);

LoggerService

支持对内置 Logger 的重启,可配合 logrotate 进行一些对日志文件的操作。

StatsService

内置的数据统计服务

SpringBoot 项目通过gRPC向V2Ray服务添加用户、删除用户

v2ray服务config.json配置开放api接口

  • 从github拉去v2ray代码:git clone git@github.com:v2ray/v2ray-core.git
  • 编译v2ray:cd v2ray-core/main; go build -ldflags ‘-w -s’ -o v2ray
  • 编译v2ctl:cd v2ray-core/infra/control/main; go build -ldflags ‘-w -s’ -o v2ctl
  • 配置config.json,开放API接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
{
"api": {
"services": [
"HandlerService"
],
"tag": "api"
},
"inbound": {
"port": 10086,
"protocol": "vmess",
"settings": {
"clients": [
{
"alterId": 32,
"id": "fcecbd2b-3a34-4201-bd3d-7c67d89c26ba",
"level": 0
}
]
},
"streamSettings": {
"network": "tcp"
},
"tag": "proxy"
},
"inboundDetour": [
{
"listen": "127.0.0.1",
"port": 10085,
"protocol": "dokodemo-door",
"settings": {
"address": "127.0.0.1"
},
"tag": "api"
}
],
"log": {
"loglevel": "debug",
"access" : "~/go/src/v2ray-core/log_access.log",
"error" : "~/go/src/v2ray-core/log_error.log"
},
"outbound": {
"protocol": "freedom",
"settings": {}
},
"routing": {
"settings": {
"rules": [
{
"inboundTag": [
"api"
],
"outboundTag": "api",
"type": "field"
}
]
},
"strategy": "rules"
}
}
  • 运行v2ray服务:./v2ray -config config.json

springboot项目引入gRPC

参考https://github.com/grpc/grpc-java

pom.xml 引入grpc依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.23.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.23.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.23.0</version>
</dependency>

引入自动生成代码插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.2</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.9.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.23.0:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

自动生成代码插件默认根据src/main/proto目录下的proto文件来生成java代码,放在target/generated-sources目录下

编写gRPC client 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
@Service
public class V2RayCoreService {
public static Logger log = LoggerFactory.getLogger ( V2RayCoreService.class );

@Value ( "${v2ray.core.host}" )
private String host;
@Value ( "${v2ray.core.port}" )
private int port;

//同步调用stub
private HandlerServiceGrpc.HandlerServiceBlockingStub blockingStub;
//异步调用stub
private HandlerServiceGrpc.HandlerServiceStub asyncStub;

private ManagedChannel channelBuild(String host, int port) {
return NettyChannelBuilder.forAddress ( host, port)
.negotiationType ( NegotiationType.PLAINTEXT ).build ();
}

public synchronized HandlerServiceGrpc.HandlerServiceBlockingStub constructBlockingStub() {
if (blockingStub == null) {
blockingStub = HandlerServiceGrpc.newBlockingStub(channelBuild ( host, port ));
}
return blockingStub;
}

public synchronized HandlerServiceGrpc.HandlerServiceStub constructAsyncStub() {
if (asyncStub == null) {
asyncStub = HandlerServiceGrpc.newStub ( channelBuild ( host, port ) );
}
return asyncStub;
}

/**
* @Description: 在一个入站代理中添加一个用户
* v2RayCoreService.addUser ("proxy",
* 0,
* "test@gmail.com",
* "4c4893c5-7c8f-03a0-0f86-afc800f2897a",
* 5);
*/
public Command.AlterInboundRequest addUser(String inBoundTag, int userLevel, String userEmail,
String uuid, int alterId) {

Headers.SecurityConfig securityConfig = Headers.SecurityConfig.newBuilder()
.setType ( Headers.SecurityType.AUTO )
.build();

AccountOuterClass.Account account = AccountOuterClass.Account.newBuilder()
.setId ( uuid )
.setAlterId ( alterId )
.setSecuritySettings ( securityConfig )
.build();

UserOuterClass.User user = UserOuterClass.User.newBuilder()
.setLevel ( userLevel )
.setEmail ( userEmail )
.setAccount ( toTypedMessage ( account, "v2ray.core.proxy.vmess.Account" ) )
.build();

Command.AddUserOperation addUserOperation = Command.AddUserOperation.newBuilder()
.setUser ( user )
.build();

TypedMessageOuterClass.TypedMessage operation = toTypedMessage ( addUserOperation, "v2ray.core.app.proxyman.command.AddUserOperation");

return Command.AlterInboundRequest.newBuilder()
.setTag ( inBoundTag )
.setOperation ( operation )
.build();
}

/**
* @Description: 在一个入站代理中删除一个用户
* v2RayCoreService.removeUser ( "proxy",
* "test@gmail.com")
*/
public Command.AlterInboundRequest removeUser(String inBoundTag, String email) {
Command.RemoveUserOperation removeUserOperation = Command.RemoveUserOperation.newBuilder()
.setEmail ( email )
.build();
TypedMessageOuterClass.TypedMessage operation = toTypedMessage ( removeUserOperation, "v2ray.core.app.proxyman.command.RemoveUserOperation" );
return Command.AlterInboundRequest.newBuilder()
.setTag ( inBoundTag )
.setOperation ( operation )
.build();
}

/**
* 同步接口
* 使用示例:添加用户
*
* v2RayCoreService.alterInboudBlocking (
* v2RayCoreService.addUser ("proxy",
* 0,
* "test@gmail.com",
* "4c4893c5-7c8f-03a0-0f86-afc800f2897a",
* 5)
* );
* 使用示例:删除用户
* v2RayCoreService.alterInboudBlocking (
* v2RayCoreService.removeUser ( "proxy",
* "test@gmail.com")
* );
*/
public void alterInboudBlocking(Command.AlterInboundRequest alterInboundRequest) {
constructBlockingStub ().alterInbound ( alterInboundRequest );
}
/**
* 异步接口
*/
public void alterInboundAsync(Command.AlterInboundRequest alterInboundRequest) {
constructAsyncStub ().alterInbound ( alterInboundRequest, new StreamObserver<Command.AlterInboundResponse> () {
@Override
public void onNext(Command.AlterInboundResponse alterInboundResponse) {
log.info ( "async alter inbound onNext response {}", alterInboundResponse );
}

@Override
public void onError(Throwable throwable) {
log.error ( "async alter inbound error {}", throwable );
}

@Override
public void onCompleted() {
log.info ( "async alter inbound completed" );
}
} );
}

private TypedMessageOuterClass.TypedMessage toTypedMessage(Message message, String type) {
if (message == null) {
return null;
}
return TypedMessageOuterClass.TypedMessage.newBuilder()
.setType ( type )
.setValue ( message.toByteString () )
.build();
}
}
-------------本文结束感谢您的阅读-------------
Good for you!