切换网络代理macos app开发

前言

因为每次在设置里手动切换代理太浪费时间了,所以这两天想做一个可以mac app, 可以打开或关闭网络代理,app不能有主窗口,只在菜单栏上有个按钮,这样比较简洁。

截屏2020-02-29下午9.11.18.png

步骤一:创建项目

new project->macos->app。

步骤二:去掉主窗口

截屏2020-02-29下午9.22.39.png

打开main.storyboard,打这两项删掉(直接按delete),再次run之后发现主窗口不显示了

步骤三:添加菜单栏

在AppDelegate.m里添加代码如下:

@interface AppDelegate ()

@property (nonatomic,strong) NSStatusItem *statusItem; //必须应用、且强引用,否则不会显示。

@end

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application
    NSStatusBar *statusBar = [NSStatusBar systemStatusBar];
        
    NSStatusItem *statusItem = [statusBar statusItemWithLength: NSSquareStatusItemLength];
    [statusItem setHighlightMode:YES];
    [statusItem.button setImage: [NSImage imageNamed:@"abc"]]; //设置图标,请注意尺寸
    [statusItem.button setToolTip:@"代理切换"];
    
    NSMenu *subMenu = [[NSMenu alloc] initWithTitle:@"Load_TEXT"];
    [subMenu addItemWithTitle:@"打开"action:@selector(clickItem1) keyEquivalent:@"E"];
    [subMenu addItemWithTitle:@"关闭"action:@selector(clickItem2) keyEquivalent:@"R"];
    statusItem.menu = subMenu;
    
    self.statusItem = statusItem;
}

- (void)clickItem1{
}

- (void)clickItem2{
}

注意:图片是本地,选个大小适中的图片。
运行后发现menu上显示app图标,说明成功了。

步骤四:执行shell命令

一. 切换网络代理命令

切换网络代理命令会用到networksetup指令,
打开自动代理配置命令:
sudo networksetup -setautoproxyurl 网络名称 pac地址//设置自动代理的url
sudo networksetup -setautoproxystate 网络名称 on//自动代理的状态改为 打开
关闭自动代理配置命令:
sudo networksetup -setautoproxystate 网络名称 off
注意:“网络名称”一般都是“Wi-Fi”,也可能是其他的,但是不是你家wifi的名字...,看下图:

截屏2020-02-29下午9.41.49.png

二. 运行shell命令的几种方法
1. c语言的system方法
 system(@"sudo networksetup -setautoproxystate Wi-Fi on".UTF8String);

此方法只能运行非root权限命令

2. NSTask

可以运行root或非root权限命令。运行root命令时会弹用户名密码提示
代码如下:

- (NSString *)runNSTaskCmd:(NSString *)cmd
{
    NSMutableArray *components = [[cmd componentsSeparatedByString:@" "] mutableCopy];
    NSString *launchPath = components[0];
    [components removeObjectAtIndex:0];
    
    // 初始化并设置shell路径
    NSTask *task = [[NSTask alloc] init];
    [task setLaunchPath: launchPath];
    // -c 用来执行string-commands(命令字符串),也就说不管后面的字符串里是什么都会被当做shellcode来执行
    NSArray *arguments = components;
    [task setArguments: arguments];
    
    // 新建输出管道作为Task的输出
    NSPipe *pipe = [NSPipe pipe];
    [task setStandardOutput: pipe];
    
    // 开始task
    NSFileHandle *file = [pipe fileHandleForReading];
    [task launch];
    
    // 获取运行结果
    NSData *data = [file readDataToEndOfFile];
    return [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
}

调用代码如下:

    [self runNSTaskCmd:@"/usr/sbin/networksetup -setautoproxystate Wi-Fi off"];
3. 第三方库STPrivilegedTask Github地址
- (void)runPrivilegedTask:(NSString*) cmdStr{
    STPrivilegedTask *privilegedTask = [[STPrivilegedTask alloc] init];
    
    NSMutableArray *components = [[cmdStr componentsSeparatedByString:@" "] mutableCopy];
    NSString *launchPath = components[0];
    [components removeObjectAtIndex:0];
    
    [privilegedTask setLaunchPath:launchPath];
    [privilegedTask setArguments:components];
    [privilegedTask setCurrentDirectoryPath:[[NSBundle mainBundle] resourcePath]];
    
    //set it off
    OSStatus err = [privilegedTask launch];
    if (err != errAuthorizationSuccess) {
        if (err == errAuthorizationCanceled) {
            NSLog(@"User cancelled");
            return;
        }  else {
            NSLog(@"Something went wrong: %d", (int)err);
            // For error codes, see http://www.opensource.apple.com/source/libsecurity_authorization/libsecurity_authorization-36329/lib/Authorization.h
        }
    }
    
    [privilegedTask waitUntilExit];
}

调用代码如下:

    [self runPrivilegedTask:@"/usr/sbin/networksetup -setautoproxystate Wi-Fi off"];

其实NSTask或者是STPrivilegedTask的传的参数是一样的,不过内部原理不同(STPrivilegedTask使用了AuthorizationRef进行提权)。
推荐使用NSTask,理由如下:
NSTask除了在第一次使用时会弹权限提示窗口,后面即便杀掉app再启动时执行shell命令也不会弹权限提示窗口,总之只弹了一次,我很奇怪,这样难道不更不安全了吗?而STPrivilegedTask无论重不重启app,只要执行一次shell命令就会弹权限窗口,非常的烦。

结语

代码放在了 github上,欢迎大家指正~~

你可能感兴趣的:(切换网络代理macos app开发)