<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>肉包子的摇滚生活 &#187; Java</title>
	<atom:link href="http://www.fangyuqiang.com/archives/category/java/feed" rel="self" type="application/rss+xml" />
	<link>http://www.fangyuqiang.com</link>
	<description>前端开发，交互设计，用户体验</description>
	<lastBuildDate>Wed, 01 Sep 2010 09:06:13 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>JRadius在windows下的编译错误，警惕windows与linux的编译环境差别</title>
		<link>http://www.fangyuqiang.com/archives/664</link>
		<comments>http://www.fangyuqiang.com/archives/664#comments</comments>
		<pubDate>Tue, 03 Nov 2009 02:38:46 +0000</pubDate>
		<dc:creator>fangyuqiang</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[radius]]></category>

		<guid isPermaLink="false">http://www.fangyuqiang.com/?p=664</guid>
		<description><![CDATA[目前在做radius项目，项目采用Freeradius，c语言写的，提供java开发接口，我们部门主要以java为主，所以需要调研JRadius的开发。 JRadius虽然是个不错的项目，但是看得出来，无论是作者还是使用JRadius的开发者都让人觉得漫不经心。。论坛空空荡荡，mailing list几乎为空，wiki上面的页面也相当少。杯具。 svn上面down下来JRadius，我采用的是tags下面的1.0版本，虽然官方文档上的例子是trunk版本，但是trunk其实一般都是出于开发阶段，我们不是想要研究而是想要使用的，所以还是采用正式的tags下的版本比较好。 mvn install。编译失败。 D:eclipseworkspacejradius-1.0dictionarytargetdictionary-srcnetjradiusdi ctionaryvsa_dhcpAttr_DHCPBootFilename.java:[20,13] 类 Attr_DHCPBootFileName 是 公共的，应在名为 Attr_DHCPBootFileName.java 的文件中声明 我囧，这是啥错误啊，看来看去这文件名跟类名没有错啊，尝试了无数次，反复查看了文件名跟类名，没错啊。。。 最终，终于让我发现了一个问题，生成的文件名是DHCPBootFilename，而类名是DHCPBootFileName，大小写不同。奇怪，这又是怎么回事，查看了下它用来生成这个文件的代码，没有错啊。为什么生成的文件大小写会不一样？ 查看它用来生成代码的工具类，反复调试。。没错。。完全没错。。郁闷了。。 查看了dictionary配置文件，艹，终于让我发现了端倪，配置文件需要2个属性，分别是： ATTRIBUTE    DHCP-Boot-Filename            269    string      # 128 octets ATTRIBUTE    DHCP-Boot-File-Name            67    string 按照生成器的逻辑，它会生成： DHCPBootFilename.java DHCPBootFileName.java 关键的问题就在于，我的环境是windows，windows文件名不区分大小写，结果文件内容会被直接覆盖掉。而开发者的环境是linux，所以他可以顺利编译发布出来。 解决这个问题，没什么特别的方法，要嘛就是开发了以后到linux上面编译。或者把dictionary的编译放到linux上面，然后作为依赖包引入到开发环境中。 个人建议是所有开发的代码都上传到linux下去编译，因为产品环境会在linux下面，在linux下面编译测试才最接近实际。毕竟项目的编译会依赖于操作系统。]]></description>
			<content:encoded><![CDATA[<p>目前在做radius项目，项目采用<a href="http://freeradius.org/" target="_blank">Freeradius</a>，c语言写的，提供java开发接口，我们部门主要以java为主，所以需要调研<a href="http://www.coova.org/JRadius" target="_blank">JRadius</a>的开发。</p>
<p><a href="http://www.coova.org/JRadius" target="_blank">JRadius</a><strong>虽然是个不错的项目，但是看得出来，无论是作者还是使用</strong><a href="http://www.coova.org/JRadius" target="_blank">JRadius</a><strong>的开发者都让人觉得漫不经心</strong>。。论坛空空荡荡，mailing list几乎为空，wiki上面的页面也相当少。杯具。<span id="more-664"></span></p>
<p>svn上面down下来<a href="http://www.coova.org/JRadius" target="_blank">JRadius</a>，我采用的是tags下面的1.0版本，虽然官方文档上的例子是trunk版本，但是trunk其实一般都是出于开发阶段，我们不是想要研究而是想要使用的，所以还是采用正式的tags下的版本比较好。</p>
<p>mvn install。编译失败。</p>
<p>D:eclipseworkspacejradius-1.0dictionarytargetdictionary-srcnetjradiusdi<br />
ctionaryvsa_dhcpAttr_DHCPBootFilename.java:[20,13] 类 Attr_DHCPBootFileName 是<br />
公共的，应在名为 Attr_DHCPBootFileName.java 的文件中声明</p>
<p>我囧，这是啥错误啊，看来看去这文件名跟类名没有错啊，尝试了无数次，反复查看了文件名跟类名，没错啊。。。</p>
<p>最终，<strong>终于让我发现了一个问题，生成的文件名是DHCPBootFilename，而类名是DHCPBootFileName，大小写不同。奇怪，这又是怎么回事</strong>，查看了下它用来生成这个文件的代码，没有错啊。为什么生成的文件大小写会不一样？</p>
<p>查看它用来生成代码的工具类，反复调试。。没错。。完全没错。。郁闷了。。</p>
<p>查看了dictionary配置文件，艹，终于让我发现了端倪，配置文件需要2个属性，分别是：</p>
<p>ATTRIBUTE    DHCP-Boot-Filename            269    string      # 128 octets</p>
<p>ATTRIBUTE    DHCP-Boot-File-Name            67    string</p>
<p>按照生成器的逻辑，它会生成：</p>
<p>DHCPBootFilename.java</p>
<p>DHCPBootFileName.java</p>
<p><strong>关键的问题就在于，我的环境是windows，windows文件名不区分大小写，结果文件内容会被直接覆盖掉。而开发者的环境是linux，所以他可以顺利编译发布出来。</strong></p>
<p>解决这个问题，没什么特别的方法，要嘛就是开发了以后到linux上面编译。或者把dictionary的编译放到linux上面，然后作为依赖包引入到开发环境中。</p>
<p>个人建议是所有开发的代码都上传到linux下去编译，因为产品环境会在linux下面，在linux下面编译测试才最接近实际。毕竟项目的编译会依赖于操作系统。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fangyuqiang.com/archives/664/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Java实现RFC2865 Radius协议的密码加密</title>
		<link>http://www.fangyuqiang.com/archives/448</link>
		<comments>http://www.fangyuqiang.com/archives/448#comments</comments>
		<pubDate>Fri, 07 Aug 2009 02:20:18 +0000</pubDate>
		<dc:creator>fangyuqiang</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[radius]]></category>
		<category><![CDATA[算法]]></category>

		<guid isPermaLink="false">http://www.fangyuqiang.com/?p=448</guid>
		<description><![CDATA[最近项目中要做Radius协议的客户端，一看Radius几百页的协议，我头皮都发麻。放狗搜索一番，发现了一个实现好的Java的Radius客户端，哈哈，很好很强大： http://sourceforge.net/projects/jradius-client/ 摸索了一番，挖出了里面关于用户密码加密的一段代码，相信对很多想学习下Java MD5加密的同学会很有帮助。 首先来了解一番背景跟算法要求： User-Password 描述 该属性表示用户用来认证的用户密码，或者是用户在收到接入挑战报文后用户的输入。它只出现在接入请求报文中。 在传输的过程中，密码是隐藏起来的，首先，将密码用null（\0）填充到16的整数倍个字节长，然后对在请求认证字后面加上共享密钥的字节流进行MD5加密生成hash值，将该hash值和密码的第一个16字节进行异或，然后将结果放入User-Password属性的第一个16字节中。 如果密码长度超过16个字符，则对第一个异或值后面加上共享密钥的字节流进行MD5加密生成hash值。该hash值和密码的第二个16个字节进行异或，然后将异或值放入User-Password属性的第二个16字节中。 如果必要，重复这个操作，每一个异或值后面加上共享密钥产生下一个hash值，然后和密码下一段的16字节进行异或。密码最长不能超过128个字符。 这个方法引用自Kaufman, Perlman和Speciner合写的《网络安全》一书，在第109到第110页。该方法的一个更加精确的解释如下： 共享密钥为S，伪随机数Request Authenticator为RA，将密码拆分成多个16个字节的块p1，p2，等等。最后一块用null（\0）填充满16个字节，密码块为c(1)，c(2)，等等。中间值为b1，b2，等等。 b1 = MD5(S + RA) c(1) = p1 xor b1 b2 = MD5(S + c(1)) c(2) = p2 xor b2 . . . . . . bi = MD5(S + c(i-1)) c(i) = pi xor bi User-Password属性字符串为c(1)+c(2)+&#8230;c(i)，+表示连接。 一旦接收到报文，逆向处理就能得到密码明文。 User-Password属性的格式如下所示。各个域是按照自左向右的顺序传输的。 0 [...]]]></description>
			<content:encoded><![CDATA[<p>最近项目中要做Radius协议的客户端，一看Radius几百页的协议，我头皮都发麻。放狗搜索一番，发现了一个实现好的Java的Radius客户端，哈哈，很好很强大：</p>
<p><a href="http://sourceforge.net/projects/jradius-client/" target="_blank">http://sourceforge.net/projects/jradius-client/ </a></p>
<p>摸索了一番，挖出了里面关于用户密码加密的一段代码，相信对很多想学习下Java MD5加密的同学会很有帮助。</p>
<p><span id="more-448"></span>首先来了解一番背景跟算法要求：</p>
<p>User-Password</p>
<p><strong>描述</strong></p>
<p>该属性表示用户用来认证的用户密码，或者是用户在收到接入挑战报文后用户的输入。它只出现在接入请求报文中。</p>
<p>在传输的过程中，密码是隐藏起来的，首先，将密码用null（\0）填充到16的整数倍个字节长，然后对在请求认证字后面加上共享密钥的字节流进行MD5加密生成hash值，将该hash值和密码的第一个16字节进行异或，然后将结果放入User-Password属性的第一个16字节中。</p>
<p>如果密码长度超过16个字符，则对第一个异或值后面加上共享密钥的字节流进行MD5加密生成hash值。该hash值和密码的第二个16个字节进行异或，然后将异或值放入User-Password属性的第二个16字节中。</p>
<p>如果必要，重复这个操作，每一个异或值后面加上共享密钥产生下一个hash值，然后和密码下一段的16字节进行异或。密码最长不能超过128个字符。</p>
<p>这个方法引用自Kaufman, Perlman和Speciner合写的《网络安全》一书，在第109到第110页。该方法的一个更加精确的解释如下：</p>
<blockquote><p>共享密钥为S，伪随机数Request Authenticator为RA，将密码拆分成多个16个字节的块p1，p2，等等。最后一块用null（\0）填充满16个字节，密码块为c(1)，c(2)，等等。中间值为b1，b2，等等。</p>
<p>b1 = MD5(S + RA)       c(1) = p1 xor b1<br />
b2 = MD5(S + c(1))     c(2) = p2 xor b2<br />
.                       .<br />
.                       .<br />
.                       .<br />
bi = MD5(S + c(i-1))   c(i) = pi xor bi<br />
User-Password属性字符串为c(1)+c(2)+&#8230;c(i)，+表示连接。<br />
一旦接收到报文，逆向处理就能得到密码明文。</p></blockquote>
<p>User-Password属性的格式如下所示。各个域是按照自左向右的顺序传输的。</p>
<pre>    0              1               2
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
   |     Type      |    Length     |  String ...
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-</pre>
<p><strong>类型</strong></p>
<p>2               代表用户密码</p>
<p><strong>长度</strong></p>
<p>至少18字节，但不能超过130个字节</p>
<p><strong>字符串</strong></p>
<p>本字符串占位16到128（包含）个字节。</p>
<h3>Java RADIUS Client的密码生成实现</h3>
<p>//生成请求的Authenticator</p>
<pre title="code" class="java">byte [] requestAuthenticator = this.makeRFC2865RequestAuthenticator();

//Authenticator的生成
private byte[] makeRFC2865RequestAuthenticator() {
 byte [] requestAuthenticator = new byte[16];

 Random r = new Random();

 for (int i = 0; i &lt; 16; i++)
 {
 requestAuthenticator[i] = (byte) r.nextInt();
 }

 this.md5MessageDigest.reset();
 this.md5MessageDigest.update(this.sharedSecret.getBytes());//sharedSecret指的是服务器端与客户端的共享密钥
 this.md5MessageDigest.update(requestAuthenticator);

 return this.md5MessageDigest.digest();//返回生成的hash值
 }
//生成加密后的密钥，userPass指未加密的密码
byte [] encryptedPass = this.encodePapPassword(userPass, requestAuthenticator);

//用来加密的算法
	private byte[] encodePapPassword(final byte[] userPass,
			final byte[] requestAuthenticator) {
		// encrypt the password.
		byte[] userPassBytes = null;
//密码必须大于16小于或者等于128bytes,如果不是16的整数倍个字节长，不足的位补0。如果大于128位，则截断成128位。

		if (userPass.length > 128) {
			userPassBytes = new byte[128];
			System.arraycopy(userPass, 0, userPassBytes, 0, 128);
		} else {
			userPassBytes = userPass;
		}
		// declare the byte array to hold the final product
		byte[] encryptedPass = null;

		if (userPassBytes.length < 128) {
			if (userPassBytes.length % 16 == 0) {
				 // 如果是16的整数倍长
				encryptedPass = new byte[userPassBytes.length];
			} else {
				// 数组长度变成16位的整数倍长
				encryptedPass = new byte[((userPassBytes.length / 16) * 16) + 16];
			}
		} else {
			// the encrypted password must be between 16 and 128 bytes
			encryptedPass = new byte[128];
		}

		 // 填充0
		System.arraycopy(userPassBytes, 0, encryptedPass, 0,
				userPassBytes.length);
		for (int i = userPassBytes.length; i < encryptedPass.length; i++) {
			encryptedPass[i] = 0; // fill it out with zeroes
		}
		this.md5MessageDigest.reset();
		 // 添加共享密钥
		this.md5MessageDigest.update(this.sharedSecret.getBytes());
		 // 添加请求Authenticator
		this.md5MessageDigest.update(requestAuthenticator);
		//  //获得md5 hash( b1 = MD5(S + RA) ).
		byte bn[] = this.md5MessageDigest.digest();

		for (int i = 0; i < 16; i++) {
			// perform the XOR as specified by RFC 2865.
			encryptedPass[i] = (byte) (bn[i] ^ encryptedPass[i]);
		}

		if (encryptedPass.length > 16) {
			for (int i = 16; i < encryptedPass.length; i += 16) {
				this.md5MessageDigest.reset();
				// add the shared secret
				this.md5MessageDigest.update(this.sharedSecret.getBytes());
				// add the previous(encrypted) 16 bytes of the user password
				this.md5MessageDigest.update(encryptedPass, i - 16, 16);
				// get the md5 hash( bn = MD5(S + c(i-1)) ).
				bn = this.md5MessageDigest.digest();
				for (int j = 0; j < 16; j++) {
					// perform the XOR as specified by RFC 2865.
					encryptedPass[i + j] = (byte) (bn[j] ^ encryptedPass[i + j]);
				}
			}
		}
		return encryptedPass;
	}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.fangyuqiang.com/archives/448/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Linux i386跟x86_64版本的区别</title>
		<link>http://www.fangyuqiang.com/archives/391</link>
		<comments>http://www.fangyuqiang.com/archives/391#comments</comments>
		<pubDate>Mon, 27 Jul 2009 03:50:19 +0000</pubDate>
		<dc:creator>fangyuqiang</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.fangyuqiang.com/?p=391</guid>
		<description><![CDATA[32位的CPU不懂x86_64的指令集，但是64位的CPU可以看懂i386的指令集，如果判断不出的话，建议用i386的版本。 而我这次问题的原因，就在于，在服务器上安装的版本都是x86_64的版本，而服务器是32位的！]]></description>
			<content:encoded><![CDATA[<p><a href="http://ithelp.ithome.com.tw/question/10005649">請問Linux i386跟x86_64版本？ &#8211; IT邦幫忙::IT知識分享社群</a>.</p>
<p>原文链接见上。</p>
<p>这2天安装apacheds 1.5.4版本，安装很简单，但是后来服务却无论如何都启动不了，多方查找都毫无结果，启动服务的时候都提示cannot execute binary 的错误，最终在官方网站乱翻的时候，发现linux提供了2个版本，一个i386，一个x86-64版本，搜索一下这2个版本的区别，如上：</p>
<p>i386是指cpu在32位元以上的的pc可以安裝<br />
x86_64是指cpu在64位元以上的的pc可以安裝</p>
<p>32位的CPU不懂x86_64的指令集，但是64位的CPU可以看懂i386的指令集，如果判断不出的话，建议用i386的版本。</p>
<p>而我这次问题的原因，就在于，在服务器上安装的版本都是x86_64的版本，而服务器是32位的！</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fangyuqiang.com/archives/391/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>java的CMS，前途在哪里</title>
		<link>http://www.fangyuqiang.com/archives/194</link>
		<comments>http://www.fangyuqiang.com/archives/194#comments</comments>
		<pubDate>Fri, 12 Jun 2009 01:35:48 +0000</pubDate>
		<dc:creator>fangyuqiang</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[cms]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://playfish.javaeye.com/blog/407097</guid>
		<description><![CDATA[最近在用CMS做项目。由于部门的技术主流是java，所以希望选择java的开源CMS来做开发。但是看过了几个java的CMS，确实很无语，对于互联网应用，java的CMS，前途在哪里？ php的CMS，drupal，joomla，WordPress，堪称巨作。而java，别说巨作了，连一个比较好用的都没有。。 当然，java确实有比较出色的CMS,但是这些CMS几乎都是企业级的内容管理，不是用来做互联网应用的。面对php CMS的遍地开花，java真是毫无胜算。 除了java语言自身的优势之外，java有非常非常丰富的开源组件可以支持CMS的二次开发。而动态语言如php，优势在于快速，方便，性能好。 CMS很大的一个重点，就是主题的开发，我们以博客系统为例，php的WordPress跟java的roller，WordPress的主题模板系统基于php标记，WordPress提供了丰富的函数可以调用，修改主题很方便。而roller的主题模板采用的也是主流的java模板引擎vm，但是这里就有问题了，java的特性决定了如果修改调用的函数的代码，那么就需要重启容器才能生效，它需要重新编译。如果这是在服务器上面的，那么就是改一次代码重新启动一次服务器。光这点，就让人非常受不了。开发者调试非常的不方便。或许这就是为什么WordPress的主题满天下，roller的主题除了自带的之外几乎没有的关系吧。 另外一个很大的限制因素，应该就是运行环境了。安装配置amp环境比起安装配置jre环境简单得多。java程序号称一次编译到处运行，但是要想到处运行，要求的条件却不少。所以java主机比起其他主机贵得多，任谁都难以接受，除非都是自己的服务器，否则java的主机很难都调整到一个所有人都可以用的状态。 WordPress的功能不仅仅是一个博客了，已经完全可以当成一个CMS来用。它的工程代码大小为8M左右，而roller的仅仅是一个博客而已，它的代码达到28M。。由于必然的。。引入很多的开源组件。WordPress实现全部功能大概仅用了10来个表，而roller就一个博客，却有二三十个的表。 看着寥寥无几的java开源CMS跟遍地的php CMS，我想互联网的应用，java的CMS，何时能出一个可以闪亮的产品啊。]]></description>
			<content:encoded><![CDATA[<p>最近在用CMS做项目。由于部门的技术主流是java，所以希望选择java的开源CMS来做开发。但是看过了几个java的CMS，确实很无语，对于互联网应用，java的CMS，前途在哪里？<span id="more-194"></span></p>
<p>php的CMS，drupal，joomla，WordPress，堪称巨作。而java，别说巨作了，连一个比较好用的都没有。。</p>
<p>当然，java确实有比较出色的CMS,但是这些CMS几乎都是企业级的内容管理，不是用来做互联网应用的。面对php CMS的遍地开花，java真是毫无胜算。</p>
<p>除了java语言自身的优势之外，java有非常非常丰富的开源组件可以支持CMS的二次开发。而动态语言如php，优势在于快速，方便，性能好。</p>
<p>CMS很大的一个重点，就是主题的开发，我们以博客系统为例，php的WordPress跟java的roller，WordPress的主题模板系统基于php标记，WordPress提供了丰富的函数可以调用，修改主题很方便。而roller的主题模板采用的也是主流的java模板引擎vm，但是这里就有问题了，java的特性决定了如果修改调用的函数的代码，那么就需要重启容器才能生效，它需要重新编译。如果这是在服务器上面的，那么就是改一次代码重新启动一次服务器。光这点，就让人非常受不了。开发者调试非常的不方便。或许这就是为什么WordPress的主题满天下，roller的主题除了自带的之外几乎没有的关系吧。</p>
<p>另外一个很大的限制因素，应该就是运行环境了。安装配置amp环境比起安装配置jre环境简单得多。java程序号称一次编译到处运行，但是要想到处运行，要求的条件却不少。所以java主机比起其他主机贵得多，任谁都难以接受，除非都是自己的服务器，否则java的主机很难都调整到一个所有人都可以用的状态。</p>
<p>WordPress的功能不仅仅是一个博客了，已经完全可以当成一个CMS来用。它的工程代码大小为8M左右，而roller的仅仅是一个博客而已，它的代码达到28M。。由于必然的。。引入很多的开源组件。WordPress实现全部功能大概仅用了10来个表，而roller就一个博客，却有二三十个的表。</p>
<p>看着寥寥无几的java开源CMS跟遍地的php CMS，我想互联网的应用，java的CMS，何时能出一个可以闪亮的产品啊。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fangyuqiang.com/archives/194/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>搞定struts中cookie</title>
		<link>http://www.fangyuqiang.com/archives/203</link>
		<comments>http://www.fangyuqiang.com/archives/203#comments</comments>
		<pubDate>Tue, 18 Nov 2008 06:34:05 +0000</pubDate>
		<dc:creator>fangyuqiang</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[struts]]></category>

		<guid isPermaLink="false">http://playfish.javaeye.com/blog/269436</guid>
		<description><![CDATA[今天碰到的一个问题：配置页提交一个信息到struts的action中，action将这个信息保存到cookie以及action result中。用户如果以后登陆的话，从cookie中读出上次的配置信息。 检查过所有读写cookie的代码，没错，但是诡异的是，JSP上面就是无法获得在action中设置的cookie的值。 思想向后，查找了下cookie的规范，看看cookie是如何读写，如何跟随，如何判断作用域的。规范没找到多少，但是至少明白了一点，只有同一个域下面才可以读取cookie。 后来想到，到C:\Documents and Settings\fangyuqiang\Cookies 这里面找到本机上保存的所有cookie文件，按时间排序选一下最近生成的cookie，打开一看： sourceType 163 localhost/mod/stock/ 1024 1134027776 29974744 1271499136 29968709 * stockCodesStr "000001,600222" localhost/mod/stock/ 1024 1134027776 29974744 1271499136 29968709 * 这个cookie允许的读取域是localhost/mod/stock/，而jsp页的地址确是localhost/mod/pages/stock/view.jsp。终于明白，原来是struts的action中写cookie造成了这样的问题。 在struts的action中写cookie，它会将作用域设置成action的路径，如果action的路径跟实际的jsp地址有出入，不在同一个域下，例如这里的localhost/mod/stock/跟localhost/mod/pages/stock/，这样在localhost/mod/pages/stock/这里的jsp就读取不到这个cookie，对此，你只要修改下struts的路径，将cookie的路径保持跟你jsp的路径相一致就可以了。此外，cookie应该是可以手动的设置路径的，我想这个也能解决问题，不过我没做尝试。 顺便便宜放送下cookie读写的代码： cookie生成与添加，这段代码可以放在struts的action里面或者servlet里面 //保存信息到cookie中 Cookie sourceTypeCookie = new Cookie("sourceType", sourceType); Cookie stockCodesStrCookie = new Cookie("stockCodesStr", stockCodesStr); // 生命周期 sourceTypeCookie.setMaxAge(60 * 60 * 24 * 30); // 保存30天 stockCodesStrCookie.setMaxAge(60 [...]]]></description>
			<content:encoded><![CDATA[<p>今天碰到的一个问题：配置页提交一个信息到struts的action中，action将这个信息保存到cookie以及action result中。用户如果以后登陆的话，从cookie中读出上次的配置信息。</p>
<p>检查过所有读写cookie的代码，没错，但是诡异的是，JSP上面就是无法获得在action中设置的cookie的值。<span id="more-203"></span></p>
<p>思想向后，查找了下cookie的规范，看看cookie是如何读写，如何跟随，如何判断作用域的。规范没找到多少，但是至少明白了一点，只有同一个域下面才可以读取cookie。</p>
<p>后来想到，到C:\Documents and Settings\fangyuqiang\Cookies 这里面找到本机上保存的所有cookie文件，按时间排序选一下最近生成的cookie，打开一看：</p>
<pre class="java">sourceType
163
localhost/mod/stock/
1024
1134027776
29974744
1271499136
29968709
*
stockCodesStr
"000001,600222"
localhost/mod/stock/
1024
1134027776
29974744
1271499136
29968709
*</pre>
<p>这个cookie允许的读取域是localhost/mod/stock/，而jsp页的地址确是localhost/mod/pages/stock/view.jsp。终于明白，原来是struts的action中写cookie造成了这样的问题。</p>
<p><strong>在struts的action中写cookie，它会将作用域设置成action的路径，如果action的路径跟实际的jsp地址有出入，不在同一个域下，例如这里的localhost/mod/stock/跟localhost/mod/pages/stock/，这样在localhost/mod/pages/stock/这里的jsp就读取不到这个cookie，对此，你只要修改下struts的路径，将cookie的路径保持跟你jsp的路径相一致就可以了。</strong>此外，cookie应该是可以手动的设置路径的，我想这个也能解决问题，不过我没做尝试。</p>
<p>顺便便宜放送下cookie读写的代码：</p>
<p>cookie生成与添加，这段代码可以放在struts的action里面或者servlet里面</p>
<pre class="java" title="code">			//保存信息到cookie中
Cookie sourceTypeCookie = new Cookie("sourceType", sourceType);
Cookie stockCodesStrCookie = new Cookie("stockCodesStr", stockCodesStr);

// 生命周期
sourceTypeCookie.setMaxAge(60 * 60 * 24 * 30); // 保存30天
stockCodesStrCookie.setMaxAge(60 * 60 * 24 * 30); // 保存30天

response.addCookie(sourceTypeCookie);
response.addCookie(stockCodesStrCookie);</pre>
<p>一些cookie资料的补充</p>
<h3>什么是Cookies?</h3>
<p>你会问，什么是cookies呢?<strong> cookie 是浏览器保存在用户计算机上的少量数据。它与特定的WEB页或WEB站点关联起来，自动地在WEB浏览器和WEB服务器之间传递</strong>。</p>
<p>比如，如果你运行的是Windows操作系统，使用Internet Explorer上网，那么你会发现在你的“Windows”目录下面有一个子目录，叫做“Temporary Internet Files”。如果你有空看看这个目录，就会发现里面有一些文件，文件名称看起来就象电子邮件地址。比如在我机器上的这个目录里，就有 “jim@support.microsoft.com”这样的文件。这是一个cookie 文件，这个文件从哪来呢？猜一猜，它来自微软的支持站点。顺便说一句，这不是我的电子邮件地址，特此澄清。</p>
<p>对于管理细小的、不重要的、不想保存在中央数据库里的细节信息，Cookies 是个很不错的方案。（这不是说大家的名字不重要。）比如，目前网站上不断增长的自定义服务，可以为每个用户定制他们要看的内容。如果你设计的就是这样一个站点，那么你怎么来管理这样的信息：一个用户喜欢绿色的菜单条，而另一个喜欢红色的。确实是个累人的问题。不过，这样的信息，可以很安全地记录到 cookie，并保存在用户的计算机上，而你自己的数据库空间可以留给更长久更有意义的数据。</p>
<p>FYI: Cookies 对于安全用途，通常很有用。我不想在此就这一问题过于深入，只是提供一个示例，可以看到如何使用在一段时间之后过期的cookies来保证站点安全：</p>
<p>使用用户名和口令，通过 SSL 登录。</p>
<p>在服务器的数据库里检查用户名和口令。如果登录成功，建立一个当前时间标签的消息摘要 (比如 MD5) ，并把它保存在cookie和服务器数据库里。把用户的登录时间保存在服务器数据库里面的用户记录里。</p>
<p>在进行每个安全事务时（用户处于登录状态的任何事务），把cookie的消息摘要和保存在服务器数据库里的摘要进行比较，如果比较失败，就把用户引导到登录界面。</p>
<p>如果第3步检查通过，那么检查当前时间和登录时间之音经过的时间是否超过允许的时间长度。如果用户已经超时，那么就把用户引到登录界面。</p>
<p>如果第3步和第4步都通过了，那么把登录时间重新设置成当前时间，允许事务发生。那些需要你登录的安全站点，可能多数使用的都是和这里介绍的类似的方法。</p>
<h3>Cookie的构成</h3>
<p>Cookies 最初设计时，是为了CGI编程。但是，我们也可以使用Javascript脚本来操纵cookies。在本文里，我们将演示如何使用Javascript 脚本来操纵cookies。(如果有需求，我可能会在以后的文章里介绍如何使用Perl进行cookie管理。但是如果实在等不得，那么我现在就教你一手：仔细看看CGI.pm。在这个CGI包里有一个cookie()函数，可以用它建立cookie。但是，还是让我们先来介绍cookies的本质。</p>
<p><strong>在Javascript脚本里，一个cookie 实际就是一个字符串属性。当你读取cookie的值时，就得到一个字符串，里面当前WEB页使用的所有cookies的名称和值。每个cookie除了 name名称和value值这两个属性以外，还有四个属性。这些属性是： expires过期时间、 path路径、 domain域、以及 secure安全</strong>。</p>
<p>Expires – 过期时间。指定cookie的生命期。具体是值是过期日期。如果想让cookie的存在期限超过当前浏览器会话时间，就必须使用这个属性。当过了到期日期时，浏览器就可以删除cookie文件，没有任何影响。</p>
<p>Path – 路径。指定与cookie关联的WEB页。值可以是一个目录，或者是一个路径。如果http://www.zdnet.com/devhead /index.html 建立了一个cookie，那么在http://www.zdnet.com/devhead/目录里的所有页面，以及该目录下面任何子目录里的页面都可以访问这个cookie。这就是说，在http://www.zdnet.com/devhead/stories/articles 里的任何页面都可以访问http://www.zdnet.com/devhead/index.html建立的cookie。但是，如果http: //www.zdnet.com/zdnn/ 需要访问http://www.zdnet.com/devhead/index.html设置的cookes，该怎么办？这时，我们要把cookies 的path属性设置成“/”。在指定路径的时候，凡是来自同一服务器，URL里有相同路径的所有WEB页面都可以共享cookies。现在看另一个例子：如果想让 http://www.zdnet.com/devhead/filters/ 和http://www.zdnet.com/devhead/stories/共享cookies，就要把path设成“/devhead”。</p>
<p>Domain – 域。指定关联的WEB服务器或域。值是域名，比如zdnet.com。这是对path路径属性的一个延伸。如果我们想让 catalog.mycompany.com 能够访问shoppingcart.mycompany.com设置的cookies，该怎么办? 我们可以把domain属性设置成“mycompany.com”，并把path属性设置成“/”。FYI：<strong>不能把cookies域属性设置成与设置它的服务器的所在域不同的值</strong>。</p>
<p>Secure – 安全。指定cookie的值通过网络如何在用户和WEB服务器之间传递。这个属性的值或者是“secure”，或者为空。缺省情况下，该属性为空，也就是使用不安全的HTTP连接传递数据。如果一个 cookie 标记为secure，那么，它与WEB服务器之间就通过HTTPS或者其它安全协议传递数据。不过，设置了secure属性不代表其他人不能看到你机器本地保存的cookie。换句话说，把cookie设置为secure，只保证cookie与WEB服务器之间的数据传输过程加密，而保存在本地的 cookie文件并不加密。如果想让本地cookie也加密，得自己加密数据。</p>
<h3>操纵Cookies</h3>
<p>请记住，cookie 就是文档的一个字符串属性。要保存cookie，只要建立一个字符串，格式是name=（名称＝值），然后把文档的 document.cookie 设置成与它相等即可。比如，假设想保存表单接收到的用户名，那么代码看起来就象这样：</p>
<p>document.cookie = &#8220;username&#8221; + escape(form.username.value);</p>
<p>在这里，使用 escape() 函数非常重要，因为cookie值里可能包含分号、逗号或者空格。这就是说，在读取cookie值时，必须使用对应的unescape()函数给值解码。</p>
<p>我们当然还得介绍cookie的四个属性。这些属性用下面的格式加到字符串值后面：</p>
<p>name=[; expires=][; domain=][; path= ][; secure]</p>
<p>名称=&lt;值&gt;[; expires=&lt;日期&gt;][; domain=&lt;域&gt;][; path=&lt;路径&gt;][; 安全],</p>
<p>的.toGMTString() 方法得到这一GMT格式的日期值。方括号代表这项是可选的。比如在 [; secure]两边的方括号代表要想把cookie设置成安全的，就需要把&#8221;; secure&#8221; 加到cookie字符串值的后面。如果&#8221;; secure&#8221; 没有加到cookie字符串后面，那么这个cookie就是不安全的。不要把尖括号&lt;&gt; 和方括号[] 加到cookie里（除非它们是某些值的内容）。设置属性时，不限属性，可以用任何顺序设置。</p>
<p>下面是一个例子，在这个例子里，cookie &#8220;username&#8221; 被设置成在15分钟之后过期，可以被服务器上的所有目录访问，可以被&#8221;mydomain.com&#8221;域里的所有服务器访问，安全状态为安全。</p>
<pre class="js" title="code">// Date() 的构造器设置以毫秒为单位
// .getTime() 方法返回时间，单位为毫秒
// 所以要设置15分钟到期，要用60000毫秒乘15分钟
var expiration = new Date((new Date()).getTime() + 15 * 60000);
document.cookie = "username=" + escape(form.username.value)
+ "; expires ="+ expiration.toGMTString()
+ "; path=" + "/" + ";_domain="
+ "mydomain.com" + "; secure";

读取cookies值有点象个小把戏，因为你一次就得到了属于当前文档的所有cookies。
// 下面这个语句读取了属于当前文档的所有cookies
var allcookies = document.cookie;</pre>
<p>现在，我们得解析allcookies变量里的不同cookies，找到感兴趣的指定cookie。这个工作很简单，因为我们可以利用Javascript语言提供的扩展字符串支持。</p>
<p>如果我们对前面分配的cookie &#8220;username&#8221; 感兴趣，可以用下面的脚本来读取它的值。</p>
<p>// 我们定义一个函数，用来读取特定的cookie值。</p>
<pre class="js" title="code">function getCookie(cookie_name){

var allcookies = document.cookie;
var cookie_pos = allcookies.indexOf(cookie_name);
  // 如果找到了索引，就代表cookie存在，
// 反之，就说明不存在。
if (cookie_pos != -1)
{
  // 把cookie_pos放在值的开始，只要给值加1即可。
  cookie_pos += cookie_name.length + 1;
  var cookie_end = allcookies.indexOf(";", cookie_pos);

  if (cookie_end == -1)
  {
   cookie_end = allcookies.length;
  }

  var value = unescape(allcookies.substring(cookie_pos, cookie_end));
}

return value;
}

// 调用函数
var cookie_val = getCookie("username");</pre>
<p>上面例程里的 cookie_val 变量可以用来生成动态内容，或者发送给服务器端CGI脚本进行处理。现在你知道了使用Javascript脚本操纵cookies的基本方法。但是，如果你跟我一样，那么我们要做的第一件事，就是建立一些接口函数，把cookies处理上的麻烦隐藏起来。不过，在你开始编程之前，稍候片刻。这些工作，早就有人替你做好了。你要做的，只是到哪去找这些接口函数而已。</p>
<p>比如，在David Flangan的Javascript: The Definitive Guide 3rd Ed.这本书里，可以找到很好的cookie应用类。你也可以在Oreilly的WEB站点上找到这本书里的例子。本文最后的链接列表里，有一些访问这些 cookie示例的直接链接。</p>
<h3>Cookies 怪兽</h3>
<p>因为某些原因Cookies 的名声很不好。许多人利用cookies做一些卑鄙的事情，比如流量分析、点击跟踪。Cookies 也不是非常安全，特别是没有secure属性的cookies。不过，即使你用了安全的cookies，如果你和别人共用计算机，比如在网吧，那么别人就可以窥探计算机硬盘上未加密保存的cookie文件，也就有可能窃取你的敏感信息。所以，如果你是一个WEB开发人员，那么你要认真考虑这些问题。不要滥用cookies。不要把用户可能认为是敏感的数据保存在cookies里。如果把用户的社会保险号、信用卡号等保存在cookie里，等于把这些敏感信息放在窗户纸下，无异于把用户投到极大危险之中。一个好的原则是，如果你不想陌生人了解你的这些信息，那就不要把它们保存在cookies里。</p>
<p>另外，cookies还有一些实际的限制。Cookies保留在计算机上，不跟着用户走。如果用户想换计算机，那么新计算机无法得到原来的 cookie。甚至用户在同一台计算机上使用不同浏览器，也得不到原来的cookie：Netscape 不能读取Internet Explorer 的cookies。</p>
<p>还有，用户也不愿意接受cookies。所以不要以为所有的浏览器都能接受你发出的cookies。如果浏览器不接受cookies，你要保证自己的WEB站点不致因此而崩溃或中断。</p>
<p>另外WEB 浏览器能保留的cookies不一定能超过300个。也没有标准规定浏览器什么时候、怎么样作废cookies。所以达到限制时，浏览器能够有效地随机删除cookies。浏览器保留的来自一个WEB服务器上的cookies，不超过20个，每个cookie的数据（包括名称和值），不超过4K字节。(不过，本文里的cookie尺寸没问题，它只占了12 K字节，保存在3个3 cookies里。)</p>
<p>简而言之，注意保持cookie简单。不要依赖cookies的存在，不要在每个cookie里保存太多信息。不要保存太多的cookes。但是，抛除这些限制，在技巧高超的WEB管理员手里，cookie的概念是一个有用的工具。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fangyuqiang.com/archives/203/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
