1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sourceforge.statelessfilter.backend.jsonaescookie;
17
18 import java.io.IOException;
19 import java.security.SignatureException;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Set;
23
24 import javax.crypto.Cipher;
25 import javax.crypto.spec.IvParameterSpec;
26 import javax.crypto.spec.SecretKeySpec;
27 import javax.servlet.http.HttpServletRequest;
28 import javax.servlet.http.HttpServletResponse;
29
30 import net.sourceforge.statelessfilter.backend.ISessionData;
31 import net.sourceforge.statelessfilter.backend.support.CookieBackendSupport;
32 import net.sourceforge.statelessfilter.backend.support.CookieDataSupport;
33
34 import org.apache.commons.codec.binary.Base64;
35 import org.apache.commons.lang.ArrayUtils;
36 import org.apache.commons.lang.StringUtils;
37 import org.codehaus.jackson.map.ObjectMapper;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public class JSONAESCookieBackend extends CookieBackendSupport {
59 private static final String DESERIALIZE_ERROR = "Cannot deserialize session. A new one will be created";
60 private static final String ENCRYPTION = "AES";
61 private static final String ENCRYPTION_WITH_PARAM = "AES/CBC/PKCS5Padding";
62 private static final String ID = "jsonaescookie";
63 private static final String PARAM_IV = "iv";
64 private static final String PARAM_KEY = "key";
65 private static final String SEPARATOR = "B";
66
67 private IvParameterSpec iv = null;
68 Logger logger = LoggerFactory.getLogger(JSONAESCookieBackend.class);
69 ObjectMapper mapper = null;
70 private SecretKeySpec secretKey = null;
71
72 public JSONAESCookieBackend() {
73 setCookieName("es");
74 }
75
76
77
78
79 @Override
80 public void destroy() {
81
82 }
83
84 private byte[] getEncryptionBytes(String data, int length) {
85 byte[] keyRaw = new byte[length];
86 for (int i = 0; i < length; i++) {
87 keyRaw[i] = 0;
88 }
89
90 byte[] dataRaw = Base64.decodeBase64(data);
91 System.arraycopy(dataRaw, 0, keyRaw, 0,
92 dataRaw.length > length ? length : dataRaw.length);
93
94 return keyRaw;
95 }
96
97
98
99
100 @Override
101 public String getId() {
102 return ID;
103 }
104
105
106
107
108
109
110
111 @Override
112 public void init(Map<String, String> config) throws Exception {
113 super.init(config);
114 mapper = new ObjectMapper();
115 String key = config.get(PARAM_KEY);
116 String iv = config.get(PARAM_IV);
117
118 if (StringUtils.isEmpty(key) || StringUtils.isEmpty(iv)) {
119 throw new Exception(
120 ID
121 + "."
122 + PARAM_KEY
123 + " or "
124 + ID
125 + "." + PARAM_IV + " parameter missing in /stateless.properties.");
126 }
127
128 secretKey = new SecretKeySpec(getEncryptionBytes(key, 16), ENCRYPTION);
129 this.iv = new IvParameterSpec(getEncryptionBytes(iv, 16));
130
131 }
132
133
134
135
136 @Override
137 public ISessionData restore(HttpServletRequest request) {
138 try {
139
140 byte[] data = getCookieData(request, null);
141
142 if (data != null) {
143 int index = ArrayUtils.indexOf(data, SEPARATOR.getBytes()[0]);
144
145 int size = Integer.parseInt(new String(ArrayUtils.subarray(
146 data, 0, index)));
147 data = ArrayUtils.subarray(data, index + 1, data.length);
148
149 Cipher decryptCipher = Cipher
150 .getInstance(ENCRYPTION_WITH_PARAM);
151 decryptCipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
152 data = decryptCipher.doFinal(data);
153
154 data = ArrayUtils.subarray(data, 0, size + 1);
155
156 CookieDataSupport s = mapper.readValue(new String(data),
157 CookieDataSupport.class);
158
159 if (s.isValid()
160 && s.getRemoteAddress().equals(
161 getFullRemoteAddr(request))) {
162 return s;
163 }
164 }
165 } catch (Exception e) {
166 logger.info(DESERIALIZE_ERROR, e);
167 }
168
169 return null;
170 }
171
172
173
174
175
176
177 @Override
178 public void save(ISessionData session, List<String> dirtyAttributes,
179 HttpServletRequest request, HttpServletResponse response)
180 throws IOException {
181 try {
182 if (session != null) {
183 CookieDataSupport cookieData = new CookieDataSupport(session);
184 cookieData.setRemoteAddress(getFullRemoteAddr(request));
185
186
187 ensureStrings(session.getContent());
188
189 String dataString = mapper.writeValueAsString(cookieData);
190
191 byte[] data;
192 try {
193 Cipher encryptCipher = Cipher
194 .getInstance(ENCRYPTION_WITH_PARAM);
195 encryptCipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
196 data = encryptCipher.doFinal(dataString.getBytes());
197 } catch (Exception e) {
198 throw new IOException(e.getMessage());
199 }
200
201 byte[] size = (data.length + SEPARATOR).getBytes();
202
203 setCookieData(request, response, ArrayUtils.addAll(size, data));
204
205 if (logger.isDebugEnabled()) {
206 logger.debug("Cookie size : " + ArrayUtils.addAll(size, data).length);
207 }
208
209 } else {
210 setCookieData(request, response, null);
211 }
212 } catch (SignatureException e) {
213 throw new IOException(e);
214 }
215 }
216
217
218
219
220
221
222
223 private void ensureStrings(Map<String, Object> map) {
224
225 if (map == null || map.size() == 0)
226 return;
227
228 Set<String> keys = map.keySet();
229 for (String key : keys) {
230 if (!(map.get(key) instanceof String)) {
231 throw new IllegalArgumentException(
232 key
233 + " is not a String. JSON stateless session only support string data.");
234 }
235 }
236 }
237 }