记一次Base64以及URLEncode转码过程中浏览器请求后台问题

base64_encode 编码后的字符串中含有 "/", "+", "=" 等字符,

 

一次请求经过默认base64编码后为: url:http://localhost/pre?merchantNo=222222&cipherJson=FZbDffV4jzH8SHFl8bakEJGyZLYrQdEM0F4CU3tiEcS7s6SyCIFa3cDU5fJAiZnTxjURePnMJ5wcqfmfh8+NcISbe4grbJsr&sign=65d9eac59e06eba3e88899e20eb8d25a

其中cipherJson=FZbDffV4jzH8SHFl8bakEJGyZLYrQdEM0F4CU3tiEcS7s6SyCIFa3cDU5fJAiZnTxjURePnMJ5wcqfmfh8+NcISbe4grbJsr

在浏览器中通过get方式请求到后台,获取到cipherJson=FZbDffV4jzH8SHFl8bakEJGyZLYrQdEM0F4CU3tiEcS7s6SyCIFa3cDU5fJAiZnTxjURePnMJ5wcqfmfh8 NcISbe4grbJsr

其中将+ 变为了 空格

 

这些字符在url编码中又是特殊字符,比如 "+" ,它就表示 “空格”,但是不同的浏览器对“空格”的编码又不一样,有的是用“+”表示,有的是用“20%”表示,也就是说,让这些base64_encode编码后的字符串在url中传递,用不同的浏览器去浏览时,服务端得到值不一样。

 

eg:

String a = "2+2 3";
System.out.println(URLEncoder.encode(a, "UTF-8")); //输出结果: 2%2B2+3

结论: 在url中请求时,请求中带有 + , 服务器默认 url中+ 是经过URLEncoder.encode后的结果, tomcat服务器会将相关请求做URLDecoder.decode( )操作, 所以会将请求中+ 转换为 空格

 

URLEncode和URLDecoder作用

一、基本原理

对于URL传递到后台,会对其中的有些字符进行编码(即encode),以下是我百度到的一些资料。

 

 网页中的表单使用POST/GET方法提交时,数据内容的类型是 application/x-www-form-urlencoded,这种类型会:

  1.字符"a"-"z","A"-"Z","0"-"9",".","-","*",和"_" 都不会被编码;

  2.将空格转换为加号 (+) ;

  3.将非文本内容转换成"%xy"的形式,xy是两位16进制的数值;

  4.在每个 name=value 对之间放置 & 符号。

  URLEncoder类包含将字符串转换为application/x-www-form-urlencoded MIME 格式的静态方法。

urlencode()编码:对字符串中除了 -_. 之外的所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数,空格则编码为加号(+)。

二、说明

所以URLEncode就是将URL中特殊部分进行编码。URLDecoder就是对特殊部分进行解码。

 

例如:

一产品名称为A&T Plastic,在产品列表中就产生了这样的一个联接<a href="product.asp?name=A&T Plastic">A&T Plastic</a>,在服务器端接收此参数的时候怎么也无法接收到准确的产品名。

-->用urlencode方法

特殊特殊字符的含义

————————————————————————————

字符 特殊字符的含义 URL编码

# 用来标志特定的文档位置 %23

% 对特殊字符进行编码 %25

& 分隔不同的变量值对 %26

+ 在变量值中表示空格 %2B

\ 表示目录路径 %2F

= 用来连接键和值 %3D

? 表示查询字符串的开始 %3F

 

当键值中含有以上列表中的一些字符时就无法准确的接收其中的值。

 

为什么要对url进行encode

发现现在几乎所有的网站都对url中的汉字和特殊的字符,进行了urlencode操作,也就是:

http://hi.baidu.com/%BE%B2%D0%C4%C0%CF%C8%CB/creat/blog/

这个样子,中间%形式的,肯定就是我的登录用户名称了吧。

为什么对这些字符进行了u的编码形式,是为了字符编码(gbk、utf8)还是为了不出现特殊的字符在url中?都知道要转,但是转了的真正好处呢。查看了网上的很多资料,也没有找到更加准确的说法。

url转义其实也只是为了符合url的规范而已。因为在标准的url规范中中文和很多的字符是不允许出现在url中的。

看一下php的urlencode的说明了。

urlencode — 编码 URL 字符串

<div u"="">

string urlencode ( string $str )

返回字符串,此字符串中除了 -_. 之外的所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数,空格则编码为加号(+)。此编码与 WWW 表单 POST 数据的编码方式是一样的,同时与 application/x-www-form-urlencoded 的媒体类型编码方式一样。由于历史原因,此编码在将空格编码为加号(+)方面与 RFC1738 编码(参见 rawurlencode())不同。此函数便于将字符串编码并将其用于 URL 的请求部分,同时它还便于将变量传递给下一页。

标准的英文说明是:

"...Only alphanumerics [0-9a-zA-Z], the special characters "$-_.+!*'(),"  [not including the quotes - ed], and reserved characters used for their reserved purposes may be used unencoded within a URL."
 

那哪些字符是需要转化的呢?

1. ASCII 的控制字符

这些字符都是不可打印的,自然需要进行转化。

2. 一些非ASCII字符

这些字符自然是非法的字符范围。转化也是理所当然的了。

3. 一些保留字符

很明显最常见的就是“&”了,这个如果出现在url中了,那你认为是url中的一个字符呢,还是特殊的参数分割用的呢?

4. 就是一些不安全的字符了。

例如:空格。为了防止引起歧义,需要被转化为“+”。

明白了这些,也就知道了为什么需要转化了,而转化的规则也是很简单的。

按照每个字符对应的字符编码,不是符合我们范围的,统统的转化为%的形式也就是了。自然也是16进制的形式。

和字符编码无关

通过urlencode的转化规则和目的,我们也很容易的看出,urleocode是基于字符编码的。同样的一个汉字,不同的编码类型,肯定对应不同的urleocode的串。gbk编码的有gbk的encode结果。

apache等服务器,接受到字符串后,可以进行decode,但是还是无法解决编码的问题。编码问题,还是需要靠约定或者字符编码的判断解决。

因此,urleocode只是为了url中一些非ascii字符,可以正确无误的被传输,至于使用哪种编码,就不是eocode所关心和解决的问题了。

编码问题,不是urlencode所要解决的。
转自:http://apps.hi.baidu.com/share/detail/32230450

参考资料:

http://www.blooberry.com/indexdot/html/topics/urlencoding.htm

http://cn.php.net/manual/zh/function.urlencode.php


 

 

BASE64中相关参数:

 

CRLF 这个参数看起来比较眼熟,它就是Win风格的换行符,意思就是使用CR LF这一对作为一行的结尾而不是Unix风格的LF

DEFAULT 这个参数是默认,使用默认的方法来加密

NO_PADDING 这个参数是略去加密字符串最后的”=”

NO_WRAP 这个参数意思是略去所有的换行符(设置后CRLF就没用了)

URL_SAFE 这个参数意思是加密时不使用对URL和文件名有特殊意义的字符来作为加密字符,具体就是以-和_取代+和/

 

 

其实之前也用Base64转过字符串,不过那时候是将一张图片通过base64转换为字符串传递给服务端了,以前总是用那个sun.misc.BASE64Decoder.jar了,其实android里面里面已经自带了的base64工具类,所以这次我们用这个方法吧。它有encodeToString方法,如果不需要特殊处理,那么第二个参数直接传递Base64.DEFAULT就好了。我们可以试一下:对字符串String param = “北京市哈哈取某某门门南大街靠近值招商银行(北二环支行)&province=北京市&city=北京市&area=某某区&country=中国&street=哈哈门西大街&aaid=000000&time=1505370170813”进行base64编码。android自带的Base64.encodeToString方法,即为Base64.encodeToString(param.getBytes(),Base64.DEFAULT)我们得到的结果为:

5YyX5Lqs5biC5ZOI5ZOI5Y+W5p+Q5p+Q6Zeo6Zeo5Y2X5aSn6KGX6Z2g6L+R5YC85oub5ZWG6ZO2

6KGMKOWMl+S6jOeOr+aUr+ihjCkmcHJvdmluY2U95YyX5Lqs5biCJmNpdHk95YyX5Lqs5biCJmFy

ZWE95p+Q5p+Q5Yy6JmNvdW50cnk95Lit5Zu9JnN0cmVldD3lk4jlk4jpl6jopb/lpKfooZcmYWFp

ZD0wMDAwMDAmdGltZT0xNTA1MzcwMTcwODEz

是的,它换行了,因为我们是让h5同学去解编码的,让h5同学一看,当时就懵了,直接就不能解,为什么会换行呢?那是因为默认模式下,编码后的如果长度超过76,那么每逢76个字符就会加一个换行符。前面我们看到encodeToString有两个参数,那就看看第二个参数还有别的什么值吧,可以看到注释如下:

/**

* Default values for encoder/decoder flags.

*/

public static final int DEFAULT = 0;

/**

* Encoder flag bit to omit the padding '=' characters at the end

* of the output (if any).

*/

public static final int NO_PADDING = 1;

/**

* Encoder flag bit to omit all line terminators (i.e., the output

* will be on one long line).

*/

public static final int NO_WRAP = 2;

/**

* Encoder flag bit to indicate lines should be terminated with a

* CRLF pair instead of just an LF. Has no effect if {@code

* NO_WRAP} is specified as well.

*/

public static final int CRLF = 4;

/**

* Encoder/decoder flag bit to indicate using the "URL and

* filename safe" variant of Base64 (see RFC 3548 section 4) where

* {@code -} and {@code _} are used in place of {@code +} and

* {@code /}.

*/

public static final int URL_SAFE = 8;

/**

* Flag to pass to {@link Base64OutputStream} to indicate that it

* should not close the output stream it is wrapping when it

* itself is closed.

*/

public static final int NO_CLOSE = 16;

可以看到第二个参数有6种情况,第一种就是默认,我们刚刚试了。如果要解决换行问题,那么得用NO_WRAP了,因为它的意思就是会将所有字符输出至一行,所以我们换成Base64.encodeToString(param.getBytes(),Base64.NO_WRAP),输出结果如下:5YyX5Lqs5biC5ZOI5ZOI5Y+W5p+Q5p+Q6Zeo6Zeo5Y2X5aSn6KGX6Z2g6L+R5YC85oub5ZWG6ZO26KGMKOWMl+S6jOeOr+aUr+ihjCkmcHJvdmluY2U95YyX5Lqs5biCJmNpdHk95YyX5Lqs5biCJmFyZWE95p+Q5p+Q5Yy6JmNvdW50cnk95Lit5Zu9JnN0cmVldD3lk4jlk4jpl6jopb/lpKfooZcmYWFpZD0wMDAwMDAmdGltZT0xNTA1MzcwMTcwODEz;对,这次没有换行, 貌似是我们想要的呢,假装我们拼接后生成二维码背后的地址就是https://www.baidu.com?param=5YyX5Lqs5biC5ZOI5ZOI5Y+W5p+Q5p+Q6Zeo6Zeo5Y2X5aSn6KGX6Z2g6L+R5YC85oub5ZWG6ZO26KGMKOWMl+S6jOeOr+aUr+ihjCkmcHJvdmluY2U95YyX5Lqs5biCJmNpdHk95YyX5Lqs5biCJmFyZWE95p+Q5p+Q5Yy6JmNvdW50cnk95Lit5Zu9JnN0cmVldD3lk4jlk4jpl6jopb/lpKfooZcmYWFpZD0wMDAwMDAmdGltZT0xNTA1MzcwMTcwODEz;好的用微信扫描该地址生成的二维码可以出来页面,但是我们的页面还有引导用户下载我们apk的功能,但是微信里面是不支持下载的,故而必须提示用户在浏览器中打开,所以最终这个地址是需要在浏览了器中打开,那我们可以将其生成二维码,然后用微信扫描后再次在手机浏览器中打开试试(一定得是手机浏览器中哦),可是地址变成了下面酱紫:https://www.baidu.com/?param=5YyX5Lqs5biC5ZOI5ZOI5Y%20W5p%20Q5p%20Q6Zeo6Zeo5Y2X5aSn6KGX6Z2g6L%20R5YC85oub5ZWG6ZO26KGMKOWMl%20S6jOeOr%20aUr%20ihjCkmcHJvdmluY2U95YyX5Lqs5biCJmNpdHk95YyX5Lqs5biCJmFyZWE95p%20Q5p%20Q5Yy6JmNvdW50cnk95Lit5Zu9JnN0cmVldD3lk4jlk4jpl6jopb%2FlpKfooZcmYWFpZD0wMDAwMDAmdGltZT0xNTA1MzcwMTcwODEz;可以看到跟上面相比所有的+以及斜杠都转义成了%,那这后面一串让h5去解肯定会失败,那就看看其他的参数;突然发现URL_SAFE这个意思是能够将编码后的+以及/转换成-以_了,貌似符合我们个预期,我们试试,最终结果如下:

5YyX5Lqs5biC5ZOI5ZOI5Y-W5p-Q5p-Q6Zeo6Zeo5Y2X5aSn6KGX6Z2g6L-R5YC85oub5ZWG6ZO2

6KGMKOWMl-S6jOeOr-aUr-ihjCkmcHJvdmluY2U95YyX5Lqs5biCJmNpdHk95YyX5Lqs5biCJmFy

ZWE95p-Q5p-Q5Yy6JmNvdW50cnk95Lit5Zu9JnN0cmVldD3lk4jlk4jpl6jopb_lpKfooZcmYWFp

ZD0wMDAwMDAmdGltZT0xNTA1MzcwMTcwODEz

的确那些加号以及斜杠被替换了,可是又换行了呀,所以在想如果有一个属性能够同时实现URL_SAFE和NO_WRAP就好了呢,可是属性就那么6个,没有一个能够达到这种效果,可以看到后面第二个参数其实是一个常量,其实之前这样写过,view.setGravity(Gravity.CENTER_HORIZONTAL|Gravity.BOTTOM)就是希望该view在其父布局的底部且水平方向居中。那么encodeToString方法是否可以这样子写呢,试了下,答案是可以的,哈哈。所以现在可以酱紫写Base64.encodeToString(param.getBytes(),Base64.URL_SAFE|Base64.NO_WRAP)),我们的得到的结果是什么呢?结果如下:5YyX5Lqs5biC5ZOI5ZOI5Y-W5p-Q5p-Q6Zeo6Zeo5Y2X5aSn6KGX6Z2g6L-R5YC85oub5ZWG6ZO26KGMKOWMl-S6jOeOr-aUr-ihjCkmcHJvdmluY2U95YyX5Lqs5biCJmNpdHk95YyX5Lqs5biCJmFyZWE95p-Q5p-Q5Yy6JmNvdW50cnk95Lit5Zu9JnN0cmVldD3lk4jlk4jpl6jopb_lpKfooZcmYWFpZD0wMDAwMDAmdGltZT0xNTA1MzcwMTcwODEz;嗯,这次没有换行且没有特殊字符了,终于满足我们的预期。其实还有最后一点,在有些定位地址下,编码后的字符串最后几位会出现=,而=在浏览器里面打开也是有问题的,所以也需要替换掉,此时可以用上面的NO_PADDING,它的意思就是在最后会省略填充=。那么最终我们的方式是这样子的:Base64.encodeToString(param.getBytes(),Base64.URL_SAFE|Base64.NO_WRAP|Base64.NO_PADDING)的。如果不是非要在浏览器中打开,其实是不用这么费事的。

 

或者可以参考使用

commons-code 包中的org.apache.commons.codec.binary.Base64 类中相关方法

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值