1# 连接VPN 2<!--Kit: Network Kit--> 3<!--Subsystem: Communication--> 4<!--Owner: @wmyao_mm--> 5<!--Designer: @guo-min_net--> 6<!--Tester: @tongxilin--> 7<!--Adviser: @zhang_yixin13--> 8 9## 简介 10 11VPN,即虚拟专用网络(Virtual Private Network),是在公用网络上建立专用网络的一种技术。在VPN网络中,任意两个节点间的连接并非依赖传统专用网络所需要的端到端的物理链路,而是构建在公用网络服务商提供的平台(如Internet)之上的逻辑网络。用户数据在这一逻辑链路中进行传输。 12 13OpenHarmony为开发者提供了用于创建VPN的API解决方案。当前提供三方VPN能力主要用于创建虚拟网卡及配置VPN路由信息,连接隧道过程及内部连接的协议需要应用内部自行实现。本文将指导您如何开发自己的VPN客户端。 14 15> **说明:** 16> 17>- 为了保证应用的运行效率,所有API调用都是异步的,对于异步调用的API均提供了Promise的方式,以下示例均采用Promise方式,更多方式可以查阅[API参考](../reference/apis-network-kit/js-apis-net-vpnExtension.md)。 18>- 完整的JS API说明以及示例代码请参考:[VPN扩展应用API](../reference/apis-network-kit/js-apis-net-vpnExtension.md)。 19>- 使用该功能需要[ohos.permission.INTERNET](../security/AccessToken/permissions-for-all.md#ohospermissioninternet)权限。 20 21## VPN应用的显示体验 22 23借助系统提供的VPN Extension接口开发者可以构建支持不同协议的VPN服务。OpenHarmony系统提供了界面 (UI) 使用户可以了解当前VPN应用服务的启动和连接: 24 25- 在VPN应用首次启动连接之前,系统会显示VPN连接授权对话框。该对话框会提示用户是否信任该VPN应用并接受VPN连接请求。 26- 当VPN启动连接成功时,状态栏显示一个VPN (钥匙) 图标以提醒用户VPN处于连接状态。 27 28为了使用户可以方便的查看和配置,您的VPN应用还需要提供以下界面: 29 30- 用于手动启动和停止连接的控件。 31- 当VPN启动连接时,在通知栏显示VPN应用的连接状态或提供网络统计信息 (如连接时长、流量等) 。点击该通知能够将您的VPN应用调入前台。 32 33 34## 开发步骤 35 36### 创建VPN Extension Ability 37 38如果想使您的应用支持VPN能力,首先您需要创建一个继承于VpnExtensionAbility的extensionAbilities。 39 40```ts 41// 举例:在应用的module.json5中定义MyVpnExtAbility。 42"extensionAbilities": [ 43 { 44 "name": "MyVpnExtAbility", 45 "description": "vpnservice", 46 "type": "vpn", 47 "srcEntry": "./ets/serviceextability/MyVpnExtAbility.ts" 48 } 49] 50``` 51 52> **注意:** 53> 54> 如果DevEco Studio工具提示不能识别"type": "vpn",需要您手动在SDK的toolchains\modulecheck\module.json文件中,给extensionAbilities对应的type枚举添加"vpn"定义,并清除build缓存。 55 56接下来您需要在创建的VpnExtensionAbility中实现VPN的配置、启动和停止操作: 57 58- 建立一个VPN的网络隧道,以UDP隧道为例(参考本文下方VPN Demo示例工程文件[napi_init](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case/entry/src/main/cpp/napi_init.cpp)的UdpConnect()方法); 59- 通过VpnConnection.[protect](../reference/apis-network-kit/js-apis-net-vpnExtension.md#protect)保护前一步建立的UDP隧道; 60- 构建VPN Config参数,参考[VPN Config参数说明](#vpn-config参数说明); 61- 通过VpnConnection.[create](../reference/apis-network-kit/js-apis-net-vpnExtension.md#create)建立VPN网络连接; 62- 处理虚拟网卡的数据,如:读写操作。 63 64### 启动VPN Extension Ability 65 66当VPN应用启动VPN连接时,需要调用startVpnExtensionAbility接口,携带需要启动的VpnExtensionAbility信息,其中bundleName需要与您的VPN应用bundleName一致,abilityName为您在前面创建的VpnExtensionAbility名。您可参考如下示例: 67 68```ts 69import { common, Want } from '@kit.AbilityKit'; 70import { vpnExtension } from '@kit.NetworkKit'; 71 72let want: Want = { 73 deviceId: "", 74 bundleName: "com.example.myvpndemo", 75 abilityName: "MyVpnExtAbility", 76}; 77 78@Entry 79@Component 80struct Index { 81 @State message: string = 'Hello World'; 82 83 build() { 84 Row() { 85 Column() { 86 Text(this.message) 87 .fontSize(50) 88 .fontWeight(FontWeight.Bold).onClick(() => { 89 console.info("btn click") }) 90 Button('Start Extension').onClick(() => { 91 vpnExtension.startVpnExtensionAbility(want); 92 }).width('70%').fontSize(45).margin(16) 93 }.width('100%') 94 }.height('100%') 95 } 96} 97``` 98 99如果您的VPN应用未获取用户信任,系统将弹出VPN连接的授权对话框,当获取用户授权后,系统将自动调用并启动您实现的VPN Extension Ability的[onCreate](../reference/apis-network-kit/js-apis-VpnExtensionAbility.md#vpnextensionabilityoncreate)方法将被调用。 100 101目前系统仅支持启动一个VPN连接服务,当VPN已经启动时应用新调用启动接口会收到系统拒绝错误,此时建议您的应用可以提醒用户先断开当前已经激活的VPN应用连接。 102 103 104 105### 停止VPN Extension Ability 106 107当VPN应用需要停止VPN连接时,需要调用stopVpnExtensionAbility接口,携带需要停止的VpnExtensionAbility信息。系统会对调用方做权限校验,stopVpnExtensionAbility的调用方应用必须获取了用户的VPN信任授权,且只允许停止应用自己启动的VpnExtensionAbility,所以接口传入的参数中bundleName需要与您的VPN应用bundleName一致,abilityName为指定停止VPN的VpnExtensionAbility名。 108 109您可参考如下示例: 110 111```ts 112import { common, Want } from '@kit.AbilityKit'; 113import { vpnExtension } from '@kit.NetworkKit'; 114 115let want: Want = { 116 deviceId: "", 117 bundleName: "com.example.myvpndemo", 118 abilityName: "MyVpnExtAbility", 119}; 120 121@Entry 122@Component 123struct Index { 124 @State message: string = 'Hello World'; 125 126 build() { 127 Row() { 128 Column() { 129 Text(this.message) 130 .fontSize(50) 131 .fontWeight(FontWeight.Bold).onClick(() => { 132 console.info("btn click") }) 133 Button('Start Extension').onClick(() => { 134 vpnExtension.startVpnExtensionAbility(want); 135 }).width('70%').fontSize(45).margin(16) 136 Button('Stop Extension').onClick(() => { 137 console.info("btn end") 138 vpnExtension.stopVpnExtensionAbility(want); 139 }).width('70%').fontSize(45).margin(16) 140 141 }.width('100%') 142 }.height('100%') 143 } 144} 145``` 146 147stopVpnExtensionAbility后,您的VPN Extension Ability的[onDestroy](../reference/apis-network-kit/js-apis-VpnExtensionAbility.md#vpnextensionabilityondestroy)方法将被调用,您可在此时destroy vpn连接。 148 149```ts 150import { vpnExtension, VpnExtensionAbility } from '@kit.NetworkKit'; 151import { common, Want } from '@kit.AbilityKit'; 152import { BusinessError } from '@kit.BasicServicesKit'; 153 154let context: vpnExtension.VpnExtensionContext; 155export default class MyVpnExtAbility extends VpnExtensionAbility { 156 onDestroy() { 157 let VpnConnection : vpnExtension.VpnConnection = vpnExtension.createVpnConnection(context); 158 console.info("vpn createVpnConnection: " + JSON.stringify(VpnConnection)); 159 VpnConnection.destroy().then(() => { 160 console.info("destroy success."); 161 }).catch((error : BusinessError) => { 162 console.error("destroy fail" + JSON.stringify(error)); 163 }); 164 } 165} 166``` 167 168### 生成VPN Id 169 170创建新的VPN时,应生成一个VPN Id作为VPN的唯一标识。 171可参考如下示例: 172 173```ts 174import VpnExtensionAbility from "@ohos.app.ability.VpnExtensionAbility"; 175import { vpnExtension } from "@kit.NetworkKit"; 176 177export default class VpnTest extends VpnExtensionAbility { 178 vpnId:string = '' 179 180 getVpnId(){ 181 let vpnConnection = vpnExtension.createVpnConnection(this.context); 182 vpnConnection?.generateVpnId().then((data)=>{ 183 if (data) { 184 this.vpnId = data; 185 } 186 }); 187 } 188} 189``` 190 191### 断开VPN 192 193若需断开VPN,可参考如下示例: 194```ts 195import VpnExtensionAbility from "@ohos.app.ability.VpnExtensionAbility"; 196import { vpnExtension } from "@kit.NetworkKit"; 197 198export default class VpnTest extends VpnExtensionAbility { 199 vpnId: string = 'test_vpn_id' 200 vpnConnection: vpnExtension.VpnConnection | undefined 201 202 destroy(){ 203 this.vpnConnection = vpnExtension.createVpnConnection(this.context); 204 this.vpnConnection?.destroy(this.vpnId); 205 } 206} 207``` 208 209## 服务生命周期 210 211为了保障设备的网络连接,当系统观察到VPN相关应用出现异常时会主动停止VPN连接: 212 213- 当调用startVpnExtensionAbility接口的应用进程退出时。 214- 当VPN服务进程销毁时。 215 216## VPN Config参数说明 217 218| 名称 | 类型 | 必填 | 说明 | 219| ------------------- | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ | 220| addresses | Array\<[LinkAddress](../reference/apis-network-kit/js-apis-net-connection.md#linkaddress)\> | 是 | VPN虚拟网卡的IP地址。 | 221| routes | Array\<[RouteInfo](../reference/apis-network-kit/js-apis-net-connection.md#routeinfo)\> | 否 | VPN虚拟网卡的路由信息(目前最多可配置1024条路由)。 | 222| dnsAddresses | Array\<string\> | 否 | DNS服务器地址信息。配置DNS服务器地址后,VPN启动状态下,被代理的应用上网时,使用配置的DNS服务器进行DNS查询。 | 223| searchDomains | Array\<string\> | 否 | DNS的搜索域列表。 | 224| mtu | number | 否 | 最大传输单元MTU值(单位:字节)。 | 225| isIPv4Accepted | boolean | 否 | 是否支持IPV4,默认值为true。true:支持IPV4;false:不支持IPV4。 | 226| isIPv6Accepted | boolean | 否 | 是否支持IPV6,默认值为false。true:支持IPV6;false:不支持IPV6。 | 227| isInternal | boolean | 否 | 是否支持内置VPN,默认值为false。true:支持内置VPN;false:不支持内置VPN。 | 228| isBlocking | boolean | 否 | 是否阻塞模式,默认值为false。true:是阻塞模式;false:不是阻塞模式。 | 229| trustedApplications | Array\<string\> | 否 | 受信任的应用信息列表,以string类型表示的包名。配置此列表后,仅列表中的应用数据才能根据routes被VPN代理。<br>注:trustedApplications和blockedApplications列表不能同时配置。 | 230| blockedApplications | Array\<string\> | 否 | 被阻止的应用信息列表,string类型表示的包名。当配置该列表后,该列表中的应用数据不会被VPN代理,其他应用可以根据routes配置被VPN代理。<br>注:trustedApplications和blockedApplications列表不能同时配置。 | 231 232**示例:** 233 234```ts 235import { vpnExtension} from '@kit.NetworkKit'; 236 237let vpnConfig: vpnExtension.VpnConfig = { 238 // 配置VPN虚拟网卡的IP地址。 239 addresses: [{ 240 address: { 241 address:'192.x.x.5', 242 family:1 243 }, 244 prefixLength:24 245 }], 246 // 配置路由参数。 247 routes: [{ 248 // VPN虚拟网卡接口名固定为“vpn-tun”。 249 interface: 'vpn-tun', 250 destination: { 251 address: { 252 address:'10.x.x.8', 253 family:1, 254 port:8080 255 }, 256 prefixLength:24 257 }, 258 gateway: { 259 address:'10.x.x.5', 260 family:1, 261 port:8080 262 }, 263 hasGateway: false, 264 isDefaultRoute: false, 265 }], 266 // 配置最大传输单元值。 267 mtu: 1400, 268 // 配置VPN使用的DNS服务器。 269 dnsAddresses: ['223.x.x.5', '223.x.x.6'], 270 // VPN生效白名单的应用。 271 trustedApplications: ['com.test.browser'], 272 // 不生效VPN黑名单的应用。 273 blockedApplications: ['com.test.games'], 274} 275let context: vpnExtension.VpnExtensionContext; 276 277function vpnCreate(){ 278 let VpnConnection: vpnExtension.VpnConnection = vpnExtension.createVpnConnection(context); 279 VpnConnection.create(vpnConfig).then((data) => { 280 console.info("vpn create " + JSON.stringify(data)); 281 }) 282} 283``` 284 285 286 287## VPN Demo示例 288 289OpenHarmony开源项目包含一个名为[VPN](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case)的示例应用。此应用展示了如何设置和连接 VPN 服务。 290 291 292 293