MAC OS X中遇到的授權(quán)問題

關(guān)于MAC OS X##

去年十月份從iOS開發(fā)轉(zhuǎn)到OS X開發(fā),OS X主要由五大API來開發(fā),分別是`Cocoa`、`Carbon`、`POSIX`、`X11`和`Java`。目前我用的是`Cocoa`來開發(fā),OS X開發(fā)中考慮的問題更多一些,網(wǎng)上的資料更少一些,遇到問題后最好的解決辦法就是查看[官方文檔](https://developer.apple.com/search/?q=mac%20os%20x)。

什么時(shí)候需要授權(quán)?

在OS X開發(fā)中,我們會(huì)經(jīng)常遇到權(quán)限的問題,比如你安裝APP的時(shí)候,你如你需要?jiǎng)h除一些配置文件的時(shí)候都需要權(quán)限,不然就無法操作,我們的項(xiàng)目中需要做一個(gè)卸載工具和退域工具,里面涉及到刪除配置文件以及退出進(jìn)程等一些需要權(quán)限的操作。

具體內(nèi)容

刪除配置文件的時(shí)候,通常情況下通過`NSTask`這個(gè)類打開終端,輸入命令進(jìn)行操作的,如:

[NSTask launchedTaskWithLaunchPath:@"/usr/bin/sudo" arguments: [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchDaemons/com.tencent.MacScmClient.Daemon.plist",nil]];

這行代碼就是用NSTask來調(diào)用終端來進(jìn)行刪除文件的,大概意思就是在終端輸入sudo,然后執(zhí)行launchctlunload的命令,但是呢,怎樣寫IE肯定是不行的,為什么呢,因?yàn)槟銢]有權(quán)限這么做,有興趣的同學(xué)可以在終端嘗試一下,輸入這個(gè)命令是需要輸入密碼的,所以怎么獲取權(quán)限呢?
之前我們的工程中有獲取授權(quán)的代碼,這個(gè)下文再說,當(dāng)時(shí)同事寫了一段代碼使用AppleScript來獲取權(quán)限的,我再網(wǎng)上找了下,也找到了原文,具體的代碼如下:

Boolean runProcessAsAdministrator( NSString *scriptPath, NSArray *arguments,BOOL isAdmin, NSString **output,NSString **errorDescription)
{
   NSString * allArgs = [arguments componentsJoinedByString:@" "];
   NSString *isAdminPre = @"";
   if (isAdmin) {
    isAdminPre = @"with administrator privileges";
   }
   NSString * fullScript = [NSString stringWithFormat:@"%@ %@", scriptPath, allArgs];
   NSDictionary *errorInfo = [NSDictionary new];
   NSString *script = [NSString stringWithFormat:@"do shell script \"%@\" %@", fullScript,      isAdminPre];
   NSLog(@"script = %@",script);
   NSAppleScript *appleScript = [[NSAppleScript new] initWithSource:script];
   NSAppleEventDescriptor * eventResult = [appleScript executeAndReturnError:&errorInfo];
   // Check errorInfo/var/tmp
   if (! eventResult)
   {
       // Describe common errors
       *errorDescription = nil;
       if ([errorInfo valueForKey:NSAppleScriptErrorNumber])
       {
           NSNumber * errorNumber = (NSNumber *)[errorInfo valueForKey:NSAppleScriptErrorNumber];
           if ([errorNumber intValue] == -128)
            *errorDescription = @"The administrator password is required to do this.";
       }
       // Set error message from provided message
       if (*errorDescription == nil)
       {
           if ([errorInfo valueForKey:NSAppleScriptErrorMessage])
            *errorDescription = (NSString *)[errorInfo valueForKey:NSAppleScriptErrorMessage];
       }
       return NO;
   }
   else
   {
       // Set output to the AppleScript's output
    *output = [eventResult stringValue];
       return YES;
   }
}

這段代碼就是用AppleScript來獲取權(quán)限的,使用方法如下:

NSString *output = @"";
NSString *error= @"";
runProcessAsAdministrator(@"", [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchAgents/com.******.******.plist",nil],false, &output, &error);

但是這么寫會(huì)有一個(gè)問題,就是我是獲得權(quán)限了,但是卸載的時(shí)候往往不可能只刪除一個(gè)配置文件,而是要?jiǎng)h除很多個(gè)配置文件,起初我是這么寫的:

runProcessAsAdministrator(@"", [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchAgents/com.******.******.plist",nil],false, &output, &error);
    NSLog(@"output = %@, error = %@", output, error);
runProcessAsAdministrator(@"", [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchDaemons/com.******.******.Daemon.plist",nil], true, &output, &error);
    NSLog(@"output = %@, error = %@", out

我就很開心的運(yùn)行,但是問題來了,什么問題呢?就是你刪除幾個(gè)配置文件就需要輸入幾次用戶名和密碼,這樣的話用戶體驗(yàn)相當(dāng)?shù)牟缓?,理想狀態(tài)是什么呢?輸入一次用戶名和密碼之后,獲取的權(quán)限以后其他的刪除配置文件的操作就不需要再次獲取權(quán)限了,所以我就看了下前輩們是怎么寫的,大概的代碼如下:

int main(int argc, char * argv[]) {
@autoreleasepool {
    
    if (argc == 2) {
        NSLog(@"AuthHelperTool executing self-repair");
        OSStatus myStatus;
        AuthorizationFlags myFlags = kAuthorizationFlagDefaults;
        AuthorizationRef myAuthorizationRef;
        
        myStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, myFlags, &myAuthorizationRef);
        if (myStatus != errAuthorizationSuccess)
            return myStatus;
        
        AuthorizationItem myItems = {kAuthorizationRightExecute, 0, NULL, 0};
        AuthorizationRights myRights = {1, &myItems};
        myFlags = kAuthorizationFlagDefaults |
        kAuthorizationFlagInteractionAllowed |
        kAuthorizationFlagPreAuthorize |
        kAuthorizationFlagExtendRights;
        
        myStatus = AuthorizationCopyRights (myAuthorizationRef, &myRights, NULL, myFlags, NULL );
        if (myStatus != errAuthorizationSuccess)
            return myStatus;
        
        char *myToolPath = argv[1];
        char *myArguments[] = {argv[1], "--fix", NULL};
        FILE *myCommunicationsPipe = NULL;
        
        myFlags = kAuthorizationFlagDefaults;
        //這句是獲取管理員權(quán)限的代碼
        myStatus = AuthorizationExecuteWithPrivileges(myAuthorizationRef, myToolPath, myFlags, myArguments, &myCommunicationsPipe);
        return 0;

    }
    else if (argc == 3){
    
    NSString *output = @"";
    NSString *error= @"";

    runProcessAsAdministrator(@"", [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchAgents/com.******.******.plist",nil],false, &output, &error);
    runProcessAsAdministrator(@"", [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchDaemons/com.******.******.Daemon.plist",nil], true, &output, &error);
}

這段代碼是個(gè)可執(zhí)行文件,然后在ViewController中調(diào)用的這個(gè)可執(zhí)行文件。我們看到這個(gè)獲取權(quán)限的方法是使用Authorization的類來獲取權(quán)限的,這個(gè)也是官方文檔中的獲取權(quán)限的方法,具體的可以參考這里
,繼續(xù)說上面的代碼,前文說了這個(gè)是個(gè)可執(zhí)行文件,是在ViewController中調(diào)用的,代碼如下:

NSString *helperToolPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/ExitDomain"] ;
NSLog(@"path = %@",helperToolPath);
NSArray *args = [NSArray arrayWithObjects:helperToolPath, nil];
[NSTask launchedTaskWithLaunchPath:helperToolPath arguments:args];

從這兒看到,我們是用NSTask這個(gè)類來打開終端來執(zhí)行這個(gè)可執(zhí)行文件的,參數(shù)為helperToolPath也就是這個(gè)可執(zhí)行文件的路徑,所以到了可執(zhí)行文件的mian函數(shù)中,argc就是2,然后來執(zhí)行下面的獲取權(quán)限的代碼的,下面獲取權(quán)限的代碼有個(gè)需要注意的地方:

char *myToolPath = argv[1];
        char *myArguments[] = {argv[1], "--fix", NULL};
        FILE *myCommunicationsPipe = NULL;
        
        myFlags = kAuthorizationFlagDefaults;
        //這句是獲取管理員權(quán)限的代碼
        myStatus = AuthorizationExecuteWithPrivileges(myAuthorizationRef, myToolPath, myFlags, myArguments, &myCommunicationsPipe);

這幾行代碼需要注意,首先myToolPath這個(gè)參數(shù)就是我們從ViewController中配置的那個(gè)參數(shù),也就是可執(zhí)行文件的路徑,其次myArguments里面多了個(gè)--fix,這個(gè)不是命令,只是為了使argc的個(gè)數(shù)變?yōu)槿?,?/p>

myStatus = AuthorizationExecuteWithPrivileges(myAuthorizationRef, myToolPath, myFlags, myArguments, &myCommunicationsPipe);

這行代碼就是獲取管理員權(quán)限的代碼,從官方文檔來看在10.1和10.7中是不能使用的,具體的可以看官方文檔,如果想測試自己的系統(tǒng)能不能用,請參考github上的這個(gè)第三方庫中用的測試方法,然后這行代碼還有個(gè)作用,就是它又調(diào)用了一次這個(gè)可執(zhí)行文件,方法中有個(gè)參數(shù)是myToolPath,這個(gè)就是可執(zhí)行文件的路徑,所以esle if中的代碼才會(huì)執(zhí)行,按照這種方法寫完后就只需要輸入一次用戶名和密碼,用戶體驗(yàn)明顯好轉(zhuǎn)。

總結(jié)##

本文講了兩種獲取權(quán)限的方法,一種是使用`AppleScript`來獲取權(quán)限,這樣寫是能獲取到權(quán)限,但是如多刪除多個(gè)文件的話,就需要輸入多次用戶名和密碼;另一種是用`Authorization`這個(gè)類來獲取權(quán)限,先獲取到權(quán)限后,然后再執(zhí)行刪除配置文件的操作就可以了,需要注意的地方就是,我再使用第二種方法的時(shí)候`esle if`中調(diào)用的還是`AppleScript`刪除文件的方法,沒有用`NSTask`這個(gè)類,這個(gè)說明一下,這兒完全可以使用`NSTask`這個(gè)類做這些事情,也就是下面這段代碼:

runProcessAsAdministrator(@"", [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchAgents/com.******.******.plist",nil],false, &output, &error);

完全可以用這段替換掉:

[NSTask launchedTaskWithLaunchPath:@"/usr/bin/sudo" arguments: [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchDaemons/com.tencent.MacScmClient.Daemon.plist",nil]];

這個(gè)只是我項(xiàng)目中遇到的一些關(guān)于權(quán)限的問題以及解決辦法,還有很多不足之處,希望各路大神多多指正,我也會(huì)再繼續(xù)深入學(xué)習(xí),及時(shí)修改這篇文章。

參考資料

[https://developer.apple.com/search/?q=Authorization&type=Guides]
[http://stackoverflow.com/questions/29796363/how-to-add-root-privileges-to-my-osx-application]
[http://codego.net/213006/]
[http://www.tanhao.me/pieces/1279.html/]
[https://github.com/sveinbjornt/STPrivilegedTask]

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容