├── .gitignore
├── README.md
├── pom.xml
└── src
└── main
└── java
└── com
└── ctlok
└── web
└── session
├── StatelessSession.java
├── StatelessSessionConfig.java
├── StatelessSessionFilter.java
└── crypto
├── AesEncryptor.java
├── CryptoUtils.java
└── Encryptor.java
/.gitignore:
--------------------------------------------------------------------------------
1 | #*
2 | *$
3 | *.BAK
4 | *.Z
5 | *.bak
6 | *.class
7 | *.elc
8 | *.ln
9 | *.log
10 | *.o
11 | *.obj
12 | *.olb
13 | *.old
14 | *.orig
15 | *.pyc
16 | *.pyo
17 | *.rej
18 | *~
19 | ,*
20 | .#*
21 | .DS_Store
22 | .del-*
23 | .deployables
24 | .make.state
25 | .nse_depinfo
26 | .svn
27 | CVS.adm
28 | RCS
29 | RCSLOG
30 | SCCS
31 | _$*
32 | _svn
33 | target
34 | .settings
35 | .classpath
36 | .project
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## HTTP Stateless / Cookie-based Session for Java
2 |
3 | HTTP Stateless Session help you to build stateless web application base on Java.
4 | Stateless Session compliable with `HttpSession`.
5 |
6 | What are the benefits of a stateless web application?
7 |
8 | 1. Reduces memory usage.
9 | 2. Easier to support server farms.
10 | 3. Reduce session expiration problems.
11 |
12 | Reference: [http://stackoverflow.com/questions/5539823/what-are-the-benefits-of-a-stateless-web-application] (http://stackoverflow.com/questions/5539823/what-are-the-benefits-of-a-stateless-web-application)
13 |
14 | ### Limitation
15 |
16 | 1. Data total size cannot over 4KB, because all session data is storded in cookie.
17 | 2. Data type must be String.
18 |
19 | ## Basic Usage
20 |
21 | ### Dependency:
22 |
23 | * commons-codec 1.7 or above
24 | * gson 2.2.2 or above
25 |
26 | ### Maven
27 |
28 | ```
29 |
30 | com.ctlok
31 | stateless-http-session
32 | 1.2.4
33 |
34 | ```
35 |
36 | ### Basic Web.xml Config
37 |
38 | ```
39 |
40 | statelessSessionFilter
41 | com.ctlok.web.session.StatelessSessionFilter
42 |
43 | HMAC_SHA1_KEY
44 | aDg3uE6t8X57bnFwcqRql8tvd
45 |
46 |
47 |
48 |
49 | statelessSessionFilter
50 | /*
51 |
52 | ```
53 |
54 | `HMAC_SHA1_KEY` is a mandatory field for check session data is it modified.
55 | If session data was modified by client, all session data will destroy and create a new session.
56 |
57 | ### Other Config
58 |
59 | 1. `ENCRYPTION_SECRET_KEY` is a secret key to encrypt session data. By default, session data is not encrypted.
60 | 2. `ENCRYPTION_IMPL_CLASS` is a class name implemented `com.ctlok.web.session.crypto.Encryptor`. Default: `com.ctlok.web.session.crypto.AesEncryptor`.
61 | 3. `SESSION_NAME` is a session cookie name. Default: `SESSION`.
62 | 4. `SESSION_MAX_AGE` is a session cookie max age. Default: `-1` expire when browser closed.
63 | 5. `SESSION_PATH` is a session cookie path on current domain. Default: `/`.
64 | 6. `SESSION_DOMAIN` is a session cookie domain. Default is null.
65 |
66 | ### Java Code Example
67 |
68 | ```
69 | HttpSession session = request.getSession(true);
70 | session.setAttribute("user", "lawrence");
71 | session.getAttribute("user");
72 | ```
73 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 | com.ctlok
4 | stateless-http-session
5 | 1.2.5-SNAPSHOT
6 | jar
7 | Stateless HTTP Session
8 | HTTP Stateless Session for Java
9 | https://github.com/lawrence0819/spring-webmvc-rythm
10 |
11 |
12 |
13 | The Apache Software License, Version 2.0
14 | http://www.apache.org/licenses/LICENSE-2.0.txt
15 | repo
16 | A business-friendly OSS license
17 |
18 |
19 |
20 |
21 | https://github.com/lawrence0819/java-stateless-http-session
22 | scm:git:git://github.com/lawrence0819/java-stateless-http-session.git
23 | scm:git:git@github.com:lawrence0819/java-stateless-http-session.git
24 | HEAD
25 |
26 |
27 |
28 |
29 | lawrence
30 | Lawrence Cheung
31 | lawrence0819@gmail.com
32 | +8
33 |
34 |
35 |
36 |
37 |
38 | sonatype-nexus-snapshots
39 | Sonatype Nexus snapshot repository
40 | https://oss.sonatype.org/content/repositories/snapshots
41 |
42 |
43 | sonatype-nexus-staging
44 | Sonatype Nexus release repository
45 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
46 |
47 |
48 |
49 |
50 | UTF-8
51 | 1.6
52 |
53 | 3.0.1
54 | 1.7
55 | 2.2.2
56 |
57 | 2.3.2
58 | 2.4.1
59 | 1.4
60 |
61 |
62 |
63 |
64 | release-sign-artifacts
65 |
66 |
67 | performRelease
68 | true
69 |
70 |
71 |
72 |
73 |
74 | org.apache.maven.plugins
75 | maven-gpg-plugin
76 | ${plugin.gpg.version}
77 |
78 | ${gpg.passphrase}
79 |
80 |
81 |
82 | sign-artifacts
83 | verify
84 |
85 | sign
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | javax.servlet
98 | javax.servlet-api
99 | ${dependency.servlet-api.version}
100 | provided
101 |
102 |
103 |
104 | commons-codec
105 | commons-codec
106 | ${dependency.commons-codec.version}
107 |
108 |
109 |
110 | com.google.code.gson
111 | gson
112 | ${dependency.gson.version}
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 | org.apache.maven.plugins
121 | maven-compiler-plugin
122 | ${plugin.maven-compiler.version}
123 |
124 | ${java.version}
125 | ${java.version}
126 | ${project.build.sourceEncoding}
127 |
128 |
129 |
130 |
131 | org.apache.maven.plugins
132 | maven-release-plugin
133 | ${plugin.release.version}
134 |
135 | -Dgpg.passphrase=${gpg.passphrase}
136 |
137 |
138 |
139 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/src/main/java/com/ctlok/web/session/StatelessSession.java:
--------------------------------------------------------------------------------
1 | package com.ctlok.web.session;
2 |
3 | import java.math.BigInteger;
4 | import java.security.InvalidKeyException;
5 | import java.util.Collections;
6 | import java.util.Enumeration;
7 | import java.util.Map;
8 | import java.util.TreeMap;
9 | import java.util.UUID;
10 |
11 | import javax.servlet.ServletContext;
12 | import javax.servlet.http.Cookie;
13 | import javax.servlet.http.HttpSession;
14 | import javax.servlet.http.HttpSessionContext;
15 |
16 | import com.ctlok.web.session.crypto.CryptoUtils;
17 | import com.google.gson.Gson;
18 |
19 | public class StatelessSession implements HttpSession {
20 |
21 | private static final String CHECKSUM_KEY = "__s";
22 | private static final String ID_KEY = "__id";
23 | private static final String CREATION_TIME_KEY = "__ct";
24 |
25 | private final Map attributes = new TreeMap();
26 | private final Gson gson = new Gson();
27 |
28 | private final StatelessSessionConfig config;
29 | private boolean newSession;
30 |
31 | private String sessionId;
32 | private long creationTime;
33 |
34 | public StatelessSession(final StatelessSessionConfig config){
35 |
36 | this.config = config;
37 |
38 | try{
39 |
40 | final Cookie sessionCookie = findSessionCookie();
41 | if (sessionCookie == null){
42 | this.initNewSession();
43 | }else{
44 | String cookieValue = sessionCookie.getValue();
45 |
46 | if (this.config.getSecretKey() != null){
47 |
48 | cookieValue = this.config.getEncryptor().decrypt(
49 | this.config.getSecretKey(), cookieValue);
50 |
51 | }
52 |
53 | if (this.isValidSessionCookieValue(cookieValue)){
54 | this.attributes.putAll(this.jsonToMap(cookieValue));
55 | this.sessionId = this.attributes.get(ID_KEY);
56 | this.creationTime = Long.valueOf(this.attributes.get(CREATION_TIME_KEY));
57 |
58 | this.attributes.remove(CHECKSUM_KEY);
59 | this.attributes.remove(ID_KEY);
60 | this.attributes.remove(CREATION_TIME_KEY);
61 | }else{
62 | this.initNewSession();
63 | }
64 | }
65 |
66 | } catch (Exception e){
67 | e.printStackTrace();
68 | this.initNewSession();
69 | }
70 | }
71 |
72 | private void initNewSession(){
73 | this.attributes.clear();
74 | this.sessionId = this.generateSessionId();
75 | this.creationTime = System.currentTimeMillis();
76 | this.newSession = true;
77 | }
78 |
79 | protected Cookie findSessionCookie(){
80 | Cookie sessionCookie = null;
81 |
82 | if (this.config.getRequest().getCookies() != null){
83 |
84 | for (final Cookie cookie: this.config.getRequest().getCookies()){
85 | if (this.config.getSessionName().equals(cookie.getName())){
86 | sessionCookie = cookie;
87 | }
88 | }
89 |
90 | }
91 |
92 | return sessionCookie;
93 | }
94 |
95 | protected boolean isValidSessionCookieValue(final String cookieValue){
96 | final Map map = jsonToMap(cookieValue);
97 |
98 | if (map.containsKey(CHECKSUM_KEY)
99 | && map.containsKey(ID_KEY)
100 | && map.containsKey(CREATION_TIME_KEY)){
101 |
102 | final String checksum = map.get(CHECKSUM_KEY);
103 | map.remove(CHECKSUM_KEY);
104 |
105 | return checksum.equals(this.mapChecksum(map));
106 |
107 | }
108 |
109 | return false;
110 | }
111 |
112 | protected String mapToJson(final Map map){
113 | return gson.toJson(map);
114 | }
115 |
116 | @SuppressWarnings("unchecked")
117 | protected Map jsonToMap(final String json){
118 | return gson.fromJson(json, Map.class);
119 | }
120 |
121 | protected String mapChecksum(Map map){
122 | try {
123 | return CryptoUtils.hmacSha1(this.config.getHmacSHA1Key(), this.mapToJson(map));
124 | } catch (InvalidKeyException e) {
125 | throw new IllegalStateException(e);
126 | }
127 | }
128 |
129 | protected String generateSessionId(){
130 | final String uuid = UUID.randomUUID().toString();
131 | return new BigInteger(uuid.replaceAll("-", ""), 16).toString(32);
132 | }
133 |
134 | protected Cookie createCookie() {
135 | try{
136 | final Map map = new TreeMap();
137 | map.putAll(this.attributes);
138 | map.put(ID_KEY, sessionId);
139 | map.put(CREATION_TIME_KEY, Long.toString(creationTime));
140 |
141 | final String checksum = mapChecksum(map);
142 | map.put(CHECKSUM_KEY, checksum);
143 |
144 | final String json = mapToJson(map);
145 | final String cookieValue = this.config.getSecretKey() == null ? json :
146 | this.config.getEncryptor().encrypt(this.config.getSecretKey(), json);
147 |
148 | final Cookie cookie = new Cookie(this.config.getSessionName(), cookieValue);
149 |
150 | cookie.setMaxAge(this.config.getSessionMaxAge());
151 | cookie.setPath(this.config.getPath());
152 | cookie.setHttpOnly(this.config.isHttpOnly());
153 |
154 | if (this.config.getDomain() != null){
155 | cookie.setDomain(this.config.getDomain());
156 | }
157 |
158 | return cookie;
159 | } catch (final Exception e){
160 | throw new IllegalStateException(e);
161 | }
162 | }
163 |
164 | public void flush(){
165 | this.config.getResponse().addCookie(this.createCookie());
166 | }
167 |
168 | @Override
169 | public long getCreationTime() {
170 | return this.creationTime;
171 | }
172 |
173 | @Override
174 | public String getId() {
175 | return this.sessionId;
176 | }
177 |
178 | @Override
179 | public long getLastAccessedTime() {
180 | return 0;
181 | }
182 |
183 | @Override
184 | public ServletContext getServletContext() {
185 | return this.config.getServletContext();
186 | }
187 |
188 | @Override
189 | public void setMaxInactiveInterval(int interval) {
190 |
191 | }
192 |
193 | @Override
194 | public int getMaxInactiveInterval() {
195 | return 0;
196 | }
197 |
198 | @Override
199 | public HttpSessionContext getSessionContext() {
200 | return null;
201 | }
202 |
203 | @Override
204 | public Object getAttribute(String name) {
205 | return this.attributes.get(name);
206 | }
207 |
208 | @Override
209 | public Object getValue(String name) {
210 | return this.getAttribute(name);
211 | }
212 |
213 | @Override
214 | public Enumeration getAttributeNames() {
215 | return Collections.enumeration(this.attributes.keySet());
216 | }
217 |
218 | @Override
219 | public String[] getValueNames() {
220 | return this.attributes.keySet().toArray(new String[0]);
221 | }
222 |
223 | @Override
224 | public void setAttribute(String name, Object value) {
225 | if (value instanceof String){
226 | this.attributes.put(name, value.toString());
227 | this.flush();
228 | }else{
229 | throw new IllegalArgumentException("Stateless session only accept String value");
230 | }
231 | }
232 |
233 | @Override
234 | public void putValue(String name, Object value) {
235 | this.setAttribute(name, value);
236 | }
237 |
238 | @Override
239 | public void removeAttribute(String name) {
240 | this.attributes.remove(name);
241 | this.flush();
242 | }
243 |
244 | @Override
245 | public void removeValue(String name) {
246 | this.removeAttribute(name);
247 | }
248 |
249 | @Override
250 | public void invalidate() {
251 | final Cookie cookie = this.createCookie();
252 | cookie.setMaxAge(0);
253 | this.config.getResponse().addCookie(cookie);
254 | }
255 |
256 | @Override
257 | public boolean isNew() {
258 | return this.newSession;
259 | }
260 |
261 | }
262 |
--------------------------------------------------------------------------------
/src/main/java/com/ctlok/web/session/StatelessSessionConfig.java:
--------------------------------------------------------------------------------
1 | package com.ctlok.web.session;
2 |
3 | import javax.servlet.ServletContext;
4 | import javax.servlet.http.HttpServletRequest;
5 | import javax.servlet.http.HttpServletResponse;
6 |
7 | import com.ctlok.web.session.crypto.Encryptor;
8 |
9 | public class StatelessSessionConfig {
10 |
11 | private final ServletContext servletContext;
12 | private final HttpServletRequest request;
13 | private final HttpServletResponse response;
14 |
15 | private final String hmacSHA1Key;
16 | private final String secretKey;
17 | private final Encryptor encryptor;
18 | private final String sessionName;
19 |
20 | private final int sessionMaxAge;
21 | private final String path;
22 | private final String domain;
23 | private final boolean httpOnly;
24 |
25 | public StatelessSessionConfig(ServletContext servletContext,
26 | HttpServletRequest request, HttpServletResponse response,
27 | String hmacSHA1Key, String secretKey, Encryptor encryptor,
28 | String sessionName, int sessionMaxAge, String path, String domain,
29 | boolean httpOnly) {
30 | super();
31 | this.servletContext = servletContext;
32 | this.request = request;
33 | this.response = response;
34 | this.hmacSHA1Key = hmacSHA1Key;
35 | this.secretKey = secretKey;
36 | this.encryptor = encryptor;
37 | this.sessionName = sessionName;
38 | this.sessionMaxAge = sessionMaxAge;
39 | this.path = path;
40 | this.domain = domain;
41 | this.httpOnly = httpOnly;
42 | }
43 |
44 | public ServletContext getServletContext() {
45 | return servletContext;
46 | }
47 |
48 | public HttpServletRequest getRequest() {
49 | return request;
50 | }
51 |
52 | public HttpServletResponse getResponse() {
53 | return response;
54 | }
55 |
56 | public String getHmacSHA1Key() {
57 | return hmacSHA1Key;
58 | }
59 |
60 | public String getSecretKey() {
61 | return secretKey;
62 | }
63 |
64 | public Encryptor getEncryptor() {
65 | return encryptor;
66 | }
67 |
68 | public String getSessionName() {
69 | return sessionName;
70 | }
71 |
72 | public int getSessionMaxAge() {
73 | return sessionMaxAge;
74 | }
75 |
76 | public String getPath() {
77 | return path;
78 | }
79 |
80 | public String getDomain() {
81 | return domain;
82 | }
83 |
84 | public boolean isHttpOnly() {
85 | return httpOnly;
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/src/main/java/com/ctlok/web/session/StatelessSessionFilter.java:
--------------------------------------------------------------------------------
1 | package com.ctlok.web.session;
2 |
3 | import java.io.IOException;
4 | import java.security.InvalidKeyException;
5 |
6 | import javax.servlet.Filter;
7 | import javax.servlet.FilterChain;
8 | import javax.servlet.FilterConfig;
9 | import javax.servlet.ServletException;
10 | import javax.servlet.ServletRequest;
11 | import javax.servlet.ServletResponse;
12 | import javax.servlet.http.Cookie;
13 | import javax.servlet.http.HttpServletRequest;
14 | import javax.servlet.http.HttpServletRequestWrapper;
15 | import javax.servlet.http.HttpServletResponse;
16 | import javax.servlet.http.HttpSession;
17 |
18 | import com.ctlok.web.session.crypto.CryptoUtils;
19 | import com.ctlok.web.session.crypto.Encryptor;
20 |
21 | /**
22 | * @author Lawrence Cheung
23 | *
24 | */
25 | public class StatelessSessionFilter implements Filter {
26 |
27 | private static final String PARAM_HMAC_SHA1_KEY = "HMAC_SHA1_KEY";
28 | private static final String PARAM_ENCRYPTION_SECRET_KEY = "ENCRYPTION_SECRET_KEY";
29 | private static final String PARAM_ENCRYPTION_IMPL_CLASS = "ENCRYPTION_IMPL_CLASS";
30 |
31 | private static final String PARAM_SESSION_NAME = "SESSION_NAME";
32 | private static final String PARAM_SESSION_MAX_AGE = "SESSION_MAX_AGE";
33 | private static final String PARAM_SESSION_PATH = "SESSION_PATH";
34 | private static final String PARAM_SESSION_DOMAIN = "SESSION_DOMAIN";
35 | private static final String PARAM_SESSION_HTTP_ONLY = "HTTP_ONLY";
36 |
37 | private static final String DEFAULT_ENCRYPTION_IMPL_CLASS = "com.ctlok.web.session.crypto.AesEncryptor";
38 |
39 | private static final String DEFAULT_SESSION_NAME = "SESSION";
40 | private static final String DEFAULT_SESSION_MAX_AGE = "-1";
41 | private static final String DEFAULT_SESSION_PATH = "/";
42 | private static final String DEFAULT_SESSION_DOMAIN = null;
43 | private static final String DEFAULT_SESSION_HTTP_ONLY = "true";
44 |
45 | private FilterConfig filterConfig;
46 | private String hmacSha1Key;
47 | private String secretkey;
48 | private Encryptor encryptor;
49 |
50 | private String sessionName;
51 | private int sessionMaxAge;
52 | private String sessionPath;
53 | private String sessionDomain;
54 | private boolean httpOnly;
55 |
56 | @Override
57 | public void init(FilterConfig filterConfig) throws ServletException {
58 | this.filterConfig = filterConfig;
59 |
60 | this.hmacSha1Key = filterConfig.getInitParameter(PARAM_HMAC_SHA1_KEY);
61 | if (this.hmacSha1Key == null){
62 | throw new ServletException("HMAC_SHA1_KEY is mandatory value");
63 | }
64 |
65 | try {
66 | CryptoUtils.hmacSha1("test", this.hmacSha1Key);
67 | } catch (InvalidKeyException e) {
68 | throw new ServletException("Invalid HMAC_SHA1_KEY", e);
69 | }
70 |
71 | this.secretkey = filterConfig.getInitParameter(PARAM_ENCRYPTION_SECRET_KEY);
72 |
73 | if (this.secretkey != null){
74 | final String className = this.getConfig(filterConfig, PARAM_ENCRYPTION_IMPL_CLASS, DEFAULT_ENCRYPTION_IMPL_CLASS);
75 | try {
76 | this.encryptor = (Encryptor) Class.forName(className).newInstance();
77 |
78 | if (!isValidEncryptor()){
79 | throw new IllegalStateException("Not a valid encryptor");
80 | }
81 | } catch (final Exception e) {
82 | throw new ServletException("Create encryptor occur problem", e);
83 | }
84 | }
85 |
86 | this.sessionName = this.getConfig(filterConfig, PARAM_SESSION_NAME, DEFAULT_SESSION_NAME);
87 | this.sessionMaxAge = Integer.valueOf(this.getConfig(filterConfig, PARAM_SESSION_MAX_AGE, DEFAULT_SESSION_MAX_AGE));
88 | this.sessionPath = this.getConfig(filterConfig, PARAM_SESSION_PATH, DEFAULT_SESSION_PATH);
89 | this.sessionDomain = this.getConfig(filterConfig, PARAM_SESSION_DOMAIN, DEFAULT_SESSION_DOMAIN);
90 | this.httpOnly = Boolean.valueOf(this.getConfig(filterConfig, PARAM_SESSION_HTTP_ONLY, DEFAULT_SESSION_HTTP_ONLY));
91 | }
92 |
93 | @Override
94 | public void doFilter(ServletRequest req, ServletResponse resp,
95 | FilterChain chain) throws IOException, ServletException {
96 |
97 | final HttpServletRequest request = (HttpServletRequest) req;
98 | final HttpServletResponse response = (HttpServletResponse) resp;
99 |
100 | final StatelessSessionConfig sessionConfig = createStatelessSessionConfig(request, response);
101 | final HttpServletRequest requestWrapper = new RequestWrapper(request, sessionConfig);
102 |
103 | chain.doFilter(requestWrapper, response);
104 |
105 | }
106 |
107 | protected StatelessSessionConfig createStatelessSessionConfig(
108 | final HttpServletRequest request,
109 | final HttpServletResponse response){
110 |
111 | return new StatelessSessionConfig(this.filterConfig.getServletContext(),
112 | request, response, this.hmacSha1Key,
113 | this.secretkey, this.encryptor,
114 | this.sessionName, this.sessionMaxAge,
115 | this.sessionPath, this.sessionDomain,
116 | this.httpOnly);
117 |
118 | }
119 |
120 | @Override
121 | public void destroy() {
122 |
123 | }
124 |
125 | protected String getConfig(final FilterConfig filterConfig,
126 | final String name, final String defaultValue){
127 |
128 | String value = filterConfig.getInitParameter(name);
129 |
130 | if (value == null){
131 | value = defaultValue;
132 | }
133 |
134 | return value;
135 | }
136 |
137 | protected boolean isValidEncryptor() throws Exception{
138 | final String data = "test";
139 | final String encryptedString = this.encryptor.encrypt(this.secretkey, data);
140 |
141 | if (this.encryptor.decrypt(this.secretkey, encryptedString).equals(data)){
142 | return true;
143 | }else{
144 | return false;
145 | }
146 | }
147 |
148 | static class RequestWrapper extends HttpServletRequestWrapper{
149 |
150 | private final HttpServletRequest request;
151 | private final StatelessSessionConfig sessionConfig;
152 |
153 | private HttpSession session;
154 |
155 | public RequestWrapper(final HttpServletRequest request,
156 | final StatelessSessionConfig sessionConfig) {
157 | super(request);
158 | this.request = request;
159 | this.sessionConfig = sessionConfig;
160 |
161 | if (isSessionCookieExist(sessionConfig.getSessionName())){
162 | this.session = createStatelessSession(sessionConfig);
163 | }
164 | }
165 |
166 | @Override
167 | public HttpSession getSession(boolean create) {
168 | if (create && this.session == null){
169 | this.session = new StatelessSession(this.sessionConfig);
170 | }
171 | return session;
172 | }
173 |
174 | @Override
175 | public HttpSession getSession() {
176 | return getSession(true);
177 | }
178 |
179 | protected HttpSession createStatelessSession(final StatelessSessionConfig sessionConfig){
180 | return new StatelessSession(this.sessionConfig);
181 | }
182 |
183 | private boolean isSessionCookieExist(final String sessionName){
184 | boolean result = false;
185 |
186 | if (request.getCookies() != null){
187 |
188 | for (final Cookie cookie: request.getCookies()){
189 | if (cookie.getName().equals(sessionName)){
190 | result = true;
191 | break;
192 | }
193 | }
194 |
195 | }
196 |
197 | return result;
198 | }
199 |
200 | }
201 |
202 | }
203 |
--------------------------------------------------------------------------------
/src/main/java/com/ctlok/web/session/crypto/AesEncryptor.java:
--------------------------------------------------------------------------------
1 | package com.ctlok.web.session.crypto;
2 |
3 | /**
4 | * @author Lawrence Cheung
5 | *
6 | */
7 | public class AesEncryptor implements Encryptor {
8 |
9 | private static final String ALGORITHM = "AES";
10 |
11 | @Override
12 | public String encrypt(String key, String data) throws Exception {
13 | return CryptoUtils.encrypt(ALGORITHM, key, data);
14 | }
15 |
16 | @Override
17 | public String decrypt(String key, String data) throws Exception {
18 | return CryptoUtils.decrypt(ALGORITHM, key, data);
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/ctlok/web/session/crypto/CryptoUtils.java:
--------------------------------------------------------------------------------
1 | package com.ctlok.web.session.crypto;
2 |
3 | import java.security.InvalidKeyException;
4 | import java.security.Key;
5 | import java.security.NoSuchAlgorithmException;
6 |
7 | import javax.crypto.BadPaddingException;
8 | import javax.crypto.Cipher;
9 | import javax.crypto.IllegalBlockSizeException;
10 | import javax.crypto.Mac;
11 | import javax.crypto.NoSuchPaddingException;
12 | import javax.crypto.spec.SecretKeySpec;
13 |
14 | import org.apache.commons.codec.DecoderException;
15 | import org.apache.commons.codec.binary.Base64;
16 | import org.apache.commons.codec.binary.Hex;
17 |
18 | /**
19 | * @author Lawrence Cheung
20 | *
21 | */
22 | public class CryptoUtils {
23 |
24 | private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
25 |
26 | public static String hmacSha1(final String key, final String data)
27 | throws InvalidKeyException {
28 | String result = null;
29 |
30 | try {
31 | final Key secretKey = new SecretKeySpec(key.getBytes(),
32 | HMAC_SHA1_ALGORITHM);
33 | final Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
34 | mac.init(secretKey);
35 | result = bytesToHex(mac.doFinal(data.getBytes()));
36 | } catch (final NoSuchAlgorithmException e) {
37 | throw new IllegalStateException(e);
38 | }
39 |
40 | return result;
41 | }
42 |
43 | public static String encrypt(final String algorithm, final String key,
44 | final String data) throws IllegalBlockSizeException,
45 | BadPaddingException, InvalidKeyException, NoSuchAlgorithmException,
46 | NoSuchPaddingException, DecoderException {
47 |
48 | return encryptDecrypt(algorithm, true, key, data);
49 |
50 | }
51 |
52 | public static String decrypt(final String algorithm, final String key,
53 | final String data) throws InvalidKeyException,
54 | NoSuchAlgorithmException, NoSuchPaddingException,
55 | IllegalBlockSizeException, BadPaddingException, DecoderException {
56 |
57 | return encryptDecrypt(algorithm, false, key, data);
58 |
59 | }
60 |
61 | public static String bytesToHex(final byte[] bytes) {
62 | return Hex.encodeHexString(bytes);
63 | }
64 |
65 | public static byte[] hexStringToBytes(final String hexString) throws DecoderException {
66 | return Hex.decodeHex(hexString.toCharArray());
67 | }
68 |
69 | public static String encodeBase64(final byte[] bytes){
70 | return Base64.encodeBase64String(bytes);
71 | }
72 |
73 | public static byte[] decodeBase64(final String str){
74 | return Base64.decodeBase64(str);
75 | }
76 |
77 | private static String encryptDecrypt(final String algorithm,
78 | boolean encrypt, final String key, final String data)
79 | throws NoSuchAlgorithmException, NoSuchPaddingException,
80 | IllegalBlockSizeException, BadPaddingException, InvalidKeyException, DecoderException{
81 |
82 | final int mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
83 | final Key secretKey = new SecretKeySpec(key.getBytes(), algorithm);
84 | final Cipher cipher = Cipher.getInstance(algorithm);
85 | cipher.init(mode, secretKey);
86 |
87 | return encrypt ?
88 | encodeBase64(cipher.doFinal(data.getBytes())) :
89 | new String(cipher.doFinal(decodeBase64(data)));
90 |
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/src/main/java/com/ctlok/web/session/crypto/Encryptor.java:
--------------------------------------------------------------------------------
1 | package com.ctlok.web.session.crypto;
2 |
3 | /**
4 | * @author Lawrence Cheung
5 | *
6 | */
7 | public interface Encryptor {
8 |
9 | public String encrypt(String key, String data) throws Exception;
10 | public String decrypt(String key, String data) throws Exception;
11 |
12 | }
13 |
--------------------------------------------------------------------------------