B11-MSTR移动app的二次开发(iOS)
二次开发方案
一、方案的确定及要实现的效果
首先,最多的信息获取还是官方文档:https://lw.microstrategy.com/msdz/MobileUpdates/941_iOSU5/docs/mergedProjects/mobile_sdk/mobilesdk.htm
iOS的开发其实只在本项目的一小部分,项目需求也是改来改去,最终需要实现的有:
- 应用图标的替换,应用名称的更改,启动图片的替换。
- 自定义登录界面。
- 登录时进行VPN验证,通过VPN服务器实现外网访问。
- 动态更改app的显示模式(文件夹目录或者默认报表)。
- 服务器集群,负载均衡的使用。
二、实现步骤
1、需求一的实现很简单,图标更改在Images.xcassets里面直接替换。应用名称在Info_IPad.plist,Bundle display name对应的名字改掉就可以。
2、由于mstr的工程只有一个main.m文件可以看到。所以要想在原app的基础上进行二次开发,首先要创建一个自定义的CustomAppDelegate.h CustomAppDelegate.m文件。此部分详解在文档的目录为:Home > Mobile SDK > Mobile SDK for iOS > Customizing MicroStrategy Mobile > Customization scenarios > Adding functionality with a custom Application Delegate。
// // CustomAppDelegate.m // MicroStrategyMobile // // Copyright (c) 2014 MicroStrategy Inc. All rights reserved. // #import "CustomAppDelegate.h" #import "CustomLoginView.h" #import <MicroStrategyMobileSDK/MSIAuthenticationModule.h> #import <MicroStrategyMobileSDK/MSIAuthenticationPromptViewController.h> #import <MicroStrategyMobileSDK/MSIMobileLoginManager.h> @implementation CustomAppDelegate -(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ BOOL res = [super application:application didFinishLaunchingWithOptions:launchOptions]; //add custom logic here return res; } @end
创建自定义的登录界面
需要创建CustomLoginView.h CustomLoginView.m文件。可参照文档目录:Home > Mobile SDK > Mobile SDK for iOS > Customizing authentication > Client-side customizations > Programmatic customizations > Customizing login screen UI and authentication parameters programmatically
#import "CustomLoginView.h" @implementation CustomLoginView - (void) setupWithParameters:(NSDictionary*) promptViewParameters delegate:(id<MSIMobileLoginPromptViewDelegate>) delegate{ [super setupWithParameters:promptViewParameters delegate:delegate]; //After calling super, add additional code to setup the login UI, for example, add textfields, login buttons, background image } -(void) login{ [self.delegate loginPromptView:self didInputAuthenticationParameters:@{ @"username":([usernameTextField text]?[usernameTextField text]:EMPTY_STRING), @"password":([passwordTextField text]?[passwordTextField text]:EMPTY_STRING) }]; } @end
同时CustomAppDelegate.m文件也要做更改
// // CustomAppDelegate.m // MicroStrategyMobile // // Copyright (c) 2014 MicroStrategy Inc. All rights reserved. // #import "CustomAppDelegate.h" #import "CustomLoginView.h" #import <MicroStrategyMobileSDK/MSIAuthenticationModule.h> #import <MicroStrategyMobileSDK/MSIAuthenticationPromptViewController.h> #import <MicroStrategyMobileSDK/MSIMobileLoginManager.h> #import <MicroStrategyMobileSDK/ProjectInfo.h> @implementation CustomAppDelegate -(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ CustomLoginView *loginView = [[CustomLoginView alloc] initWithFrame:self.window.frame]; MSIAuthenticationPromptViewController *loginViewController = [[MSIAuthenticationPromptViewController alloc] init]; MSIAuthenticationModule *loginModule = [[MSIAuthenticationModule alloc] init]; [[MSIMobileLoginManager sharedMobileLoginManager] setView:loginView controller:loginViewController module:loginModule forPromptType:AuthenticationPromptType]; BOOL res = [super application:application didFinishLaunchingWithOptions:launchOptions]; return res; } @end
其实MSIAuthenticationPromptViewController也可以创建再继承,用来监听登录的成功和失败,文档目录:Home > Mobile SDK > Mobile SDK for iOS > Customizing authentication > Client-side customizations > Programmatic customizations > Adding custom logic during authentication process。
到此,自定义登录界面已经实现。
3、VPN服务器的认证和使用
在iOS平台下,第三方应用程序通过调用移动应用安全认证开发包,使用证书与VPN服务器建立安全通道、以及单点登录等功能的实现。此处以北京国富安提供的开发包为例(具体细节参照国富安提供的开发文档)。
- 首先在iPad端安装移动认证证书(国富安提供)。
- 从Keychain里读取证书的cn。设置指定CN的证书,用此证书来做为身份。根据身份证书去跟vpn建立ssl连接。
- 根据VPN上配置的资源,启动本地监听的端口。
- 通过VPN调用服务器接口,获取配置文件。
- 显示登录界面,点击登录,根据证书和主账号密码进行统一安全认证。
- 认证成功返回的json,包含移动平台用户的主从账号信息,并会在Keychain中存储session,附应用可以通过Keychain中存储的session直接登录成功。
- 根据json信息,将主账号作为mstr用户账号和mstr用户默认密码一起,在应用内自动进行mstr的认证。成功界面会跳转,不跳转且无提示可以检查所使用mstr用户是否存在。(注:以移动平台主账号为账号,自定义统一的默认密码,提前在mstr创建用户)
/** * 2、从设备的钥匙串中获取证书,如果获取失败就返回nil; * @return 证书或者nil */ -(NSString *)getCNfromKeychain { NSMutableArray *outArray = [[NSMutableArray alloc] init]; //获取证书cn项,因为有可能有多个证书,所以返回的是一个cn项的数组 BOOL ret = [[CertHelper sharedInstance] getCNfromKeychain:outArray]; if (!ret) { NSLog(@"获取证书失败,请联系管理员"); return nil; } //一般情况只有一个证书 NSString *certCN = [outArray objectAtIndex:0]; return certCN; } if ([[CertHelper sharedInstance] setIndexbyCN:[self getCNfromKeychain]] == -1) //指定身份证书,返回-1表示设置指定证书失败 { NSLog(@"your identity error"); } /** * 将身份证书和VPN做认证以登录VPN创建ssl连接 * @return VPN连接状态 */ -(BOOL)connectVPN { int iResult = -1; @try { iResult =[[L4Proxy sharedInstance] L4Proxy_ShakeHands_With_VPN:kVPNServer IPPort:kVPNPort username:@"" password:@""]; } @catch (NSException *exception) { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"网络连接失败" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alertView show]; return NO; } @finally { if (iResult != 0) { NSLog(@"VPN 登录失败"); return NO; }else { NSLog(@"VPN 登录成功"); return YES; } } }
// 3、获取监听端口 - (void)refreshConnectionInfo { Constants* constants = [Constants sharedInstance]; int webPort = [[L4Proxy sharedInstance] startLocalListen:@"BI-8080"]; int port = [[L4Proxy sharedInstance] startLocalListen:@"BI-34952"]; if(webPort > 0) { constants.webPort = webPort; constants.webServer = LOCAL_HOST; } if(port > 0) { constants.port = port; constants.iserver = LOCAL_HOST; } }
/* 5、6、点击登陆时调用的方法,将密码输入框的参数传过来,返回证书的主从账号信息,并会在Keychain中存储session,附应用可以通过调用相应的方法找到Keychain中存储的session直接登录成功 */ - (Account *)ssoLogin:(NSString*) password { int ssoLocalListenPort = [[L4Proxy sharedInstance] startLocalListen:ssoDesStr]; if (ssoLocalListenPort > 0) { NSString *ssoHost = [NSString stringWithFormat:@"https://%@:%d",LOCAL_HOST, ssoLocalListenPort]; NSString *retJson = [[SsoAuth sharedInstance] ssoAuthByCert:password withAppID:appId withServerAddress:ssoHost]; NSLog(@"retJson = %@", retJson); NSError *error = nil; NSDictionary* result = [NSJSONSerialization JSONObjectWithData:[retJson dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:&error]; NSLog(@"%@",result); if (error == nil && [@"success" isEqualToString:[result objectForKey:@"status"]]) { if(_account == nil) { _account = [[Account alloc] init]; } _account.name = [result objectForKey:@"masterAccout"]; _account.token = [result objectForKey:@"token"]; return _account; } else { NSString *message = [result objectForKey:@"message"]; if(message == nil) { message = @"请检查网络"; } UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:message delegate:nil cancelButtonTitle:@"确认" otherButtonTitles:nil, nil]; [alertView show]; NSLog(@"单点登录JSON解析失败, error:%@", error); return nil; } } else { return nil; } }
4、动态更改app的显示模式(文件夹目录或者默认报表)
上步的第4步骤,通过VPN调用服务器接口,获取配置文件,就是为了获取XML文件,每次启动都重新获取XML文件,所以可以在后台对XML文件内容进行更改,下次启动的时候,就会显示新更改的模式。
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSString *str = [NSString stringWithFormat:@"http://127.0.0.1:%d/MicroStrategy/findDocumentId?loginname=%@",constant.webPort,certName]; NSString *urlString = [str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSURL *url = [NSURL URLWithString:urlString]; NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url]; NSURLResponse *response = nil; NSError *error = nil; NSData *allStringdata = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; NSString *allString = [[NSString alloc] initWithData:allStringdata encoding:NSUTF8StringEncoding]; //保存到沙盒目录下 NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString *xmlPath1 = [path stringByAppendingString:@"/Preferences.xml"]; [allString writeToFile:xmlPath1 atomically:YES encoding:NSUTF8StringEncoding error:nil]; return YES; }
XML文件的获取,用浏览器登录mstr的mobile server,在mobile配置的地方,进行主屏幕的配置。之后会在mstr服务器的安装路径下找到一个刚生成的XML文件,例如,D:\apache-tomcat-6.0.36\webapps\MicroStrategyMobile\WEB-INF\xml\mobile。
注意:取出的XML文件需要把服务器地址改为本地,端口号改为本地监听端口,项目地址不改。
5、服务器集群,负载均衡的使用。
负载均衡的使用,不需要app作何更改。由于VPN的使用app中只有本地地址127.0.0.1和监听端口号的存在。同样配置文件XML文件,里面的项目地址也无须更改。