Skip to content

Commit a59aa16

Browse files
committed
simplify jwt support configuration; JWT support bug fix
1 parent 9ec11be commit a59aa16

5 files changed

Lines changed: 116 additions & 14 deletions

File tree

src/main/java/act/conf/AppConfig.java

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,7 @@
3636
import act.handler.event.ResultEvent;
3737
import act.i18n.I18n;
3838
import act.security.CSRFProtector;
39-
import act.session.CookieSessionMapper;
40-
import act.session.DefaultSessionCodec;
41-
import act.session.HeaderTokenSessionMapper;
42-
import act.session.SessionCodec;
39+
import act.session.*;
4340
import act.util.*;
4441
import act.view.TemplatePathResolver;
4542
import act.view.View;
@@ -119,9 +116,13 @@ public AppConfig<T> app(App app) {
119116
}
120117

121118
public void preloadConfigurations() {
119+
// ensure JWT get evaluated first to set
120+
// default value for dependency settings
121+
jwt();
122+
122123
for (Method method : AppConfig.class.getDeclaredMethods()) {
123124
boolean isPublic = Modifier.isPublic(method.getModifiers());
124-
if (isPublic && 0 == method.getParameterTypes().length && void.class != method.getReturnType() && Void.class != method.getReturnType()) {
125+
if (isPublic && 0 == method.getParameterTypes().length && !"preloadConfigurations".equals(method.getName())) {
125126
$.invokeVirtual(this, method);
126127
}
127128
}
@@ -934,6 +935,37 @@ private void _mergeI18nEnabled(AppConfig conf) {
934935
i18nEnabled = conf.i18nEnabled;
935936
}
936937
}
938+
939+
private Boolean jwt;
940+
protected T jwt(boolean enabled) {
941+
jwt = enabled;
942+
return me();
943+
}
944+
public boolean jwt() {
945+
if (null == jwt) {
946+
jwt = get(JWT, false);
947+
if (jwt) {
948+
if (!hasConfiguration(SESSION_HEADER)) {
949+
sessionHeader("Authorization");
950+
}
951+
if (!hasConfiguration(SESSION_HEADER_PAYLOAD_PREFIX)) {
952+
sessionHeaderPayloadPrefix("Bearer ");
953+
}
954+
if (!hasConfiguration(SESSION_MAPPER)) {
955+
sessionMapper(new HeaderTokenSessionMapper(this));
956+
}
957+
if (!hasConfiguration(SESSION_CODEC)) {
958+
sessionCodec(new JsonWebTokenSessionCodec(this));
959+
}
960+
}
961+
}
962+
return jwt;
963+
}
964+
private void _mergeJWT(AppConfig config) {
965+
if (!hasConfiguration(JWT)) {
966+
jwt = config.jwt;
967+
}
968+
}
937969

938970
private String localeParamName;
939971
protected T localeParamName(String name) {
@@ -1963,9 +1995,8 @@ private void _mergeSessionEncrpt(AppConfig config) {
19631995

19641996
private act.session.SessionMapper sessionMapper = null;
19651997

1966-
protected T sessionMapper(act.session.SessionMapper sessionMapper) {
1998+
protected void sessionMapper(act.session.SessionMapper sessionMapper) {
19671999
this.sessionMapper = sessionMapper;
1968-
return me();
19692000
}
19702001

19712002
public act.session.SessionMapper sessionMapper() {
@@ -1983,9 +2014,8 @@ private void _mergeSessionMapper(AppConfig config) {
19832014

19842015
private SessionCodec sessionCodec = null;
19852016

1986-
protected T sessionCodec(SessionCodec codec) {
2017+
protected void sessionCodec(SessionCodec codec) {
19872018
this.sessionCodec = $.notNull(codec);
1988-
return me();
19892019
}
19902020

19912021
public SessionCodec sessionCodec() {
@@ -2001,6 +2031,25 @@ private void _mergeSessionCodec(AppConfig config) {
20012031
}
20022032
}
20032033

2034+
private String sessionHeader;
2035+
private boolean sessionHeaderSet;
2036+
protected void sessionHeader(String header) {
2037+
this.sessionHeader = header;
2038+
this.sessionHeaderSet = true;
2039+
}
2040+
public String sessionHeader() {
2041+
if (!sessionHeaderSet) {
2042+
sessionHeader = get(SESSION_HEADER, null);
2043+
sessionHeaderSet = true;
2044+
}
2045+
return sessionHeader;
2046+
}
2047+
private void _mergeSessionHeader(AppConfig conf) {
2048+
if (!hasConfiguration(SESSION_HEADER)) {
2049+
sessionHeader = conf.sessionHeader;
2050+
sessionHeaderSet = conf.sessionHeaderSet;
2051+
}
2052+
}
20042053

20052054
private String sessionHeaderPrefix;
20062055
protected T sessionHeaderPrefix(String prefix) {
@@ -2015,9 +2064,8 @@ public String sessionHeaderPrefix() {
20152064
}
20162065

20172066
private String sessionHeaderPayloadPrefix = null;
2018-
protected T sessionHeaderPayloadPrefix(String prefix) {
2067+
protected void sessionHeaderPayloadPrefix(String prefix) {
20192068
this.sessionHeaderPayloadPrefix = prefix;
2020-
return me();
20212069
}
20222070
public String sessionHeaderPayloadPrefix() {
20232071
if (null == sessionHeaderPayloadPrefix) {

src/main/java/act/conf/AppConfigKey.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,19 @@ public enum AppConfigKey implements ConfigKey {
567567
*/
568568
JOB_POOL_SIZE("job.pool.size"),
569569

570+
/**
571+
* `jwt.enabled`, toggle JWT (JSON Web Token) support.
572+
*
573+
* Enable this configuration has the same effect of setting
574+
*
575+
* * {@link #SESSION_CODEC} - {@link act.session.JsonWebTokenSessionCodec}
576+
* * {@link #SESSION_HEADER_PAYLOAD_PREFIX} - `Bearer `
577+
* * {@link #SESSION_HEADER} - `Authorization`
578+
*
579+
* Default value: `false`
580+
*/
581+
JWT("jwt.enabled"),
582+
570583
/**
571584
* {@code act.locale} specifies the application default locale
572585
* <p>Default value: {@link java.util.Locale#getDefault}</p>
@@ -795,6 +808,19 @@ public <T> T val(Map<String, ?> configuration) {
795808
@Deprecated
796809
SESSION_MAPPER_HEADER_PREFIX("session.mapper.header.prefix"),
797810

811+
/**
812+
* `session.header` - specify the session header name.
813+
*
814+
* Effective only when {@link act.session.SessionMapper} is
815+
* {@link act.session.HeaderTokenSessionMapper}.
816+
*
817+
* If this configuration is set then {@link #SESSION_HEADER_PREFIX}
818+
* is ignored for session header name.
819+
*
820+
* Default value: `null`
821+
*/
822+
SESSION_HEADER("session.header"),
823+
798824
/**
799825
* `session.header.prefix`, specify the prefix of session
800826
* header.
@@ -803,6 +829,11 @@ public <T> T val(Map<String, ?> configuration) {
803829
* to {@link act.session.HeaderTokenSessionMapper} or any
804830
* compound session mapper that support it.
805831
*
832+
* If {@link #SESSION_HEADER} is not set, then header name of
833+
* session token is `${session.header.prefix}-Session`.
834+
*
835+
* The flash header name is always `${flash.header.prefix}-Flash`.
836+
*
806837
* Default value: {@link act.session.HeaderTokenSessionMapper#DEF_HEADER_PREFIX}
807838
*/
808839
SESSION_HEADER_PREFIX("session.header.prefix"),

src/main/java/act/session/DefaultSessionCodec.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public class DefaultSessionCodec extends DestroyableBase implements SessionCodec
5151
@Inject
5252
public DefaultSessionCodec(AppConfig conf) {
5353
app = conf.app();
54-
ttl = conf.sessionTtl();
54+
ttl = conf.sessionTtl() * 1000;
5555
sessionWillExpire = ttl > 0;
5656
pingPath = conf .pingPath();
5757
encryptSession = conf.encryptSession();

src/main/java/act/session/HeaderTokenSessionMapper.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ public class HeaderTokenSessionMapper implements SessionMapper {
4444
public HeaderTokenSessionMapper(AppConfig config) {
4545
String prefix = config.sessionHeaderPrefix();
4646
String headerPrefix = S.blank(prefix) ? DEF_HEADER_PREFIX : prefix;
47-
sessionHeader = S.pathConcat(headerPrefix, '-', "Session");
47+
sessionHeader = config.sessionHeader();
48+
if (null == sessionHeader) {
49+
sessionHeader = S.pathConcat(headerPrefix, '-', "Session");
50+
}
4851
flashHeader = S.pathConcat(headerPrefix, '-', "Flash");
4952
sessionPayloadPrefix = config.sessionHeaderPayloadPrefix();
5053
hasSessionPayloadPrefix = S.notBlank(sessionPayloadPrefix);

src/main/java/act/session/JsonWebTokenSessionCodec.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
import java.util.Date;
4141
import java.util.Map;
4242

43+
import static org.osgl.http.H.Session.KEY_EXPIRATION;
44+
4345
@Singleton
4446
@Lazy
4547
public class JsonWebTokenSessionCodec implements SessionCodec {
@@ -56,18 +58,34 @@ public JsonWebTokenSessionCodec(AppConfig conf) {
5658
} catch (UnsupportedEncodingException e) {
5759
throw E.unexpected(e);
5860
}
59-
ttl = conf.sessionTtl();
61+
ttl = conf.sessionTtl() * 1000;
6062
sessionWillExpire = ttl > 0;
6163
pingPath = conf .pingPath();
6264
}
6365

6466
@Override
6567
public String encodeSession(H.Session session) {
68+
if (null == session) {
69+
return null;
70+
}
71+
boolean sessionChanged = session.changed();
72+
if (!sessionChanged && (session.empty() || !sessionWillExpire)) {
73+
// Nothing changed and no cookie-expire or empty, consequently send nothing back.
74+
return null;
75+
}
76+
session.id(); // ensure session ID is generated
77+
if (sessionWillExpire && !session.contains(KEY_EXPIRATION)) {
78+
// session get cleared before
79+
session.put(KEY_EXPIRATION, $.ms() + ttl);
80+
}
6681
return builder(session, JWT.create().withJWTId(session.id())).sign(algorithm);
6782
}
6883

6984
@Override
7085
public String encodeFlash(H.Flash flash) {
86+
if (null == flash || flash.isEmpty()) {
87+
return null;
88+
}
7189
return builder(flash, JWT.create()).sign(algorithm);
7290
}
7391

@@ -123,6 +141,8 @@ private void resolveFromJwtToken(H.KV<?> state, String token, boolean isSession)
123141
if (isSession) {
124142
state.put(H.Session.KEY_ID, val);
125143
}
144+
} else if ("exp".equals(key)) {
145+
state.put(H.Session.KEY_EXPIRATION, entry.getValue().asLong());
126146
} else if ("iss".equals(key)) {
127147
// ignore
128148
} else {

0 commit comments

Comments
 (0)