(empty log message)
@@ -1,60 +0,0 @@ | ||
1 | -package jp.sf.amateras.sessioncookiefilter; | |
2 | - | |
3 | -import java.util.HashMap; | |
4 | -import java.util.Map; | |
5 | - | |
6 | -import jp.sf.amateras.sessioncookiefilter.chiper.Chiper; | |
7 | -import net.arnx.jsonic.JSON; | |
8 | - | |
9 | -/** | |
10 | - * | |
11 | - * @author Naoki Takezoe | |
12 | - */ | |
13 | -public class CookieSessionUtil { | |
14 | - | |
15 | - public static String encode(Chiper chiper, Map<String, Object> attributes){ | |
16 | - Map<String, Map<String, String>> map = new HashMap<String, Map<String,String>>(); | |
17 | - | |
18 | - for(Map.Entry<String, Object> entry: attributes.entrySet()){ | |
19 | - String name = entry.getKey(); | |
20 | - Object value = entry.getValue(); | |
21 | - | |
22 | - Map<String, String> info = new HashMap<String, String>(); | |
23 | - info.put("type", value.getClass().getName()); | |
24 | - info.put("json", JSON.encode(value).replaceAll("\"", "\\\\\"")); | |
25 | - | |
26 | - map.put(name, info); | |
27 | - } | |
28 | - | |
29 | - String json = JSON.encode(map); | |
30 | - String encodedValue = chiper.encode(json); | |
31 | - | |
32 | - return encodedValue; | |
33 | - } | |
34 | - | |
35 | - public static Map<String, Object> decode(Chiper chiper, String cookie){ | |
36 | - String decodedValue = chiper.decode(cookie); | |
37 | - | |
38 | - @SuppressWarnings("unchecked") | |
39 | - Map<String, Map<String, String>> map = (Map<String, Map<String, String>>) JSON.decode(decodedValue); | |
40 | - Map<String, Object> attributes = new HashMap<String, Object>(); | |
41 | - for(Map.Entry<String, Map<String, String>> entry: map.entrySet()){ | |
42 | - String name = entry.getKey(); | |
43 | - Map<String, String> info = entry.getValue(); | |
44 | - String type = info.get("type"); | |
45 | - String json = info.get("json"); | |
46 | - | |
47 | - try { | |
48 | - Object value = JSON.decode(json, Class.forName(type)); | |
49 | - attributes.put(name, value); | |
50 | - | |
51 | - } catch(ClassNotFoundException ex){ | |
52 | - // TODO 適切な例外を定義する | |
53 | - throw new RuntimeException(ex); | |
54 | - } | |
55 | - } | |
56 | - | |
57 | - return attributes; | |
58 | - } | |
59 | - | |
60 | -} |
@@ -2,13 +2,14 @@ | ||
2 | 2 | |
3 | 3 | import java.util.HashMap; |
4 | 4 | |
5 | +import javax.servlet.ServletContext; | |
5 | 6 | import javax.servlet.http.Cookie; |
6 | 7 | import javax.servlet.http.HttpServletRequest; |
7 | 8 | import javax.servlet.http.HttpServletRequestWrapper; |
8 | 9 | import javax.servlet.http.HttpSession; |
9 | 10 | |
10 | -import jp.sf.amateras.sessioncookiefilter.CookieSessionUtil; | |
11 | 11 | import jp.sf.amateras.sessioncookiefilter.chiper.Chiper; |
12 | +import jp.sf.amateras.sessioncookiefilter.util.CookieSessionUtil; | |
12 | 13 | |
13 | 14 | public class CookieSessionRequest extends HttpServletRequestWrapper { |
14 | 15 |
@@ -18,10 +19,14 @@ | ||
18 | 19 | |
19 | 20 | private Chiper chiper; |
20 | 21 | |
21 | - public CookieSessionRequest(HttpServletRequest request, String cookieName, Chiper chiper) { | |
22 | + private ServletContext context; | |
23 | + | |
24 | + public CookieSessionRequest(HttpServletRequest request, | |
25 | + String cookieName, Chiper chiper, ServletContext context) { | |
22 | 26 | super(request); |
23 | 27 | this.cookieName = cookieName; |
24 | 28 | this.chiper = chiper; |
29 | + this.context = context; | |
25 | 30 | } |
26 | 31 | |
27 | 32 | @Override |
@@ -45,7 +50,7 @@ | ||
45 | 50 | if(create == false){ |
46 | 51 | return null; |
47 | 52 | } else { |
48 | - session = new CookieSession(null, new HashMap<String, Object>(), true); | |
53 | + session = new CookieSession(context, new HashMap<String, Object>(), true); | |
49 | 54 | return session; |
50 | 55 | } |
51 | 56 | } |
@@ -5,6 +5,7 @@ | ||
5 | 5 | import javax.servlet.Filter; |
6 | 6 | import javax.servlet.FilterChain; |
7 | 7 | import javax.servlet.FilterConfig; |
8 | +import javax.servlet.ServletContext; | |
8 | 9 | import javax.servlet.ServletException; |
9 | 10 | import javax.servlet.ServletRequest; |
10 | 11 | import javax.servlet.ServletResponse; |
@@ -14,6 +15,9 @@ | ||
14 | 15 | |
15 | 16 | import jp.sf.amateras.sessioncookiefilter.chiper.Chiper; |
16 | 17 | import jp.sf.amateras.sessioncookiefilter.chiper.RawChiper; |
18 | +import jp.sf.amateras.sessioncookiefilter.exception.InitializationException; | |
19 | +import jp.sf.amateras.sessioncookiefilter.util.CookieSessionUtil; | |
20 | +import jp.sf.amateras.sessioncookiefilter.util.StringUtil; | |
17 | 21 | import jp.sf.amateras.sessioncookiefilter.wrapper.CookieSession; |
18 | 22 | import jp.sf.amateras.sessioncookiefilter.wrapper.CookieSessionRequest; |
19 | 23 |
@@ -23,15 +27,66 @@ | ||
23 | 27 | */ |
24 | 28 | public class CookieSessionFilter implements Filter { |
25 | 29 | |
30 | + private static final String CONFIG_COOKIE_NAME = "cookieName"; | |
31 | + | |
32 | + private static final String CONFIG_CHIPER = "chiper"; | |
33 | + | |
34 | + private static final String CONFIG_MAX_COOKIE = "maxCookie"; | |
35 | + | |
36 | + private static final String CONFIG_COOKIE_SIZE = "cookieSize"; | |
37 | + | |
26 | 38 | private String cookieName = "session-cookie"; |
27 | 39 | |
28 | 40 | private Chiper chiper = new RawChiper(); |
29 | 41 | |
42 | + private int maxCookie = 5; | |
43 | + | |
44 | + private int cookieSize = 4096; | |
45 | + | |
46 | + private ServletContext context; | |
47 | + | |
30 | 48 | public void init(FilterConfig filterConfig) throws ServletException { |
31 | - String cookieName = filterConfig.getInitParameter("cookieName"); | |
32 | - if(cookieName != null && cookieName.trim().length() != 0){ | |
49 | + this.context = filterConfig.getServletContext(); | |
50 | + | |
51 | + // cookieName | |
52 | + String cookieName = filterConfig.getInitParameter(CONFIG_COOKIE_NAME); | |
53 | + if(StringUtil.isNotEmpty(cookieName)){ | |
33 | 54 | this.cookieName = cookieName; |
34 | 55 | } |
56 | + | |
57 | + // chiper | |
58 | + String chiperClassName = filterConfig.getInitParameter(CONFIG_CHIPER); | |
59 | + if(StringUtil.isEmpty(chiperClassName)){ | |
60 | + throw new InitializationException("chiper has not been specified."); | |
61 | + } | |
62 | + try { | |
63 | + @SuppressWarnings("unchecked") | |
64 | + Class<Chiper> clazz = (Class<Chiper>) Class.forName(chiperClassName); | |
65 | + chiper = clazz.newInstance(); | |
66 | + } catch(Exception ex){ | |
67 | + throw new InitializationException(ex); | |
68 | + } | |
69 | + chiper.init(filterConfig); | |
70 | + | |
71 | + // maxCookie | |
72 | + String maxCookie = filterConfig.getInitParameter(CONFIG_MAX_COOKIE); | |
73 | + if(StringUtil.isNotEmpty(maxCookie)){ | |
74 | + try { | |
75 | + this.maxCookie = Integer.parseInt(maxCookie.trim()); | |
76 | + } catch(Exception ex){ | |
77 | + throw new InitializationException("maxCookie has not been a number."); | |
78 | + } | |
79 | + } | |
80 | + | |
81 | + // cookieSize | |
82 | + String cookieSize = filterConfig.getInitParameter(CONFIG_COOKIE_SIZE); | |
83 | + if(StringUtil.isNotEmpty(cookieSize)){ | |
84 | + try { | |
85 | + this.cookieSize = Integer.parseInt(cookieSize.trim()); | |
86 | + } catch(Exception ex){ | |
87 | + throw new InitializationException("cookieSize has not been a number."); | |
88 | + } | |
89 | + } | |
35 | 90 | } |
36 | 91 | |
37 | 92 | public void doFilter(ServletRequest request, ServletResponse response, |
@@ -38,7 +93,7 @@ | ||
38 | 93 | FilterChain chain) throws IOException, ServletException { |
39 | 94 | |
40 | 95 | CookieSessionRequest requestWrapper = new CookieSessionRequest( |
41 | - (HttpServletRequest) request, cookieName, chiper); | |
96 | + (HttpServletRequest) request, cookieName, chiper, context); | |
42 | 97 | |
43 | 98 | try { |
44 | 99 | chain.doFilter(requestWrapper, response); |
@@ -1,5 +1,7 @@ | ||
1 | 1 | package jp.sf.amateras.sessioncookiefilter.chiper; |
2 | 2 | |
3 | +import javax.servlet.FilterConfig; | |
4 | + | |
3 | 5 | /** |
4 | 6 | * This chiper does not encode a given value. |
5 | 7 | * <p> |
@@ -9,12 +11,16 @@ | ||
9 | 11 | */ |
10 | 12 | public class RawChiper implements Chiper { |
11 | 13 | |
12 | - public String encode(String value) { | |
14 | + public void init(FilterConfig config) { | |
15 | + } | |
16 | + | |
17 | + public String encrypt(String value) { | |
13 | 18 | return value; |
14 | 19 | } |
15 | 20 | |
16 | - public String decode(String value) { | |
21 | + public String decrypt(String value) { | |
17 | 22 | return value; |
18 | 23 | } |
19 | 24 | |
25 | + | |
20 | 26 | } |
@@ -0,0 +1,57 @@ | ||
1 | +package jp.sf.amateras.sessioncookiefilter.chiper; | |
2 | + | |
3 | +import javax.crypto.Cipher; | |
4 | +import javax.crypto.spec.SecretKeySpec; | |
5 | +import javax.servlet.FilterConfig; | |
6 | + | |
7 | +import jp.sf.amateras.sessioncookiefilter.exception.ChiperException; | |
8 | +import jp.sf.amateras.sessioncookiefilter.exception.InitializationException; | |
9 | +import jp.sf.amateras.sessioncookiefilter.util.StringUtil; | |
10 | + | |
11 | +import org.apache.commons.codec.binary.Hex; | |
12 | + | |
13 | +public class BlowfishChiper implements Chiper { | |
14 | + | |
15 | + private static final String CONFIG_KEY = "key"; | |
16 | + | |
17 | + private String key; | |
18 | + | |
19 | + public void init(FilterConfig config){ | |
20 | + String key = config.getInitParameter(CONFIG_KEY); | |
21 | + if(StringUtil.isEmpty(key)){ | |
22 | + throw new InitializationException("Blowfish key has not been specified."); | |
23 | + } | |
24 | + this.key = key; | |
25 | + } | |
26 | + | |
27 | + public String encrypt(String value) { | |
28 | + try { | |
29 | + SecretKeySpec sksSpec = new SecretKeySpec(key.getBytes(), "Blowfish"); | |
30 | + Cipher cipher = Cipher.getInstance("Blowfish"); | |
31 | + cipher.init(Cipher.ENCRYPT_MODE, sksSpec); | |
32 | + byte[] encrypted = cipher.doFinal(value.getBytes()); | |
33 | + | |
34 | + return new String(Hex.encodeHex(encrypted)); | |
35 | + | |
36 | + } catch(Exception ex){ | |
37 | + throw new ChiperException(ex); | |
38 | + } | |
39 | + } | |
40 | + | |
41 | + public String decrypt(String value) { | |
42 | + try { | |
43 | + byte[] encrypted = Hex.decodeHex(value.toCharArray()); | |
44 | + | |
45 | + SecretKeySpec sksSpec = new SecretKeySpec(key.getBytes(), "Blowfish"); | |
46 | + Cipher cipher = Cipher.getInstance("Blowfish"); | |
47 | + cipher.init(Cipher.DECRYPT_MODE, sksSpec); | |
48 | + byte[] decrypted = cipher.doFinal(encrypted); | |
49 | + | |
50 | + return new String(decrypted); | |
51 | + | |
52 | + } catch(Exception ex){ | |
53 | + throw new ChiperException(ex); | |
54 | + } | |
55 | + } | |
56 | + | |
57 | +} |
@@ -1,5 +1,11 @@ | ||
1 | 1 | package jp.sf.amateras.sessioncookiefilter.chiper; |
2 | 2 | |
3 | +import javax.servlet.FilterConfig; | |
4 | + | |
5 | +import jp.sf.amateras.sessioncookiefilter.CookieSessionFilter; | |
6 | +import jp.sf.amateras.sessioncookiefilter.exception.ChiperException; | |
7 | +import jp.sf.amateras.sessioncookiefilter.exception.InitializationException; | |
8 | + | |
3 | 9 | /** |
4 | 10 | * An interface for chiper which encodes and decodes session cookie values. |
5 | 11 | * |
@@ -8,12 +14,21 @@ | ||
8 | 14 | public interface Chiper { |
9 | 15 | |
10 | 16 | /** |
17 | + * Initializes this chiper. | |
18 | + * | |
19 | + * @param config the FilterConfig of {@link CookieSessionFilter} | |
20 | + * @throws InitializationException when initialization failed. | |
21 | + */ | |
22 | + public void init(FilterConfig config) throws InitializationException; | |
23 | + | |
24 | + /** | |
11 | 25 | * Encodes the session cookie value. |
12 | 26 | * |
13 | 27 | * @param value the session cookie value |
14 | 28 | * @return the encoded string |
29 | + * @throws ChiperException when encryption failed. | |
15 | 30 | */ |
16 | - public String encode(String value); | |
31 | + public String encrypt(String value) throws ChiperException; | |
17 | 32 | |
18 | 33 | /** |
19 | 34 | * Decodes the session cookie value. |
@@ -20,7 +35,8 @@ | ||
20 | 35 | * |
21 | 36 | * @param value the encoded string |
22 | 37 | * @return the decoded string |
38 | + * @exception ChiperException when decryption failed. | |
23 | 39 | */ |
24 | - public String decode(String value); | |
40 | + public String decrypt(String value) throws ChiperException; | |
25 | 41 | |
26 | 42 | } |
@@ -0,0 +1,13 @@ | ||
1 | +package jp.sf.amateras.sessioncookiefilter.util; | |
2 | + | |
3 | +public class StringUtil { | |
4 | + | |
5 | + public static boolean isEmpty(String value){ | |
6 | + return value == null || value.trim().length() == 0; | |
7 | + } | |
8 | + | |
9 | + public static boolean isNotEmpty(String value){ | |
10 | + return !isEmpty(value); | |
11 | + } | |
12 | + | |
13 | +} |
@@ -0,0 +1,60 @@ | ||
1 | +package jp.sf.amateras.sessioncookiefilter.util; | |
2 | + | |
3 | +import java.util.HashMap; | |
4 | +import java.util.Map; | |
5 | + | |
6 | +import jp.sf.amateras.sessioncookiefilter.chiper.Chiper; | |
7 | +import net.arnx.jsonic.JSON; | |
8 | + | |
9 | +/** | |
10 | + * | |
11 | + * @author Naoki Takezoe | |
12 | + */ | |
13 | +public class CookieSessionUtil { | |
14 | + | |
15 | + public static String encode(Chiper chiper, Map<String, Object> attributes){ | |
16 | + Map<String, Map<String, String>> map = new HashMap<String, Map<String,String>>(); | |
17 | + | |
18 | + for(Map.Entry<String, Object> entry: attributes.entrySet()){ | |
19 | + String name = entry.getKey(); | |
20 | + Object value = entry.getValue(); | |
21 | + | |
22 | + Map<String, String> info = new HashMap<String, String>(); | |
23 | + info.put("type", value.getClass().getName()); | |
24 | + info.put("json", JSON.encode(value).replaceAll("\"", "\\\\\"")); | |
25 | + | |
26 | + map.put(name, info); | |
27 | + } | |
28 | + | |
29 | + String json = JSON.encode(map); | |
30 | + String encodedValue = chiper.encrypt(json); | |
31 | + | |
32 | + return encodedValue; | |
33 | + } | |
34 | + | |
35 | + public static Map<String, Object> decode(Chiper chiper, String cookie){ | |
36 | + String decodedValue = chiper.decrypt(cookie); | |
37 | + | |
38 | + @SuppressWarnings("unchecked") | |
39 | + Map<String, Map<String, String>> map = (Map<String, Map<String, String>>) JSON.decode(decodedValue); | |
40 | + Map<String, Object> attributes = new HashMap<String, Object>(); | |
41 | + for(Map.Entry<String, Map<String, String>> entry: map.entrySet()){ | |
42 | + String name = entry.getKey(); | |
43 | + Map<String, String> info = entry.getValue(); | |
44 | + String type = info.get("type"); | |
45 | + String json = info.get("json"); | |
46 | + | |
47 | + try { | |
48 | + Object value = JSON.decode(json, Class.forName(type)); | |
49 | + attributes.put(name, value); | |
50 | + | |
51 | + } catch(ClassNotFoundException ex){ | |
52 | + // TODO 適切な例外を定義する | |
53 | + throw new RuntimeException(ex); | |
54 | + } | |
55 | + } | |
56 | + | |
57 | + return attributes; | |
58 | + } | |
59 | + | |
60 | +} |
@@ -0,0 +1,19 @@ | ||
1 | +package jp.sf.amateras.sessioncookiefilter.exception; | |
2 | + | |
3 | +public class InitializationException extends RuntimeException { | |
4 | + | |
5 | + private static final long serialVersionUID = 1L; | |
6 | + | |
7 | + public InitializationException(String message, Throwable cause) { | |
8 | + super(message, cause); | |
9 | + } | |
10 | + | |
11 | + public InitializationException(String message) { | |
12 | + super(message); | |
13 | + } | |
14 | + | |
15 | + public InitializationException(Throwable cause) { | |
16 | + super(cause); | |
17 | + } | |
18 | + | |
19 | +} |
@@ -0,0 +1,19 @@ | ||
1 | +package jp.sf.amateras.sessioncookiefilter.exception; | |
2 | + | |
3 | +public class ChiperException extends RuntimeException { | |
4 | + | |
5 | + private static final long serialVersionUID = 1L; | |
6 | + | |
7 | + public ChiperException(String message, Throwable cause) { | |
8 | + super(message, cause); | |
9 | + } | |
10 | + | |
11 | + public ChiperException(String message) { | |
12 | + super(message); | |
13 | + } | |
14 | + | |
15 | + public ChiperException(Throwable cause) { | |
16 | + super(cause); | |
17 | + } | |
18 | + | |
19 | +} |