
js new Date
new Date()对参数不管是格式还是内容都要求,且只返回字符串
new Date();
//Fri Aug 21 2015 15:51:55 GMT+0800 (中国标准时间)
new Date(1293879600000);
new Date('2011-01-01T11:00:00')
new Date('2011/01/01 11:00:00')
new Date(2011,0,1,11,0,0)
new Date('jan 01 2011,11 11:00:00')
new Date('Sat Jan 01 2011 11:00:00')
//Sat Jan 01 2011 11:00:00 GMT+0800 (中国标准时间)
new Date('sss');
new Date('2011/01/01T11:00:00');
new Date('2011-01-01-11:00:00')
new Date('1293879600000');
//Invalid Date
new Date('2011-01-01T11:00:00')-new Date('1992/02/11 12:00:12')
//596069988000
从上面几个测试结果可以很容易发现
new Date()在参数正常的情况只会返回当前时间的字符串(且是当前时区的时间)
new Date()在解析一个具体的时间的时候,对参数有较严格的格式要求,格式不正确的时候会直接返回Invalid Date,比如将number类的时间戳转换成string类的时候也会导致解析出错
虽然new Date()的返回值是字符串,然而两个new Date()的结果字符串是可以直接相减的,结果为相差的毫秒数。
那么,new Date()能接受的参数格式到底是什么标准呢?(相对于严格要求的多参数传值方法。非严格的单参数(数字日期表示格式)更常用且更容易出错,所以下文只考虑单参数数字时间字符串转换的情况)
参考:http://chitanda.me/2015/08/21/the-trivia-of-js-date-function/
new Date()对参数不管是格式还是内容都要求,且只返回字符串
new Date();
//Fri Aug 21 2015 15:51:55 GMT+0800 (中国标准时间)
new Date(1293879600000);
new Date('2011-01-01T11:00:00')
new Date('2011/01/01 11:00:00')
new Date(2011,0,1,11,0,0)
new Date('jan 01 2011,11 11:00:00')
new Date('Sat Jan 01 2011 11:00:00')
//Sat Jan 01 2011 11:00:00 GMT+0800 (中国标准时间)
new Date('sss');
new Date('2011/01/01T11:00:00');
new Date('2011-01-01-11:00:00')
new Date('1293879600000');
//Invalid Date
new Date('2011-01-01T11:00:00')-new Date('1992/02/11 12:00:12')
//596069988000
从上面几个测试结果可以很容易发现
new Date()在参数正常的情况只会返回当前时间的字符串(且是当前时区的时间)
new Date()在解析一个具体的时间的时候,对参数有较严格的格式要求,格式不正确的时候会直接返回Invalid Date,比如将number类的时间戳转换成string类的时候也会导致解析出错
虽然new Date()的返回值是字符串,然而两个new Date()的结果字符串是可以直接相减的,结果为相差的毫秒数。
那么,new Date()能接受的参数格式到底是什么标准呢?(相对于严格要求的多参数传值方法。非严格的单参数(数字日期表示格式)更常用且更容易出错,所以下文只考虑单参数数字时间字符串转换的情况)
参考:http://chitanda.me/2015/08/21/the-trivia-of-js-date-function/

如何使用HBuilderX开发微信小程序
注意,本文讲的是使用HBuilderX开发原生微信小程序,不是uni-app。使用uni-app请在HBuilderX中新建
uni-app
项目
很多开发者需要开发小程序,但小程序的开发IDE却总被众多开发者吐槽。
很多开发者只把微信开发工具当模拟器用,代码编写仍然在其他专业编辑器里。
HBuilder作为专业的开发工具,近期也提供了对微信小程序的开发支持:
- 强大的代码提示
- 高效的字处理
- 保存代码时自动刷新微信模拟器。
下文简单讲解使用步骤。
新建微信小程序
在HBuilderX中新建项目时,支持小程序类型,如下:
小程序项目创建后,默认工程目录如下:
]
导入已有小程序
若已存在微信小程序项目,则可以直接将工程目录拖到HBuidlerX中。
小程序语法提示
语法提示是HBuilder一贯的长项,在HBuilder中对小程序语法也有很好的提示。
小程序JS API提示:
小程序wxml标签提示:
同步到小程序模拟器
HBuilderX支持同步代码到微信开发者工具,如下图所示,点击“微信开发者工具”
系统会尝试检测并启动微信开发者工具。
若是已存在的微信项目(之前已使用微信开发者工具打开过的项目),则会直接打开并显示模拟器、编译器等截面,直接跳到下方第4步骤继续阅读即可。
若是新建的项目,则需要按照如下步骤进行小程序项目的初始化导入。
1、微信开发者工具成功启动后界面:
2、选择小程序项目,并在新打开的窗口中点击右下角的“+”,打开新项目向导:
3、项目目录设置为刚刚在HBuilderX中新建的工程根目录
4、在微信开发者工具中,点击左上角的“编译器”,关闭微信编译器;若暂时不需要调试,也可以将调试器关闭,仅保留模拟器。
5、拖一下HBuilderX和微信开发者工具的位置,像如下方式,左侧为HBuilderX的编辑器截面,右侧为小程序的模拟器截面
之后,在左侧HBuilderX中修改小程序代码,右侧模拟器会自动刷新,如下是一个实际录屏示例:
最后,微信小程序有自己的appid,向微信申请后,把appid填写在项目的project.config.json里,有个appid参数。
有appid才能正式发布上传。
注意,本文讲的是使用HBuilderX开发原生微信小程序,不是uni-app。使用uni-app请在HBuilderX中新建
uni-app
项目
很多开发者需要开发小程序,但小程序的开发IDE却总被众多开发者吐槽。
很多开发者只把微信开发工具当模拟器用,代码编写仍然在其他专业编辑器里。
HBuilder作为专业的开发工具,近期也提供了对微信小程序的开发支持:
- 强大的代码提示
- 高效的字处理
- 保存代码时自动刷新微信模拟器。
下文简单讲解使用步骤。
新建微信小程序
在HBuilderX中新建项目时,支持小程序类型,如下:
小程序项目创建后,默认工程目录如下:
]
导入已有小程序
若已存在微信小程序项目,则可以直接将工程目录拖到HBuidlerX中。
小程序语法提示
语法提示是HBuilder一贯的长项,在HBuilder中对小程序语法也有很好的提示。
小程序JS API提示:
小程序wxml标签提示:
同步到小程序模拟器
HBuilderX支持同步代码到微信开发者工具,如下图所示,点击“微信开发者工具”
系统会尝试检测并启动微信开发者工具。
若是已存在的微信项目(之前已使用微信开发者工具打开过的项目),则会直接打开并显示模拟器、编译器等截面,直接跳到下方第4步骤继续阅读即可。
若是新建的项目,则需要按照如下步骤进行小程序项目的初始化导入。
1、微信开发者工具成功启动后界面:
2、选择小程序项目,并在新打开的窗口中点击右下角的“+”,打开新项目向导:
3、项目目录设置为刚刚在HBuilderX中新建的工程根目录
4、在微信开发者工具中,点击左上角的“编译器”,关闭微信编译器;若暂时不需要调试,也可以将调试器关闭,仅保留模拟器。
5、拖一下HBuilderX和微信开发者工具的位置,像如下方式,左侧为HBuilderX的编辑器截面,右侧为小程序的模拟器截面
之后,在左侧HBuilderX中修改小程序代码,右侧模拟器会自动刷新,如下是一个实际录屏示例:
最后,微信小程序有自己的appid,向微信申请后,把appid填写在项目的project.config.json里,有个appid参数。
有appid才能正式发布上传。

ajax如何带上cookie
之前都有这样一个理解:
ajax请求时是不会自动带上cookie的,要是想让他带上的话,必须哟啊设置withCredential为true。
这个说法会让人产生完全扭曲的误解,我就是其中之一。
完整的无歧义的表述应该是这样:
1.ajax会自动带上同源的cookie,不会带上不同源的cookie
- 可以通过前端设置withCredentials为true, 后端设置Header的方式让ajax自动带上不同源的cookie,但是这个属性对同源请求没有任何影响。会被自动忽略。
- 这是MDN对withCredentials的解释: MDN-withCredentials ,我接着解释一下同源。
众所周知,ajax请求是有同源策略的,虽然可以应用CORS等手段来实现跨域,但是这并不是说这样就是“同源”了。ajax在请求时就会因为这个同源的问题而决定是否带上cookie,这样解释应该没有问题了吧,还不知道同源策略的,应该去谷歌一下看看。
实验
第一步: 建立一个本地服务器
1.新建一个demo文件夹,进入文件夹,用php -S localhost:9000开启一个本地服务器
2.在demo文件夹下新建一个index.php文件, 内容为:
<?php
header("Access-Control-Allow-Origin: http://localhost:9999");
header('Access-Control-Allow-Credentials:true');
$value = "something with cookie";
setcookie("testcookie", $value, time() + 3600);
echo "cookie has seted";
注意: Access-Control-Allow-Origin必须制定特定的URL,不能是*, 且需要加上Access-Control-Allow-Credentials
第二步: 编写请求测试代码
在桌面上新建一个test.html文件,内容为:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
</head>
<body>
<script>
$.ajax({
url: "http://localhost:9000/",
type: 'GET',
success: function(data) {
console.log(data)
},
error: function(err) {
console.error(err)
}
})
</script>
</body>
</html>
- 在desktop目录下起一个服务器,用php -S localhost:9999开启一个本地服务器
第三步: 测试
1.在浏览器中访问localhost:9999/test.html,打开调试工具->application->cookie可以看到cookie设置成功。
2.打开调试工具->netwoek,刷新一下,可以看到一个localhost请求,检查这个localhost请求的Request Headers,发现没有cookie这个头部,说明不同源的请求时不会带上cookie的(即使有CORS)
3.把test.html放到demo文件夹下,在访问localhost:9000/test.html,查看Request Headers,会发现cookie头部, 说明同源请求自动带上了cookie
4.把test.html的内容更改为以下内容,请求时会有cookie头部。说明withCredentials起作用了<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="jquery-2.2.4.min.js"></script> </head> <body> <script> $.ajax({ url: "http://localhost:9000", type: 'GET', xhrFields: { withCredentials: true // 这里设置了withCredentials }, success: function(data) { console.log(data) }, error: function(err) { console.error(err) } }) </script> </body> </html>
参考:https://zhuanlan.zhihu.com/p/28818954
https://www.zhihu.com/question/25427931
同源策略
http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html
https://stackoverflow.com/questions/2870371/why-is-jquerys-ajax-method-not-sending-my-session-cookie
服务器也可以在设置Cookie的时候,指定Cookie的所属域名为一级域名,比如.example.com。
Set-Cookie: key=value; domain=.example.com; path=/
这样的话,二级域名和三级域名不用做任何设置,都可以读取这个Cookie。
同源策略是浏览器最核心也最基本的安全功能首先:web是开放的世界, 需要互联链接. 你的网站, 可以使用别人的图片, img, 使用别人 script 做统计, 做广告联盟
假设没有同源, 互联网世界是什么样?链接跳转导致的问题. http://a.com , 放一个链接到 icbc.com, 然后 window.open来打开, 获取窗口句柄, 然后可以拥有对这个页面完全的控制权. 拦截表单,捕获数据,将账号密码上传到a.com.ajax请求, 要啥就有啥. 你登录jd.com; 然后打开a.com, 通过ajax 请求http://jd.com 的用户信息接口, 这时候因为访问的jd.com,所以浏览器自动带上了jd的cookie,然后获取到你的订单list ,昵称, 所有私密信息.所以,需要要同源策略
在同一个域内,客户端脚本可以任意读写同源内的资源,dom,cookie;但是不同的域,就不行.
之前都有这样一个理解:
ajax请求时是不会自动带上cookie的,要是想让他带上的话,必须哟啊设置withCredential为true。
这个说法会让人产生完全扭曲的误解,我就是其中之一。
完整的无歧义的表述应该是这样:
1.ajax会自动带上同源的cookie,不会带上不同源的cookie
- 可以通过前端设置withCredentials为true, 后端设置Header的方式让ajax自动带上不同源的cookie,但是这个属性对同源请求没有任何影响。会被自动忽略。
- 这是MDN对withCredentials的解释: MDN-withCredentials ,我接着解释一下同源。
众所周知,ajax请求是有同源策略的,虽然可以应用CORS等手段来实现跨域,但是这并不是说这样就是“同源”了。ajax在请求时就会因为这个同源的问题而决定是否带上cookie,这样解释应该没有问题了吧,还不知道同源策略的,应该去谷歌一下看看。
实验
第一步: 建立一个本地服务器
1.新建一个demo文件夹,进入文件夹,用php -S localhost:9000开启一个本地服务器
2.在demo文件夹下新建一个index.php文件, 内容为:
<?php
header("Access-Control-Allow-Origin: http://localhost:9999");
header('Access-Control-Allow-Credentials:true');
$value = "something with cookie";
setcookie("testcookie", $value, time() + 3600);
echo "cookie has seted";
注意: Access-Control-Allow-Origin必须制定特定的URL,不能是*, 且需要加上Access-Control-Allow-Credentials
第二步: 编写请求测试代码
在桌面上新建一个test.html文件,内容为:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
</head>
<body>
<script>
$.ajax({
url: "http://localhost:9000/",
type: 'GET',
success: function(data) {
console.log(data)
},
error: function(err) {
console.error(err)
}
})
</script>
</body>
</html>
- 在desktop目录下起一个服务器,用php -S localhost:9999开启一个本地服务器
第三步: 测试
1.在浏览器中访问localhost:9999/test.html,打开调试工具->application->cookie可以看到cookie设置成功。
2.打开调试工具->netwoek,刷新一下,可以看到一个localhost请求,检查这个localhost请求的Request Headers,发现没有cookie这个头部,说明不同源的请求时不会带上cookie的(即使有CORS)
3.把test.html放到demo文件夹下,在访问localhost:9000/test.html,查看Request Headers,会发现cookie头部, 说明同源请求自动带上了cookie
4.把test.html的内容更改为以下内容,请求时会有cookie头部。说明withCredentials起作用了<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="jquery-2.2.4.min.js"></script> </head> <body> <script> $.ajax({ url: "http://localhost:9000", type: 'GET', xhrFields: { withCredentials: true // 这里设置了withCredentials }, success: function(data) { console.log(data) }, error: function(err) { console.error(err) } }) </script> </body> </html>
参考:https://zhuanlan.zhihu.com/p/28818954
https://www.zhihu.com/question/25427931
同源策略
http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html
https://stackoverflow.com/questions/2870371/why-is-jquerys-ajax-method-not-sending-my-session-cookie
服务器也可以在设置Cookie的时候,指定Cookie的所属域名为一级域名,比如.example.com。
Set-Cookie: key=value; domain=.example.com; path=/
这样的话,二级域名和三级域名不用做任何设置,都可以读取这个Cookie。
同源策略是浏览器最核心也最基本的安全功能首先:web是开放的世界, 需要互联链接. 你的网站, 可以使用别人的图片, img, 使用别人 script 做统计, 做广告联盟
假设没有同源, 互联网世界是什么样?链接跳转导致的问题. http://a.com , 放一个链接到 icbc.com, 然后 window.open来打开, 获取窗口句柄, 然后可以拥有对这个页面完全的控制权. 拦截表单,捕获数据,将账号密码上传到a.com.ajax请求, 要啥就有啥. 你登录jd.com; 然后打开a.com, 通过ajax 请求http://jd.com 的用户信息接口, 这时候因为访问的jd.com,所以浏览器自动带上了jd的cookie,然后获取到你的订单list ,昵称, 所有私密信息.所以,需要要同源策略
在同一个域内,客户端脚本可以任意读写同源内的资源,dom,cookie;但是不同的域,就不行.
收起阅读 »
深入浏览器cookie,安全与共享
我们都知道,HTTP是无状态的,但我们的服务器有时需要有区别的对待不同的客服端,如何区分,其实就是一种状态的表现,Cookie应运而生,当然,本文主题并不是介绍Cookie的历史和作用。
cookie安全
最近和同事在写管理系统前端时,想到用测试机上的数据来测试本地代码,这样我们在本地也能非常友好的去复现测试机上的bug,而且代码还是非混淆,非压缩的。
之前其实内部是有相关的代理工具,基本原理是通过将测试机的的ip指向本地(在自己机器的host上配置),然后本地配置nginx server,将静态资源请求转向本地的dev server,接口请求转向测试机。但我们觉得,既然本地起来一个dev的server,我们直接给这个server配置转发,书写规则,让其转发到测试机上对应的地址即可。
理想很丰满,现实很骨感:
服务器上对应接口永远返回的是401(未授权),好像确实少了某些操作。
服务器如何识别客服端,我想有过服务端开发经验的小伙伴不会陌生,对,就是session,服务端的一种状态化解决方案,当然,session的实现需要客服端cookie的配合,因为session id会通过cookie的方式下发到客服端,后续的请求都会带上这个session id,然后服务端取出存储的用户状态。基于这一点,我们将Cookie分为会话Cookie和持久性Cookie。
Session cookies
会话性质的Cookie,不会指定Expires或者Max-Age,当浏览器被关闭的时候将会被删除。当我们看到浏览器Cookie面板中Cookie的Expires / Max-Age为1969-12-31T23:59:59.000Z,也就是计算机时间0的前一秒时(Chrome中),我们差不多就可以认为这是个session cookie了。
Permanent cookies
持久性Cookie,可通过Expires/Max-Age来设置Cookies的过期时间。
回到问题
那么回到我们上面的问题,直接访问本地开的dev server是肯定带不过去Cookie的,因为localhost.com或者127.0.0.1 和 test.somsite.com/contextName之间从domain方面来说是压根没有半毛线关系的。
那如果我们对local.test.somsite.com配置host,然后指向127.0.0.1呢?访问local.test.somsite.com/contextName,会发现有一部分cookie是带过去了的,唯独我们的session cookie没有被带上。
注:此处有朋友可能会问,为毛不直接将host设置为test.somsite.com,一了百了,其实不是不可以,只是如果和测试机设置一样的hostname的话,我们可能要不断在这两个host之间switch了。
为毛带不过去,谁定义的规则,在rfc6265中,我想我们能够找到答案。这里多说一句,在rfc6265之前,其实是还有一个rfc 2109的。然后,你懂得,在一些旧版本的浏览器,或者一些后端技术中,依然会去尊重RFC 2109的一些规则。博主也会在后面提到我所遇到的一些和这相关的。
host-only-flag in Cookie
在rfc6265的5.3节(Storage Model),定义了浏览器在接收到服务端的Set-Cookie之后的一个存储机制,其实浏览器针对服务端设置的每一个Cookie,都会存储如下字段: name, value, expiry-time, domain, path, creation-time, last-access-time, persistent-flag, host-only-flag, secure-only-flag, and http-only-flag.当然,这并不是要求服务端设置这么多个字段,而是有默认值的,这个缺省值,就是我们这儿要讨论的。我们定位到第六点:
6. If the domain-attribute is non-empty:
If the canonicalized request-host does not domain-match the
domain-attribute:
Ignore the cookie entirely and abort these steps.
Otherwise:
Set the cookie's host-only-flag to false.
Set the cookie's domain to the domain-attribute.
Otherwise:
Set the cookie's host-only-flag to true.
Set the cookie's domain to the canonicalized request-host.
dimain-attribute为空时,将host-only-flag标记为true。
而host-only-flag是干啥的呢?我们先不看它的定义,看浏览器时如何使用这个属性的,同样是rfc6265, 5.4节(The Cookie Header),我们可以看到User-Agent是如何去决定带Cookie的:
The user agent MUST use an algorithm equivalent to the following algorithm to compute the "cookie-string" from a cookie store and a request-uri: (也就是用户代理在发送请求时,在决定是否带上对应Cookie方面的一个决策算法,注意其语法: MUST)
1. Let cookie-list be the set of cookies from the cookie store that
meets all of the following requirements:
* Either:
The cookie's host-only-flag is true and the canonicalized
request-host is identical to the cookie's domain.
Or:
The cookie's host-only-flag is false and the canonicalized
request-host domain-matches the cookie's domain.
* The request-uri's path path-matches the cookie's path.
* If the cookie's secure-only-flag is true, then the request-
uri's scheme must denote a "secure" protocol (as defined by
the user agent).
NOTE: The notion of a "secure" protocol is not defined by
this document. Typically, user agents consider a protocol
secure if the protocol makes use of transport-layer
security, such as SSL or TLS. For example, most user
agents consider "https" to be a scheme that denotes a
secure protocol.
* If the cookie's http-only-flag is true, then exclude the
cookie if the cookie-string is being generated for a "non-
HTTP" API (as defined by the user agent).
我们可以看到其中第一点的第一小点,当 host-only-flag 被设置为true是,请求的主机的域名就必须与cookie中设置的domain完全匹配,否则不会带上此Cookie。
回到我们的session-cookie, 我们看下这个JSESSIONID 的 Cookie是如何被设置的:

那我们上面的问题就很清晰了,没有设置cookie的domain属性,对于请求的cookie的携带使用强domain匹配规则,自然是像子域这种也不会被发送过去的,这也符合我们session id这样的高安全需求且独立操作的场景要求。
Cookie共享
说完安全谈共享(share),其实二者并不是独立的,而是共生。正是因为共享,我们才需要安全的规则去限制,Web所营造的一个共享的环境让人有机可趁,而随之而来的一些安全策略就是去确保我们的信息,个人隐私的安全。Cookie如此,Same-origin policy亦是如此。
那么说到Cookie的共享,官方并不是用源(origin)来进行描述,而是"third-party"这个词,第三方。而针对用户代理如何去对待第三方Cookie,rfc并未作出明确的规定。
User agents vary widely in their third-party cookie policies. This document grants user agents wide latitude to experiment with third-party cookie policies that balance the privacy and compatibility needs of their users. However, this document does not endorse any particular third-party cookie policy.
这里其实还需要补充一点的是,对于请求是否该携带Cookie,我们cookie的domain字段在set-cookie时就已经做了明确的限定了,当没有指定domain的时候,上面已经列出了相关的策略,那如果指定了domain呢(我们同样可以看到rfc6265的Domain Matching),有兴趣的读者可以点击前往看下,我这里简单介绍下细则:
一个字符串str如果满足匹配一个给定的domain的话,它至少应该满足下面条件中的一个:
强匹配,就是完全一样
或者同时满足下面的条件:
domain字段的值是这个str的后缀
str中最后一个不包含在domain值中的字符应该是个点(%x2E (".")),这个其实很好理解,domain: google.com and str: map.google.com => OK; domain: google.com and str: map.mgoogle.com => SAD
这个字符必须是个域名(主机名),而不能是IP地址。
读者需要注意的是,Domain match,也就是我们上面的这个匹配规则,只是浏览器在决定是否携带我们的Cookie项时诸多参考项中的一个,即必要不充分。浏览器还会参考诸如是否过期,以及Path-Match等项。
说完浏览器对于携带Cookie项的一个决策策略,其实整个流程(或者说是Cookie的生命周期)还有一部分没有涉及,就是一开始,我们设置Cookie的时候,此时,浏览器也会有一系列的约束。这在我们规范的5.3. Storage Model部分做了相关的说明,我们可以注意到其4,5,6点(第六点在本文的前面有部分提到):
4. If the cookie-attribute-list contains an attribute with an
attribute-name of "Domain":
Let the domain-attribute be the attribute-value of the last
attribute in the cookie-attribute-list with an attribute-name
of "Domain".
Otherwise:
Let the domain-attribute be the empty string.
5. If the user agent is configured to reject "public suffixes" and
the domain-attribute is a public suffix:
If the domain-attribute is identical to the canonicalized
request-host:
Let the domain-attribute be the empty string.
Otherwise:
Ignore the cookie entirely and abort these steps.
6. If the domain-attribute is non-empty:
If the canonicalized request-host does not domain-match the
domain-attribute:
Ignore the cookie entirely and abort these steps.
Otherwise:
Set the cookie's host-only-flag to false.
Set the cookie's domain to the domain-attribute.
Otherwise:
Set the cookie's host-only-flag to true.
Set the cookie's domain to the canonicalized request-host.
总结来说,当浏览器决定是否存储Cookie项时,在Domain这方面的决策大致为:首先检验当前domain属性的设值是否命中了我们浏览器的公共前缀(public suffixes),如果命中且当前这个domain的值能够完全匹配当前请求的域名,将domain值重置为空,走我们前面提到的host-only-flag流程;如果命中但domain值不能匹配当前请求的域名,本Cookie项设置失败。前面的case还有一个前提是浏览器被配置为零容忍(不允许)public suffixes(is configured to reject "public suffixes")。如果上面步骤没凉的话,第六点当domain属性未被置空时,这里就会有一次的domain-match的检测,检测失败则设置失败呗。
Cookies use a separate definition of origins. A page can set a cookie for its own domain or any parent domain, as long as the parent domain is not a public suffix. Firefox and Chrome use the Public Suffix List to determine if a domain is a public suffix. Internet Explorer uses its own internal method to determine if a domain is a public suffix. The browser will make a cookie available to the given domain including any sub-domains, no matter which protocol (HTTP/HTTPS) or port is used. When you set a cookie, you can limit its availability using the Domain, Path, Secure and Http-Only flags. When you read a cookie, you cannot see from where it was set. Even if you use only secure https connections, any cookie you see may have been set using an insecure connection.
同时,感兴趣的同学也可以浏览下紫云飞大大的这篇博文: SameSite Cookie,防止 CSRF 攻击
rfc2109 与 rfc6265
对,新旧版本,自然而然有时候服务端的一些Set-Cookie会做向前兼容的处理
Dot prefix in cookie domain
domain中前置的点(dot)意味着这个cookie对于子域也同样有效(The leading dot means that the cookie is valid for subdomains as well)。但这是rfc2109种的规则,在rfc6265中,我们看到了这句话:
The Domain attribute specifies those hosts to which the cookie will be sent. For example, if the value of the Domain attribute is "example.com", the user agent will include the cookie in the Cookie header when making HTTP requests to example.com, www.example.com, and www.corp.example.com. (Note that a leading %x2E ("."), if present, is ignored even though that character is not permitted, but a trailing %x2E ("."), if present, will cause the user agent to ignore the attribute.) If the server omits the Domain attribute, the user agent will return the cookie only to the origin server.
rfc6265 会直接忽略前置的点,其实算是对老版本的一种兼容了。所以如果你在浏览器的Cookie面板中看到domain列的值有前置点,不要惊讶~
说在最后
其实在写这篇cookie安全相关的博文的时候,我脑袋了又冒出了同源策略这个词,加上我们这里的Cookie的安全策略,其实所谈到的都是关乎于源的共享的一个问题,那么其实cookie的话,还加上了一个源之间的干扰隔离(Set-Cookie domain字段限制)。谁决定源(resource)的访问权,当然是源的拥有者(服务端),这就不难理解,你想去hack同源策略,或者说CORS,自然也是需要服务端"答应"的,但这个策略实际由浏览器实施,作用于想去跨源访问资源的文档或脚本。
相关链接:https://lancelou.com/post/browser-cookie-deeping
https://www.jianshu.com/p/643427c17877
ajax跨域请求中的cookie问题
update 另一个问题
ajax在进行复杂请求如PUT,POST,DELETE等时,当请求为cross domain request是,会先发一个OPTIONS请求确认服务器的跨域支持情况,在发送原来的请求,所以对于服务器,需要对OPTIONS请求做一次xiang'yin
遇到的问题
对于前后端分离的应用,使用ajax跨域请求时,默认情况下是无法传输cookie的。具体的异常表现如下
客户端发送给服务器的请求中不包含cookie信息
服务器返回给客户端的响应中包含了Set Cookie 的信息,但是在浏览器的cookie中,没有记录词条cookie信息
解决方法
需要前后端都做一些小的改动
服务器端
以nodejs的后端为例,使用express框架,需要加上几行代码
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", config().allow_origin);
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
res.header("Access-Control-Allow-Credentials", "true");
res.header("X-Powered-By", ' 3.2.1')
res.header("Content-Type", "application/json;charset=utf-8");
next();
});
注意这句话:
res.header("Access-Control-Allow-Credentials", "true");
这句话用来允许跨域访问时带上cookie信息,此外有一个问题,就是当我们"Access-Control-Allow-Origin"设置为的时候,上面这句话是无法使用的。所以不能够设置为,否则无法使用cookie。
浏览器端
以jquery的ajax请求为例
$.ajax({
url,
type: 'get',
dataType: 'json',
// 允许跨域
crossDomain: true,
// 下面这句话允许跨域的cookie访问
xhrFields: {
withCredentials: true
},
success: (res) => {
console.log(res);
}
});
总结
这个问题就是这样解决的,建议只允许自己前端网站的域名进行跨域访问,防止CSRF之类的攻击。
作者:cooody
链接:https://www.jianshu.com/p/643427c17877
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
我们都知道,HTTP是无状态的,但我们的服务器有时需要有区别的对待不同的客服端,如何区分,其实就是一种状态的表现,Cookie应运而生,当然,本文主题并不是介绍Cookie的历史和作用。
cookie安全
最近和同事在写管理系统前端时,想到用测试机上的数据来测试本地代码,这样我们在本地也能非常友好的去复现测试机上的bug,而且代码还是非混淆,非压缩的。
之前其实内部是有相关的代理工具,基本原理是通过将测试机的的ip指向本地(在自己机器的host上配置),然后本地配置nginx server,将静态资源请求转向本地的dev server,接口请求转向测试机。但我们觉得,既然本地起来一个dev的server,我们直接给这个server配置转发,书写规则,让其转发到测试机上对应的地址即可。
理想很丰满,现实很骨感:
服务器上对应接口永远返回的是401(未授权),好像确实少了某些操作。
服务器如何识别客服端,我想有过服务端开发经验的小伙伴不会陌生,对,就是session,服务端的一种状态化解决方案,当然,session的实现需要客服端cookie的配合,因为session id会通过cookie的方式下发到客服端,后续的请求都会带上这个session id,然后服务端取出存储的用户状态。基于这一点,我们将Cookie分为会话Cookie和持久性Cookie。
Session cookies
会话性质的Cookie,不会指定Expires或者Max-Age,当浏览器被关闭的时候将会被删除。当我们看到浏览器Cookie面板中Cookie的Expires / Max-Age为1969-12-31T23:59:59.000Z,也就是计算机时间0的前一秒时(Chrome中),我们差不多就可以认为这是个session cookie了。
Permanent cookies
持久性Cookie,可通过Expires/Max-Age来设置Cookies的过期时间。
回到问题
那么回到我们上面的问题,直接访问本地开的dev server是肯定带不过去Cookie的,因为localhost.com或者127.0.0.1 和 test.somsite.com/contextName之间从domain方面来说是压根没有半毛线关系的。
那如果我们对local.test.somsite.com配置host,然后指向127.0.0.1呢?访问local.test.somsite.com/contextName,会发现有一部分cookie是带过去了的,唯独我们的session cookie没有被带上。
注:此处有朋友可能会问,为毛不直接将host设置为test.somsite.com,一了百了,其实不是不可以,只是如果和测试机设置一样的hostname的话,我们可能要不断在这两个host之间switch了。
为毛带不过去,谁定义的规则,在rfc6265中,我想我们能够找到答案。这里多说一句,在rfc6265之前,其实是还有一个rfc 2109的。然后,你懂得,在一些旧版本的浏览器,或者一些后端技术中,依然会去尊重RFC 2109的一些规则。博主也会在后面提到我所遇到的一些和这相关的。
host-only-flag in Cookie
在rfc6265的5.3节(Storage Model),定义了浏览器在接收到服务端的Set-Cookie之后的一个存储机制,其实浏览器针对服务端设置的每一个Cookie,都会存储如下字段: name, value, expiry-time, domain, path, creation-time, last-access-time, persistent-flag, host-only-flag, secure-only-flag, and http-only-flag.当然,这并不是要求服务端设置这么多个字段,而是有默认值的,这个缺省值,就是我们这儿要讨论的。我们定位到第六点:
6. If the domain-attribute is non-empty:
If the canonicalized request-host does not domain-match the
domain-attribute:
Ignore the cookie entirely and abort these steps.
Otherwise:
Set the cookie's host-only-flag to false.
Set the cookie's domain to the domain-attribute.
Otherwise:
Set the cookie's host-only-flag to true.
Set the cookie's domain to the canonicalized request-host.
dimain-attribute为空时,将host-only-flag标记为true。
而host-only-flag是干啥的呢?我们先不看它的定义,看浏览器时如何使用这个属性的,同样是rfc6265, 5.4节(The Cookie Header),我们可以看到User-Agent是如何去决定带Cookie的:
The user agent MUST use an algorithm equivalent to the following algorithm to compute the "cookie-string" from a cookie store and a request-uri: (也就是用户代理在发送请求时,在决定是否带上对应Cookie方面的一个决策算法,注意其语法: MUST)
1. Let cookie-list be the set of cookies from the cookie store that
meets all of the following requirements:
* Either:
The cookie's host-only-flag is true and the canonicalized
request-host is identical to the cookie's domain.
Or:
The cookie's host-only-flag is false and the canonicalized
request-host domain-matches the cookie's domain.
* The request-uri's path path-matches the cookie's path.
* If the cookie's secure-only-flag is true, then the request-
uri's scheme must denote a "secure" protocol (as defined by
the user agent).
NOTE: The notion of a "secure" protocol is not defined by
this document. Typically, user agents consider a protocol
secure if the protocol makes use of transport-layer
security, such as SSL or TLS. For example, most user
agents consider "https" to be a scheme that denotes a
secure protocol.
* If the cookie's http-only-flag is true, then exclude the
cookie if the cookie-string is being generated for a "non-
HTTP" API (as defined by the user agent).
我们可以看到其中第一点的第一小点,当 host-only-flag 被设置为true是,请求的主机的域名就必须与cookie中设置的domain完全匹配,否则不会带上此Cookie。
回到我们的session-cookie, 我们看下这个JSESSIONID 的 Cookie是如何被设置的:
那我们上面的问题就很清晰了,没有设置cookie的domain属性,对于请求的cookie的携带使用强domain匹配规则,自然是像子域这种也不会被发送过去的,这也符合我们session id这样的高安全需求且独立操作的场景要求。
Cookie共享
说完安全谈共享(share),其实二者并不是独立的,而是共生。正是因为共享,我们才需要安全的规则去限制,Web所营造的一个共享的环境让人有机可趁,而随之而来的一些安全策略就是去确保我们的信息,个人隐私的安全。Cookie如此,Same-origin policy亦是如此。
那么说到Cookie的共享,官方并不是用源(origin)来进行描述,而是"third-party"这个词,第三方。而针对用户代理如何去对待第三方Cookie,rfc并未作出明确的规定。
User agents vary widely in their third-party cookie policies. This document grants user agents wide latitude to experiment with third-party cookie policies that balance the privacy and compatibility needs of their users. However, this document does not endorse any particular third-party cookie policy.
这里其实还需要补充一点的是,对于请求是否该携带Cookie,我们cookie的domain字段在set-cookie时就已经做了明确的限定了,当没有指定domain的时候,上面已经列出了相关的策略,那如果指定了domain呢(我们同样可以看到rfc6265的Domain Matching),有兴趣的读者可以点击前往看下,我这里简单介绍下细则:
一个字符串str如果满足匹配一个给定的domain的话,它至少应该满足下面条件中的一个:
强匹配,就是完全一样
或者同时满足下面的条件:
domain字段的值是这个str的后缀
str中最后一个不包含在domain值中的字符应该是个点(%x2E (".")),这个其实很好理解,domain: google.com and str: map.google.com => OK; domain: google.com and str: map.mgoogle.com => SAD
这个字符必须是个域名(主机名),而不能是IP地址。
读者需要注意的是,Domain match,也就是我们上面的这个匹配规则,只是浏览器在决定是否携带我们的Cookie项时诸多参考项中的一个,即必要不充分。浏览器还会参考诸如是否过期,以及Path-Match等项。
说完浏览器对于携带Cookie项的一个决策策略,其实整个流程(或者说是Cookie的生命周期)还有一部分没有涉及,就是一开始,我们设置Cookie的时候,此时,浏览器也会有一系列的约束。这在我们规范的5.3. Storage Model部分做了相关的说明,我们可以注意到其4,5,6点(第六点在本文的前面有部分提到):
4. If the cookie-attribute-list contains an attribute with an
attribute-name of "Domain":
Let the domain-attribute be the attribute-value of the last
attribute in the cookie-attribute-list with an attribute-name
of "Domain".
Otherwise:
Let the domain-attribute be the empty string.
5. If the user agent is configured to reject "public suffixes" and
the domain-attribute is a public suffix:
If the domain-attribute is identical to the canonicalized
request-host:
Let the domain-attribute be the empty string.
Otherwise:
Ignore the cookie entirely and abort these steps.
6. If the domain-attribute is non-empty:
If the canonicalized request-host does not domain-match the
domain-attribute:
Ignore the cookie entirely and abort these steps.
Otherwise:
Set the cookie's host-only-flag to false.
Set the cookie's domain to the domain-attribute.
Otherwise:
Set the cookie's host-only-flag to true.
Set the cookie's domain to the canonicalized request-host.
总结来说,当浏览器决定是否存储Cookie项时,在Domain这方面的决策大致为:首先检验当前domain属性的设值是否命中了我们浏览器的公共前缀(public suffixes),如果命中且当前这个domain的值能够完全匹配当前请求的域名,将domain值重置为空,走我们前面提到的host-only-flag流程;如果命中但domain值不能匹配当前请求的域名,本Cookie项设置失败。前面的case还有一个前提是浏览器被配置为零容忍(不允许)public suffixes(is configured to reject "public suffixes")。如果上面步骤没凉的话,第六点当domain属性未被置空时,这里就会有一次的domain-match的检测,检测失败则设置失败呗。
Cookies use a separate definition of origins. A page can set a cookie for its own domain or any parent domain, as long as the parent domain is not a public suffix. Firefox and Chrome use the Public Suffix List to determine if a domain is a public suffix. Internet Explorer uses its own internal method to determine if a domain is a public suffix. The browser will make a cookie available to the given domain including any sub-domains, no matter which protocol (HTTP/HTTPS) or port is used. When you set a cookie, you can limit its availability using the Domain, Path, Secure and Http-Only flags. When you read a cookie, you cannot see from where it was set. Even if you use only secure https connections, any cookie you see may have been set using an insecure connection.
同时,感兴趣的同学也可以浏览下紫云飞大大的这篇博文: SameSite Cookie,防止 CSRF 攻击
rfc2109 与 rfc6265
对,新旧版本,自然而然有时候服务端的一些Set-Cookie会做向前兼容的处理
Dot prefix in cookie domain
domain中前置的点(dot)意味着这个cookie对于子域也同样有效(The leading dot means that the cookie is valid for subdomains as well)。但这是rfc2109种的规则,在rfc6265中,我们看到了这句话:
The Domain attribute specifies those hosts to which the cookie will be sent. For example, if the value of the Domain attribute is "example.com", the user agent will include the cookie in the Cookie header when making HTTP requests to example.com, www.example.com, and www.corp.example.com. (Note that a leading %x2E ("."), if present, is ignored even though that character is not permitted, but a trailing %x2E ("."), if present, will cause the user agent to ignore the attribute.) If the server omits the Domain attribute, the user agent will return the cookie only to the origin server.
rfc6265 会直接忽略前置的点,其实算是对老版本的一种兼容了。所以如果你在浏览器的Cookie面板中看到domain列的值有前置点,不要惊讶~
说在最后
其实在写这篇cookie安全相关的博文的时候,我脑袋了又冒出了同源策略这个词,加上我们这里的Cookie的安全策略,其实所谈到的都是关乎于源的共享的一个问题,那么其实cookie的话,还加上了一个源之间的干扰隔离(Set-Cookie domain字段限制)。谁决定源(resource)的访问权,当然是源的拥有者(服务端),这就不难理解,你想去hack同源策略,或者说CORS,自然也是需要服务端"答应"的,但这个策略实际由浏览器实施,作用于想去跨源访问资源的文档或脚本。
相关链接:https://lancelou.com/post/browser-cookie-deeping
https://www.jianshu.com/p/643427c17877
ajax跨域请求中的cookie问题
update 另一个问题
ajax在进行复杂请求如PUT,POST,DELETE等时,当请求为cross domain request是,会先发一个OPTIONS请求确认服务器的跨域支持情况,在发送原来的请求,所以对于服务器,需要对OPTIONS请求做一次xiang'yin
遇到的问题
对于前后端分离的应用,使用ajax跨域请求时,默认情况下是无法传输cookie的。具体的异常表现如下
客户端发送给服务器的请求中不包含cookie信息
服务器返回给客户端的响应中包含了Set Cookie 的信息,但是在浏览器的cookie中,没有记录词条cookie信息
解决方法
需要前后端都做一些小的改动
服务器端
以nodejs的后端为例,使用express框架,需要加上几行代码
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", config().allow_origin);
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
res.header("Access-Control-Allow-Credentials", "true");
res.header("X-Powered-By", ' 3.2.1')
res.header("Content-Type", "application/json;charset=utf-8");
next();
});
注意这句话:
res.header("Access-Control-Allow-Credentials", "true");
这句话用来允许跨域访问时带上cookie信息,此外有一个问题,就是当我们"Access-Control-Allow-Origin"设置为的时候,上面这句话是无法使用的。所以不能够设置为,否则无法使用cookie。
浏览器端
以jquery的ajax请求为例
$.ajax({
url,
type: 'get',
dataType: 'json',
// 允许跨域
crossDomain: true,
// 下面这句话允许跨域的cookie访问
xhrFields: {
withCredentials: true
},
success: (res) => {
console.log(res);
}
});
总结
这个问题就是这样解决的,建议只允许自己前端网站的域名进行跨域访问,防止CSRF之类的攻击。
作者:cooody
链接:https://www.jianshu.com/p/643427c17877
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

安卓离线打包的一定要看,少走弯路!!!
看官方的教程做离线打包尝试,因为官方默认的教程是用eclipse来开发安卓程序,就下载最新SDK按照教程一步步操作,结果各种错误,完全无法便于运行。官方离线打包教程连接:http://ask.dcloud.net.cn/docs/#//ask.dcloud.net.cn/article/38
因为认为hbuilder也是用eclipse来修改的,官方教程默认也是讲eclipse怎么离线,那么eclipse一定是最好。按照官方的来操作肯定是没问题的。
但是。。。。测试两天,遇到各种问题,搜索后,还是无法解决打包,打包到手机上运行,连影都没有,死活不行。真是万念俱灰啊!!!
最终搜索看到Android Studio打包的教程,要不咱试试? 说干就干,Android Studio一路安装下来都比较顺利,安装好后导入H5+ SDK的DEMO,提示需要下载和升级什么的,全部选下载更新。 http://ask.dcloud.net.cn/article/508
最后,在使用官方DEOM情况下,基本上没遇到什么问题,成功实现在真机上离线打包。当然DEMO离线打包成功了,开发的项目替换就很简单了。
最终个人理解分析:官方应该是用Android Studio来做离线打包的,但是教程还是老的写的用eclipse打包。不然不会出这么多问题过不了。
总结:官方的教程真的很坑啊,然后百度搜索了一下,才明白现在基本上都是用Android Studio来做开发,eclipse开发安卓很少了,而且ADT谷歌也不更新了,但是官方教程还是让用eclipse来离线打包。可能有人会说:这不都是常识问题吗?我想说,咋们以前没做过原生开发,才来学dcloud,完全按照教程来就对了,结果这里情况并不是这个样式。官方教程也不可靠啊! 看得论坛里面很多人说DCLOUD文档写得很垃圾,平时查资料确实也觉得不是还好理解,但是慢慢的搜索还是能明白。不好理解是一会事,但是别挖坑是吧。我承认DCLOUD技术是不错,但是学DCLOUD98%的都是不懂原生的小白,所以能不能把文档这些做好一点,用点心。最终,还是希望DCLUD越来越好,才发的这篇算闹骚了。
看官方的教程做离线打包尝试,因为官方默认的教程是用eclipse来开发安卓程序,就下载最新SDK按照教程一步步操作,结果各种错误,完全无法便于运行。官方离线打包教程连接:http://ask.dcloud.net.cn/docs/#//ask.dcloud.net.cn/article/38
因为认为hbuilder也是用eclipse来修改的,官方教程默认也是讲eclipse怎么离线,那么eclipse一定是最好。按照官方的来操作肯定是没问题的。
但是。。。。测试两天,遇到各种问题,搜索后,还是无法解决打包,打包到手机上运行,连影都没有,死活不行。真是万念俱灰啊!!!
最终搜索看到Android Studio打包的教程,要不咱试试? 说干就干,Android Studio一路安装下来都比较顺利,安装好后导入H5+ SDK的DEMO,提示需要下载和升级什么的,全部选下载更新。 http://ask.dcloud.net.cn/article/508
最后,在使用官方DEOM情况下,基本上没遇到什么问题,成功实现在真机上离线打包。当然DEMO离线打包成功了,开发的项目替换就很简单了。
最终个人理解分析:官方应该是用Android Studio来做离线打包的,但是教程还是老的写的用eclipse打包。不然不会出这么多问题过不了。
总结:官方的教程真的很坑啊,然后百度搜索了一下,才明白现在基本上都是用Android Studio来做开发,eclipse开发安卓很少了,而且ADT谷歌也不更新了,但是官方教程还是让用eclipse来离线打包。可能有人会说:这不都是常识问题吗?我想说,咋们以前没做过原生开发,才来学dcloud,完全按照教程来就对了,结果这里情况并不是这个样式。官方教程也不可靠啊! 看得论坛里面很多人说DCLOUD文档写得很垃圾,平时查资料确实也觉得不是还好理解,但是慢慢的搜索还是能明白。不好理解是一会事,但是别挖坑是吧。我承认DCLOUD技术是不错,但是学DCLOUD98%的都是不懂原生的小白,所以能不能把文档这些做好一点,用点心。最终,还是希望DCLUD越来越好,才发的这篇算闹骚了。
收起阅读 »
HBuilder苹果APP推送通知之ios推送证书申请和配置使用
很多人初次接触推送通知,不知道怎么去申请ios推送证书和配置推送。
很多人犯的错误就是用推送证书p12去打包ipa,推送不是用来打包的,下面详细介绍ios推证书的申请和配置使用。
ios推送证书分为测试调试用的iOS推送证书(开发环境)和上架到App Store的ios 推送证书!(生产环境)
APP要推送通知首先要在创建APPID时勾选推送服务。
推送证书是配置上传到推送平台的,如极光推送、个推、小米推送等,不是用来打包ipa的,下面会有介绍。
一、创建唯一标示符App IDs
首先打开开发者中心https://developer.apple.com/account,进入证书页面。
如果之前创建过appid,进去修改添加下推送服务就行了,不用重新创建。
1.1点击证书、ID及配件文件,进入设置。
1.2选择App IDs –>点击+创建一个新的App ID
其中有两项需要你自己填:
第一项Name,用来描述你的App ID,这个随便填,没有什么限制,最好是项目名称,这样方便自己辨识(不允许中文)
第二项Bundle ID (App ID Suffix),这是你App ID的后缀,需要仔细填写。用来标示我们的 app,使它有一个固定的身份,和你的程序直接相关。填写 Explicit App ID 的格式为:com.company.appName(要有两个点.)照着格式写,写个方便记的,后面很多地方要用到。
第三项配置服务权限,默认会选择2项,不能修改,其它常用的苹果支付,APP推送通知,这里要推送通知就勾选上,然后点击Continue确认,下一步。
Register后点击Done完成App ID的创建。
推送通知那项服务现在还是黄色的,因为还没创建ios推送证书,等下用Appuploader创建了ios推送证书,就会变成绿色,说明生效了。
二、iOS开发推送证书、开发真机调试用(开发环境、配合开发证书使用,用开发证书打包就能用开发环境ios推送证书测试推送通知iOS开发证书申请教程)
Appuploader可以实现是Windows电脑申请ios证书和上架APP。很方便的辅助ios上架工具!
1、打开Appuploader,用苹果开发者账号登录。
2、选择证书选项
3、点击+ADD\选择Apple Push Notification service SSL(Sandbox) —iOS开发环境推送证书
输入证书名称(随意)、邮箱(随意)、密码,选择你的APP对于的应用id,点击ok创建。
4、下载保存好.p12 iOS证书文件
推送证书是没描述文件的,只有一个p12,不用申请描述文件。
三、iOS发布推送证书、上架App Store用(生产环境、配合发布证书使用,开发环境推送证书测试好了推送,用发布证书上架成功了,就到推送后台切换为生成环境推送证书)
1、打开Appuploader,用苹果开发者账号登录。
2、选择证书选项
3、点击+ADD\选择Push Notification service SLL(Sandbox & Producyion)—iOS生产环境推送证书
输入证书名称(随意)、邮箱(随意)、密码,选择你的APP对于的appids,点击ok创建。
3、下载保存好.p12 iOS证书文件
四、配置ios推送证书p12
1、注册个推平台登录
http://www.getui.com/
点击左侧个推-消息推送,进入页面再点击右侧上方的登记应用
2、配置APP的基本信息
APP名称:你的APP名字
应用平台:安卓和苹果,ios开发环境(测试用的,配合开发证书使用)ios生产环境(上架用的,配合上架App Store使用)
如果你现在是测试选择ios开发环境,上传ios开发环境推送证书p12,输入证书密码确定。
应用标识:就是appid、应用id
3、配置好确定会生成推送接口参数,等下要配置到开发工具打包。
4、打开manifest.json配置文件,选择模块权限配置,选择消息推送模块。
5、然后再选择SDK配置,把刚才在个推生成的几个接口参数一一对应填上去。
6、用对应的开发证书打包APP安装到手机就能测试推送了
描述:随便写
消息内容:{title:"通知标题",content:"通知内容",payload:"通知去干嘛这里可以自定义"} 一定要用这个格式文字可以改
title:推送通知标题
body:推送内容
其他选项默认就行,
7、然后点击发送预览,再点击确定,推送通知就发送出去了,然后看手机系统栏有没有收到通知。接受到就说明测试成功了,推送正常使用,如没收到检查各项配置是否正确。
很多人初次接触推送通知,不知道怎么去申请ios推送证书和配置推送。
很多人犯的错误就是用推送证书p12去打包ipa,推送不是用来打包的,下面详细介绍ios推证书的申请和配置使用。
ios推送证书分为测试调试用的iOS推送证书(开发环境)和上架到App Store的ios 推送证书!(生产环境)
APP要推送通知首先要在创建APPID时勾选推送服务。
推送证书是配置上传到推送平台的,如极光推送、个推、小米推送等,不是用来打包ipa的,下面会有介绍。
一、创建唯一标示符App IDs
首先打开开发者中心https://developer.apple.com/account,进入证书页面。
如果之前创建过appid,进去修改添加下推送服务就行了,不用重新创建。
1.1点击证书、ID及配件文件,进入设置。
1.2选择App IDs –>点击+创建一个新的App ID
其中有两项需要你自己填:
第一项Name,用来描述你的App ID,这个随便填,没有什么限制,最好是项目名称,这样方便自己辨识(不允许中文)
第二项Bundle ID (App ID Suffix),这是你App ID的后缀,需要仔细填写。用来标示我们的 app,使它有一个固定的身份,和你的程序直接相关。填写 Explicit App ID 的格式为:com.company.appName(要有两个点.)照着格式写,写个方便记的,后面很多地方要用到。
第三项配置服务权限,默认会选择2项,不能修改,其它常用的苹果支付,APP推送通知,这里要推送通知就勾选上,然后点击Continue确认,下一步。
Register后点击Done完成App ID的创建。
推送通知那项服务现在还是黄色的,因为还没创建ios推送证书,等下用Appuploader创建了ios推送证书,就会变成绿色,说明生效了。
二、iOS开发推送证书、开发真机调试用(开发环境、配合开发证书使用,用开发证书打包就能用开发环境ios推送证书测试推送通知iOS开发证书申请教程)
Appuploader可以实现是Windows电脑申请ios证书和上架APP。很方便的辅助ios上架工具!
1、打开Appuploader,用苹果开发者账号登录。
2、选择证书选项
3、点击+ADD\选择Apple Push Notification service SSL(Sandbox) —iOS开发环境推送证书
输入证书名称(随意)、邮箱(随意)、密码,选择你的APP对于的应用id,点击ok创建。
4、下载保存好.p12 iOS证书文件
推送证书是没描述文件的,只有一个p12,不用申请描述文件。
三、iOS发布推送证书、上架App Store用(生产环境、配合发布证书使用,开发环境推送证书测试好了推送,用发布证书上架成功了,就到推送后台切换为生成环境推送证书)
1、打开Appuploader,用苹果开发者账号登录。
2、选择证书选项
3、点击+ADD\选择Push Notification service SLL(Sandbox & Producyion)—iOS生产环境推送证书
输入证书名称(随意)、邮箱(随意)、密码,选择你的APP对于的appids,点击ok创建。
3、下载保存好.p12 iOS证书文件
四、配置ios推送证书p12
1、注册个推平台登录
http://www.getui.com/
点击左侧个推-消息推送,进入页面再点击右侧上方的登记应用
2、配置APP的基本信息
APP名称:你的APP名字
应用平台:安卓和苹果,ios开发环境(测试用的,配合开发证书使用)ios生产环境(上架用的,配合上架App Store使用)
如果你现在是测试选择ios开发环境,上传ios开发环境推送证书p12,输入证书密码确定。
应用标识:就是appid、应用id
3、配置好确定会生成推送接口参数,等下要配置到开发工具打包。
4、打开manifest.json配置文件,选择模块权限配置,选择消息推送模块。
5、然后再选择SDK配置,把刚才在个推生成的几个接口参数一一对应填上去。
6、用对应的开发证书打包APP安装到手机就能测试推送了
描述:随便写
消息内容:{title:"通知标题",content:"通知内容",payload:"通知去干嘛这里可以自定义"} 一定要用这个格式文字可以改
title:推送通知标题
body:推送内容
其他选项默认就行,
7、然后点击发送预览,再点击确定,推送通知就发送出去了,然后看手机系统栏有没有收到通知。接受到就说明测试成功了,推送正常使用,如没收到检查各项配置是否正确。

mongoDB数据库基础操作命令分享
对于创建数据库可以说是大多数后端程序员都会的,但是其中的一些基本的操作命令很多人还是需要查看的,那么下面专业的app开发报价燚轩科技就来为大家分享一下mongoDB数据库基础操作命令:
创建数据库
> use runoob --->创建数据库runoob
> db ---> 查看当前数据库
> show dbs --> 查看所有数据库, 这时是看不见runoob这个数据库的,我们必须插入一点数据才能够给看见
admin (empty)
local 0.078GB
> db.runoob.insert({"name":"菜鸟教程"})
WriteResult({ "nInserted" : 1 }) ---> 成功
> show dbs --> 这时就能看见runoob这个数据库了
admin (empty)
local 0.078GB
runoob 0.078GB
删除数据库
> use runoob --> 切换到要删除的数据库
switched to db runoob
> db.dropDatabase() --> 删除数据库
{ "dropped" : "runoob", "ok" : 1 }
创建集合
> db.createCollection('runoob') -->创建集合runoob
{ "ok" : 1 }
> show collections --> 查看所有集合
runoob
system.indexes
创建固定集合 mycol,整个集合空间大小 6142800 KB, 文档最大个数为 10000 个。
> db.createCollection('runoob1', {capped:true,autoIndexID:true,size:6142800,max:10000})
{ "ok" : 1 }
在 MongoDB 中,你不需要创建集合。当你插入一些文档时,MongoDB 会自动创建集合。
> db.runoob2.insert({'name':'弱鸡'})
WriteResult({ "nInserted" : 1 })
> show collections
runoob
runoob1
runoob2
system.indexes
> db.runoob2.drop() --->删除runoob2集合
true
向runoob2集合中插入文档, 他会自动生成id列
> db.runoob1.insert({'name':'lucy','age':18,'sex':'woman'})
WriteResult({ "nInserted" : 1 })
查看runoob2集合中的文档
> db.runoob1.find()
{ "_id" : ObjectId("5b0d4c0f81322a46cfc77208"), "name" : "lucy", "age" : 18, "sex" : "woman" }
更新runoob2中文档,将名字lucy该为tom
> db.runoob1.update({'name':'lucy'},{$set:{'name':'tom'}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.runoob.insert({'xx':12})
WriteResult({ "nInserted" : 1 })
> db.runoob.remove({'xx':12})
WriteResult({ "nRemoved" : 1 })
如果大家想要了解更多的话,可以留言咨询我们。
对于创建数据库可以说是大多数后端程序员都会的,但是其中的一些基本的操作命令很多人还是需要查看的,那么下面专业的app开发报价燚轩科技就来为大家分享一下mongoDB数据库基础操作命令:
创建数据库
> use runoob --->创建数据库runoob
> db ---> 查看当前数据库
> show dbs --> 查看所有数据库, 这时是看不见runoob这个数据库的,我们必须插入一点数据才能够给看见
admin (empty)
local 0.078GB
> db.runoob.insert({"name":"菜鸟教程"})
WriteResult({ "nInserted" : 1 }) ---> 成功
> show dbs --> 这时就能看见runoob这个数据库了
admin (empty)
local 0.078GB
runoob 0.078GB
删除数据库
> use runoob --> 切换到要删除的数据库
switched to db runoob
> db.dropDatabase() --> 删除数据库
{ "dropped" : "runoob", "ok" : 1 }
创建集合
> db.createCollection('runoob') -->创建集合runoob
{ "ok" : 1 }
> show collections --> 查看所有集合
runoob
system.indexes
创建固定集合 mycol,整个集合空间大小 6142800 KB, 文档最大个数为 10000 个。
> db.createCollection('runoob1', {capped:true,autoIndexID:true,size:6142800,max:10000})
{ "ok" : 1 }
在 MongoDB 中,你不需要创建集合。当你插入一些文档时,MongoDB 会自动创建集合。
> db.runoob2.insert({'name':'弱鸡'})
WriteResult({ "nInserted" : 1 })
> show collections
runoob
runoob1
runoob2
system.indexes
> db.runoob2.drop() --->删除runoob2集合
true
向runoob2集合中插入文档, 他会自动生成id列
> db.runoob1.insert({'name':'lucy','age':18,'sex':'woman'})
WriteResult({ "nInserted" : 1 })
查看runoob2集合中的文档
> db.runoob1.find()
{ "_id" : ObjectId("5b0d4c0f81322a46cfc77208"), "name" : "lucy", "age" : 18, "sex" : "woman" }
更新runoob2中文档,将名字lucy该为tom
> db.runoob1.update({'name':'lucy'},{$set:{'name':'tom'}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.runoob.insert({'xx':12})
WriteResult({ "nInserted" : 1 })
> db.runoob.remove({'xx':12})
WriteResult({ "nRemoved" : 1 })
如果大家想要了解更多的话,可以留言咨询我们。

支付开发,高德地图,下拉刷新,申请打包证书
从菜鸟到新手,遇见问题解决问题是家常便饭,今天记录一下遇到的一些问题,希望能对同样被困扰过的同学的一些帮助。
一、修改状态栏背景色,在入口页面加这句代码
plus.navigator.setStatusBarBackground('#000'); //修改状态栏背景色
二、申请私有安卓打包证书
1.下载dcloud公有证书http://download.dcloud.net.cn/HBuilder.keystore
参考这篇文章http://ask.dcloud.net.cn/article/12718
2.使用第三方软件uploader,可以快速申请
三.调用微信和支付宝功能
1.先在demo里找到beecloud.html,把页面做出来
2.去蚂蚁金服和微信开放平台注册开发者账号,并进行开发者资质认证,然后在各自平台创建APP应用,进行各自开发配置,比如回调地址,可以得到返回的订单信息。然后你还要去beecloud官网去注册企业账号,注册完了之后创建支付应用,这里会得到一个APP ID,这个APP ID是需要在hbuilder填写的,然后在这个支付应用里进行交易渠道参数设置,支付宝APP支付和微信APP支付都需要配置,填写的信息就来自你之前在各自开放平台创建的应用。缺少什么就申请什么,比如收款商户。Beecloud的东西都配置好了,支付功能就可以使用了,注意一下,微信支付只有打包之后才行,真机运行会报错。
3.以上的账号申请和各种配置,比较繁琐,但是都有教程,不要急躁,按步骤一步步来,把重要信息保存好。建议在做支付功能之前就开始申请注册,全程需要半个月的审核时间。
四、高德地图定位不准确
细心的同学会发现,在APP开发的时候调用高德地图api进行定位时,会有几百米的偏差,相当蛋疼。查阅资料之后发现是坐标系的问题,需要自己加一个纠偏算法。这里我详细的说一下怎么用。
这是纠偏之前的代码,使用浏览器定位
map = new AMap.Map('container', {
resizeEnable: true
});
//获取定位
map.plugin('AMap.Geolocation', function() {
geolocation = new AMap.Geolocation({
enableHighAccuracy: true,//是否使用高精度定位,默认:true
timeout: 10000, //超过10秒后停止定位,默认:无穷大
buttonOffset: new AMap.Pixel(10, 20),//定位按钮与设置的停靠位置的偏移量,默认:Pixel(10, 20)
zoomToAccuracy: true, //定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false
buttonPosition:'RB'
});
map.addControl(geolocation);
geolocation.getCurrentPosition();
AMap.event.addListener(geolocation, 'complete', onComplete);//返回定位信息
AMap.event.addListener(geolocation, 'error', onError); //返回定位出错信息
});
然后开始纠偏,首先把下面代码复制进来,放在获取定位之前
//纠偏算法
var a = 6378245.0;
var ee = 0.00669342162296594323;
var lnglat = new Array(2);
function transform(wgLon, wgLat) {
console.log(Math.PI);
if(outOfChina(wgLat, wgLon)) {
console.log("outOfChina");
lnglat[0] = wgLon;
lnglat[1] = wgLat;
console.log(lnglat[0] + "|" + lnglat[1]);
return lnglat;
}
var dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
var dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
var radLat = wgLat / 180.0 * Math.PI;
var magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
var sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * Math.PI);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * Math.PI);
lnglat[0] = wgLon + dLon;
lnglat[1] = wgLat + dLat;
console.log(lnglat[0] + "|" + lnglat[1] + "|" + dLon + "|" + dLat);
return lnglat;
}
function outOfChina(lat, lon) {
if(lon < 72.004 || lon > 137.8347)
return true;
if(lat < 0.8293 || lat > 55.8271)
return true;
return false;
}
function transformLat(x, y) {
ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin(y / 3.0 * Math.PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * Math.PI) + 320 * Math.sin(y * Math.PI / 30.0)) * 2.0 / 3.0;
return ret;
}
function transformLon(x, y) {
ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin(x / 3.0 * Math.PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * Math.PI) + 300.0 * Math.sin(x / 30.0 * Math.PI)) * 2.0 / 3.0;
return ret;
}
然后,把之前获取定位的代码修改一下,把打点都false掉,纠偏之后重新打点。
/获取定位
map.plugin('AMap.Geolocation', function() {
geolocation = new AMap.Geolocation({
enableHighAccuracy: true, //是否使用高精度定位,默认:true
timeout: 10000, //超过10秒后停止定位,默认:无穷大
showMarker: false,
showCircle: false,
panToLocation: true,
buttonPosition: 'RB', //定位按钮停靠位置,默认:'LB',左下角
});
map.addControl(geolocation); // 把定位插件加入地图实例
geolocation.getCurrentPosition(function(status, result) {
if(status == "complete") {
var pos = result.position;
var newPos = transform(pos.lng, pos.lat);
var marker = new AMap.Marker({
position: newPos
});
marker.setMap(map);
}
}); // 调用定位
这样定位就比较准确了,目测还会有十米的偏差,但是已经在可接受范围了,基本达到我们的要求。
五、禁用下拉刷新
这个就比较简单了,把这段代码注释掉就可以了,然后重新运行一下,敲重点,要重新运行,否则看不到效果。
down: {
style: 'circle',
callback: function() {
}
},
重写mui.back()这个更简单了
mui.back = function(){
//需要执行的代码
};
还有一些比较常用的代码
plus.webview.currentWebview().close();//关闭当前页面
window.localStorage.removeItem("orderId")//清除本地缓存
暂时想起来这么多,希望能对大家有所帮助
从菜鸟到新手,遇见问题解决问题是家常便饭,今天记录一下遇到的一些问题,希望能对同样被困扰过的同学的一些帮助。
一、修改状态栏背景色,在入口页面加这句代码
plus.navigator.setStatusBarBackground('#000'); //修改状态栏背景色
二、申请私有安卓打包证书
1.下载dcloud公有证书http://download.dcloud.net.cn/HBuilder.keystore
参考这篇文章http://ask.dcloud.net.cn/article/12718
2.使用第三方软件uploader,可以快速申请
三.调用微信和支付宝功能
1.先在demo里找到beecloud.html,把页面做出来
2.去蚂蚁金服和微信开放平台注册开发者账号,并进行开发者资质认证,然后在各自平台创建APP应用,进行各自开发配置,比如回调地址,可以得到返回的订单信息。然后你还要去beecloud官网去注册企业账号,注册完了之后创建支付应用,这里会得到一个APP ID,这个APP ID是需要在hbuilder填写的,然后在这个支付应用里进行交易渠道参数设置,支付宝APP支付和微信APP支付都需要配置,填写的信息就来自你之前在各自开放平台创建的应用。缺少什么就申请什么,比如收款商户。Beecloud的东西都配置好了,支付功能就可以使用了,注意一下,微信支付只有打包之后才行,真机运行会报错。
3.以上的账号申请和各种配置,比较繁琐,但是都有教程,不要急躁,按步骤一步步来,把重要信息保存好。建议在做支付功能之前就开始申请注册,全程需要半个月的审核时间。
四、高德地图定位不准确
细心的同学会发现,在APP开发的时候调用高德地图api进行定位时,会有几百米的偏差,相当蛋疼。查阅资料之后发现是坐标系的问题,需要自己加一个纠偏算法。这里我详细的说一下怎么用。
这是纠偏之前的代码,使用浏览器定位
map = new AMap.Map('container', {
resizeEnable: true
});
//获取定位
map.plugin('AMap.Geolocation', function() {
geolocation = new AMap.Geolocation({
enableHighAccuracy: true,//是否使用高精度定位,默认:true
timeout: 10000, //超过10秒后停止定位,默认:无穷大
buttonOffset: new AMap.Pixel(10, 20),//定位按钮与设置的停靠位置的偏移量,默认:Pixel(10, 20)
zoomToAccuracy: true, //定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false
buttonPosition:'RB'
});
map.addControl(geolocation);
geolocation.getCurrentPosition();
AMap.event.addListener(geolocation, 'complete', onComplete);//返回定位信息
AMap.event.addListener(geolocation, 'error', onError); //返回定位出错信息
});
然后开始纠偏,首先把下面代码复制进来,放在获取定位之前
//纠偏算法
var a = 6378245.0;
var ee = 0.00669342162296594323;
var lnglat = new Array(2);
function transform(wgLon, wgLat) {
console.log(Math.PI);
if(outOfChina(wgLat, wgLon)) {
console.log("outOfChina");
lnglat[0] = wgLon;
lnglat[1] = wgLat;
console.log(lnglat[0] + "|" + lnglat[1]);
return lnglat;
}
var dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
var dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
var radLat = wgLat / 180.0 * Math.PI;
var magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
var sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * Math.PI);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * Math.PI);
lnglat[0] = wgLon + dLon;
lnglat[1] = wgLat + dLat;
console.log(lnglat[0] + "|" + lnglat[1] + "|" + dLon + "|" + dLat);
return lnglat;
}
function outOfChina(lat, lon) {
if(lon < 72.004 || lon > 137.8347)
return true;
if(lat < 0.8293 || lat > 55.8271)
return true;
return false;
}
function transformLat(x, y) {
ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin(y / 3.0 * Math.PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * Math.PI) + 320 * Math.sin(y * Math.PI / 30.0)) * 2.0 / 3.0;
return ret;
}
function transformLon(x, y) {
ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin(x / 3.0 * Math.PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * Math.PI) + 300.0 * Math.sin(x / 30.0 * Math.PI)) * 2.0 / 3.0;
return ret;
}
然后,把之前获取定位的代码修改一下,把打点都false掉,纠偏之后重新打点。
/获取定位
map.plugin('AMap.Geolocation', function() {
geolocation = new AMap.Geolocation({
enableHighAccuracy: true, //是否使用高精度定位,默认:true
timeout: 10000, //超过10秒后停止定位,默认:无穷大
showMarker: false,
showCircle: false,
panToLocation: true,
buttonPosition: 'RB', //定位按钮停靠位置,默认:'LB',左下角
});
map.addControl(geolocation); // 把定位插件加入地图实例
geolocation.getCurrentPosition(function(status, result) {
if(status == "complete") {
var pos = result.position;
var newPos = transform(pos.lng, pos.lat);
var marker = new AMap.Marker({
position: newPos
});
marker.setMap(map);
}
}); // 调用定位
这样定位就比较准确了,目测还会有十米的偏差,但是已经在可接受范围了,基本达到我们的要求。
五、禁用下拉刷新
这个就比较简单了,把这段代码注释掉就可以了,然后重新运行一下,敲重点,要重新运行,否则看不到效果。
down: {
style: 'circle',
callback: function() {
}
},
重写mui.back()这个更简单了
mui.back = function(){
//需要执行的代码
};
还有一些比较常用的代码
plus.webview.currentWebview().close();//关闭当前页面
window.localStorage.removeItem("orderId")//清除本地缓存
暂时想起来这么多,希望能对大家有所帮助
收起阅读 »
区分escape、encodeURI和encodeURIComponent
1.简单来说,escape是对字符串(string)进行编码(而另外两种是对URL),作用是让它们在所有电脑上可读。
编码之后的效果是%XX或者%uXXXX这种形式。
其中 ASCII字母、数、@/ ,这几个字符不会被编码,其余的都会。
最关键的是,当你需要对URL编码时,请忘记这个方法,这个方法是针对字符串使用的,不适用于URL。*****
2.最常用的encodeURI和encodeURIComponent
对URL编码是常见的事,所以这两个方法应该是实际中要特别注意的。
它们都是编码URL,唯一区别就是编码的字符范围,其中
encodeURI方法不会对下列字符编码 ASCII字母、数字、~!@#$&()=:/,;?+'
encodeURIComponent方法不会对下列字符编码 ASCII字母、数字、~!()'
所以encodeURIComponent比encodeURI编码的范围更大。
实际例子来说,encodeURIComponent会把 http:// 编码成 http%3A%2F%2F 而encodeURI却不会。
3.使用
(1)如果只是编码字符串,不和URL有半毛钱关系,那么用escape。
(2)如果你需要编码整个URL,然后需要使用这个URL,那么用encodeURI。
(3)当你需要编码URL中的参数的时候,那么encodeURIComponent是最好方法。
参考: https://stackoverflow.com/questions/11294107/how-can-i-send-the-ampersand-character-via-ajax
https://www.zhihu.com/question/21861899
可以读一下 《HTTP 权威指南》的第二章第 4 节——各种令人头疼的字符
http://www.ituring.com.cn/book/844
1.简单来说,escape是对字符串(string)进行编码(而另外两种是对URL),作用是让它们在所有电脑上可读。
编码之后的效果是%XX或者%uXXXX这种形式。
其中 ASCII字母、数、@/ ,这几个字符不会被编码,其余的都会。
最关键的是,当你需要对URL编码时,请忘记这个方法,这个方法是针对字符串使用的,不适用于URL。*****
2.最常用的encodeURI和encodeURIComponent
对URL编码是常见的事,所以这两个方法应该是实际中要特别注意的。
它们都是编码URL,唯一区别就是编码的字符范围,其中
encodeURI方法不会对下列字符编码 ASCII字母、数字、~!@#$&()=:/,;?+'
encodeURIComponent方法不会对下列字符编码 ASCII字母、数字、~!()'
所以encodeURIComponent比encodeURI编码的范围更大。
实际例子来说,encodeURIComponent会把 http:// 编码成 http%3A%2F%2F 而encodeURI却不会。
3.使用
(1)如果只是编码字符串,不和URL有半毛钱关系,那么用escape。
(2)如果你需要编码整个URL,然后需要使用这个URL,那么用encodeURI。
(3)当你需要编码URL中的参数的时候,那么encodeURIComponent是最好方法。
参考: https://stackoverflow.com/questions/11294107/how-can-i-send-the-ampersand-character-via-ajax
https://www.zhihu.com/question/21861899
可以读一下 《HTTP 权威指南》的第二章第 4 节——各种令人头疼的字符
http://www.ituring.com.cn/book/844

uploader又拍云上传文件集成
项目中用到又拍云文件存储,找了一遍没找到关于又拍云上传的方法,只能自己造了
首先我封装到一个文件了
(function($, request, upyun){
upyun.bucket = '您的服务名称'
upyun.upload = function (path, success, fail) {
var self = this
success = success || mui.noop
fail = fail || mui.noop
var name = path.substr(path.lastIndexOf('/') + 1)
this.getSignature({name: name}, function(err, res){
if (err) {
fail && fail(err)
return
}
var task = plus.uploader.createUpload(
'https://v0.api.upyun.com/'+ self.bucket,
{method:'POST'},
function ( t, status ) {
if ( status == 200 ) {
var data = JSON.parse(t.responseText)
if(data.code == 200){
success(data)
}else{
fail(data.message)
}
} else {
fail({err:status,message:'上传文件出错'+ JSON.stringify(t)})
}
})
task.addFile(path, {key:"file"})
task.addData("authorization", res.signature || '')
task.addData("policy", res.policy || '')
task.start()
})
}
upyun.getSignature = function (data, cb) {
request.post(api.upsign, data).then(function(data){
cb(null, data)
}).catch(function(err){
cb(err)
})
}
})(mui, request, window.upyun={})
后端采用php签名
$key = 'upyun.operator‘;
$secret = md5('upyun.password');
$method = "POST";
$uri = "/".‘upyun.bucket';
$date = gmdate('D, d M Y H:i:s \G\M\T');
$name = $_POST['name'];
$ext = substr($name, strrpos($name, '.') + 1);
$name =str_replace('.','', microtime(true)).".".$ext;
$path = '/test/'.$name; //远程文件路径
$policy = base64_encode(json_encode([
'save-key' => $path,
'bucket' => 'upyun.bucket',
'expiration' => time() + 3600,
'date' => gmdate('D, d M Y H:i:s \G\M\T')
]));
$elems = array();
foreach (array($method, $uri, $date, $policy) as $v)
{
if ($v)
{
$elems[] = $v;
}
}
$value = implode('&', $elems);
$sign = base64_encode(hash_hmac('sha1', $value, $secret, true));
$sign = 'UPYUN ' . $key . ':' . $sign;
$data = ['policy' => $policy, 'signature' => $sign];
echo json_encode($data);
使用方法很简单
upyun.upload(path, function(data){
console.log(JSON.stringify(data))
},function(err){
console.log(JSON.stringify(err))
})
项目中用到又拍云文件存储,找了一遍没找到关于又拍云上传的方法,只能自己造了
首先我封装到一个文件了
(function($, request, upyun){
upyun.bucket = '您的服务名称'
upyun.upload = function (path, success, fail) {
var self = this
success = success || mui.noop
fail = fail || mui.noop
var name = path.substr(path.lastIndexOf('/') + 1)
this.getSignature({name: name}, function(err, res){
if (err) {
fail && fail(err)
return
}
var task = plus.uploader.createUpload(
'https://v0.api.upyun.com/'+ self.bucket,
{method:'POST'},
function ( t, status ) {
if ( status == 200 ) {
var data = JSON.parse(t.responseText)
if(data.code == 200){
success(data)
}else{
fail(data.message)
}
} else {
fail({err:status,message:'上传文件出错'+ JSON.stringify(t)})
}
})
task.addFile(path, {key:"file"})
task.addData("authorization", res.signature || '')
task.addData("policy", res.policy || '')
task.start()
})
}
upyun.getSignature = function (data, cb) {
request.post(api.upsign, data).then(function(data){
cb(null, data)
}).catch(function(err){
cb(err)
})
}
})(mui, request, window.upyun={})
后端采用php签名
$key = 'upyun.operator‘;
$secret = md5('upyun.password');
$method = "POST";
$uri = "/".‘upyun.bucket';
$date = gmdate('D, d M Y H:i:s \G\M\T');
$name = $_POST['name'];
$ext = substr($name, strrpos($name, '.') + 1);
$name =str_replace('.','', microtime(true)).".".$ext;
$path = '/test/'.$name; //远程文件路径
$policy = base64_encode(json_encode([
'save-key' => $path,
'bucket' => 'upyun.bucket',
'expiration' => time() + 3600,
'date' => gmdate('D, d M Y H:i:s \G\M\T')
]));
$elems = array();
foreach (array($method, $uri, $date, $policy) as $v)
{
if ($v)
{
$elems[] = $v;
}
}
$value = implode('&', $elems);
$sign = base64_encode(hash_hmac('sha1', $value, $secret, true));
$sign = 'UPYUN ' . $key . ':' . $sign;
$data = ['policy' => $policy, 'signature' => $sign];
echo json_encode($data);
使用方法很简单
upyun.upload(path, function(data){
console.log(JSON.stringify(data))
},function(err){
console.log(JSON.stringify(err))
})
收起阅读 »