2424import java .util .*;
2525
2626/**
27+ * A builder-style class for constructing JSON Web Token (JWT) payloads.
28+ * <p>
29+ * Provides a fluent interface to set standard registered claims (e.g., subject, issuer, audience)
30+ * and custom claims for a JWT payload. The class supports chaining method calls to build the
31+ * payload incrementally, which can then be retrieved as a map for use in JWT creation. Ensures that
32+ * registered claims are set using dedicated methods to prevent misuse.
2733 *
34+ * @author zihluwang
2835 */
2936public class TokenPayload {
3037
38+ /**
39+ * Creates a new instance of {@link TokenPayload} with an empty payload.
40+ * <p>
41+ * Initialises the payload with empty collections for claims and audiences, ready for
42+ * configuration via the builder methods.
43+ *
44+ * @return a new {@link TokenPayload} instance
45+ */
3146 public static TokenPayload createPayload () {
3247 return new TokenPayload ();
3348 }
3449
50+ /**
51+ * The map storing custom claims for the JWT payload.
52+ */
3553 private final Map <String , Object > payload ;
54+
55+ /**
56+ * The list of audience identifiers for the JWT.
57+ */
3658 private final List <String > audiences ;
3759
60+ /**
61+ * The subject of the JWT, identifying the principal.
62+ */
3863 private String subject ;
64+
65+ /**
66+ * The issuer of the JWT, identifying the entity that issued the token.
67+ */
3968 private String issuer ;
69+
70+ /**
71+ * The unique identifier for the JWT.
72+ */
4073 private String tokenId ;
74+
75+ /**
76+ * The expiration time of the JWT, as seconds since the Unix epoch.
77+ */
4178 private Long expiresAt ;
79+
80+ /**
81+ * The time before which the JWT must not be accepted, as seconds since the Unix epoch.
82+ */
4283 private Long notBefore ;
84+
85+ /**
86+ * The issuance time of the JWT, as seconds since the Unix epoch.
87+ */
4388 private Long issuedAt ;
4489
4590 /**
46- * Private constructor to prevent instantiation of this utility class.
91+ * Private constructor to enforce use of the factory method.
92+ * <p>
93+ * Initialises the internal collections for storing claims and audiences, preventing direct
94+ * instantiation outside the class.
4795 */
4896 private TokenPayload () {
4997 payload = new HashMap <>();
5098 audiences = new ArrayList <>();
5199 }
52100
101+ /**
102+ * Adds a single audience to the JWT payload.
103+ * <p>
104+ * Appends the specified audience identifier to the list of audiences, allowing the token to be
105+ * validated for multiple recipients.
106+ *
107+ * @param audience the audience identifier to add
108+ * @return this {@link TokenPayload} instance for method chaining
109+ */
53110 public TokenPayload withAudience (String audience ) {
54111 audiences .add (audience );
55112 return this ;
56113 }
57114
115+ /**
116+ * Adds multiple audiences to the JWT payload.
117+ * <p>
118+ * Appends all provided audience identifiers to the list of audiences, enabling the token to be
119+ * validated for multiple recipients.
120+ *
121+ * @param audiences the audience identifiers to add
122+ * @return this {@link TokenPayload} instance for method chaining
123+ */
58124 public TokenPayload withAudiences (String ... audiences ) {
59125 this .audiences .addAll (Arrays .asList (audiences ));
60126 return this ;
61127 }
62128
129+ /**
130+ * Sets the subject of the JWT payload.
131+ * <p>
132+ * Specifies the principal that is the subject of the token, typically identifying the user or
133+ * entity the token represents.
134+ *
135+ * @param subject the subject identifier
136+ * @return this {@link TokenPayload} instance for method chaining
137+ */
63138 public TokenPayload withSubject (String subject ) {
64139 this .subject = subject ;
65140 return this ;
66141 }
67142
143+ /**
144+ * Sets the issuer of the JWT payload.
145+ * <p>
146+ * Specifies the entity that issued the token, allowing recipients to verify the token's origin.
147+ *
148+ * @param issuer the issuer identifier
149+ * @return this {@link TokenPayload} instance for method chaining
150+ */
68151 public TokenPayload withIssuer (String issuer ) {
69152 this .issuer = issuer ;
70153 return this ;
71154 }
72155
156+ /**
157+ * Sets the unique identifier for the JWT payload.
158+ * <p>
159+ * Assigns a unique token ID to the JWT, which can be used to prevent token reuse or for
160+ * tracking purposes.
161+ *
162+ * @param tokenId the unique token identifier
163+ * @return this {@link TokenPayload} instance for method chaining
164+ */
73165 public TokenPayload withTokenId (String tokenId ) {
74166 this .tokenId = tokenId ;
75167 return this ;
76168 }
77169
170+ /**
171+ * Sets the expiration time for the JWT payload.
172+ * <p>
173+ * Specifies when the token expires, converted to seconds since the Unix epoch based on the
174+ * system's default time zone.
175+ *
176+ * @param expiresAt the expiration time as a {@link LocalDateTime}
177+ * @return this {@link TokenPayload} instance for method chaining
178+ */
78179 public TokenPayload withExpiresAt (LocalDateTime expiresAt ) {
79180 this .expiresAt = expiresAt .atZone (ZoneId .systemDefault ())
80181 .toInstant ()
81182 .getEpochSecond ();
82183 return this ;
83184 }
84185
186+ /**
187+ * Sets the time before which the JWT must not be accepted.
188+ * <p>
189+ * Specifies the "not before" time, converted to seconds since the Unix epoch based on the
190+ * system's default time zone.
191+ *
192+ * @param notBefore the time before which the token is invalid, as a {@link LocalDateTime}
193+ * @return this {@link TokenPayload} instance for method chaining
194+ */
85195 public TokenPayload withNotBefore (LocalDateTime notBefore ) {
86196 this .notBefore = notBefore .atZone (ZoneId .systemDefault ())
87197 .toInstant ()
88198 .getEpochSecond ();
89199 return this ;
90200 }
91201
202+ /**
203+ * Sets the issuance time for the JWT payload.
204+ * <p>
205+ * Specifies when the token was issued, converted to seconds since the Unix epoch based on the
206+ * system's default time zone.
207+ *
208+ * @param issuedAt the issuance time as a {@link LocalDateTime}
209+ * @return this {@link TokenPayload} instance for method chaining
210+ */
92211 public TokenPayload withIssuedAt (LocalDateTime issuedAt ) {
93212 this .issuedAt = issuedAt .atZone (ZoneId .systemDefault ())
94213 .toInstant ()
95214 .getEpochSecond ();
96215 return this ;
97216 }
98217
218+ /**
219+ * Adds a custom claim to the JWT payload.
220+ * <p>
221+ * Stores a custom key-value pair in the payload, provided the key is not a registered claim.
222+ * Registered claims must be set using their dedicated methods to ensure proper handling.
223+ *
224+ * @param name the name of the custom claim
225+ * @param value the value of the custom claim
226+ * @return this {@link TokenPayload} instance for method chaining
227+ * @throws IllegalStateException if the claim name is a registered claim
228+ */
99229 public TokenPayload withClaim (String name , String value ) {
100230 if (RegisteredClaims .VALUES .contains (name )) {
101231 throw new IllegalStateException ("Please set registered claims with pre-defined methods" );
@@ -105,10 +235,26 @@ public TokenPayload withClaim(String name, String value) {
105235 return this ;
106236 }
107237
238+ /**
239+ * Checks if the JWT payload has a valid issuer.
240+ * <p>
241+ * Returns {@code true} if the issuer is non-null and not blank, indicating that an issuer has
242+ * been set.
243+ *
244+ * @return {@code true} if an issuer is set, {@code false} otherwise
245+ */
108246 public boolean hasIssuer () {
109247 return Objects .nonNull (issuer ) && !issuer .isBlank ();
110248 }
111249
250+ /**
251+ * Retrieves the complete JWT payload as a map.
252+ * <p>
253+ * Constructs a map containing all custom claims, registered claims (if set), and audiences.
254+ * Only non-empty or non-blank values are included to ensure a clean payload.
255+ *
256+ * @return a map containing the JWT payload
257+ */
112258 public Map <String , Object > getPayload () {
113259 var _payload = new HashMap <>(payload );
114260
@@ -128,7 +274,7 @@ public Map<String, Object> getPayload() {
128274 .ifPresent ((jti ) -> _payload .put (RegisteredClaims .TOKEN_ID , jti ));
129275
130276 Optional .ofNullable (issuer )
131- .map ((iss ) -> !iss .isBlank ())
277+ .filter ((iss ) -> !iss .isBlank ())
132278 .ifPresent ((iss ) -> _payload .put (RegisteredClaims .ISSUER , iss ));
133279
134280 Optional .ofNullable (issuedAt )
@@ -139,5 +285,4 @@ public Map<String, Object> getPayload() {
139285
140286 return _payload ;
141287 }
142-
143288}
0 commit comments