1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.bremersee.security.authentication;
18
19 import com.nimbusds.jwt.EncryptedJWT;
20 import com.nimbusds.jwt.JWT;
21 import com.nimbusds.jwt.PlainJWT;
22 import com.nimbusds.jwt.SignedJWT;
23 import java.text.ParseException;
24 import java.time.Duration;
25 import java.util.Date;
26 import java.util.Objects;
27 import java.util.Optional;
28 import javax.validation.constraints.NotNull;
29 import org.bremersee.exception.ServiceException;
30 import org.springframework.cache.Cache;
31 import org.springframework.validation.annotation.Validated;
32
33
34
35
36
37
38 @Validated
39 public interface AccessTokenCache {
40
41
42
43
44 String CACHE_NAME = "jwt";
45
46
47
48
49
50
51
52 Optional<String> findAccessToken(@NotNull String key);
53
54
55
56
57
58
59
60 void putAccessToken(@NotNull String key, @NotNull String accessToken);
61
62
63
64
65
66
67
68
69
70 static boolean isExpired(@NotNull String tokenValue, Duration accessTokenThreshold) {
71 Duration duration = Objects
72 .requireNonNullElseGet(accessTokenThreshold, () -> Duration.ofSeconds(20L));
73 long millis = System.currentTimeMillis() + duration.toMillis();
74 return Optional.ofNullable(getExpirationTime(tokenValue))
75 .map(date -> date.getTime() < millis)
76 .orElse(true);
77 }
78
79
80
81
82
83
84
85 static Date getExpirationTime(@NotNull String tokenValue) {
86 JWT jwt = parse(tokenValue);
87 try {
88 if (jwt.getJWTClaimsSet() != null
89 && jwt.getJWTClaimsSet().getExpirationTime() != null) {
90 return jwt.getJWTClaimsSet().getExpirationTime();
91 }
92
93 } catch (ParseException e) {
94
95 }
96 return null;
97 }
98
99
100
101
102
103
104
105 static JWT parse(@NotNull String tokenValue) {
106 try {
107 return SignedJWT.parse(tokenValue);
108 } catch (Exception e0) {
109 try {
110 return EncryptedJWT.parse(tokenValue);
111 } catch (Exception e1) {
112 try {
113 return PlainJWT.parse(tokenValue);
114 } catch (Exception e2) {
115 throw ServiceException.internalServerError("Parsing jwt failed.");
116 }
117 }
118 }
119 }
120
121
122
123
124
125
126 static Builder builder() {
127 return new Builder.Impl();
128 }
129
130
131
132
133 interface Builder {
134
135
136
137
138
139
140
141 Builder withExternalCache(Cache externalCache);
142
143
144
145
146
147
148
149 Builder withExpirationTimeThreshold(Duration duration);
150
151
152
153
154
155
156
157 Builder withKeyPrefix(String keyPrefix);
158
159
160
161
162
163
164 AccessTokenCache build();
165
166
167
168
169 class Impl implements Builder {
170
171 private Cache externalCache;
172
173 private Duration expirationTimeThreshold;
174
175 private String keyPrefix;
176
177 @Override
178 public Builder withExternalCache(Cache externalCache) {
179 this.externalCache = externalCache;
180 return this;
181 }
182
183 @Override
184 public Builder withExpirationTimeThreshold(Duration duration) {
185 this.expirationTimeThreshold = duration;
186 return this;
187 }
188
189 @Override
190 public Builder withKeyPrefix(String keyPrefix) {
191 this.keyPrefix = keyPrefix;
192 return this;
193 }
194
195 @Override
196 public AccessTokenCache build() {
197 return new AccessTokenCacheImpl(externalCache, expirationTimeThreshold, keyPrefix);
198 }
199 }
200 }
201 }