Skip to content

Commit fec780f

Browse files
committed
Session API: add browser/cookie signed session
- simplify API - code cleanup - document new API
1 parent cfc3588 commit fec780f

File tree

14 files changed

+363
-157
lines changed

14 files changed

+363
-157
lines changed

jooby/src/main/java/io/jooby/Router.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -682,9 +682,9 @@ default Router error(@Nonnull Predicate<StatusCode> predicate,
682682
* @param converter Custom value converter.
683683
* @return This router.
684684
*/
685-
@Nonnull Router converter(@Nonnull ValueConverter converter);/**
685+
@Nonnull Router converter(@Nonnull ValueConverter converter);
686686

687-
/**
687+
/**
688688
* Add a custom bean value converter.
689689
*
690690
* @param converter Custom value converter.

jooby/src/main/java/io/jooby/Session.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,14 +207,24 @@ public interface Session {
207207
/**
208208
* Creates a new session.
209209
*
210+
* @param ctx Web context.
210211
* @param id Session ID.
211212
* @return A new session.
212213
*/
213214
static @Nonnull Session create(@Nonnull Context ctx, @Nonnull String id) {
214215
return new SessionImpl(ctx, id);
215216
}
216217

217-
static @Nonnull Session create(@Nonnull Context ctx, @Nonnull String id, @Nonnull Map<String, String> data) {
218+
/**
219+
* Creates a new session.
220+
*
221+
* @param ctx Web context.
222+
* @param id Session ID.
223+
* @param data Session attributes.
224+
* @return A new session.
225+
*/
226+
static @Nonnull Session create(@Nonnull Context ctx, @Nonnull String id,
227+
@Nonnull Map<String, String> data) {
218228
return new SessionImpl(ctx, id, data);
219229
}
220230
}

jooby/src/main/java/io/jooby/SessionOptions.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ public class SessionOptions {
2626

2727
private final SessionStore store;
2828

29-
public SessionOptions(SessionStore store) {
29+
/**
30+
*
31+
* @param store
32+
*/
33+
public SessionOptions(@Nonnull SessionStore store) {
3034
this.store = store;
3135
}
3236

jooby/src/main/java/io/jooby/SessionStore.java

Lines changed: 76 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
*/
66
package io.jooby;
77

8+
import io.jooby.internal.CookieSessionStore;
89
import io.jooby.internal.MemorySessionStore;
910

1011
import javax.annotation.Nonnull;
1112
import javax.annotation.Nullable;
12-
import java.time.Duration;
1313
import java.time.Instant;
1414

1515
/**
@@ -20,62 +20,115 @@
2020
*/
2121
public interface SessionStore {
2222

23-
Cookie SID = new Cookie("jooby.sid")
24-
.setMaxAge(Duration.ofSeconds(-1))
25-
.setHttpOnly(true)
26-
.setPath("/");
27-
28-
@Nonnull SessionToken getSessionToken();
29-
3023
/**
3124
* Creates a new session. This method must:
3225
*
3326
* - Set session as new {@link Session#setNew(boolean)}
34-
* - Set session creation time {@link Session#setCreationTime(Instant)}
35-
* - Set session last accessed time {@link Session#setLastAccessedTime(Instant)}
27+
* - Optionally, set session creation time {@link Session#setCreationTime(Instant)}
28+
* - Optionally, set session last accessed time {@link Session#setLastAccessedTime(Instant)}
3629
*
37-
* @param id Session ID.
30+
* @param ctx Web context.
3831
* @return A new session.
3932
*/
4033
@Nonnull Session newSession(@Nonnull Context ctx);
4134

4235
/**
4336
* Find an existing session by ID. For existing session this method must:
4437
*
45-
* - Retrieve/restore session creation time
46-
* - Set session last accessed time {@link Session#setLastAccessedTime(Instant)}
38+
* - Optionally, Retrieve/restore session creation time
39+
* - Optionally, Set session last accessed time {@link Session#setLastAccessedTime(Instant)}
4740
*
48-
* @param id Session ID.
41+
* @param ctx Web context.
4942
* @return An existing session or <code>null</code>.
5043
*/
5144
@Nullable Session findSession(@Nonnull Context ctx);
5245

5346
/**
5447
* Delete a session from store. This method must NOT call {@link Session#destroy()}.
5548
*
56-
* @param id Session ID.
49+
* @param ctx Web context.
50+
* @param session Current session.
5751
*/
58-
void deleteSession(@Nonnull Context ctx);
52+
void deleteSession(@Nonnull Context ctx, @Nonnull Session session);
53+
54+
/**
55+
* Session attributes/state has changed. Every time a session attribute is put or removed it,
56+
* this method is executed as notification callback.
57+
*
58+
* @param ctx Web context.
59+
* @param session Current session.
60+
*/
61+
void touchSession(@Nonnull Context ctx, @Nonnull Session session);
5962

6063
/**
6164
* Save a session. This method must save:
6265
*
6366
* - Session attributes/data
64-
* - Session metadata like: creationTime, lastAccessed time, etc.
67+
* - Optionally set Session metadata like: creationTime, lastAccessed time, etc.
68+
*
69+
* This method is call after response is send to client, so context and response shouldn't be
70+
* modified.
6571
*
66-
* @param session Session to save.
72+
* @param ctx Web context.
73+
* @param session Current session.
6774
*/
68-
void save(@Nonnull Context ctx);
75+
void saveSession(@Nonnull Context ctx, @Nonnull Session session);
6976

70-
static SessionStore memory() {
71-
return memory(SID);
77+
/**
78+
* Creates a cookie based session and store data in memory. Session data is not keep after
79+
* restart.
80+
*
81+
* It uses the default session cookie: {@link SessionToken#SID}.
82+
*
83+
* @return Session store.
84+
*/
85+
static @Nonnull SessionStore memory() {
86+
return memory(SessionToken.SID);
7287
}
7388

74-
static SessionStore memory(Cookie cookie) {
89+
/**
90+
* Creates a cookie based session and store data in memory. Session data is not keep after
91+
* restart.
92+
*
93+
* @param cookie Cookie to use.
94+
* @return Session store.
95+
*/
96+
static @Nonnull SessionStore memory(@Nonnull Cookie cookie) {
7597
return memory(SessionToken.cookie(cookie));
7698
}
7799

78-
static SessionStore memory(SessionToken token) {
100+
/**
101+
* Creates a session store that save data in memory. Session data is not keep after restart.
102+
*
103+
* @param token Session token.
104+
* @return Session store.
105+
*/
106+
static @Nonnull SessionStore memory(@Nonnull SessionToken token) {
79107
return new MemorySessionStore(token);
80108
}
109+
110+
/**
111+
* Creates a session store that save data into Cookie. Cookie data is signed it using
112+
* <code>HMAC_SHA256</code>. See {@link Cookie#sign(String, String)}.
113+
*
114+
* @param secret Secret token to signed data.
115+
* @param cookie Cookie to use.
116+
* @return A browser session store.
117+
*/
118+
static @Nonnull SessionStore cookie(@Nonnull String secret, @Nonnull Cookie cookie) {
119+
return new CookieSessionStore(secret, cookie);
120+
}
121+
122+
/**
123+
* Creates a session store that save data into Cookie. Cookie data is signed it using
124+
* <code>HMAC_SHA256</code>. See {@link Cookie#sign(String, String)}.
125+
*
126+
* It uses the default session cookie: {@link SessionToken#SID}.
127+
*
128+
* @param secret Secret token to signed data.
129+
* @return A browser session store.
130+
*/
131+
static @Nonnull SessionStore cookie(@Nonnull String secret) {
132+
return cookie(secret, SessionToken.SID);
133+
}
81134
}

jooby/src/main/java/io/jooby/SessionToken.java

Lines changed: 82 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,37 @@
55
*/
66
package io.jooby;
77

8+
import io.jooby.internal.MultipleSessionToken;
9+
810
import javax.annotation.Nonnull;
911
import javax.annotation.Nullable;
12+
import java.security.SecureRandom;
13+
import java.time.Duration;
1014

1115
/**
12-
* Find, save and delete a session token (cookie or header) into/from the web {@link Context}.
16+
* Find, save and delete a session token (cookie, header, parameter, etc)
17+
* into/from the web {@link Context}.
1318
*
1419
* @author edgar
1520
*/
1621
public interface SessionToken {
1722

23+
/**
24+
* Looks for a session ID from request cookie headers. This strategy:
25+
*
26+
* - find a token from a request cookie.
27+
* - on save, set a response cookie on new sessions or when cookie has a max-age value.
28+
* - on destroy, expire the cookie.
29+
*/
1830
class CookieID implements SessionToken {
1931

2032
private final Cookie cookie;
2133

34+
/**
35+
* Creates a Cookie ID.
36+
*
37+
* @param cookie Cookie to use.
38+
*/
2239
public CookieID(@Nonnull Cookie cookie) {
2340
this.cookie = cookie;
2441
}
@@ -40,11 +57,23 @@ public CookieID(@Nonnull Cookie cookie) {
4057
}
4158
}
4259

60+
/**
61+
* Looks for a session ID from request headers. This strategy:
62+
*
63+
* - find a token from a request header.
64+
* - on save, send the header back as response header.
65+
* - on session destroy. don't send response header back.
66+
*/
4367
class HeaderID implements SessionToken {
4468

4569
private final String name;
4670

47-
public HeaderID(String name) {
71+
/**
72+
* Creates a new Header ID.
73+
*
74+
* @param name Header's name.
75+
*/
76+
public HeaderID(@Nonnull String name) {
4877
this.name = name;
4978
}
5079

@@ -61,6 +90,15 @@ public HeaderID(String name) {
6190
}
6291
}
6392

93+
/**
94+
* Default cookie for cookie based session stores.
95+
* Uses <code>jooby.sid</code> as name. It never expires, use the root, only for HTTP.
96+
*/
97+
Cookie SID = new Cookie("jooby.sid")
98+
.setMaxAge(Duration.ofSeconds(-1))
99+
.setHttpOnly(true)
100+
.setPath("/");
101+
64102
/**
65103
* Find session ID.
66104
*
@@ -73,35 +111,70 @@ public HeaderID(String name) {
73111
* Save session ID in the web context.
74112
*
75113
* @param ctx Web context.
76-
* @param token
114+
* @param token Token/data to save.
77115
*/
78116
void saveToken(@Nonnull Context ctx, @Nonnull String token);
79117

80118
/**
81119
* Delete session ID in the web context.
82120
*
83121
* @param ctx Web context.
84-
* @param token Session ID to save.
122+
* @param token Token/data to delete.
85123
*/
86124
void deleteToken(@Nonnull Context ctx, @Nonnull String token);
87125

126+
/* **********************************************************************************************
127+
* Factory methods
128+
* **********************************************************************************************
129+
*/
130+
88131
/**
89-
* Create a cookie-based Session ID.
132+
* Create a cookie-based Session ID. This strategy:
133+
*
134+
* - find a token from a request cookie.
135+
* - on save, set a response cookie on new sessions or when cookie has a max-age value.
136+
* - on destroy, expire the cookie.
90137
*
91-
* @param cookie Cookie template.
92-
* @return Session ID.
138+
* @param cookie Cookie to use.
139+
* @return Session Token.
93140
*/
94141
static @Nonnull SessionToken cookie(@Nonnull Cookie cookie) {
95142
return new CookieID(cookie);
96143
}
97144

98145
/**
99-
* Create a header-based Session ID.
146+
* Create a header-based Session Token. This strategy:
147+
*
148+
* - find a token from a request header.
149+
* - on save, send the header back as response header.
150+
* - on session destroy. don't send response header back.
100151
*
101152
* @param name Header name.
102-
* @return Session ID.
153+
* @return Session Token.
103154
*/
104155
static @Nonnull SessionToken header(@Nonnull String name) {
105156
return new HeaderID(name);
106157
}
158+
159+
/**
160+
* Combine/compose two or more session tokens. Example:
161+
*
162+
* <pre>{@code
163+
* SessionToken token = SessionToken.combine(
164+
* SessionToken.header("TOKEN"),
165+
* SessionToken.cookie(SID)
166+
* );
167+
* }
168+
* </pre>
169+
*
170+
* On new session, creates a response header and cookie.
171+
* On save token, generates a response header or cookie based on best matches.
172+
* On delete token, generates a response header or cookie based on best matches.
173+
*
174+
* @param tokens Tokens to use.
175+
* @return A composed session token.
176+
*/
177+
static @Nonnull SessionToken combine(@Nonnull SessionToken... tokens) {
178+
return new MultipleSessionToken(tokens);
179+
}
107180
}

0 commit comments

Comments
 (0)