1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
|
18 | |
|
19 | |
package org.apache.shiro.web.servlet; |
20 | |
|
21 | |
import org.apache.shiro.util.StringUtils; |
22 | |
import org.slf4j.Logger; |
23 | |
import org.slf4j.LoggerFactory; |
24 | |
|
25 | |
import javax.servlet.http.HttpServletRequest; |
26 | |
import javax.servlet.http.HttpServletResponse; |
27 | |
import java.text.DateFormat; |
28 | |
import java.text.SimpleDateFormat; |
29 | |
import java.util.Calendar; |
30 | |
import java.util.Date; |
31 | |
import java.util.Locale; |
32 | |
import java.util.TimeZone; |
33 | |
|
34 | |
|
35 | |
|
36 | |
|
37 | |
|
38 | |
|
39 | |
|
40 | |
|
41 | |
|
42 | |
public class SimpleCookie implements Cookie { |
43 | |
|
44 | |
|
45 | |
|
46 | |
|
47 | |
public static final int DEFAULT_MAX_AGE = -1; |
48 | |
|
49 | |
|
50 | |
|
51 | |
|
52 | |
public static final int DEFAULT_VERSION = -1; |
53 | |
|
54 | |
|
55 | |
protected static final String NAME_VALUE_DELIMITER = "="; |
56 | |
protected static final String ATTRIBUTE_DELIMITER = "; "; |
57 | |
protected static final long DAY_MILLIS = 86400000; |
58 | |
protected static final String GMT_TIME_ZONE_ID = "GMT"; |
59 | |
protected static final String COOKIE_DATE_FORMAT_STRING = "EEE, dd-MMM-yyyy HH:mm:ss z"; |
60 | |
|
61 | |
protected static final String COOKIE_HEADER_NAME = "Set-Cookie"; |
62 | |
protected static final String PATH_ATTRIBUTE_NAME = "Path"; |
63 | |
protected static final String EXPIRES_ATTRIBUTE_NAME = "Expires"; |
64 | |
protected static final String MAXAGE_ATTRIBUTE_NAME = "Max-Age"; |
65 | |
protected static final String DOMAIN_ATTRIBUTE_NAME = "Domain"; |
66 | |
protected static final String VERSION_ATTRIBUTE_NAME = "Version"; |
67 | |
protected static final String COMMENT_ATTRIBUTE_NAME = "Comment"; |
68 | |
protected static final String SECURE_ATTRIBUTE_NAME = "Secure"; |
69 | |
protected static final String HTTP_ONLY_ATTRIBUTE_NAME = "HttpOnly"; |
70 | |
|
71 | 1 | private static final transient Logger log = LoggerFactory.getLogger(SimpleCookie.class); |
72 | |
|
73 | |
private String name; |
74 | |
private String value; |
75 | |
private String comment; |
76 | |
private String domain; |
77 | |
private String path; |
78 | |
private int maxAge; |
79 | |
private int version; |
80 | |
private boolean secure; |
81 | |
private boolean httpOnly; |
82 | |
|
83 | 41 | public SimpleCookie() { |
84 | 41 | this.maxAge = DEFAULT_MAX_AGE; |
85 | 41 | this.version = DEFAULT_VERSION; |
86 | 41 | this.httpOnly = true; |
87 | 41 | } |
88 | |
|
89 | |
public SimpleCookie(String name) { |
90 | 40 | this(); |
91 | 40 | this.name = name; |
92 | 40 | } |
93 | |
|
94 | 5 | public SimpleCookie(Cookie cookie) { |
95 | 5 | this.name = cookie.getName(); |
96 | 5 | this.value = cookie.getValue(); |
97 | 5 | this.comment = cookie.getComment(); |
98 | 5 | this.domain = cookie.getDomain(); |
99 | 5 | this.path = cookie.getPath(); |
100 | 5 | this.maxAge = Math.max(DEFAULT_MAX_AGE, cookie.getMaxAge()); |
101 | 5 | this.version = Math.max(DEFAULT_VERSION, cookie.getVersion()); |
102 | 5 | this.secure = cookie.isSecure(); |
103 | 5 | this.httpOnly = cookie.isHttpOnly(); |
104 | 5 | } |
105 | |
|
106 | |
public String getName() { |
107 | 31 | return name; |
108 | |
} |
109 | |
|
110 | |
public void setName(String name) { |
111 | 0 | if (!StringUtils.hasText(name)) { |
112 | 0 | throw new IllegalArgumentException("Name cannot be null/empty."); |
113 | |
} |
114 | 0 | this.name = name; |
115 | 0 | } |
116 | |
|
117 | |
public String getValue() { |
118 | 10 | return value; |
119 | |
} |
120 | |
|
121 | |
public void setValue(String value) { |
122 | 7 | this.value = value; |
123 | 7 | } |
124 | |
|
125 | |
public String getComment() { |
126 | 10 | return comment; |
127 | |
} |
128 | |
|
129 | |
public void setComment(String comment) { |
130 | 0 | this.comment = comment; |
131 | 0 | } |
132 | |
|
133 | |
public String getDomain() { |
134 | 14 | return domain; |
135 | |
} |
136 | |
|
137 | |
public void setDomain(String domain) { |
138 | 0 | this.domain = domain; |
139 | 0 | } |
140 | |
|
141 | |
public String getPath() { |
142 | 14 | return path; |
143 | |
} |
144 | |
|
145 | |
public void setPath(String path) { |
146 | 0 | this.path = path; |
147 | 0 | } |
148 | |
|
149 | |
public int getMaxAge() { |
150 | 10 | return maxAge; |
151 | |
} |
152 | |
|
153 | |
public void setMaxAge(int maxAge) { |
154 | 19 | this.maxAge = Math.max(DEFAULT_MAX_AGE, maxAge); |
155 | 19 | } |
156 | |
|
157 | |
public int getVersion() { |
158 | 14 | return version; |
159 | |
} |
160 | |
|
161 | |
public void setVersion(int version) { |
162 | 0 | this.version = Math.max(DEFAULT_VERSION, version); |
163 | 0 | } |
164 | |
|
165 | |
public boolean isSecure() { |
166 | 14 | return secure; |
167 | |
} |
168 | |
|
169 | |
public void setSecure(boolean secure) { |
170 | 0 | this.secure = secure; |
171 | 0 | } |
172 | |
|
173 | |
public boolean isHttpOnly() { |
174 | 10 | return httpOnly; |
175 | |
} |
176 | |
|
177 | |
public void setHttpOnly(boolean httpOnly) { |
178 | 37 | this.httpOnly = httpOnly; |
179 | 37 | } |
180 | |
|
181 | |
|
182 | |
|
183 | |
|
184 | |
|
185 | |
|
186 | |
|
187 | |
|
188 | |
|
189 | |
private String calculatePath(HttpServletRequest request) { |
190 | 11 | String path = StringUtils.clean(getPath()); |
191 | 11 | if (!StringUtils.hasText(path)) { |
192 | 10 | path = StringUtils.clean(request.getContextPath()); |
193 | |
} |
194 | |
|
195 | |
|
196 | 11 | if (path == null) { |
197 | 7 | path = ROOT_PATH; |
198 | |
} |
199 | 11 | log.trace("calculated path: {}", path); |
200 | 11 | return path; |
201 | |
} |
202 | |
|
203 | |
public void saveTo(HttpServletRequest request, HttpServletResponse response) { |
204 | |
|
205 | 7 | String name = getName(); |
206 | 7 | String value = getValue(); |
207 | 7 | String comment = getComment(); |
208 | 7 | String domain = getDomain(); |
209 | 7 | String path = calculatePath(request); |
210 | 7 | int maxAge = getMaxAge(); |
211 | 7 | int version = getVersion(); |
212 | 7 | boolean secure = isSecure(); |
213 | 7 | boolean httpOnly = isHttpOnly(); |
214 | |
|
215 | 7 | addCookieHeader(response, name, value, comment, domain, path, maxAge, version, secure, httpOnly); |
216 | 7 | } |
217 | |
|
218 | |
private void addCookieHeader(HttpServletResponse response, String name, String value, String comment, |
219 | |
String domain, String path, int maxAge, int version, |
220 | |
boolean secure, boolean httpOnly) { |
221 | |
|
222 | 11 | String headerValue = buildHeaderValue(name, value, comment, domain, path, maxAge, version, secure, httpOnly); |
223 | 11 | response.addHeader(COOKIE_HEADER_NAME, headerValue); |
224 | |
|
225 | 11 | if (log.isDebugEnabled()) { |
226 | 11 | log.debug("Added HttpServletResponse Cookie [{}]", headerValue); |
227 | |
} |
228 | 11 | } |
229 | |
|
230 | |
|
231 | |
|
232 | |
|
233 | |
|
234 | |
|
235 | |
|
236 | |
|
237 | |
protected String buildHeaderValue(String name, String value, String comment, |
238 | |
String domain, String path, int maxAge, int version, |
239 | |
boolean secure, boolean httpOnly) { |
240 | |
|
241 | 12 | if (!StringUtils.hasText(name)) { |
242 | 0 | throw new IllegalStateException("Cookie name cannot be null/empty."); |
243 | |
} |
244 | |
|
245 | 12 | StringBuilder sb = new StringBuilder(name).append(NAME_VALUE_DELIMITER); |
246 | |
|
247 | 12 | if (StringUtils.hasText(value)) { |
248 | 12 | sb.append(value); |
249 | |
} |
250 | |
|
251 | 12 | appendComment(sb, comment); |
252 | 12 | appendDomain(sb, domain); |
253 | 12 | appendPath(sb, path); |
254 | 12 | appendExpires(sb, maxAge); |
255 | 12 | appendVersion(sb, version); |
256 | 12 | appendSecure(sb, secure); |
257 | 12 | appendHttpOnly(sb, httpOnly); |
258 | |
|
259 | 12 | return sb.toString(); |
260 | |
|
261 | |
} |
262 | |
|
263 | |
private void appendComment(StringBuilder sb, String comment) { |
264 | 12 | if (StringUtils.hasText(comment)) { |
265 | 1 | sb.append(ATTRIBUTE_DELIMITER); |
266 | 1 | sb.append(COMMENT_ATTRIBUTE_NAME).append(NAME_VALUE_DELIMITER).append(comment); |
267 | |
} |
268 | 12 | } |
269 | |
|
270 | |
private void appendDomain(StringBuilder sb, String domain) { |
271 | 12 | if (StringUtils.hasText(domain)) { |
272 | 1 | sb.append(ATTRIBUTE_DELIMITER); |
273 | 1 | sb.append(DOMAIN_ATTRIBUTE_NAME).append(NAME_VALUE_DELIMITER).append(domain); |
274 | |
} |
275 | 12 | } |
276 | |
|
277 | |
private void appendPath(StringBuilder sb, String path) { |
278 | 12 | if (StringUtils.hasText(path)) { |
279 | 12 | sb.append(ATTRIBUTE_DELIMITER); |
280 | 12 | sb.append(PATH_ATTRIBUTE_NAME).append(NAME_VALUE_DELIMITER).append(path); |
281 | |
} |
282 | 12 | } |
283 | |
|
284 | |
private void appendExpires(StringBuilder sb, int maxAge) { |
285 | |
|
286 | |
|
287 | |
|
288 | |
|
289 | |
|
290 | |
|
291 | 12 | if (maxAge >= 0) { |
292 | 5 | sb.append(ATTRIBUTE_DELIMITER); |
293 | 5 | sb.append(MAXAGE_ATTRIBUTE_NAME).append(NAME_VALUE_DELIMITER).append(maxAge); |
294 | 5 | sb.append(ATTRIBUTE_DELIMITER); |
295 | |
Date expires; |
296 | 5 | if (maxAge == 0) { |
297 | |
|
298 | 5 | expires = new Date(System.currentTimeMillis() - DAY_MILLIS); |
299 | |
} else { |
300 | |
|
301 | 0 | Calendar cal = Calendar.getInstance(); |
302 | 0 | cal.add(Calendar.SECOND, maxAge); |
303 | 0 | expires = cal.getTime(); |
304 | |
} |
305 | 5 | String formatted = toCookieDate(expires); |
306 | 5 | sb.append(EXPIRES_ATTRIBUTE_NAME).append(NAME_VALUE_DELIMITER).append(formatted); |
307 | |
} |
308 | 12 | } |
309 | |
|
310 | |
private void appendVersion(StringBuilder sb, int version) { |
311 | 12 | if (version > DEFAULT_VERSION) { |
312 | 0 | sb.append(ATTRIBUTE_DELIMITER); |
313 | 0 | sb.append(VERSION_ATTRIBUTE_NAME).append(NAME_VALUE_DELIMITER).append(version); |
314 | |
} |
315 | 12 | } |
316 | |
|
317 | |
private void appendSecure(StringBuilder sb, boolean secure) { |
318 | 12 | if (secure) { |
319 | 1 | sb.append(ATTRIBUTE_DELIMITER); |
320 | 1 | sb.append(SECURE_ATTRIBUTE_NAME); |
321 | |
} |
322 | 12 | } |
323 | |
|
324 | |
private void appendHttpOnly(StringBuilder sb, boolean httpOnly) { |
325 | 12 | if (httpOnly) { |
326 | 7 | sb.append(ATTRIBUTE_DELIMITER); |
327 | 7 | sb.append(HTTP_ONLY_ATTRIBUTE_NAME); |
328 | |
} |
329 | 12 | } |
330 | |
|
331 | |
|
332 | |
|
333 | |
|
334 | |
|
335 | |
|
336 | |
|
337 | |
private static String toCookieDate(Date date) { |
338 | 5 | TimeZone tz = TimeZone.getTimeZone(GMT_TIME_ZONE_ID); |
339 | 5 | DateFormat fmt = new SimpleDateFormat(COOKIE_DATE_FORMAT_STRING, Locale.US); |
340 | 5 | fmt.setTimeZone(tz); |
341 | 5 | return fmt.format(date); |
342 | |
} |
343 | |
|
344 | |
public void removeFrom(HttpServletRequest request, HttpServletResponse response) { |
345 | 4 | String name = getName(); |
346 | 4 | String value = DELETED_COOKIE_VALUE; |
347 | 4 | String comment = null; |
348 | 4 | String domain = getDomain(); |
349 | 4 | String path = calculatePath(request); |
350 | 4 | int maxAge = 0; |
351 | 4 | int version = getVersion(); |
352 | 4 | boolean secure = isSecure(); |
353 | 4 | boolean httpOnly = false; |
354 | |
|
355 | 4 | addCookieHeader(response, name, value, comment, domain, path, maxAge, version, secure, httpOnly); |
356 | |
|
357 | 4 | log.trace("Removed '{}' cookie by setting maxAge=0", name); |
358 | 4 | } |
359 | |
|
360 | |
public String readValue(HttpServletRequest request, HttpServletResponse ignored) { |
361 | 13 | String name = getName(); |
362 | 13 | String value = null; |
363 | 13 | javax.servlet.http.Cookie cookie = getCookie(request, name); |
364 | 13 | if (cookie != null) { |
365 | 4 | value = cookie.getValue(); |
366 | 4 | log.debug("Found '{}' cookie value [{}]", name, value); |
367 | |
} else { |
368 | 9 | log.trace("No '{}' cookie value", name); |
369 | |
} |
370 | |
|
371 | 13 | return value; |
372 | |
} |
373 | |
|
374 | |
|
375 | |
|
376 | |
|
377 | |
|
378 | |
|
379 | |
|
380 | |
|
381 | |
|
382 | |
|
383 | |
private static javax.servlet.http.Cookie getCookie(HttpServletRequest request, String cookieName) { |
384 | 13 | javax.servlet.http.Cookie cookies[] = request.getCookies(); |
385 | 13 | if (cookies != null) { |
386 | 6 | for (javax.servlet.http.Cookie cookie : cookies) { |
387 | 5 | if (cookie.getName().equals(cookieName)) { |
388 | 4 | return cookie; |
389 | |
} |
390 | |
} |
391 | |
} |
392 | 9 | return null; |
393 | |
} |
394 | |
} |