在iOS 8中以编程方式连接到VPN
自从iOS8测试版发布以来,我在其捆绑包中找到了一个网络扩展框架,它将允许开发人员以编程方式配置并连接到VPN服务器,而无需安装任何配置文件 该框架包含一个名为NEVPNManager的主要类。这个类还有3个主要方法,可以保存、加载或删除VPN首选项。我在viewDidLoad方法中编写了一段代码,如下所示:在iOS 8中以编程方式连接到VPN,ios,objective-c,ios8,vpn,Ios,Objective C,Ios8,Vpn,自从iOS8测试版发布以来,我在其捆绑包中找到了一个网络扩展框架,它将允许开发人员以编程方式配置并连接到VPN服务器,而无需安装任何配置文件 该框架包含一个名为NEVPNManager的主要类。这个类还有3个主要方法,可以保存、加载或删除VPN首选项。我在viewDidLoad方法中编写了一段代码,如下所示: NEVPNManager *manager = [NEVPNManager sharedManager]; [[NSNotificationCenter defaultCenter] ad
NEVPNManager *manager = [NEVPNManager sharedManager];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(vpnConnectionStatusChanged) name:NEVPNStatusDidChangeNotification object:nil];
[manager loadFromPreferencesWithCompletionHandler:^(NSError *error) {
if(error) {
NSLog(@"Load error: %@", error);
}}];
NEVPNProtocolIPSec *p = [[NEVPNProtocolIPSec alloc] init];
p.username = @“[My username]”;
p.passwordReference = [KeyChainAccess loadDataForServiceNamed:@"VIT"];
p.serverAddress = @“[My Server Address]“;
p.authenticationMethod = NEVPNIKEAuthenticationMethodCertificate;
p.localIdentifier = @“[My Local identifier]”;
p.remoteIdentifier = @“[My Remote identifier]”;
p.useExtendedAuthentication = NO;
p.identityData = [My VPN certification private key];
p.disconnectOnSleep = NO;
[manager setProtocol:p];
[manager setOnDemandEnabled:NO];
[manager setLocalizedDescription:@"VIT VPN"];
NSArray *array = [NSArray new];
[manager setOnDemandRules: array];
NSLog(@"Connection desciption: %@", manager.localizedDescription);
NSLog(@"VPN status: %i", manager.connection.status);
[manager saveToPreferencesWithCompletionHandler:^(NSError *error) {
if(error) {
NSLog(@"Save error: %@", error);
}
}];
我还在视图中放置了一个按钮,并将其TouchUpInside操作设置为以下方法:
- (IBAction)buttonPressed:(id)sender {
NSError *startError;
[[NEVPNManager sharedManager].connection startVPNTunnelAndReturnError:&startError];
if(startError) {
NSLog(@"Start error: %@", startError.localizedDescription);
}
}
这里有两个问题:
1) 当我尝试保存首选项时,将抛出以下错误:
保存错误:error Domain=NEVPNErrorDomain Code=4“该操作无法完成。(NEVPNErrorDomain error 4)。”
这个错误是什么?我如何解决这个问题
2) [[NEVPNManager sharedManager].connection startVPNTunnelAndReturnError:&startError];方法在调用它时不会返回任何错误,但连接状态会从断开连接变为连接,只需片刻,然后返回断开连接状态
任何帮助都将不胜感激:)问题在于保存时出现的错误:
保存错误:错误域=NEVPNErrorDomain代码=4
如果查看NEVPNManager.h头文件,您将看到错误代码4为“NEVPNErrorConfigurationStale”。该配置已过时,需要加载。
您应该调用loadFromPreferencesWithCompletionHandler:
,并在完成处理程序中修改要修改的值,然后调用saveToPreferencesWithCompletionHandler:
。问题中的示例是在加载完成之前修改配置,这就是为什么会出现此错误
更像这样:
[manager loadFromPreferencesWithCompletionHandler:^(NSError *error) {
// do config stuff
[manager saveToPreferencesWithCompletionHandler:^(NSError *error) {
}];
}];
这个答案将有助于那些正在寻找使用网络扩展框架的解决方案的人 我的要求是使用IKEv2协议连接/断开VPN服务器(当然,您也可以通过更改vpnManager协议配置将此解决方案用于IPSec) 注意:如果您正在寻找L2TP协议,则使用网络扩展无法连接VPN服务器。请参阅: 以下是我的工作代码片段: 声明VPNManager对象和其他有用的东西
var vpnManager = NEVPNManager.shared()
var isConnected = false
@IBOutlet weak var switchConntectionStatus: UISwitch!
@IBOutlet weak var labelConntectionStatus: UILabel!
在viewDidLoad中添加观察者以获取VPN状态,并在Keychain中存储vpnPassword,您也可以存储sharedSecret,这将需要IPSec协议
override func viewDidLoad() {
super.viewDidLoad()
let keychain = KeychainSwift()
keychain.set("*****", forKey: "vpnPassword")
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.VPNStatusDidChange(_:)), name: NSNotification.Name.NEVPNStatusDidChange, object: nil)
}
现在,在我的应用程序中,有UISwitch来连接/断开VPN服务器
func switchClicked() {
switchConntectionStatus.isOn = false
if !isConnected {
initVPNTunnelProviderManager()
}
else{
vpnManager.removeFromPreferences(completionHandler: { (error) in
if((error) != nil) {
print("VPN Remove Preferences error: 1")
}
else {
self.vpnManager.connection.stopVPNTunnel()
self.labelConntectionStatus.text = "Disconnected"
self.switchConntectionStatus.isOn = false
self.isConnected = false
}
})
}
}
点击交换机后,使用下面的代码启动VPN隧道
func initVPNTunnelProviderManager(){
self.vpnManager.loadFromPreferences { (error) -> Void in
if((error) != nil) {
print("VPN Preferences error: 1")
}
else {
let p = NEVPNProtocolIKEv2()
// You can change Protocol and credentials as per your protocol i.e IPSec or IKEv2
p.username = "*****"
p.remoteIdentifier = "*****"
p.serverAddress = "*****"
let keychain = KeychainSwift()
let data = keychain.getData("vpnPassword")
p.passwordReference = data
p.authenticationMethod = NEVPNIKEAuthenticationMethod.none
// p.sharedSecretReference = KeychainAccess.getData("sharedSecret")!
// Useful for when you have IPSec Protocol
p.useExtendedAuthentication = true
p.disconnectOnSleep = false
self.vpnManager.protocolConfiguration = p
self.vpnManager.isEnabled = true
self.vpnManager.saveToPreferences(completionHandler: { (error) -> Void in
if((error) != nil) {
print("VPN Preferences error: 2")
}
else {
self.vpnManager.loadFromPreferences(completionHandler: { (error) in
if((error) != nil) {
print("VPN Preferences error: 2")
}
else {
var startError: NSError?
do {
try self.vpnManager.connection.startVPNTunnel()
}
catch let error as NSError {
startError = error
print(startError)
}
catch {
print("Fatal Error")
fatalError()
}
if((startError) != nil) {
print("VPN Preferences error: 3")
let alertController = UIAlertController(title: "Oops..", message:
"Something went wrong while connecting to the VPN. Please try again.", preferredStyle: UIAlertControllerStyle.alert)
alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.default,handler: nil))
self.present(alertController, animated: true, completion: nil)
print(startError)
}
else {
self.VPNStatusDidChange(nil)
print("VPN started successfully..")
}
}
})
}
})
}
}
}
一旦VPN成功启动,您可以相应地更改状态,即通过调用VPNStatusDidChange
func VPNStatusDidChange(_ notification: Notification?) {
print("VPN Status changed:")
let status = self.vpnManager.connection.status
switch status {
case .connecting:
print("Connecting...")
self.labelConntectionStatus.text = "Connecting..."
self.switchConntectionStatus.isOn = false
self.isConnected = false
break
case .connected:
print("Connected")
self.labelConntectionStatus.text = "Connected"
self.switchConntectionStatus.isOn = true
self.isConnected = true
break
case .disconnecting:
print("Disconnecting...")
self.labelConntectionStatus.text = "Disconnecting..."
self.switchConntectionStatus.isOn = false
self.isConnected = false
break
case .disconnected:
print("Disconnected")
self.labelConntectionStatus.text = "Disconnected..."
self.switchConntectionStatus.isOn = false
self.isConnected = false
break
case .invalid:
print("Invalid")
self.labelConntectionStatus.text = "Invalid Connection"
self.switchConntectionStatus.isOn = false
self.isConnected = false
break
case .reasserting:
print("Reasserting...")
self.labelConntectionStatus.text = "Reasserting Connection"
self.switchConntectionStatus.isOn = false
self.isConnected = false
break
}
}
我在这里提到:
谢谢:)奇怪,当我在beta 4设备上运行您的代码副本时,我从[NEVPNManager sharedManager]返回了一个零;啊,我没有将“个人VPN”权限添加到我的应用程序ID或权限文件中。要这样做,请转到“功能”“在项目下,您如何从钥匙链检索密码?我已将我帐户的密码存储在密钥链中,并尝试将持久引用(文档暗示这是必要的)检索到protocol.passwordReference字段中,但当我连接到VPN服务时,它仍会提示我输入密码(一旦输入密码,我就连接了,因此其他一切看起来都很好)。使用
NEVPNManager
类需要com.apple.developer.networking.vpn.api
权限。您可以通过在Xcode中为应用程序启用“个人VPN”功能来获得应用程序的此权限。为什么不连接免费VPN ip地址目标cIt工作正常!还有一件事是,这个功能只适用于真实设备,不适用于模拟器。顺便说一句:今晚我会在博客上写这个,我会提到你,伙计;)你在beta5上试过这个吗?我有类似的代码,可以在beta 4之前使用,在beta 5上我遇到了一个错误:Domain=NEConfigurationErrorDomain code=2“Missing name”UserInfo=0x170078940{NSLocalizedDescription=Missing name}你知道“Missing name”错误吗?在xcode 6-beta 6中出现了相同的Missing name错误!有什么解决方案吗?我已经按照您的步骤进行了操作,但是我在连接到我的ikev2服务器使用的vpn时收到“意外错误”警报。crt和我已经在我的设备上安装了它,手动添加的配置工作正常,只有具有相同配置的个人vpn安装错误。crt可以同时使用添加的配置和个人vpn吗?或者我们必须为个人vpn做些其他事情才能使用证书?KeychainSwift在这里不起作用它只是将字符串作为数据返回您需要一个keychain ref@KooroshGhorbani我面临与此类相同的问题它在保存首选项时崩溃。