此文已由作者赵计刚授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。

注:本章代码基于《第五章 企业项目开发--mybatis注解与xml并用》的代码,链接如下:

http://www.cnblogs.com/java-zhao/p/5120792.html

在实际项目中,我们会存储用户状态信息,基本使用两种手段:cookie和session

1、cookie:

1.1、流程:

  • 服务端将cookie的属性值设置好之后,通过HttpServletResponse将cookie写入响应头;

  • 服务端从请求头中通过HttpServletRequest将所有cookie(当前被访问页面的所有cookie)读取出来,遍历寻找指定name的cookie即可。

流程与下边的例子对应一下,立刻就清楚了。

1.2、包含选项:(当前使用最多的就是version0版本的cookie,这里仅说version0的几个最常用的属性)

  • name:cookie名

  • value:cookie值

  • domain:cookie写在哪一个域下(例如:aaa.com)

  • path:cookie写在哪一个uri下(例如:/admin)

  • expires:cookie过期时间,超过某个指定时间点cookie就消失,当然设成-1的话,关闭浏览器cookie就会消失;指定了时间点的话,该cookie就会存在硬盘上

对于domain,一个例子解释清楚:

假设:www.baidu.com和e.baidu.com两个域,path="/",

1)当domain="baidu.com"时,两个域都可以访问生成的cookie

2)当domain="www.baidu.com"时,只有前一个域有生成cookie

对于path,用一个例子解释清楚:

假设http://www.baidu.com/dev/zz/和http://www.baidu.com/dev/xx/,

1)当path="/dev",生成的cookie上边的两个地址共享;

2)当path="/dev/zz",生成的cookie只有第一个地址有,第二个地址没有。

根据上边的例子,对于path与domain是相互依赖的,共同决定某个页面地址是否可以生成指定的cookie

1.3、优点:

  • 基本不需要使用服务器空间,用户会话信息存在了客户端(这个只是列出来,基本不能算优点,反而应该算是缺点,因为存在客户端不安全)

  • 在分布式系统中,因为cookie存在了浏览器,所以不需要考虑同一个用户怎样定位到同一台服务器这个问题

1.4、缺点:

  • cookie个数和总大小有限制,一般而言,每个域下可存放<=50个cookie,所有cookie的总大小<=4095个字节左右,chrome浏览器的cookie总大小会大很多(>80000个字节)。这些数据参考自《深入分析Java Web技术内幕》

  • 如果cookie很多,客户端和服务端会浪费很对带宽,而且也会拖慢速度,一般而言,可以使用压缩算法做压缩,但是,可能压缩后的size也很大

  • 不安全,如果被窃听者劫取到你的cookie,即使你对这些cookie做了加密,使其看不到cookie中的信息,但是其也可以通过该cookie模拟登录行为,然后获得一些权限,执行一些操作。(这是个疑问,记住我这个功能就是这样做的,怎样做到安全的呢?)(答案见最下方)

2、项目中使用cookie存储用户会话状态

代码实现:本章代码基于上一章代码实现,下面只列出修改或添加的一些类。

通常情况下,如果工具类很多,我们可以重开一个项目,例如:ssmm0-util,然后在这个子项目中添加类,当然,如果项目本身也不大,直接将工具类放在ssmm0-data子项目下即可。

2.1、ssmm0-data:

2.1.1、pom.xml:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 4 
 5     <modelVersion>4.0.0</modelVersion>
 6 
 7     <!-- 指定父模块 -->
 8     <parent>
 9         <groupId>com.xxx</groupId>
10         <artifactId>ssmm0</artifactId>
11         <version>1.0-SNAPSHOT</version>
12     </parent>
13 
14     <groupId>com.xxx.ssmm0</groupId>
15     <artifactId>ssmm0-data</artifactId>
16 
17     <name>ssmm0-data</name>
18     <packaging>jar</packaging><!-- 只是作为其他模块使用的工具 -->
19 
20     <!-- 引入实际依赖 -->
21     <dependencies>
22         <!-- mysql -->
23         <dependency>
24             <groupId>mysql</groupId>
25             <artifactId>mysql-connector-java</artifactId>
26         </dependency>
27         <!-- 数据源 -->
28         <dependency>
29             <groupId>org.apache.tomcat</groupId>
30             <artifactId>tomcat-jdbc</artifactId>
31         </dependency>
32         <!-- mybatis -->
33         <dependency>
34             <groupId>org.mybatis</groupId>
35             <artifactId>mybatis</artifactId>
36         </dependency>
37         <dependency>
38             <groupId>org.mybatis</groupId>
39             <artifactId>mybatis-spring</artifactId>
40         </dependency>
41         <!-- servlet --><!-- 为了会用cookie -->
42         <dependency>
43             <groupId>javax.servlet</groupId>
44             <artifactId>javax.servlet-api</artifactId>
45         </dependency>
46         <!-- bc-加密 -->
47         <dependency>
48             <groupId>org.bouncycastle</groupId>
49             <artifactId>bcprov-jdk15on</artifactId>
50         </dependency>
51         <!-- cc加密 -->
52         <dependency>
53             <groupId>commons-codec</groupId>
54             <artifactId>commons-codec</artifactId>
55         </dependency>
56     </dependencies>
57 </project>

说明:

  • 引入了servlet-jar包,主要是需要HttpServletResponse向响应头写cookie和使用HttpServletRequest从请求头读取cookie;

  • 引入commons-codec和bouncy-castle,主要是为了实现AES加密与Base64编码。

注意:

  • servlet-jar的scope是provided,不具有传递性,所以如果在ssmm0-userManagement中需要用到servlet-jar,则ssmm0-userManagement需要自己再引一遍

  • commons-codec和bouncy-castle的scope都是采用了默认的compile,具有传递性,所以如果在ssmm0-userManagement中需要用到commons-codec和bouncy-castle,ssmm0-userManagement不需要自己再引一遍了

2.1.2、AESUtil:(AES加密)

  1 package com.xxx.util;
  2 
  3 import java.io.UnsupportedEncodingException;
  4 import java.security.InvalidAlgorithmParameterException;
  5 import java.security.InvalidKeyException;
  6 import java.security.Key;
  7 import java.security.NoSuchAlgorithmException;
  8 import java.security.NoSuchProviderException;
  9 import java.security.Security;
 10 import java.security.spec.InvalidKeySpecException;
 11 
 12 import javax.crypto.BadPaddingException;
 13 import javax.crypto.Cipher;
 14 import javax.crypto.IllegalBlockSizeException;
 15 import javax.crypto.KeyGenerator;
 16 import javax.crypto.NoSuchPaddingException;
 17 import javax.crypto.SecretKey;
 18 import javax.crypto.spec.SecretKeySpec;
 19 
 20 import org.apache.commons.codec.binary.Base64;
 21 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 22 
 23 /**
 24  * 基于JDK或BC的AES算法,工作模式采用ECB
 25  */
 26 public class AESUtil {
 27     private static final String ENCODING = "UTF-8";
 28     private static final String KEY_ALGORITHM = "AES";//产生密钥的算法
 29     private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";//加解密算法 格式:算法/工作模式/填充模式 注意:ECB不使用IV参数
 30     private static final String ENCRYPT_KEY = "WAUISIgpBh1R/+3f2Ze4csUU/tl/O8x56DbVb7mTs7w=";//这个是先运行一遍getKey()方法产生的,然后卸载了这里。
 31     
 32     /**
 33      * 产生密钥(线下产生好,然后配在项目中)
 34      */
 35     public static byte[] getKey() throws NoSuchAlgorithmException{
 36         Security.addProvider(new BouncyCastleProvider());//在BC中用,JDK下去除
 37         KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
 38         keyGenerator.init(256);//初始化密钥长度,128,192,256(选用192和256的时候需要配置无政策限制权限文件--JDK6)
 39         SecretKey key =keyGenerator.generateKey();//产生密钥
 40         return key.getEncoded();
 41     }
 42     
 43     /**
 44      * 还原密钥:二进制字节数组转换为Java对象
 45      */
 46     public static Key toKey(byte[] keyByte){
 47         Security.addProvider(new BouncyCastleProvider());//在BC中用,JDK下去除
 48         return new SecretKeySpec(keyByte, KEY_ALGORITHM);
 49     }
 50     
 51     /**
 52      * AES加密,并转为16进制字符串或Base64编码字符串
 53      */
 54     public static String encrypt(String data, byte[] keyByte) throws InvalidKeyException, 
 55                                                                            NoSuchAlgorithmException, 
 56                                                                            InvalidKeySpecException, 
 57                                                                            NoSuchPaddingException, 
 58                                                                            IllegalBlockSizeException, 
 59                                                                            BadPaddingException, 
 60                                                                            UnsupportedEncodingException, 
 61                                                                            NoSuchProviderException, 
 62                                                                            InvalidAlgorithmParameterException {
 63         Key key = toKey(keyByte);//还原密钥
 64         //Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);//JDK下用
 65         Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM,"BC");//BC下用
 66         cipher.init(Cipher.ENCRYPT_MODE, key);//设置加密模式并且初始化key
 67         byte[] encodedByte = cipher.doFinal(data.getBytes(ENCODING));
 68         return Base64.encodeBase64String(encodedByte);//借助CC的Base64编码
 69     }
 70     
 71     /**
 72      * AES解密
 73      * @param data        待解密数据为字符串
 74      * @param keyByte    密钥
 75      */
 76     public static String decrypt(String data, byte[] keyByte) throws InvalidKeyException, 
 77                                                                      NoSuchAlgorithmException, 
 78                                                                      InvalidKeySpecException, 
 79                                                                      NoSuchPaddingException, 
 80                                                                      IllegalBlockSizeException, 
 81                                                                      BadPaddingException, 
 82                                                                      UnsupportedEncodingException, 
 83                                                                      NoSuchProviderException, 
 84                                                                      InvalidAlgorithmParameterException {
 85         Key key = toKey(keyByte);//还原密钥
 86         //Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);//JDK下用
 87         Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM,"BC");//BC下用
 88         cipher.init(Cipher.DECRYPT_MODE, key);
 89         byte[] decryptByte = cipher.doFinal(Base64.decodeBase64(data));//注意data不可以直接采用data.getByte()方法转化为字节数组,否则会抛异常
 90         return new String(decryptByte, ENCODING);//这里注意用new String()
 91     }
 92     
 93     /**
 94      * 测试
 95      */
 96     public static void main(String[] args) throws NoSuchAlgorithmException, 
 97                                                   InvalidKeyException, 
 98                                                   InvalidKeySpecException, 
 99                                                   NoSuchPaddingException, 
100                                                   IllegalBlockSizeException, 
101                                                   BadPaddingException, 
102                                                   UnsupportedEncodingException, 
103                                                   NoSuchProviderException, 
104                                                   InvalidAlgorithmParameterException {
105         String data = "找一个好姑娘做老婆是我的梦 想!";
106         /*************测试encryptAESHex()、decrypt()**************/
107         System.out.println("原文-->"+data);
108         byte[] keyByte3 = Base64.decodeBase64(ENCRYPT_KEY); 
109         System.out.println("密钥-->"+Base64.encodeBase64String(keyByte3));//这里将二进制的密钥使用base64加密保存,这也是在实际中使用的方式
110         String encodedStr = AESUtil.encrypt(data, keyByte3);
111         System.out.println("加密后-->"+encodedStr);
112         System.out.println("解密String后-->"+AESUtil.decrypt(encodedStr, keyByte3));
113     }
114 }

说明:

关于AES的具体内容,可以参照"Java加密与解密系列的第八章",具体链接如下:

http://www.cnblogs.com/java-zhao/p/5087046.html

AESUtil这个类就是根据上边这篇博客的AESJDK这个类进行修改封装的。

注意:

AESUtil类的ENCRYPT_KEY变量的值是先运行了其中的getKey()方法,然后又通过Base64编码产生的,产生这个密钥后,我们将这个密钥写在类中。简单来说,就是线下先产生密钥,然后写在程序中,之后getKey()方法就再没有用了,可以直接删掉。

关于生成密钥的这段代码如下:

String key = Base64.encodeBase64String(AESUtil.getKey());

免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

更多网易技术、产品、运营经验分享请点击

相关文章:
【推荐】 css小点心

企业项目开发--cookie(1)的更多相关文章

  1. 第六章 企业项目开发--cookie

    注:本章代码基于<第五章 企业项目开发--mybatis注解与xml并用>的代码,链接如下: http://www.cnblogs.com/java-zhao/p/5120792.html ...

  2. 企业项目开发--cookie(3)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 2.2.3.AdminController   1 package com.xxx.web.admin;   ...

  3. 企业项目开发--cookie(2)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 2.1.3.CookieUtil:(cookie的基本操作:增删查,注意没有改)  1 package co ...

  4. 第八章 企业项目开发--分布式缓存memcached

    注意:本节代码基于<第七章 企业项目开发--本地缓存guava cache> 1.本地缓存的问题 本地缓存速度一开始高于分布式缓存,但是随着其缓存数量的增加,所占内存越来越大,系统运行内存 ...

  5. 企业项目开发--分布式缓存memcached(3)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3.3.ssmm0-data 结构: 3.3.1.pom.xml  1 <?xml version=& ...

  6. 第十一章 企业项目开发--消息队列activemq

    注意:本章代码基于 第十章 企业项目开发--分布式缓存Redis(2) 代码的github地址:https://github.com/zhaojigang/ssmm0 消息队列是分布式系统中实现RPC ...

  7. 企业项目开发--分布式缓存Redis

    第九章 企业项目开发--分布式缓存Redis(1) 注意:本章代码将会建立在上一章的代码基础上,上一章链接<第八章 企业项目开发--分布式缓存memcached> 1.为什么用Redis ...

  8. 第九章 企业项目开发--分布式缓存Redis(1)

    注意:本章代码将会建立在上一章的代码基础上,上一章链接<第八章 企业项目开发--分布式缓存memcached> 1.为什么用Redis 1.1.为什么用分布式缓存(或者说本地缓存存在的问题 ...

  9. 第十章 企业项目开发--分布式缓存Redis(2)

    注意:本章代码是在上一章的基础上进行添加修改,上一章链接<第九章 企业项目开发--分布式缓存Redis(1)> 上一章说了ShardedJedisPool的创建过程,以及redis五种数据 ...

随机推荐

  1. activiti如何获取当前节点以及下一步路径或节点(转)

    ACTIVITI相对于JBPM来说,比较年轻,用的人少,中文方面的资料更少,我根据网上到处找得资料以及看官方文档总结出来了代码,非常不容易啊.废话不多说,直接上代码吧: 首先是根据流程ID获取当前任务 ...

  2. spring学习十九 常用注解

    1. @Component 创建类对象,相当于配置<bean/>2. @Service 与@Component 功能相同. 2.1 写在 ServiceImpl 类上.3. @Reposi ...

  3. MySQL参数优化:back_log

    * 修改back_log参数值:由默认的50修改为500.(每个连接256kb, 占用:125M) back_log=500 查看mysql 当前系统默认back_log值,命令: show vari ...

  4. python学习 day2 (3月2日)

    .if if else 和 if elif else 的区别是: 前者 判断第一个 判断完第二个 之后还会执行else: 后者是只有满足条件(即都不符合if.elif里的条件时才会进入else) 不清 ...

  5. 695. Max Area of Island

    static int wing=[]() { std::ios::sync_with_stdio(false); cin.tie(NULL); ; }(); class Solution { publ ...

  6. 2019.01.19 codeforces893F.Subtree Minimum Query(线段树合并)

    传送门 线段树合并菜题. 题意简述:给一棵带点权的有根树,多次询问某个点ppp子树内距离ppp不超过kkk的点的点权最小值,强制在线. 思路: 当然可以用dfsdfsdfs序+主席树水过去. 然而线段 ...

  7. urllib — URL handling modules

    urllib is a package that collects several modules for working with URLs: •urllib.request for opening ...

  8. 第05章:MongoDB-CRUD操作--数据库

    ①显示现有的数据库 命令:show dbs 或者databases; ②显示当前的数据库 命令:db ③使用|创建数据库 命令:use 数据库名称   如果数据库不存在,则创建数据库,否则切换到指定数 ...

  9. UVa 10561 Treblecross (SG函数)

    题意:给定上一行字符串,其中只有 X 和 . 并且没有连续的三个 X,两个玩家要分别在 . 上放 X,如果出现三个连续的 X,则该玩家胜利,现在问你先手胜还是败,如果是胜则输出第一步可能的位置. 析: ...

  10. .NET 开源GIS项目

    SharpMapSharpMap是一个基于.NET 2.0使用C#开发的Map渲染类库,可以渲染ESRI Shape.PostGIS.MS SQL等格式的GIS数据,通过扩展地图数据Provider, ...