|
15 | 15 | */
|
16 | 16 | package org.springframework.security.oauth2.jwt;
|
17 | 17 |
|
18 |
| -import org.springframework.security.oauth2.core.AbstractOAuth2Token; |
19 |
| -import org.springframework.util.Assert; |
20 |
| - |
| 18 | +import java.net.URL; |
21 | 19 | import java.time.Instant;
|
| 20 | +import java.util.Collection; |
22 | 21 | import java.util.Collections;
|
| 22 | +import java.util.HashMap; |
23 | 23 | import java.util.LinkedHashMap;
|
24 | 24 | import java.util.Map;
|
| 25 | +import java.util.stream.Collectors; |
| 26 | +import java.util.stream.Stream; |
| 27 | + |
| 28 | +import org.springframework.security.core.SpringSecurityCoreVersion; |
| 29 | +import org.springframework.security.oauth2.core.AbstractOAuth2Token; |
| 30 | +import org.springframework.util.Assert; |
25 | 31 |
|
26 | 32 | /**
|
27 | 33 | * An implementation of an {@link AbstractOAuth2Token} representing a JSON Web Token (JWT).
|
|
41 | 47 | * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7516">JSON Web Encryption (JWE)</a>
|
42 | 48 | */
|
43 | 49 | public class Jwt extends AbstractOAuth2Token implements JwtClaimAccessor {
|
| 50 | + private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; |
| 51 | + |
44 | 52 | private final Map<String, Object> headers;
|
45 | 53 | private final Map<String, Object> claims;
|
46 | 54 |
|
@@ -80,4 +88,139 @@ public Map<String, Object> getHeaders() {
|
80 | 88 | public Map<String, Object> getClaims() {
|
81 | 89 | return this.claims;
|
82 | 90 | }
|
| 91 | + |
| 92 | + public static Builder<?> builder() { |
| 93 | + return new Builder<>(); |
| 94 | + } |
| 95 | + |
| 96 | + /** |
| 97 | + * Helps configure a {@link Jwt} |
| 98 | + * |
| 99 | + * @author Jérôme Wacongne <ch4mp@c4-soft.com> |
| 100 | + */ |
| 101 | + public static class Builder<T extends Builder<T>> { |
| 102 | + protected String tokenValue; |
| 103 | + protected final Map<String, Object> claims = new HashMap<>(); |
| 104 | + protected final Map<String, Object> headers = new HashMap<>(); |
| 105 | + |
| 106 | + protected Builder() { |
| 107 | + } |
| 108 | + |
| 109 | + public T tokenValue(String tokenValue) { |
| 110 | + this.tokenValue = tokenValue; |
| 111 | + return downcast(); |
| 112 | + } |
| 113 | + |
| 114 | + public T claim(String name, Object value) { |
| 115 | + this.claims.put(name, value); |
| 116 | + return downcast(); |
| 117 | + } |
| 118 | + |
| 119 | + public T clearClaims(Map<String, Object> claims) { |
| 120 | + this.claims.clear(); |
| 121 | + return downcast(); |
| 122 | + } |
| 123 | + |
| 124 | + /** |
| 125 | + * Adds to existing claims (does not replace existing ones) |
| 126 | + * @param claims claims to add |
| 127 | + * @return this builder to further configure |
| 128 | + */ |
| 129 | + public T claims(Map<String, Object> claims) { |
| 130 | + this.claims.putAll(claims); |
| 131 | + return downcast(); |
| 132 | + } |
| 133 | + |
| 134 | + public T header(String name, Object value) { |
| 135 | + this.headers.put(name, value); |
| 136 | + return downcast(); |
| 137 | + } |
| 138 | + |
| 139 | + public T clearHeaders(Map<String, Object> headers) { |
| 140 | + this.headers.clear(); |
| 141 | + return downcast(); |
| 142 | + } |
| 143 | + |
| 144 | + /** |
| 145 | + * Adds to existing headers (does not replace existing ones) |
| 146 | + * @param headers headers to add |
| 147 | + * @return this builder to further configure |
| 148 | + */ |
| 149 | + public T headers(Map<String, Object> headers) { |
| 150 | + headers.entrySet().stream().forEach(e -> this.header(e.getKey(), e.getValue())); |
| 151 | + return downcast(); |
| 152 | + } |
| 153 | + |
| 154 | + public Jwt build() { |
| 155 | + final JwtClaimSet claimSet = new JwtClaimSet(claims); |
| 156 | + return new Jwt( |
| 157 | + this.tokenValue, |
| 158 | + claimSet.getClaimAsInstant(JwtClaimNames.IAT), |
| 159 | + claimSet.getClaimAsInstant(JwtClaimNames.EXP), |
| 160 | + this.headers, |
| 161 | + claimSet); |
| 162 | + } |
| 163 | + |
| 164 | + public T audience(Stream<String> audience) { |
| 165 | + this.claim(JwtClaimNames.AUD, audience.collect(Collectors.toList())); |
| 166 | + return downcast(); |
| 167 | + } |
| 168 | + |
| 169 | + public T audience(Collection<String> audience) { |
| 170 | + return audience(audience.stream()); |
| 171 | + } |
| 172 | + |
| 173 | + public T audience(String... audience) { |
| 174 | + return audience(Stream.of(audience)); |
| 175 | + } |
| 176 | + |
| 177 | + public T expiresAt(Instant expiresAt) { |
| 178 | + this.claim(JwtClaimNames.EXP, expiresAt.getEpochSecond()); |
| 179 | + return downcast(); |
| 180 | + } |
| 181 | + |
| 182 | + public T jti(String jti) { |
| 183 | + this.claim(JwtClaimNames.JTI, jti); |
| 184 | + return downcast(); |
| 185 | + } |
| 186 | + |
| 187 | + public T issuedAt(Instant issuedAt) { |
| 188 | + this.claim(JwtClaimNames.IAT, issuedAt.getEpochSecond()); |
| 189 | + return downcast(); |
| 190 | + } |
| 191 | + |
| 192 | + public T issuer(URL issuer) { |
| 193 | + this.claim(JwtClaimNames.ISS, issuer.toExternalForm()); |
| 194 | + return downcast(); |
| 195 | + } |
| 196 | + |
| 197 | + public T notBefore(Instant notBefore) { |
| 198 | + this.claim(JwtClaimNames.NBF, notBefore.getEpochSecond()); |
| 199 | + return downcast(); |
| 200 | + } |
| 201 | + |
| 202 | + public T subject(String subject) { |
| 203 | + this.claim(JwtClaimNames.SUB, subject); |
| 204 | + return downcast(); |
| 205 | + } |
| 206 | + |
| 207 | + @SuppressWarnings("unchecked") |
| 208 | + protected T downcast() { |
| 209 | + return (T) this; |
| 210 | + } |
| 211 | + } |
| 212 | + |
| 213 | + private static final class JwtClaimSet extends HashMap<String, Object> implements JwtClaimAccessor { |
| 214 | + private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; |
| 215 | + |
| 216 | + public JwtClaimSet(Map<String, Object> claims) { |
| 217 | + super(claims); |
| 218 | + } |
| 219 | + |
| 220 | + @Override |
| 221 | + public Map<String, Object> getClaims() { |
| 222 | + return this; |
| 223 | + } |
| 224 | + |
| 225 | + } |
83 | 226 | }
|
0 commit comments