二次开发方案
一、方案的确定及要实现的效果
首先,最多的信息获取还是官方文档:https://lw.microstrategy.com/msdz/MobileUpdates/941_iOSU5/docs/mergedProjects/mobile_sdk/mobilesdk.htm
在本项目的一小部分,项目需求也是改来改去,最终需要实现的有:
应用图标的替换,应用名称的更改,启动图片的替换。
自定义登录界面。
登录时进行VPN验证,通过VPN服务器实现外网访问。
动态更改app的显示模式(文件夹目录或者默认报表)。
服务器集群,负载均衡的使用。
二、实现步骤
1、需求一的实现很简单,图标更改在Images.xcassets里面直接替换。应用名称在Info_IPad.plist,Bundle display name对应的名字改掉就可以。
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
创建自定义的登录界面
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
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文件,里面的项目地址也无须更改。