Android Studio2.0的逆天功能Instant Run - 姜家志 - SegmentFault

mikel阅读(1045)

来源: Android Studio2.0的逆天功能Instant Run – 姜家志 – SegmentFault

作为一个Android开发者,很多的时候我们需要花大量的时间在bulid,运行到真机(虚拟机)上,对于ios上的Playground羡慕不已,这种情况将在Android Studio 2.0有了很大改善,使用instant run,在第一次运行之后,就可以快速的在真机中看见修改后的结果,不仅仅是UI可以直接显示,还包括代码逻辑。不用再苦苦等build了,节约生命呀!

首先要升级到Android Studio 2.0

目前Android Studio的2.0版本还在Canary Channel(金丝雀) 上面,所以想体验2.0的同学需要先把升级版本切换到Canary Channel 上面。

Preferences -> System Settings ->Updates

可以切换升级版本

然后check for updates 就可以升级了。(如果连接不上升级服务器,请墙一下)

升级android tools build

instant run功能之后再android tools build 的2.0.0的版本才可以使用。
需要在build.gradle中指定

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.0.0-alpha1'
    }
}

设置instant Run

Preferences -> Build,Execution,Deployment -> Instant Run


中有关于 Instant Run的设置

第一个设置是,是否开启Instant Run的,默认是开启。

第二个是,当代码变动的时候重启activity(亲测没有效果,不知道是不是还不是太完善)

第三个是,每次变动的时候都有个toast提示下(我选择关闭了)

运行Instant Run

再没有运行项目的时候,我们的Run图标和以前是一样的。

(话说这个Debug的图标好可爱)

运行了项目之后的图标是这样的:

运行之后的Dialog框是这样的,和Debug的时候一样多了个Stop的按钮

再改完代码之后直接点击Run按钮就可以立刻就能在你的设备上面看到效果。

目前还是有一些小的问题,偶尔还是会出现build ->run到设备的方式,期待Google尽快的把instant Run做的更加完善,也期望AS2.0的正式版本。
另外google说优化了虚拟机部分,性能提高了50倍,是不是可以抛弃Genymotion了?

This version of android studio is incompatible with the gradle version used. - 张武不是技术宅的博客 - CSDN博客

mikel阅读(993)

来源: This version of android studio is incompatible with the gradle version used. – 张武不是技术宅的博客 – CSDN博客

问题背景

因为项目app分包需要,将工程的graddle版本升至了2.2.0 alpha4,而我的Android studio 一向升级为最新版,于是就出现了题目中的报错:

Error running app: This version of Android Studio is incompatible with the Gradle Plugin used. Try disabling Instant Run (or updating either the IDE or the Gradle plugin to the latest version)

当前版本的Android studio 与 gradle不兼容。在log中 提示我升级ide和gradle为最新版,然而我出问题的就是最新版,于是尝试 禁用Instant Run,但仍然不能解决问题。

解决方案

通过百度log中的问题,在stackoverflow上有人提出了解决方案。

参考:http://stackoverflow.com/questions/35202541/this-version-of-android-studio-is-incompatible-with-the-gradle-version-used-try

step1:

禁用Instant Run,在Settings/Preferneces > Build, Execution, Deployment option > Instant Run 中,取消所有的选中项

 

step2:

更新gradle services 的url:在project目录下的 gradle>wrapper>gradle-wrapper.properties中将

distributionUrl=https://services.gradle.org/distributions/gradle-2.10-all.zip

更新为:

distributionUrl=https://services.gradle.org/distributions/gradle-2.11-all.zip

step3:

删除project下的build文件夹

step4:

clean项目,重新编译跑程序

Failed to resolve: com.android.support.constraint:constraint-layout:1.0.0-alpha9

mikel阅读(1859)

来源: Failed to resolve: com.android.support.constraint:constraint-layout:1.0.0-alpha9 <a – CSDN博客

把一个工程导入as 的时候报错,Failed to resolve: com.Android.support.constraint:constraint-layout:1.0.0-alpha9,

很明显是constraint-layout的依赖的问题。

怎么解决这个问题呢,其实也不难,主要是你的Android studio里没有constraint-layout:1.0.0-alpha9这个东西。怎样确定你的Android studio里有哪个版本呢?

第一步,先点击1处的SDK Manager,然后就会有个弹出一个比较大的对话框

第二步,点击SDK tools。

第三步,勾选右下角的show Package Details

第四步,你就可以看到你安装了constraint-layout的哪个版本了,如果一个也没有装,那就赶紧勾选一个,然后点击ok,Android studio就会开始下载了。

刚刚我们说到你的Android studio里没有constraint-layout:1.0.0-alpha9这个东西,所以报错,那么解决这个bug的方法就是,安装constraint-layout:1.0.0-alpha9这个版本就好了。

当然比如说你的Android studio里装的是constraint-layout:1.0.0而不是constraint-layout:1.0.0-alpha9,那么你也可以把app的build.gradle里constraint-layout:1.0.0-alpha9改成constraint-layout:1.0.0。这样Sync按钮后也可以解决这个问题。

如果本文对你有帮助的话,麻烦在下方点个赞,谢谢支持。

发布ionic应用到App Store的完整步骤 - liujiawei00的博客 - CSDN博客

mikel阅读(1769)

来源: 发布ionic应用到App Store的完整步骤 – liujiawei00的博客 – CSDN博客

当我们开发完一个应用,就到了发布到市场的时候,Android的打包比较简单,签名之后可以放在我们自己的服务器上,让用户扫描二维码来下载,而苹果的就比较麻烦了,如果内测可以通过蒲公英等内测分发平台,但是这个时候使用的证书是开发证书,只有在profile里注册过uuid的设备才可以安装。如果想让所有使用苹果的用户都可以安装我们的APP,只有发布到App Store。
发布ionic应用到App Store的步骤:

1:打开Mac电脑的Xcode,注意beta版的Xcode是不能上传应用到app store的。
登录你的apple id开发者账号。如果是企业账号($99),最后的发布只能使用角色为agent的企业账户来发布。如果是个人账户,个人即为agent,team也就是个人的名字。一个开发者账户可以是多个企业账户team的开发人员,同样一个企业账户的team可以有多个角色为admin的开发人员,admin没有agent的权限多。
其实apple的证书并不复杂,就这么几种:identify certificates是带有钥匙串的唯一性证书,provisioning profile是包含identify和app id,device这些信息在一起生成的一个综合信息证书,app id是app的唯一标识。
如果只是发布,打开Xcode的preferences\account,登录agent账户,点击manage certificates,点击+号,选择app store,会自动生成production版本的identify certificates,keychain也会自动生成。

这里写图片描述

之后在苹果开发者中心网站创建app id:

这里写图片描述

这个explicit app id应该和你的ionic 项目config.xml文件里的id一致。
然后去生成provisioning profile,非常简单,选择一下app id 和identify certificates,生成之后点击下载,双击下载之后的文件,就会自动打开Xcode,这时候证书就全部在Xcode里面了,或者点击preferences\account的download all profiles也可以自动将证书在Xcode里下载好。

2:在你的ionic项目执行:ionic cordova build ios –release,如果失败,可以先build一个普通版本,到xcode里把证书选上,之后再回来build。
成功之后在xcode里打开你的项目路径下面的platforms\ios\xxxxx.xcodeproj文件:

这里写图片描述

Bundle Id应该和你的ionic项目的id一致。
Signing选项下面会自动出现Xcode里面所有可用的profile文件,选择好你刚才创建的那个用于发布的profiles,xcode会自动的找到这个profile对应的signing certificate。
这些信息都填好没有错误之后,点击product\scheme\edit scheme选择release:

这里写图片描述

然后点击Product\Archive生成.ipa文件:

这里写图片描述

生成成功之后,选择export,导出.ipa文件到你的mac。在Xcode\Open Developer Tool\Application Loader里登录开发者账号(和你在ITunes Connect里登录的一致),点击Deliver Your App打开这个ipa文件:
这里注意,如果不是发布,而是想在蒲公英等内测分发平台发布,用来内测,将这个.ipa文件上传到蒲公英你的账号就可以了。不过export的时候注意不要选择App Store 模式,而要选择 Ad Hoc模式。而且你在生成开发证书profile选择设备的时候要将声明过udid的设备选择进来才能在这些设备安装内测版本的app。

这里写图片描述

然后就可以开始上传你的app 到App Store了,大概需要半个小时的时间。

3:在开发者中心(developer.apple.com)登陆开发者账号,选择iTunes Connect,这个iTunes Connect是用来管理app store的app信息和提交,更新,分析的地方
进入之后,选择My Apps

这里写图片描述

点击+号,新建一个APP,填上所有需要的信息,如果刚才使用Application Loader上传的app成功了的话,你可以在活动里看到你构建版本的记录:

这里写图片描述

如果这里没有记录,说明上传没有成功。注意在ios10因为苹果加强了用户权限管理,你需要在plist文件里声明你需要用到的camera,contacts,bluetooth等等,最好把基础的全部声明,防止上传失败。

这里写图片描述

如果有记录,在ios准备提交的页面,构建版本旁边会有+号,点击选择你上传的版本,会出现图标和上传时间。

这里写图片描述

之后把所有的信息填好,点击存储,提交进入审核就可以进入审核阶段了。

iOS经典错误library not found for -lXXX - 简书

mikel阅读(4838)

来源: iOS经典错误library not found for -lXXX – 简书

开发中经常会和ibrary not found for -xxx打交道,尤其是我们从远程仓库或者github clone下来一个项目,编译很有可能报这个错误。如下图:

报错信息

library not found for -lXXX 属于iOS经典错误之一。下面我们通过复现这个错误的方式来说明这个错误的解决方法。其中l是lib(library)的意思。

首先声明,我的工程中引用了友盟的统计SDK,名称叫做libMobClickLibrary.a。存储在 third_party目录下,如下图(左图为libMobClickLibrary.a在工程目录/虚拟目录的位置,右图为libMobClickLibrary.a在磁盘目录/物理目录的位置):

工程目录

磁盘目录

没有配置Library Search Paths导致报错

(1)删除building setting -> Library Search Paths下的 $(PROJECT_DIR)/YunFu/third_party/UMAnalytics。如下图:
那么,你可能会问,为什么要删除这个路径而不是其他的路径呢?因为我知道这个路径就是libMobClickLibrary.a静态库的目录。

(2)command + B编译项目,然后不出所料,我们会遇到下面这个错误:

(3)点开这个错误,然后就能看到这个经典错误,如下图:
这个错误的意思是找不到名字叫做 MobClickLibrary的库。

至此,我们可知,就因为我们在Library Search Paths下删除了$(PROJECT_DIR)/YunFu/third_party/UMAnalytics 导致报这个library not found for -lMobClickLibrary的错误。所以下次遇到同类问题,我们便可知道某个静态库的路径可能没有配置。

结论一:没有配置静态库的search路径导致出现library not found for lXXX。
注意:设置的路径必须是静态库在磁盘下的路径(物理目录),不能设置为项目的虚拟目录。拿此例来说,libMobClickLibrary.a的在项目中是放在third_party文件夹下的,而其真正的磁盘路径是third_party下的UMAnalytics文件夹。


没有正确配置Library Search Paths导致报错

上面的情况是,我们没有配置某个静态库的路径会导致library not found for lXXX错误。而有时候,我们确实在Library Search Paths下配置了静态库路径,却依然报同样错误。此时我们就要考虑,这个路径配置的到底对不对。如下图(分别是libMobClickLibrary.a正确的路径和错误的路径):
正确路径:

正确路径

错误路径(去掉了最后一级目录”/UMAnalytics”):

错误路径

编译项目依然会报同样的错误library not found for -lMobClickLibrary.a,如下图:

错误信息

归根到底,原因在于,我们虽然跟他指定了libMobClickLibrary.a的目录,但是在所指定的目录下没有搜索到静态库libMobClickLibrary.a。此时又分为两种情况:

  • 情况一:如果我们指定的目录和静态库libMobClickLibrary.a的目录完全不相关,那么需要重新到Library Search Paths下设置(注意:设置的是静态库的磁盘路径)
  • 情况二:如果我们指定的目录和静态库的目录有关系。即,我们指定的目录是静态库所在目录的上级目录,或者是上上级目录。这种情况下,无需指定静态库的精确的路径,只需要把non-recursive选项修改为recursive即可。如下图:

    设置recursive

因为recursive选项代表在我们指定的目录下递归搜索静态库。即,如果在当前目录没有搜索到静态库,那么就去当前目录的子目录下继续搜索。默认情况下是non-recursive(非递归)的。

结论二:虽然Library Search Paths 中配置了静态库的search路径。但是没有正确配置而导致出现library not found for lXXX。


导入第三方framework静态库编译报错xxx.h file not found

上面讲的是当我们项目导入的是.a静态库,但因为没有在 Library Search Paths 中配置.a静态库的search路径,或者是虽然配置了路径但是路径配置错误而导致出现library not found for -xxx错误的解决办法。
但我们知道,在iOS中,静态库有两种形式:.a格式的静态库和.framework格式的静态库。
那么如果我们的项目中引入了.framework格式的静态库要不要配置search路径呢?答案是肯定的,无论我们以什么方式导入.framework静态库(cocoapods的方式或者直接把framework静态库拖拽到项目中的方式),如果没有配置framework的search路径,同样会报错,但报的错误却是和.a格式的静态库有些不同,如果我们项目中引入了framework格式静态库也使用了静态库(所谓使用就是import了framework静态库中的某个头文件),但没有配置search路径,那么就会报 xxx.h file not found 这类错误。如下图:

xxx file not found

因为我的项目中引入了一个叫做PushCenterSDK.framework的静态库。同时也使用了这个静态库中的某个API。如下图所示,分别是静态库在项目中的路径和磁盘路径:

在项目中的路径

在磁盘中的路径

我们依然采用复现问题的方式来验证解决错误的方法,首先,我的项目是可以编译通过的,然后尝试在build setting -> Framework Search Paths 下删除PushCenterSDK.framework的搜索路径(磁盘路径),下图分别是删除前和删除后的截图:

删除前:

删除前

删除后:

删除后

删除路径之后编译项目,发现出现了如下错误XXX.h file not found, 如下图:
很显然,没有找到PushCenterSDK 下的TBSDKPushCenterEngine.h文件。

报错

综上,framework静态库的路径也是必须的,如果项目中的framework静态库的路径配置错误或者没有配置就会报错 xxx.h file not found 类似错误。下次遇到这类错误,便可以从framework search paths 着手思考。

同样,framework静态库的搜索路径也支持递归搜索,我们不必配置一个精确无误的路径,可以指定一个父级目录,然后选择 recursive。

结论:无论我们以什么方式导入.framework静态库(cocoapods的方式或者直接把framework静态库拖拽到项目中的方式),如果没有配置framework的search路径,同样会报错,但报的错误却是和.a格式的静态库有些不同,如果我们项目中引入了framework格式静态库也使用了静态库(所谓使用就是import了framework静态库中的某个头文件),但没有配置search路径,那么就会报 xxx.h file not found 这类错误


文/VV木公子(简书作者)
PS:如非特别说明,所有文章均为原创作品,著作权归作者所有,转载请联系作者获得授权,并注明出处,所有打赏均归本人所有!

作者:VV木公子
链接:http://www.jianshu.com/p/72aec7e38ef0
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

微信demo下载下来运行出现 missing required architecture i386 in file “xxxxxx/libWeChatSDK.a”问题 - gyz413977349的专栏 - CSDN博客

mikel阅读(1306)

来源: 微信demo下载下来运行出现 missing required architecture i386 in file “xxxxxx/libWeChatSDK.a”问题 – gyz413977349的专栏 – CSDN博客

1. 警告:directory not found for option “xxxxxxxx” 文件路径未找到

  • 选择工程, 编译的 (targets)
  • 选择 Build Settings 菜单
  • 查找 Library Search Paths 和 Framework Search Paths, 删掉编译报warning的路径即OK
    详细步骤图文参考

2. missing required architecture i386 in file “xxxxxx/.a” 说明你的这个library文件是为Device (ARM架构)设计的,不支持Simulator (i386架构)

相关知识
参考stackoverfollow
arm架构问题
手机的架构是为arm架构

  • armv6
    iPhone
    iPhone2
    iPhone3G
    第一代和第二代iPod Touch
  • armv7
    iPhone4
    iPhone4S
    armv7s
    iPhone5
    iPhone5C
  • arm64
    iPhone5S

现在app store规定提交的应用必须包含arm64.

我遇到的问题

  1. 微信demo下载下来运行出现 missing required architecture i386 in file “xxxxxx/libWeChatSDK.a”问题,用终端查看了一下这个库文件只支持 armv7,arm64
在终端里查看你的.a库是不是支持i386

在终端里查看你的.a库是不是支持i386

解决方法:

  • 找找看有没有支持i386的库文件(下载SDK,要下载的sdk里面的那个libWeChatSDK.a才有支持i386的,里面有两个文件夹 WeChatSDK_1.5WeChatSDK_1.5_OnlyIphone,选择WeChatSDK_1.5)
    WeChatSDK_1.5

    WeChatSDK_1.5
    有i386的libWeChatSDK.a

    有i386的libWeChatSDK.a
  • 使用真机测试

2.解决架构问头后在工程里出现了以下问题:
Undefined symbols for architecture x86_64:
“operator delete”, referenced from:
+[WeChatApiUtil EncodeBase64:] in libWeChatSDK.a(WeChatApiUtil.o)

屏幕快照 2014-12-11 下午3.43.16.png

解决方法:

  • 果断加上libc++.dylib

iOS 各种证书/签名详解 - smaller_coder的博客 - CSDN博客

mikel阅读(1561)

来源: iOS 各种证书/签名详解 – smaller_coder的博客 – CSDN博客

引言

        关于开发证书配置(Certificates & Identifiers & Provisioning Profiles),相信做 iOS 开发的同学没少被折腾。对于一个 iOS 开发小白、半吊子(比如像我自己)抑或老兵,或多或少会有或曾有过以下不详、疑问、疑惑甚至困惑:

  1. 什么是App ID?Explicit/Wildcard App ID有何区别?什么是App Group ID?
  2. 什么是证书(Certificate)?如何申请?有啥用?
  3. 什么是Key Pair(公钥/私钥)?有啥用?与证书有何关联?
  4. 什么是签名(Signature)?如何签名(CodeSign)?怎样校验(Verify)?
  5. 什么是(Team)Provisioning Profiles?有啥用?
  6. Xcode如何配置才能使用iOS真机进行开发调试?
  7. 多台机器如何共享开发者账号或证书?
  8. 遇到证书配置问题怎么办?
  9. Xcode 7免证书调试真机调试

本文将围绕相关概念及背景做个系统的梳理串烧,于条分缕析中对证书体系进行抽丝剥茧,逐步揭开签名机制的神秘面纱。图穷匕首见,水落而石出,包教不包会,不会请再来。

从 Xcode 7 开始支持普通 Apple 账号进行免证书真机调试,详情参考最新官方文档《Launching Your App on Devices》,或参考本文最后一节简介。

写在前面

1.假设你使用过 Apple 设备(iMac/iPad/iPhone)且注册过 Apple ID(Apple Account),详情参考《创建和开始使用 Apple ID》。

2.假设你或你所在的开发组已加入苹果开发者计划(Enroll in iOS Developer Program to become a member),即已注册开发者账号(Apple Developer Account)。

  • 只有拥有开发者账号,才可以申请开发/发布证书及相关配置授权文件,进而在 iOS 真机上开发调试 Apps 或发布到 App Store。
  • 开发者账号分为 Individual 和 Company/Organization 两种类型。如无特别交代,下文基于 $99/Year 的普通个人开发者(Individual)账号展开。

3.若要真机调试实践,你必须至少拥有一台装有 Mac OS X/Xcode 的 Mac 开发机(iMac or MacBook),其上自带原生的 Keychain Access。

一.App ID(bundle identifier)

在苹果官方的开发者计划(Apple Developer Member Center)层面,App ID 即 Product ID,用于标识一个或者一组 App。

在 Mac/iOS 开发语境中,bundle(捆绑) 是指一个内部结构按照标准规则组织的特殊目录。在 Mac OS 应用程序目录下的某个 *.app 上可右键显示包内容(Contents),其本质上就是可执行二进制文件(MacOS/)及其资源(Resources/)的打包组合。因此,在 Xcode 中配置的 Bundle Identifier 必须和 App ID 是一致的(Explicit)或匹配的(Wildcard)。

App ID 字符串通常以反域名(reverse-domain-name)格式的 Company Identifier(Company ID)作为前缀(Prefix/Seed),一般不超过 255 个 ASCII 字符。

App ID 全名会被追加 Application Identifier Prefix(一般为 TeamID.),分为两类:

  • Explicit App ID:唯一的 App ID,用于唯一标识一个应用程序。例如“com.apple.garageband”这个 App ID,用于标识 Bundle Identifier 为“com.apple.garageband”的 App。
  • Wildcard App ID:含有通配符的 App ID,用于标识一组应用程序。例如“*”(实际上是 Application Identifier Prefix)表示所有应用程序;而“com.apple.*”可以表示 Bundle Identifier 以“com.apple.”开头(苹果公司)的所有应用程序。
用户可在 Developer Member Center 网站上注册(Register)或删除(Delete)已注册的 App IDs。
App ID 被配置到【XcodeTarget|Info|Bundle Identifier】下;对于 Wildcard App ID,只要 bundle identifier 包含其作为 Prefix/Seed 即可。

.设备(Device)

Device 就是运行 iOS 系统用于开发调试 App 的设备。每台 Apple 设备使用 UDID (Unique Device Identifier)来唯一标识。

iOS 设备连接 Mac 后,可通过 iTunes->Summary 或者 Xcode->Window->Devices 查看其UDID

Apple Member Center 网站个人账号下的 Devices 中包含了注册过的所有可用于开发和测试的设备,普通个人开发账号每年累计最多只能注册100个设备。

  • Apps signed by you or your team run only on designated development devices.
  • Apps run only on the test devices you specify.
用户可在网站上注册或启用/禁用(Enable/Disable)已注册的Device。
本文的 Devices 是指连接到 Xcode 被授权用于开发测试的iOS设备(iPhone/iPad)。

.开发证书(Certificates)

1.证书的概念

证书是由公证处或认证机关开具的证明资格或权力的证件,它是表明(或帮助断定)事理的一个凭证。证件或凭证的尾部通常会烙印公章

每个中国人一生可能需要70多个证件,含15种身份证明。证件中“必需的”有30到40个。将这些证件按时间顺序铺开,那就是一个天朝子民的一生——持准生证许可落地,以户籍证明入籍,以身份证认证身份,持结婚证以合法同居,最终以死亡证明注销。

2.数字证书的概念

数字证书就是互联网通讯中标志通讯各方身份信息的一串数字,提供了一种在 Internet 上验证通信实体身份的方式,其作用类似于司机的驾驶执照或日常生活中的身份证。它是由一个由权威机构——CA机构,又称为证书授权中心(Certificate Authority)发行的,人们可以在网上用它来识别对方的身份。

  • 数字证书是一个经证书授权中心数字签名的包含公开密钥拥有者信息以及公开密钥的文件。最简单的证书包含一个公开密钥、名称以及证书授权中心的数字签名。
  • 数字证书还有一个重要的特征就是时效性:只在特定的时间段内有效。

数字证书中的公开密钥(公钥)相当于公章。

某一认证领域内的根证书是 CA 认证中心给自己颁发的证书,是信任链的起始点。安装根证书意味着对这个 CA 认证中心的信任。

为了防止 GFW 进行中间人攻击(MitM),例如篡改 github 证书,导致无法访问 github 网站等问题,可选择不信任 CNNIC

  • 在[钥匙串-系统]中双击 CNNIC ROOT,在【信任】|【使用此证书时】下拉选择【永不信任】。

在天朝子民的一生中,户籍证明可理解为等效的根证书:有了户籍证明,才能办理身份证;有了上流的身份证,才能办理下游居住证、结婚证、计划生育证、驾驶执照等认证。

3.iOS(开发)证书

iOS 证书是用来证明 iOS App 内容(bundle with executable and resources)的合法性和完整性的数字证书。对于想安装到真机或发布到 AppStore 的应用程序(App),只有经过签名验证(Signature Validated)才能确保来源可信,并且保证 App 内容是完整、未经篡改的。

iOS 证书分为两类:Development 和 Production(Distribution)。

  • Development 证书用来开发和调试应用程序:A development certificate identifies you, as a team member, in a development provisioning profile that allows apps signed by you to launch on devices.
  • Production 主要用来分发应用程序(根据证书种类有不同作用):A distribution certificate identifies your team or organization in a distribution provisioning profile and allows you to submit  your app to the store. Only a team agent or an admin can create a distribution certificate.

普通个人开发账号最多可注册 iOS Development/Distribution 证书各2个,用户可在网站上删除(Revoke)已注册的 Certificate。

下文主要针对 iOS App 开发调试过程中的开发证书(Certificate for Development)。

4.iOS(开发)证书的根证书

那么,iOS 开发证书是谁颁发的呢?或者说我们是从哪个 CA 申请到用于 Xcode 开发调试 App 的证书呢?

iOS 以及 Mac OS X 系统(在安装 Xcode 时)将自动安装 AppleWWDRCA.cer 这个中间证书(Intermediate Certificates),它实际上就是 iOS(开发)证书的证书,即根证书(Apple Root Certificate)。

AppleWWDRCA(Apple Root CA)类似注册管理户籍的公安机关户政管理机构,AppleWWDRCA.cer 之于 iOS(开发)证书则好比户籍证之于身份证

如果 Mac Keychain Access 证书助理在申请证书时尚未安装过该证书,请先下载安装(Signing requires that you have both the signing identity and the intermediate certificate installed in your keychain)。

5.申请证书(CSR:Certificate Signing Request)

可以在缺少证书时通过 Xcode Fix Issue 自动请求证书,但是这会掩盖其中的具体流程细节。这里通过 Keychain 证书助理从证书颁发机构请求证书:填写开发账号邮件和常用名称,勾选【存储到磁盘】。

Keychain Access|Keys 中将新增一对非对称密钥对 Public/Private Key Pair(This signing identity consists of a public-private key pair that Apple issues)。同时,keychain 将生成一个包含开发者身份信息和公钥的CSR(Certificate Signing Request)文件——CertificateSigningRequest.certSigningRequest。

私钥 private key 始终保存在 Mac OS 的 Keychain Access 中,用于签名(CodeSign)本机对外发布的 App;公钥 public key 一般随证书(随Provisioning Profile,随App)散布出去,对 App 签名进行校验认证。用户必须妥善保存本地 Keychain 中的 private key,以防伪冒。

  • Keep a secure backup of your public-private key pair. If the private key is lost, you’ll have to create an entirely new identity to sign code.
  • Worse, if someone else has your private key, that person may be able to impersonate you.

在 Apple 开发网站上传包含公钥的 CSR 文件作为换取证书的凭证(Upload CSR file to generate your certificate),有点类似为github账号添加SSH公钥到服务器上进行授权。

Apple 证书颁发机构 WWDRCA(Apple Worldwide Developer Relations Certification Authority) 将使用其 private key 对 CSR 中的 public key 和一些身份信息进行加密签名生成数字证书(ios_development.cer)并记录在案(Apple Member Center)。

从 Apple Member Center 网站下载证书到 Mac 上双击即可安装(当然也可在 Xcode 中添加开发账号自动同步证书和[生成]配置文件)。证书安装成功后,在 KeychainAccess|Keys 中展开创建 CSR 时生成的 Key Pair 中的私钥前面的箭头,可以查看到包含其对应公钥的证书(Your requested certificate will be the public half of the key pair.);在 Keychain Access|Certificates 中展开安装的证书(ios_development.cer)前面的箭头,可以看到其对应的私钥。

Certificate 应被配置到【Xcode Target|Build Settings|Code Signing|Code Signing Identity】下,下拉选择 Identities from Profile “…”(一般先配置 Provisioning Profile)。以下是 Xcode 配置示例:

.供应配置文件(Provisioning Profiles

1.Provisioning Profile 的概念

Provisioning Profile 文件包含了上述的所有内容:证书、App ID 和 设备 ID

一个 Provisioning Profile 对应一个 Explicit App ID 或 Wildcard App ID(一组相同 Prefix/Seed 的 App IDs)。在网站上手动创建一个 Provisioning Profile 时,需要依次指定 App ID(单选)、证书(Certificates,可多选)和设备(Devices,可多选)。用户可在网站上删除(Delete)已注册的 Provisioning Profiles。

Provisioning Profile 决定 Xcode 用哪个证书(公钥)/私钥组合(Key Pair/Signing Identity)来签署应用程序(Signing Product),并将在应用程序打包时嵌入到 .ipa 包里。安装应用程序时,Provisioning Profile 文件被拷贝到 iOS 设备中,运行该 iOS App 的设备通过它来认证安装的程序。

如果要打包到真机上运行一个APP,一般要经历以下三步:

  • 首先,需要指明它的 App ID,并且验证 Bundle ID 是否与其一致;
  • 其次,需要证书对应的私钥来进行签名,用于标识这个 APP 是合法、安全、完整的;
  • 然后,如果是真机调试,需要确认这台设备是否授权运行该 APP。

Provisioning Profile 把这些信息全部打包在一起,方便我们在调试和发布程序打包时使用。这样,只要在不同的情况下选择不同的 Provisioning Profile 文件就可以了。

Provisioning Profile 也分为 Development 和 Distribution 两类,有效期同 Certificate 一样。Distribution 版本的 ProvisioningProfile 主要用于提交 App Store 审核,其中不指定开发测试的Devices(0,unlimited)。App ID 为 Wildcard App ID(*)。App Store 审核通过上架后,允许所有 iOS 设备(Deployment Target)上安装运行该App。

Xcode 将全部供应配置文件(包括用户手动下载安装的和 Xcode 自动创建的 Team Provisioning Profile)放在目录 ~/Library/MobileDevice/Provisioning Profiles 下。

2.Provisioning Profile的构成

以下为典型供应配置文件 *.mobileprovision 的构成简析

(1)Name:该mobileprovision的文件名。

(2)UUID:该mobileprovision文件的真实文件名。

(3)TeamName:Apple ID账号名。

(4)TeamIdentifier:Team Identity。

(5)AppIDName:explicit/wildcard App ID name(ApplicationIdentifierPrefix)。

(6)ApplicationIdentifierPrefix:完整App ID的前缀(TeamIdentifier.*)。

(7)DeveloperCertificates:包含了可以为使用该配置文件应用签名的所有证书<data><array>。

证书是基于 Base64 编码,符合 PEM(PrivacyEnhanced Mail, RFC 1848) 格式的,可使用 OpenSSL 来处理(opensslx509 -text -in file.pem)。

从 DeveloperCertificates 提取 <data></data> 之间的内容到文件 cert.cer(cert.perm):

—–BEGIN CERTIFICATE—–

将<data></data>之间的内容拷贝至此

—–END CERTIFICATE—–`

Mac 下右键 QuickLook 查看 cert.cer(cert.perm),在 Keychain Access 中右键 Get Info 查看对应证书 ios_development.cer,正常情况(公私钥 KeyPair 配对)应吻合;Windows 下没有足够信息(WWDRCA.cer),无法验证该证书。

如果你用了一个不在这个列表中的证书进行签名,无论这个证书是否有效,这个应用都将 CodeSign Fail。

(8)Entitlements 键<key>对应的<dict>:

keychain-access-groups:$(AppIdentifierPrefix),参见Code Signing Entitlements(*.entitlements)。

每个应用程序都有一个可以用于安全保存一些如密码、认证等信息的 keychain,一般而言自己的程序只能访问自己的 keychain。通过对应用签名时的一些设置,还可以利用keychain的方式实现同一开发者签证(就是相同bundle seed)下的不同应用之间共享信息的操作。比如你有一个开发者帐户,并开发了两个不同的应用A和B,然后通过对A和B的 keychain access group 这个东西指定共用的访问分组,就可以实现共享此 keychain 中的内容。

application-identifier:带前缀的全名,例如$(AppIdentifierPrefix)com.apple.garageband。

com.apple.security.application-groups:App Group ID(group. com.apple),参见Code Signing Entitlements(*.entitlements)。

com.apple.developer.team-identifier:同Team Identifier。

(9)ProvisionedDevices:该mobileprovision授权的开发设备的UDID <array>。

Provisioning Profile被配置到【XcodeTarget|Build Settings|Code Signing|Provisioning Profile】下,然后在Code Signing Identity下拉可选择Identities from Profile “…”(即Provisioning Profile中包含的Certificates)。

.开发组供应配置文件(Team Provisioning Profiles

1.Team Provisioning Profile的概念

每个 Apple 开发者账号都对应一个唯一的 Team ID,Xcode3.2.3 预发布版本中加入了 Team Provisioning Profile 这项新功能。

在 Xcode 中添加 Apple Developer Account 时,它将与 Apple Member Center 后台勾兑自动生成 iOS Team Provisioning Profile(Managed by Xcode)。

Team Provisioning Profile 包含一个为 Xcode iOS Wildcard App ID(*) 生成的 iOS Team Provisioning Profile:*(匹配所有应用程序),账户里所有的 Development Certificates 和 Devices 都可以使用它在这个 team 注册的所有设备上调试应用程序(不管bundle identifier是什么)。同时,它还会为开发者自己创建的 Wildcard/Explicit App IDs 创建对应的 iOS Team Provisioning Profile。

2.Team Provisioning Profile 生成/更新时机

  • Add an Apple ID account to Xcode
  • Fix issue “No Provisioning Profiles with a valid signing identity” in Xcode
  • Assign Your App to a Team in Xcode project settings of General|Identity
  • Register new device on the apple development website or Xcode detected new device connected

利用 Xcode 生成和管理的 iOS Team Provisioning Profile 来进行开发非常方便,可以不需要上网站手动生成下载 Provisioning Profile。

Team Provisioning Profile 同 Provisioning Profile,只不过是由 Xcode 自动生成的,也被配置到【XcodeTarget|Build Settings|Code Signing|Provisioning Profile】下,同时需要在【XcodeTarget|General|Identity】下指定 Team 账号 ID。

.App Group (ID)

1.App Group 的概念

WWDC14 除了发布了 OS X v10.10 和 switf 外,iOS 8.0 也开始变得更加开放了。说到开放,当然要数应用扩展(App Extension)了。顾名思义,应用扩展允许开发者扩展应用的自定义功能和内容,能够让用户在使用其他应用程序时使用该项功能,从而实现各个应用程序间的功能和资源共享。可以将扩展理解为一个轻量级(nimble and lightweight)的分身。

扩展和其 Containing App 各自拥有自己的沙盒,虽然扩展以插件形式内嵌在 Containing App 中,但是它们是独立的二进制包,不可以互访彼此的沙盒。为了实现 Containing App 与扩展的数据共享,苹果在 iOS 8 中引入了一个新的概念——App Group,它主要用于同一 Group 下的 APP 实现数据共享,具体来说是通过创建使用以 App Group ID 标识的共享资源区——App Group Container 来实现的。

App Group ID 同 App ID 一样,一般不超过255个ASCII字符。用户可在网站上编辑 Explicit App IDs,将其纳入 App Group(Assignment);也可删除(Delete)已注册的App Group (ID)。

2.App Group 的配置

Containing App 与 Extension 的 Explicit App ID 必须 Assign 到同一 App Group 下才能实现数据共享,并且 Containing App 与 Extension 的 App ID 命名必须符合规范:

  1. 置于同一App Group 下的 App IDs 必须是唯一的(Explicit,not Wildcard)
  2. Extension App ID 以 Containing App ID 为前缀(Prefix/Seed)

假如 Garageband 这个 App ID 为“com.apple.garageband”,则支持从语音备忘录导入到 Garageband 应用的插件的 App ID 可能形如“com.apple.garageband.extImportRecording”。

 

App(ex)

 

App Group ID

Provisioning Profile

Code Signing Identity

(Certificate Key Pair)

App ID

(bundle identifier)

Devices

(test)

GarageBand

置于同一分组:

group.com.apple

(1)共用同一证书:ios_development.cer

(2)共用证书Key Pair中的Private Key进行CodeSign

com.apple.garageband

授权开发测试设备的UDIDs

GarageBand扩展插件

com.apple.garageband.extImportRecording

关于Provisioning Profile,可以使用自己手动生成的,也可以使用 Xcode 自动生成的 Team Provisioning Profile。

App Group 会被配置到【Xcode Target|Build Settings|Code Signing|Code Signing Entitlements】文件(*.entitlements)的键com.apple.security.application-groups下,不影响 Provisioning Profile 生成流程。

.证书与签名Certificate& Signature)

1.Code Signing Identity

Xcode 中配置的 Code Signing Identity(entitlements、certificate)必须与 Provisioning Profile 匹配,并且配置的 Certificate 必须在本机 Keychain Access 中存在对应 Public/Private Key Pair,否则编译会报错。

Xcode 所在的 Mac 设备(系统)使用 CA 证书(WWDRCA.cer)来判断 Code Signing Identity 中 Certificate 的合法性:

  • 若用 WWDRCA 公钥能成功解密出证书并得到公钥(Public Key)和内容摘要(Signature),证明此证书确乃 AppleWWDRCA 颁布,即证书来源可信;
  • 再对证书本身使用哈希算法计算摘要,若与上一步得到的摘要一致,则证明此证书未被篡改过,即证书完整。

2.Code Signing

每个证书(其实是公钥)对应 Key Pair 中的私钥会被用来对内容(executable code,resources such as images and nib files aren’t signed)进行数字签名(CodeSign)——使用哈希算法生成内容摘要(digest)。

Xcode 使用指定证书配套的私钥进行签名时需要授权,选择【始终允许】后,以后使用该私钥进行签名便不会再弹出授权确认窗口。

3.Verify Code Signature with Certificate

上面已经提到,公钥被包含在数字证书里,数字证书又被包含在描述文件(Provisioning File)中,描述文件在应用被安装的时候会被拷贝到 iOS 设备中。

第一步,App 在 Mac/iOS 真机上启动时,需要对配置的 bundle ID、entitlements 和 certificate 与 Provisioning Profile 进行匹配校验:

第二步,iOS/Mac 真机上的 ios_development.cer 被 AppleWWDRCA.cer 中的 public key 解密校验合法后,获取每个开发证书中可信任的公钥对 App 的可靠性和完整性进行校验。

iOS/Mac 设备(系统)使用 App Provisioning Profile(Code Signing Identity)中的开发证书来判断App的合法性:

  • 若用证书公钥能成功解密出 App(executable bundle)的内容摘要(_CodeSignature),证明此 App 确乃认证开发者发布,即来源可信;
  • 再对 App(executable bundle)本身使用哈希算法计算摘要,若与上一步得到的摘要一致,则证明此 App 未被篡改过,即内容完整。
小结:
  • 基于 Provisioning Profile 校验了 CodeSign 的一致性;
  • 基于 Certificate 校验 App 的可靠性和完整性;
  • 启动时,真机的 device ID(UUID)必须在 Provisioning Profile 的 ProvisionedDevices 授权之列。
  • 无论是 Xcode 对 APP 进行签名打包还是真机运行 APP 进行校验,都使用了基于证书体系的非对称加密机制

.在多台机器上共享开发账户/证书

1.Xcode 导出开发者账号(*.developerprofile) 或 PKCS12 文件(*.p12)

进入 Xcode Preferences|Accounts:

  • 选中 Apple IDs 列表中对应 Account 的 Email,点击+-之后的☸|Export Accounts,可导出包含 account/code signing identity/provisioning profiles 信息的 *.developerprofile(Exporting a Developer Profile)文件供其他机器上的 Xcode 开发使用(Import 该 Account)。
选中右下列表中某行 Account Name 条目|ViewDetails,可以查看 Signing Identities 和 Provisioning Profiles。
  • 选中欲导出的 Signing Identity 条目,点击栏底+之后的☸|Export,必须输入密码,并需授权 export key “privateKey” from keychain,将导出 Certificates.p12
点击左下角的刷新按钮可从 Member Center 同步该账号下所有的 Provisioning Profile 到本地。
选中右击列表中某个 Provisioning Profile 可以【Show in Finder】到[~/Library/MobileDevice/Provisioning\ Profiles]目录,其中 Provisioning Profile 的真实名称为 $(UUID).mobileprovision,名如”2488109f-ff65-442e-9774-fd50bd6bc827.mobileprovision”,其中<key>Name</key>中为 Xcode 中看到的描述性别名。

2.Keychain Access 导出 PKCS12 文件(*.p12)

在 Keychain Access|Certificates 中选中欲导出的 certificate 或其下 private key,右键 Export 或者通过菜单 File|Export Items 导出 Certificates.p12——PKCS12 file holds the private key and certificate

其他 Mac 机器上双击 Certificates.p12(如有密码需输入密码)即可安装该共享证书。有了共享证书之后,在开发者网站上将欲调试的 iOS 设备注册到该开发者账号名下,并下载对应证书授权了 iOS 调试设备的 Provisioning Profile 文件,方可在 iOS 真机设备上开发调试。

九.证书配置常见错误

1.no such provisioning profile was found

Xcode Target|Genera|Identity Team下提示”Your build settings specify a provisioning profile with the UUID “xxx”,howerver, no such provisioning profile was found.”

Xcode Target|BuildSettings|Code Signing|当前配置的指定UDID的provisioning profile在本地不存在,此时需要更改Provisioning Profile。必要时手动去网站下载或重新生成Provisioning Profile或直接在Xcode中Fix issue予以解决(可能自动生成iOS Team ProvisioningProfile)!

2.No identities from profile

Build Settings|CodeSigning的Provisioning Profile中选择了本地安装的provisioning profile之后,Code Signing Identity中下拉提示No identities from profile “…”or No identities from keychain.

Xcode配置指定UDID的provisioning profile中的DeveloperCertificates在本地KeyChain中不存在(No identities are available)或不一致(KeyPair中的Private Key丢失),此时需去网站检查ProvisioningProfile中的App ID-Certificate-Device配置是否正确。如果是别人提供的共享账号(*.developerprofile)或共享证书(*.p12),请确保导出了对应Key Pair中的Private Key。必要时也直接在Xcode中Fix issue予以解决(可能自动生成iOS Team ProvisioningProfile)。

3.Code Signing Entitlements file do not match profile

Invalid application-identifier Entitlement” or “Code Signing Entitlements file do not match those specified in your provisioning profile.(0xE8008016).”

(1)检查对应版本(Debug)指定的*.entitlements文件中的“Keychain Access Groups”键值是否与ProvisioningProfile中的Entitlements项相吻合(后者一般为前者的Prefix/Seed)。

(2)也可以将Build Settings|Code Signing的Provisioning Profile中对应版本(Debug)的Entitlements置空。

4.The app ID cannot be registered to your development team

出现该问题通常是 app ID 冲突,即该 app ID 已经有人注册过,此时可以按照提示换一个 app ID或基于已有app ID添加后缀——Change your bundle identifier to a unique string to try again。

5.The ‘In-App Purchase’ feature is only available to users enrolled in Apple Developer Program

只有开发者账号才能真机调试 ‘In-App Purchase’ 特性,所以需要在工程配置(Capabilities)中禁用普通Apple ID不支持的特性。

同样,在 Xcode 8 中,也只有开发者账号才能真机调试The ‘Siri’ feature,否则也会报错“The ‘Siri’ feature is only available to users enrolled in Apple Developer Program.”而无法完成签名认证。

解决方法

在Xcode的 Project Navigator 中点击*.xcodeproj ,在右侧的 Targets 下选择点击目标 Target,在【Capabilities】中禁用“Push Notifications”、“In-App Purchase”、“Siri”。

如果找不到这些配置项,可尝试直接修改项目配置文件中相应feature的配置开关,步骤如下:

关闭 Xcode 正在打开的 Project,在 Finder 中右键项目配置文件 *.xcodeproj 显示包内容,使用文本编辑器(例如 Sublime Text)打开 project.pbxproj 文件,搜索“SystemCapabilities”,依次找到“com.apple.Push”、“com.apple.InAppPurchase”、“com.apple.Siri”将其 enabled 键值从1修改为0,关闭退出使用 Xcode 重新打开该项目。

6.Xcode配置反应有时候不那么及时,可刷新、重置相关配置项开关(若有)或重启Xcode试试。

十. Xcode7+ 免证书真机调试

在 Xcode 7 中,苹果改变了自己在许可权限上的策略:

  1. 此前 Xcode 只开放给注册开发者下载,现在 Xcode 7 改变了这种惯有的做法,无需注册开发者账号,仅使用普通的Apple ID就能下载和上手体验。
  2. 此前开发者需每年支付99美元的费用成为注册开发者才能在 iPhone/iPad 真机上运行调试APP,苹果新的开发者计划则放宽要求,无需购买,只要你感兴趣同样可以在设备上测试app。——Developers would be able to test apps on devices without a paid Apple developer account in Xcode 7.

所谓“免证书”真机调试,并不是真的不需要证书,Xcode真机调试原有的证书配置体系仍在——All iOS, tvOS, and watchOS appsmust becode signed and provisioned to launch on a device. 所以,上文啰嗦几千字还是有点用的。

自 Xcode7 开始,原来基于付费开发者账号及自助生成证书及配置文件的繁琐过程被苹果简化,Xcode将针对任何普通账号自动为联调真机生成所需相关的证书及配置文件。当你打算向 App Store 提交发布应用,才需要付费。

第一步:进入 Xcode Preferences|Accounts,添加自己的 Apple ID 账号。

第二步:Build Settings|Code Signing 下的 Provisioning Profile 选择 Automatic,Code Signing Identity 选择 Automatic 下的iOS Developer

第三步:General 配置 Bundle identifier,Team 下拉选择苹果Member Center自动为你的账号生成的Personal Team ID。

自己的账号在调试公司或其他第三方APP代码时,若填写 Bundle identifier 为他人账号注册的 APP ID(例如苹果相机应用 com.apple.camera),会报错:

  1. No provisioning profiles with a valid signing identity (i.e. certificate and private key pair) matching the bundle identifier “com.apple.camera” were found.

即使编译通过了,可能运行时APP自身与服务器校验也可能会报签名错误,肿么办???

Her skill:此时,可以在他人原有App ID基础上添加后缀(例如com.apple.camera.extension),配置成应用的衍生插件(相当于置于同一 App Group 下)就可以快乐的玩耍了。

解决了所有的开发配置问题,Xcode 8 Automatically manage signing 配置成功应该是这样子的:

如果启动APP时,Xcode报错“process launch failed: Security”或iPhone报错【不受信任的开发者】。

此时需要到iPhone通用配置中的描述文件(最新系统中可能叫设备管理)中,在描述文件(开发商应用)中选择对应的描述文件(你的Apple ID)点击 信任 或 验证 即可。

OK,All Done!

Meteor+Angular实现轻论坛(2)—实现用户模块 - 简书

mikel阅读(1020)

来源: Meteor+Angular实现轻论坛(2)—实现用户模块 – 简书

添加路由控制

添加用户模块之前,先添加路由控制。我们使用Angular的ui-router模块,添加路由功能。

首先添加ui-router模块到项目:

$ meteor add angularui:angular-ui-router

client文件夹下新建lib文件夹,然后把app.js文件移动到lib文件夹下面,然后把ui-router添加到Angular模块依赖中,修改app.js代码,如下所示:

angular.module('louForum', ['angular-meteor', 'ui.router']);

在client文件夹下创建routes.js文件,通过angular-ui-router定义注册和登录的路由,代码如下:

angular.module('louForum').config(['$urlRouterProvider', '$stateProvider', '$locationProvider',
    function($urlRouterProvider, $stateProvider, $locationProvider) {
        $locationProvider.html5Mode(true);
        $stateProvider
            // 首页
            .state('home', {
                // 定义URL
                url: '/',
                // 指定模板(文件必须以“.ng.html”后缀结尾)
                templateUrl: 'client/home/views/home.ng.html',
                // 指定控制器
                controller: 'HomeCtrl'
            })
            // 注册页面
            .state('register', {
                url: '/register',
                templateUrl: 'client/user/views/register.ng.html',
                controller: 'RegisterCtrl',
                // 设置当前控制器中 $scope 的一个引用为 rc
                controllerAs: 'rc'
            })
            // 登录页面
            .state('login', {
                url: '/login',
                templateUrl: 'client/user/views/login.ng.html',
                controller: 'LoginCtrl',
                controllerAs: 'lc'
            })
            // 退出登录
            .state('logout', {
                url: '/logout',
                resolve: ['$meteor', '$state', function($meteor, $state) {
                    return $meteor.logout().then(function() {
                        $state.go('home');
                    }, function(err) {
                        console.error('Logout error : ', err);
                    });
                }]
            });

        // 浏览器没有匹配到以上URL时,转到此URL
        $urlRouterProvider.otherwise('/');
    }
]);

ui-router通过嵌套的statesviews来控制路由,在HTML文件中,要引用一个链接,直接添加对应的state即可,比如链接登录页面:

<body>
    <div ui-view></div>
    <a ui-sref="login">login</a>
</body>

ui-sref是ui-router的一个指令,用于给HTML模板渲染ui-router定义的链接。而指令ui-view用于渲染HTML页面模板。

Angular通过config()方法调用ui-router$stateProvider方法根据state来配置URL。

$locationProvider.html5Mode(true);用于启用html5 mode,$location有两个控制URL格式的模式: Hashbang mode 和 html5 mode。默认使用 Hashbang mode,html5 mode 的URL格式对搜索引擎更友好,两种模式的对比如下图所示:

此处输入图片的描述

我们使用bootstrap构建前端页面,添加bootstrap到Meteor:

$ meteor add twbs:bootstrap

删除index.ng.html文件,打开index.html,修改代码如下:

<head>
    <base href="/">
</head>

<body ng-app="louForum">
    <div id="header">
        <div class="container">
            <div class="pull-left" id="main-nav">
                <a ui-sref="home">首页</a>
            </div>
            <div class="pull-right" ng-if="$root.currentUser">
                <span>Hi, {{ currentUser.username }}</span>
                <a ui-sref="logout">退出</a>
            </div>
            <div class="pull-right" ng-if="!$root.currentUser">
                <a ui-sref="login">登录</a>
                <a ui-sref="register">注册</a>
            </div>
        </div>
    </div>
    <div class="container" id="main-content">
        <div ui-view></div>
    </div>
</body>

ui-srefangular-ui-router的指令,用于指定路由控制函数中定义的state,以对应其中的URL。ng-if是一个判断表达式结果为true或者false的Angular指令,为true则渲染此HTML元素,为false则不渲染。在head标签中,我们添加了base标签,这是html5 mode 所必须的,指定了URL的根目录。我们还添加了一个带有ui-view属性的div元素,根据不同的URL,对应的Angular模板的内容会加载到这个div中,所以点击切换URL时,页面其实是没有刷新的,而只是更改了ui-view中的内容。

注册

Meteor默认加载了很多包,其中insecure包使得服务端默认会把所有的数据发送给所有的客户端,而且所有客户端都可以修改数据,比如没有登录也可以发帖。为了安全,添加用户之前,我们需要移除insecure包:

$ meteor remove insecure

然后添加Meteor用户管理包accounts-password

$ meteor add accounts-password

这个包有登录、注册、修改密码、邮箱验证等功能,非常实用。

在client文件夹中新建user文件夹,在user文件夹中新建controllers文件夹和views文件夹。controllers文件夹用于存放控制器,views文件夹用于存放模板。每一个由Angular控制的页面,都需要控制器和模板。

创建注册控制器,在controllers文件夹中新建register.js,输入如下代码(通过IDE写代码可以输入中文的哦~):

// 定义注册控制器 RegisterCtrl
// 并传入 $meteor 和 $state
angular.module('louForum').controller('RegisterCtrl', ['$meteor', '$state',
    function($meteor, $state) {
        var vm = this;

        // 定义 user model
        // 注册成功后数据将自动保存到mongoDB
        vm.credentials = {
            username: '',
            email: '',
            password: ''
        };
        // 保存错误信息
        vm.error = '';

        // 用户注册方法
        vm.register = function() {
            // 通过$meteor.createUser方法创建新用户
            // 用户信息会保存到数据库
            $meteor.createUser(vm.credentials)
            .then(function() {
                // 注册成功后跳转到首页
                $state.go('home');
            }, function(err) {
                vm.error = 'Register error : ' + err;
                console.error(vm.error);
            });
        };
    }
]);

然后再创建注册的模板,在views文件夹中新建register.ng.html,输入如下代码:

<form class="form-horizontal">
    <div class="form-group">
        <label class="col-md-2 control-label">昵称</label>
        <div class="col-md-10">
            <input type="text" class="form-control" ng-model="rc.credentials.username"><br>
        </div>
    </div>
    <div class="form-group">
        <label class="col-md-2 control-label">邮箱</label>
        <div class="col-md-10">
            <input type="email" class="form-control" ng-model="rc.credentials.email"><br>
        </div>
    </div>
    <div class="form-group">
        <label for="inputPassword3" class="col-md-2 control-label">密码</label>
        <div class="col-md-10">
            <input type="password" class="form-control" ng-model="rc.credentials.password"><br>
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <button type="button" class="btn btn-primary" ng-click="rc.register()">注册</button>
        </div>
    </div>
</form>

关于注册的控制器和模板就完成了。

登录

同样的,创建登录控制器,在controllers文件夹中新建login.js,输入如下代码:

angular.module('louForum').controller('LoginCtrl', ['$meteor', '$state',
    function($meteor, $state) {
        var vm = this;

        vm.credentials = {
            username: '',
            email: '',
            password: ''
        };
        vm.error = '';

        vm.login = function() {
            // 通过验证邮箱和密码是否匹配来判断登录
            $meteor.loginWithPassword(vm.credentials.email, vm.credentials.password)
            .then(function() {
                $state.go('home');
            }, function(err) {
                vm.error = 'Login error : ' + err;
                console.error(vm.error);
            });
        };
    }
]);

然后创建登录的模板,在views文件夹中新建login.ng.html,输入如下代码:

<form class="form-horizontal">
    <div class="form-group">
        <label class="col-md-2 control-label">邮箱</label>
        <div class="col-md-10">
            <input type="email" class="form-control" ng-model="lc.credentials.email"><br>
        </div>
    </div>
    <div class="form-group">
        <label for="inputPassword3" class="col-md-2 control-label">密码</label>
        <div class="col-md-10">
            <input type="password" class="form-control" ng-model="lc.credentials.password"><br>
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <button type="button" class="btn btn-primary" ng-click="lc.login()">登录</button>
        </div>
    </div>
</form>

由于登录成功后,会自动跳转到首页,这里我们就先添加一个空白主页,在client文件夹下新建home文件夹,在home文件夹下创建controllers文件夹和views文件夹。

在这个controllers文件夹中新建home.js文件,输入如下代码:

angular.module('louForum').controller('HomeCtrl', ['$meteor', '$state', '$scope',
    function($meteor, $state, $scope) {
    }
]);

views文件夹下新建home.ng.html文件夹,输入如下代码:

<p>home page</p>

运行项目:

$ meteor

访问http:localhost:3000/register即可注册新用户,访问http:localhost:3000/login登录。

在虚拟机中新打开一个shell终端,进入louForum目录,输入如下命令,进入Meteor的MongoDB命令行环境:

$ meteor mongo

然后输入如下命令,进入名为meteor(Meteor的默认数据库)的数据库:

> use meteor;

输入如下命令,查看该数据库中的Collection:

> show collections;

可以看到,有一个名为users的collection,输入如下命令,查看users中的数据:

> db.users.find();

可以看到打印出了刚刚注册的用户的数据。到此,注册和登录用户就完成了。

总结

这节实验主要添加了路由及用户注册和登录。

涉及到的Angular包和Meteor包:

$ meteor add angularui:angular-ui-router

$ meteor add twbs:bootstrap

$ meteor remove insecure

$ meteor add accounts-password

本课程为实验楼原创课程,转载请注明课程链接:https://www.shiyanlou.com/courses/424

实时开发框架Meteor API解读系列 Meteor.methods - CSDN博客

mikel阅读(890)

来源: 实时开发框架Meteor API解读系列 Meteor.methods – CSDN博客

写在前面的话

  1. 该系列是零散的。 写到哪个是哪个 不会有个顺序。
  2. 尽量一篇博客一个API
  3. 尽量搭配一个代码实例 (因为时间比较零碎不一定能够保证)
  4. 每篇博文我会标记所使用的Meteor版本,防止因为API变化,导致博客的内容的正确性问题
  5. 能够看懂官方API就尽量看官方的,官方的才是最准确的。我这里只是提供一个参考

V 0.7.01

本篇文章涉及到的API:Meteor.methods,Meteor.call,Meteor.apply,Meteor.starup


Meteor.methods — Anywhere

关于 Meteor.methods 先看一下官方的简单描述

Defines functions that can be invoked over the network by clients.

大概意思就是 定义一些方法可以被客户端通过网络进行调用。注意下面的解释都是以这一条为基础来解释的。

ps:因为我初次看官方文档就糊涂了。不知道大家注意了没有,官方文档在每个API 后面有个标记,标记api的使用范围是在客户端(Client)使用还是服务端(Server)使用,亦或是同时都可以使用(Anywhere)。

Meteor.methods后面的标识是Anywhere我的第一反应是服务端通过Meteor.methods定义的方法客户端可以调用,客户端通过Meteor.methods定义的方法服务端可以调用。其实如果稍微思考一下就会知道这样想是错误的。因为客户端有那么多,Meteor怎么知道到底使哪个客户端来执行这个方法呢? 但是这里的Anywhere是什么意思呢?稍后解释。

    *强调 API的作用也可以说它的主要目的是实现: 在服务端定义一组方法,使客户端能够调用。换句话说,通过这个API,客户端可以命令服务端做一些我们允许做的事情。
client 端定义的Meteor.methods叫做 'stub'(官方用的是这个单词) ,至于怎么翻译,看个人理解吧。这是下文需要用的一个描述。[我翻译成'存根',理解为 对服务端方法调用的客户端补充动作。]

参数:Object
函数Meteor.methods的参数是一个object对象,这个对象里面的属性名就是供客户端调用的函数名,该属性对应的值就是共调用的函数。
例如:

{
    "hello":function(){..},
    "welcome":function(){..},
    "bey":function(){..}
}

如下使用:(服务端)

 Meteor.methods({
        "say":function(name){
            //do somthing
            console.log("Hello " + name);
            return "Hello boy";
        } ,
        "draw":function(...){
            .....
        },
        .....
 });

返回值:JSON对象(官方原文是: EJSON-able value)或者一个Meteor.Error对象

Meteor.Error 当后台处理到异常时,应该主动抛出的出错信息。这是一个Meteor自定义的一个异常对象。这个对象与本篇主题不相关,暂不做解释。可能一篇博客介绍它。

在被调用函数的内部 (如say对应函数的内部)存在一个上下对象 this。这个对象组成如下:

{
    isSimulation: Boolean// a boolean value, true if this invocation is a stub.
    unblock: Function,//when called, allows the next method from this client to begin running.
    userId: String,//the id of the current user.
    setUserId:  Function//a function that associates the current client with a user.
    connection: Object//on the server, the connection this method call was received on.
}

根据官方的解释可能不是很i清楚,后面我们通过代码进行测试。

现在Meteor.methods的参数类型,和其中的函数应用已经讲,那么接着讲 如何来调用这些在Server端定义的内容。

有两个方法可以使用

1. Meteor.call
2. Meteor.apply

(个人认为:这两个方法的区别有点类似js的call与apply的区别)

Meteor.call — Anywhere

具体形式如下:

Meteor.call("function name",arg1,arg2,..,function(error,result){
    //do something
});

function nanme 表示 在Meteor.methods中定义的对象的属性名,通过这个属性名来表示调用服务端对应的这个方法。

arg1,arg2,.. 是任意个参数,这个参数将作为服务端函数的参数。

注意这些参数仅为json或者一般数据类型,不能传递包含function对象

最后一个是回调函数。如果服务端函数抛出异常则 将赋值给error这个参数。
如果没有异常抛出,则服务端函数的返回值将赋值给result这个参数。

接下来看具体实现和代码。

meteor create api-001 #创建meteor应用
cd api-001 #进入应用目录
rm *.html *.css *.js #删除自动生成的.html,css,js文件
mkdir server #创建server文件夹
mkdir client #创建client文件夹

(关于文件夹的作用请参考其他人博客,或官方文档,我暂时还没有写。)
在server文件夹中新建一个js文件,如 main.js 内容如下:

Meteor.methods({
    "test":function(){
        console.log("Hello  I'm Server");
    }
});

现在代码已经完成了,运行这个工程。
用浏览器(建议使用Chrome,该系列博客都将以此浏览器进行)打开http://localhost:3000 按F12 进入开发者模式 找到console控制台,输入:

Meteor.call("test")

这时在meteor启动的命令窗口将看到字符串输出。这就是Meteor.call的使用了。客户端调用服务端的方法。

(这里使用chrome控制台来完成相关测试是为了尽量保持博客的简洁性。同样的语句当然也可以写到客户端的js文件中,然后在html中引用,调用。效果是一样的,但是对于这篇博客来说 代码量就显的繁琐了。关于chrome的开发者模式的使用,请查找资料,或者查看我的博客如果你在看这篇博客时我已经写了的话)

最简单的demo已经了解过了,接下来看下它的返回值和参数的传递。
改动一下 main.js:

Meteor.methods({
    //这里的参数可以是N个
    "test":function(a,b){
        console.log(a + " " + b);
        //这里使用的返回类型是json对象,当然可以使用基本数据类型,如string,number,bool等
        return {
          a:a,
          b:b,
          c:a+b
        }
    }
});

meteor自动重启。同样在chrome控制台输入:

Meteor.call("test",1,2,function(error,result){
    //服务端抛出的异常将被这个回调函数的地一个参数接收,也就是error
    //当服务端无异常抛出时,error为undefined
    if(error){ 
        console.log("服务端异常");
        console.log(error);
        return;
    }
    //服务端函数的返回值将被第二个参数接受,也就是result
    console.log(result);
});

打印结果很明确就是服务端的返回值。

注意,因为Meteor.call可以在ServerClient同时使用,
    Server端运行Meteor.call时,最后一个参数不是回调函数的话,在可能的情况下服务端将同步执行这个方法体)验证代码如下  main.js

var fs = Npm.require('fs');
Meteor.methods({
    "test":function(a){
        return a;
    }
});

Meteor.startup(function(){
  var s = Meteor.call("test",5);
  console.log(s);
});

    强调!这仅是在Server可以这样用,在Client如果要取到Server的返回值,必须使用回调函数才能获取到。
------------------------------------
    现在,下面的这个结论如果不能理解或者对你造成了迷惑请忽略它,因为它的实际作用不大。只是因为API中对它有个简单的提及,那么就用代码代码验证一下。
    文档原文:
    "On the client, if you do not pass a callback and you are not inside a stub, call will return undefined, and you will have no way to get the return value of the method." 
    原文就是上面这样了。不知道你思考了没有我上面提及到的这句话伸展:"在Client如果要取到Server的返回值" 什么伸展呢?因为Meteor.methods在客户端也可以使用,那么如果Client Meteor.call调用ClientMeteor.methods中的函数 是不是也需要用回调函数呢? 结论是:不一定需要。
    但是根据原文文档(原文没有直接提及,是根据"you do not pass a callback and you are not inside a stub"推断出来的,所以说这个结论实际作用不大),你得满足一个条件才行。条件是:你的Meteor.call必须在stub[前文已经解释过]中。(当然Client Metoer.methods本身不能涉及到异步调用。这个和Server端的同步是同样的样要求),而且在sub中使用Method.call 只会调用Client Meteor.methods中定义的方法,而ServerMeteor.methods不会执行。
官方文档提到了这个上面的这个情况。原文如下:
 "Finally, if you are inside a stub on the client and call another method, the other method is not executed (no RPC is generated, nothing 'real' happens). If that other method has a stub, that stub stands in for the method and is executed."

通过以下代码的运行就能看到:
Server/main.js

Meteor.methods({
  hello:function(){
    console.log("server");
    return 10;
  }
});

Client/client.js

Meteor.methods({
  hello:function(){
    console.log("clent");
    return 12;
  },
  say:function(){
    console.log('say');
    var hello = Meteor.call("hello");//Server下的hello 不会执行。
    console.log(hello);//打印值是12
  }
});


Meteor.apply — Anywhere

服务端代码不需要改动,在浏览器控制台输入:

Meteor.apply("test",[1,2],function(errror,result){
    if(error){
        //do something;
        return;
    }
    // do somthing 
    console.log(result);
});

可以看到 Meteor.apply和Meteor.call 在一般用法 上基本没区别,仅是传递参数的方式不同而已,一个使用参数列表,一个使用参数数组。其实对于大多数 开发过程而言 掌握上述两种的使用已经能够满足开发需要了。因此对无更多要求的人而言,本篇博客可以到此为止。前文提到的 服务端函数 内部的上下文对象的使用 和 关于Meteor.methods的Anywhere 这两个还没解释的地方 可以忽略了。

Meteor.methods的Anywhere

大家细心一点可能发现Meteor.call,Meteor.apply同样是”Anywhere”.

先解释一下Server的call,apply和Client的call,apply的区别。

区别很简单:

    Server
        仅能调用服务端的Meteor.methods中定义的函数
    Client
        同时调用服务端和客户端的Meteor.methods中定义的函数。这个涉及到了Meteor.methodsAnywhere了。具体请往下看。

因为前面已经介绍了methods,call,apply的简单用法了。那么下面的代码就尽量简化不再描述这些了。

首先修改server的main.js。内容如下:

Meteor.methods({
    "test":function(a){
      console.log("server");
      return  a+1;
    }
});
//Meteor加载完所有文件后将执行这个方法。具体使用请参考官方文档,比较简单。
Meteor.startup(function(){
    //然meteor启动后执行test方法
    Meteor.call("test",1,function(err,result){
        console.log(result);
    });
});

另外,最开始的时候就新建了一个client文件,在该文件夹下 新建一个js文件,如client.js
内容如下:

Meteor.methods({
    "test":function(a){
        console.log("Client");
        return a+"aaa";
    }
});

代码基本就这样 然后重新运行meteor.

可以看到在Meteor.startup调用的Meteor.call只执行了Server下定义的test函数。

接着打开Chrome的控制台,输入:

 Meteor.call("test",1,function(err,result){
        console.log(result);
 });

可以发现,客户端Meteor.methods和服务端Meteor.methods定义的test方法都执行了。而且在这个call函数的里回调函数的返回值 是服务端test的返回值,客户端test函数的返回值自动被舍弃了。

如果ClientServer同时定义了Meteor.methods 函数,在进行调用时,server 执行Meteor.callMeteor.apply 只会使Server端的Meteor.methods中定义的函数执行
,而Client端执行的callapply 会同调用客户端和服务端中Meteor.methods定义的函数。
且返回值都是以Server端的函数返回值,客户端的返回值将自动舍弃。其实这个结论自己多写几个代码就能够了解。

到这里 Meteor.methods的Anywhere不用再解释,应该也明白了。

Meteor.methods中函数的上下文对象 this

this中包含五个属性如下 (这些当需要用到时,能够想起有怎么个属性存在就可以了。到时候会自动知道该怎么使用)

{
    isSimulation: Boolean// a boolean value, true if this invocation is a stub.
    unblock: Function,//when called, allows the next method from this client to begin running.
    userId: String,//the id of the current user.
    setUserId:  Function//a function that associates the current client with a user.
    connection: Object//on the server, the connection this method call was received on.
}
  1. this.userId
    调用这个函数的用户的id ,类型:string。如果用户没有登录,值为null。 该属性在 Server的Meteor.methods和Client的Meteor.methods都存在。用官方的描述就是Anywhere。 这个属性依赖Meteor的accounts 模块。这里不赘述。
  2. this.setUseId 类型:Function,该setUerId函数的接收一个字符串或者null。使用范围:Server 设置当前链接的 用户。也就是说可以改变登录用户,或者使用户登录[如果this.userId为null],注销登录用户[this.setUserId(null)] .同样需要 Meteor的accounts模块的支持。
  3. this.isSimulation 类型:Boolean 。使用范围:Anywhere
    这个属性 我解释不好(如有好的理解请在博客下方留言),官方的解释是这样的:
Access inside a method invocation. Boolean value, true if this invocation is a stub.

[我理解意思是在Client的Meteor.methods中该值为true,Server中为false]。而且经过代码如下代码测试貌似和我理解的意思差不多 :

1. Servercall apply调用时,在ServerMeteor.methods```isSimulation```false

2. Clientcallapply调用时,
    ServerMeteor.methods```isSimulation```false
    ClientMeteor.methods```isSimulation```true

测试代码:
client.js:

Meteor.methods({
    "test":function(a){
        console.log("Client");
        console.log(this.isSimulation); //false
        return a+"aaa";
    }
});

main.js:

Meteor.methods({
    "test":function(a){
      console.log("server");
      console.log(this.isSimulation);//true
      return  a+1;
    }
});

Meteor.startup(function(){
    Meteor.call("test",1,function(err,result){
        console.log(result);
    });
});

this.unblock()
解释这个之前,我刚才得到了一个结论,一直没有注意到(没有使用过this.unblick()也没注意过它是否是阻塞的,写博客确实也是一种自我学习过程,当然这是题外话了。)的结论。默认情况下在Client调用Server端的函数这种过程是阻塞式的。意识就是说,如果有多个客户端同时调用test这个函数,而test这个函数的执行需要耗费一段,那么这个调用会进行排队依次执行。而就是只要当前面N次调用都完成以后,后面的调用才会进行。可以经过一下代码验证:
清空client.js的内容,不需要。 然后main.js的内容是:

var fs = Npm.require('fs');
Meteor.methods({
    "test":function(a){
      if(a==1){
        //这里我通过多次读写文件的时间耗费进行阻塞
        //,注意文件大小最好是在10M-40M之间,太小的文件,或者一般的计算可能执行速度太快看不到效果
          for(var i=0;i<20;i++){
            var data = fs.readFileSync("/home/ec/download/NVIDIA");
            fs.writeFileSync("/home/ec/download/test/NVIDIA"+i, data);
          }
      }
      console.log(a);
    }
});

在单个浏览器端,或者同时多个浏览器运行:(把命令写在一行)

 Meteor.call("test",1); Meteor.call("test",2);

这样就可以看到阻塞的效果。[我猜大概这样是为了实现类似资源锁之类操作]

那么现在,我们不想它阻塞在这里也是很容易做的。没错!就是在函数的第一行执行:
(这里貌似存在一个bug 这个this.unblock()没有起作用 ,具体原因可参考 详情 和 解决方法

this.unblock();

这是main.js内容如下:(参考解决过后的代码)

var fs = Npm.require('fs');
var readFile = Meteor._wrapAsync(fs.readFile.bind(fs));
var writeFile = Meteor._wrapAsync(fs.writeFile.bind(fs));
Meteor.methods({
    "test":function(a){
      this.unblock();
      if(a==1){
          //这里我通过多次读写文件的时间耗费进行阻塞
          //,注意文件大小最好是在10M-40M之间,太小的文件,或者一般的计算可能执行速度太快看不到效果
          var data = readFile("/home/ec/download/NVIDIA");
         for(var i=0;i<5;i++){
            writeFile("/home/ec/download/test/NVIDIA"+i, data);
            console.log(a+"-"+i);
        }
      }
      console.log(a);   
      return a;
    }
});

好了,this.unblock()的使用是这篇博客多耗费了2天时间。总算跳过去了。现在就剩最后一个点了。

this.connection Server

这是一个Connection对象,具体可以参考Server connections和这个部分的Meteor.onConnection(callback) 此属性不再本篇博客的讨论范围内。既然不讨论,那么就抄个官方文档的解释放在这里吧。

Access inside a method invocation. The connection this method was received on. null if the method is not associated with a connection, eg. a server initiated method call.

其中一个表达的大概意思是 如果没有通过http(非Client-Server或DDP)而是直接在Server中来调用这个methods里面的方法,那么connection是一个null。

本篇博客结束了。

对了最后其实还落下了一点点。那就是关于Meteor.apply(name, params [, options] [, asyncCallback])第三个可选参数options的解释了。
应该比较好理解 。同样贴上一个官方文档。

Options #仅在client端才有此参数
wait Boolean
(Client only) If true, don’t send this method until all previous method calls have completed, and don’t send any subsequent method calls until this one is completed.
onResultReceived Function
解释:如果为true,除非前面的方法调用都已经完成回调了,才会开始执行本次的函数调用。并且在本次函数调用完成之前 不会在执行其他任何的函数调用了。
(Client only) This callback is invoked with the error or result of the method (just like asyncCallback) as soon as the error or result is available. The local cache may not yet reflect the writes performed by the method.

这个的验证代码可以自己尝试写一下。就当是思考题吧。

转载请注明出处。谢谢!

Ubuntu14.04防火墙配置_限制指定ip访问

mikel阅读(2444)

安装ufw
sudo apt-get install ufw

启用ufw
sudo ufw enable

阻止所有外部对本机的访问
sudo ufw default deny

允许所有的外部IP访问本机的22/tcp (ssh)端口
sudo ufw allow 22/tcp

允许此IP访问所有的本机端口
sudo ufw allow from 10.0.0.163

sudo ufw allow from 10.0.0.162

sudo ufw allow from 10.0.0.161

查看防火墙状态
sudo ufw status

删除上面建立的某条规则
sudo ufw delete allow smtp

防火墙规则文件路径
/lib/ufw/user.rules

关闭防火墙
sudo ufw disable

 

【使用流程】

有台Ubuntu14.04服务器需要禁止一些IP来访问,本想使用/etc/hosts.allow和/etc/hosts.deny来做限制,后来还是使用utw防火墙搞定了。

vi /etc/hosts.allow
ALL:10.0.0.163,10.0.0.162,10.0.0.161

vi /etc/hosts.deny
ALL:ALL

设置好后
/etc/init.d/networking restart