Skip to content

Commit 452cfbc

Browse files
committed
test: add JUnit tests (≥90% coverage); docs: comprehensive English JavaDoc
1 parent b7daedd commit 452cfbc

11 files changed

Lines changed: 1145 additions & 69 deletions

pom.xml

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@
5959
<maven-jacoco-plugin.version>0.8.15</maven-jacoco-plugin.version>
6060

6161
<spring-framework.version>5.3.39</spring-framework.version>
62-
<javax-servlet-api.version>4.0.1</javax-servlet-api.version></properties>
62+
<javax-servlet-api.version>4.0.1</javax-servlet-api.version>
63+
<mockito.version>5.20.0</mockito.version></properties>
6364

6465
<!-- 依赖版本统一管理(dependencyManagement) -->
6566
<dependencyManagement>
@@ -127,6 +128,26 @@
127128
<artifactId>spring-security-web</artifactId>
128129
<version>${spring-security.version}</version>
129130
</dependency>
131+
<!-- For JUnit 5 (Jupiter) -->
132+
<dependency>
133+
<groupId>org.junit.jupiter</groupId>
134+
<artifactId>junit-jupiter</artifactId>
135+
<version>${junit-jupiter.version}</version>
136+
<scope>test</scope>
137+
</dependency>
138+
<!-- Mockito test dependency for JUnit 5 integration -->
139+
<dependency>
140+
<groupId>org.mockito</groupId>
141+
<artifactId>mockito-core</artifactId>
142+
<version>${mockito.version}</version>
143+
<scope>test</scope>
144+
</dependency>
145+
<dependency>
146+
<groupId>org.mockito</groupId>
147+
<artifactId>mockito-junit-jupiter</artifactId>
148+
<version>${mockito.version}</version>
149+
<scope>test</scope>
150+
</dependency>
130151
</dependencies>
131152
</dependencyManagement>
132153

@@ -185,6 +206,24 @@
185206
<groupId>org.springframework.security</groupId>
186207
<artifactId>spring-security-web</artifactId>
187208
</dependency>
209+
210+
<!-- For JUnit 5 (Jupiter) testing -->
211+
<dependency>
212+
<groupId>org.junit.jupiter</groupId>
213+
<artifactId>junit-jupiter</artifactId>
214+
<scope>test</scope>
215+
</dependency>
216+
<!-- Mockito for JUnit 5 -->
217+
<dependency>
218+
<groupId>org.mockito</groupId>
219+
<artifactId>mockito-core</artifactId>
220+
<scope>test</scope>
221+
</dependency>
222+
<dependency>
223+
<groupId>org.mockito</groupId>
224+
<artifactId>mockito-junit-jupiter</artifactId>
225+
<scope>test</scope>
226+
</dependency>
188227
</dependencies>
189228

190229
<!-- 构建配置(Build) -->

src/main/java/org/springframework/security/boot/JwtAuthenticationToken.java

Lines changed: 143 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
/*
2-
* Copyright (c) 2018, Loong Wan (https://github.com/loong10k).
2+
* Copyright (c) 2018-present, easy-4-java (https://github.com/easy-4-java).
33
*
4-
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5-
* use this file except in compliance with the License. You may obtain a copy of
6-
* the License at
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
77
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
8+
* http://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12-
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13-
* License for the specific language governing permissions and limitations under
14-
* the License.
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
1515
*/
1616
package org.springframework.security.boot.jwt.authentication;
1717

@@ -21,38 +21,86 @@
2121
import org.springframework.security.core.GrantedAuthority;
2222

2323
/**
24-
* Jwt认证 (authentication) Token
25-
* @author [@Loong Wan](https://github.com/loong10k)
24+
* Spring Security {@link AbstractAuthenticationToken} carrying the
25+
* authentication-time credentials of a JSON Web Token (JWT) login attempt.
26+
*
27+
* <p>This token is produced by the JWT authentication entry-point and is
28+
* consumed by an {@code AuthenticationManager} (or a dedicated
29+
* {@code AuthenticationProvider}). Its lifecycle mirrors the canonical
30+
* {@code UsernamePasswordAuthenticationToken}: an unauthenticated instance is
31+
* built from the raw request payload, then a second authenticated instance
32+
* is constructed by the manager once the JWT signature has been validated
33+
* and the principal has been resolved.</p>
34+
*
35+
* <p>In addition to the standard principal/credentials pair, the token
36+
* carries a few optional request-side hints:</p>
37+
* <ul>
38+
* <li>{@link #sign} &mdash; the request parameter signature used by the
39+
* replay-protection layer;</li>
40+
* <li>{@link #longitude} / {@link #latitude} &mdash; the latest known
41+
* geo-location of the user device, which may be validated by a
42+
* location-aware authentication provider.</li>
43+
* </ul>
44+
*
45+
* <p>For authorization flows that only require a principal (already trusted),
46+
* prefer {@link JwtAuthorizationToken}.</p>
47+
*
48+
* @author <a href="https://github.com/loong10k">Loong Wan</a>
49+
* @since 3.0.0
50+
* @see JwtAuthorizationToken
51+
* @see AbstractAuthenticationToken
2652
*/
2753
@SuppressWarnings("serial")
2854
public class JwtAuthenticationToken extends AbstractAuthenticationToken {
2955

3056
// ~ Instance fields
3157
// ================================================================================================
3258

59+
/**
60+
* The authenticated principal (typically a username, user-id, or a fully
61+
* resolved {@code UserDetails} instance after authentication). Immutable
62+
* for the lifetime of this token.
63+
*/
3364
private final Object principal;
65+
66+
/**
67+
* The raw credentials supplied with the login attempt &mdash; usually the
68+
* bearer JWT string. Cleared by {@link #eraseCredentials()} once the
69+
* token has been authenticated.
70+
*/
3471
private Object credentials;
72+
3573
/**
36-
* 请求参数签名(可选)
74+
* Optional request parameter signature used for replay-protection /
75+
* anti-tamper checks. May be {@code null} when the caller does not
76+
* participate in the signing scheme.
3777
*/
3878
private String sign;
79+
3980
/**
40-
* 用户最新经度(可选)
81+
* Optional latest known longitude of the user device, expressed in
82+
* decimal degrees. Defaults to {@code 0.0} when not supplied.
4183
*/
4284
private double longitude;
85+
4386
/**
44-
* 用户最新纬度(可选)
87+
* Optional latest known latitude of the user device, expressed in
88+
* decimal degrees. Defaults to {@code 0.0} when not supplied.
4589
*/
4690
private double latitude;
47-
91+
4892
// ~ Constructors
4993
// ===================================================================================================
5094

5195
/**
52-
* This constructor can be safely used by any code that wishes to create a
53-
* <code>JwtAuthenticationToken</code>, as the {@link #isAuthenticated()}
54-
* will return <code>false</code>.
96+
* Builds an unauthenticated token from the raw principal/credentials pair
97+
* extracted from the incoming request. {@link #isAuthenticated()} returns
98+
* {@code false} for instances created through this constructor.
5599
*
100+
* @param principal the user identity to authenticate; typically a
101+
* username or user-id string, never {@code null}.
102+
* @param credentials the credentials proving the principal's identity,
103+
* usually a JWT bearer string, never {@code null}.
56104
*/
57105
public JwtAuthenticationToken(Object principal, Object credentials) {
58106
super(null);
@@ -62,14 +110,17 @@ public JwtAuthenticationToken(Object principal, Object credentials) {
62110
}
63111

64112
/**
65-
* This constructor should only be used by <code>AuthenticationManager</code> or
66-
* <code>AuthenticationProvider</code> implementations that are satisfied with
67-
* producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>)
68-
* authentication token.
113+
* Builds a trusted (already-authenticated) token. Should only be invoked
114+
* by an {@code AuthenticationManager} or {@code AuthenticationProvider}
115+
* implementation that has just verified the JWT signature.
69116
*
70-
* @param principal
71-
* @param credentials
72-
* @param authorities
117+
* @param principal the resolved principal (often a {@code UserDetails}),
118+
* never {@code null}.
119+
* @param credentials the original bearer token; may be {@code null} if
120+
* the manager has already erased sensitive material.
121+
* @param authorities the granted authorities for the authenticated user,
122+
* may be {@code null} or empty when the user has no
123+
* role mappings.
73124
*/
74125
public JwtAuthenticationToken(Object principal, Object credentials,
75126
Collection<? extends GrantedAuthority> authorities) {
@@ -82,14 +133,36 @@ public JwtAuthenticationToken(Object principal, Object credentials,
82133
// ~ Methods
83134
// ========================================================================================================
84135

136+
/**
137+
* Returns the credentials that were supplied with this authentication
138+
* attempt, typically the bearer JWT string.
139+
*
140+
* @return the credentials object, possibly {@code null} after
141+
* {@link #eraseCredentials()} has been invoked.
142+
*/
85143
public Object getCredentials() {
86144
return this.credentials;
87145
}
88146

147+
/**
148+
* Returns the principal associated with this authentication request.
149+
*
150+
* @return the principal object; never {@code null}.
151+
*/
89152
public Object getPrincipal() {
90153
return this.principal;
91154
}
92155

156+
/**
157+
* Always rejects a {@code true} transition &mdash; callers must
158+
* construct a fresh authenticated token via
159+
* {@link #JwtAuthenticationToken(Object, Object, Collection)} instead.
160+
*
161+
* @param isAuthenticated {@code true} would mark the token as trusted;
162+
* this implementation refuses and throws.
163+
* @throws IllegalArgumentException when {@code isAuthenticated} is
164+
* {@code true}.
165+
*/
93166
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
94167
if (isAuthenticated) {
95168
throw new IllegalArgumentException(
@@ -99,32 +172,76 @@ public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentExce
99172
super.setAuthenticated(false);
100173
}
101174

175+
/**
176+
* Clears sensitive material from this token. Invoked by the Spring
177+
* Security framework after the authentication result has been returned
178+
* to the caller, so that the bearer JWT is not retained any longer than
179+
* necessary.
180+
*/
102181
@Override
103182
public void eraseCredentials() {
104183
super.eraseCredentials();
105184
credentials = null;
106185
}
107-
186+
187+
/**
188+
* Returns the optional request parameter signature attached to the
189+
* authentication attempt.
190+
*
191+
* @return the signature string, or {@code null} if none was provided.
192+
*/
108193
public String getSign() {
109194
return sign;
110195
}
111196

197+
/**
198+
* Stores an optional request parameter signature on this token.
199+
*
200+
* @param sign the signature, typically produced by the caller using a
201+
* shared secret; may be {@code null}.
202+
*/
112203
public void setSign(String sign) {
113204
this.sign = sign;
114205
}
115206

207+
/**
208+
* Returns the latest known longitude of the user device.
209+
*
210+
* @return longitude in decimal degrees; defaults to {@code 0.0}.
211+
*/
116212
public double getLongitude() {
117213
return longitude;
118214
}
119215

216+
/**
217+
* Stores the latest known longitude of the user device.
218+
*
219+
* @param longitude decimal-degree longitude in the range
220+
* {@code [-180.0, +180.0]}; values outside that range
221+
* are accepted but should be rejected by the
222+
* authentication provider.
223+
*/
120224
public void setLongitude(double longitude) {
121225
this.longitude = longitude;
122226
}
123227

228+
/**
229+
* Returns the latest known latitude of the user device.
230+
*
231+
* @return latitude in decimal degrees; defaults to {@code 0.0}.
232+
*/
124233
public double getLatitude() {
125234
return latitude;
126235
}
127236

237+
/**
238+
* Stores the latest known latitude of the user device.
239+
*
240+
* @param latitude decimal-degree latitude in the range
241+
* {@code [-90.0, +90.0]}; values outside that range are
242+
* accepted but should be rejected by the authentication
243+
* provider.
244+
*/
128245
public void setLatitude(double latitude) {
129246
this.latitude = latitude;
130247
}
Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,60 @@
11
/*
2-
* Copyright (c) 2018, Loong Wan (https://github.com/loong10k).
2+
* Copyright (c) 2018-present, easy-4-java (https://github.com/easy-4-java).
33
*
4-
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5-
* use this file except in compliance with the License. You may obtain a copy of
6-
* the License at
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
77
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
8+
* http://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12-
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13-
* License for the specific language governing permissions and limitations under
14-
* the License.
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
1515
*/
1616
package org.springframework.security.boot.jwt.authentication.server;
1717

1818
import org.springframework.security.authentication.ReactiveAuthenticationManager;
1919
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
2020

2121
/**
22-
* 1、JWT Authentication Filter For Reactive (负责请求拦截)
23-
* @author [@Loong Wan](https://github.com/loong10k)
22+
* Reactive JWT authentication filter that intercepts incoming WebFlux
23+
* requests and delegates them to a {@link ReactiveAuthenticationManager}.
24+
*
25+
* <p>This filter is intentionally minimal: it extends Spring Security's
26+
* {@link AuthenticationWebFilter} so that it inherits the framework's
27+
* standard conversion between HTTP authentication requests and
28+
* {@code Authentication} objects, while leaving the actual JWT parsing,
29+
* signature verification and principal resolution to a configurable
30+
* {@code ReactiveAuthenticationManager} supplied at construction time.</p>
31+
*
32+
* <p>The filter is wired up by the surrounding application &mdash; typically
33+
* inside a {@code SecurityWebFilterChain} bean &mdash; and once registered
34+
* it will:</p>
35+
* <ol>
36+
* <li>convert each inbound exchange into an authentication request,</li>
37+
* <li>delegate to the configured manager,</li>
38+
* <li>populate the reactive {@code SecurityContext} on success.</li>
39+
* </ol>
40+
*
41+
* @author <a href="https://github.com/loong10k">Loong Wan</a>
42+
* @since 3.0.0
43+
* @see AuthenticationWebFilter
44+
* @see ReactiveAuthenticationManager
2445
*/
2546
public class JwtAuthenticationWebFilter extends AuthenticationWebFilter {
2647

48+
/**
49+
* Constructs a new JWT authentication filter that delegates to the
50+
* supplied {@link ReactiveAuthenticationManager}.
51+
*
52+
* @param authenticationManager the reactive manager responsible for
53+
* validating the JWT and resolving the
54+
* principal; must not be {@code null}.
55+
*/
2756
public JwtAuthenticationWebFilter(ReactiveAuthenticationManager authenticationManager) {
2857
super(authenticationManager);
2958
}
30-
59+
3160
}

0 commit comments

Comments
 (0)