Skip to content

Commit 21ff768

Browse files
committed
Move Builder to Authentication
Leaving the Builder in Authentication allows authentication implementations to implement Builder without needing to implement BuildableAuthentication. Issue gh-18052
1 parent 4102007 commit 21ff768

File tree

3 files changed

+120
-150
lines changed

3 files changed

+120
-150
lines changed

core/src/main/java/org/springframework/security/authentication/AbstractAuthenticationToken.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828

2929
import org.springframework.security.core.AuthenticatedPrincipal;
3030
import org.springframework.security.core.Authentication;
31-
import org.springframework.security.core.BuildableAuthentication;
3231
import org.springframework.security.core.CredentialsContainer;
3332
import org.springframework.security.core.GrantedAuthority;
3433
import org.springframework.security.core.authority.AuthorityUtils;
@@ -199,15 +198,15 @@ public String toString() {
199198
}
200199

201200
/**
202-
* A common abstract implementation of {@link BuildableAuthentication.Builder}. It
203-
* implements the builder methods that correspond to the {@link Authentication}
204-
* methods that {@link AbstractAuthenticationToken} implements
201+
* A common abstract implementation of {@link Authentication.Builder}. It implements
202+
* the builder methods that correspond to the {@link Authentication} methods that
203+
* {@link AbstractAuthenticationToken} implements
205204
*
206205
* @param <B>
207206
* @since 7.0
208207
*/
209208
protected abstract static class AbstractAuthenticationBuilder<B extends AbstractAuthenticationBuilder<B>>
210-
implements BuildableAuthentication.Builder<B> {
209+
implements Authentication.Builder<B> {
211210

212211
private boolean authenticated;
213212

core/src/main/java/org/springframework/security/core/Authentication.java

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
import java.io.Serializable;
2020
import java.security.Principal;
2121
import java.util.Collection;
22+
import java.util.Set;
23+
import java.util.function.Consumer;
24+
import java.util.stream.Collectors;
2225

2326
import org.jspecify.annotations.Nullable;
2427

@@ -136,4 +139,112 @@ public interface Authentication extends Principal, Serializable {
136139
*/
137140
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
138141

142+
/**
143+
* A builder based on a given {@link BuildableAuthentication} instance
144+
*
145+
* @author Josh Cummings
146+
* @since 7.0
147+
*/
148+
interface Builder<B extends Builder<B>> {
149+
150+
/**
151+
* Apply this authentication instance
152+
* <p>
153+
* By default, merges the authorities in the provided {@code authentication} with
154+
* the authentication being built. Only those authorities that haven't already
155+
* been specified to the builder will be added.
156+
* </p>
157+
* @param authentication the {@link Authentication} to appluy
158+
* @return the {@link Builder} for additional configuration
159+
* @see BuildableAuthentication#getAuthorities
160+
*/
161+
default B authentication(Authentication authentication) {
162+
return authorities((a) -> {
163+
Set<String> newAuthorities = a.stream()
164+
.map(GrantedAuthority::getAuthority)
165+
.collect(Collectors.toUnmodifiableSet());
166+
for (GrantedAuthority currentAuthority : authentication.getAuthorities()) {
167+
if (!newAuthorities.contains(currentAuthority.getAuthority())) {
168+
a.add(currentAuthority);
169+
}
170+
}
171+
});
172+
}
173+
174+
/**
175+
* Mutate the authorities with this {@link Consumer}.
176+
* <p>
177+
* Note that since a non-empty set of authorities implies an
178+
* {@link Authentication} is authenticated, this method also marks the
179+
* authentication as {@link #authenticated} by default.
180+
* </p>
181+
* @param authorities a consumer that receives the full set of authorities
182+
* @return the {@link Builder} for additional configuration
183+
* @see Authentication#getAuthorities
184+
*/
185+
B authorities(Consumer<Collection<GrantedAuthority>> authorities);
186+
187+
/**
188+
* Use this credential.
189+
* <p>
190+
* Note that since some credentials are insecure to store, this method is
191+
* implemented as unsupported by default. Only implement or use this method if you
192+
* support secure storage of the credential or if your implementation also
193+
* implements {@link CredentialsContainer} and the credentials are thereby erased.
194+
* </p>
195+
* @param credentials the credentials to use
196+
* @return the {@link Builder} for additional configuration
197+
* @see Authentication#getCredentials
198+
*/
199+
default B credentials(@Nullable Object credentials) {
200+
throw new UnsupportedOperationException(
201+
String.format("%s does not store credentials", this.getClass().getSimpleName()));
202+
}
203+
204+
/**
205+
* Use this details object.
206+
* <p>
207+
* Implementations may choose to use these {@code details} in combination with any
208+
* principal from the pre-existing {@link Authentication} instance.
209+
* </p>
210+
* @param details the details to use
211+
* @return the {@link Builder} for additional configuration
212+
* @see Authentication#getDetails
213+
*/
214+
B details(@Nullable Object details);
215+
216+
/**
217+
* Use this principal.
218+
* <p>
219+
* Note that in many cases, the principal is strongly-typed. Implementations may
220+
* choose to do a type check and are not necessarily expected to allow any object
221+
* as a principal.
222+
* </p>
223+
* <p>
224+
* Implementations may choose to use this {@code principal} in combination with
225+
* any principal from the pre-existing {@link Authentication} instance.
226+
* </p>
227+
* @param principal the principal to use
228+
* @return the {@link Builder} for additional configuration
229+
* @see Authentication#getPrincipal
230+
*/
231+
B principal(@Nullable Object principal);
232+
233+
/**
234+
* Mark this authentication as authenticated or not
235+
* @param authenticated whether this is an authenticated {@link Authentication}
236+
* instance
237+
* @return the {@link Builder} for additional configuration
238+
* @see Authentication#isAuthenticated
239+
*/
240+
B authenticated(boolean authenticated);
241+
242+
/**
243+
* Build an {@link Authentication} instance
244+
* @return the {@link Authentication} instance
245+
*/
246+
Authentication build();
247+
248+
}
249+
139250
}

core/src/main/java/org/springframework/security/core/BuildableAuthentication.java

Lines changed: 5 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -16,42 +16,11 @@
1616

1717
package org.springframework.security.core;
1818

19-
import java.util.Collection;
20-
import java.util.Set;
21-
import java.util.function.Consumer;
22-
import java.util.stream.Collectors;
23-
24-
import org.jspecify.annotations.Nullable;
25-
26-
import org.springframework.security.authentication.AuthenticationManager;
27-
import org.springframework.security.core.context.SecurityContextHolder;
28-
2919
/**
30-
* Represents the token for an authentication request or for an authenticated principal
31-
* once the request has been processed by the
32-
* {@link AuthenticationManager#authenticate(Authentication)} method.
33-
* <p>
34-
* Once the request has been authenticated, the <tt>Authentication</tt> will usually be
35-
* stored in a thread-local <tt>SecurityContext</tt> managed by the
36-
* {@link SecurityContextHolder} by the authentication mechanism which is being used. An
37-
* explicit authentication can be achieved, without using one of Spring Security's
38-
* authentication mechanisms, by creating an <tt>Authentication</tt> instance and using
39-
* the code:
40-
*
41-
* <pre>
42-
* SecurityContext context = SecurityContextHolder.createEmptyContext();
43-
* context.setAuthentication(anAuthentication);
44-
* SecurityContextHolder.setContext(context);
45-
* </pre>
20+
* An {@link Authentication} that is also buildable.
4621
*
47-
* Note that unless the <tt>Authentication</tt> has the <tt>authenticated</tt> property
48-
* set to <tt>true</tt>, it will still be authenticated by any security interceptor (for
49-
* method or web invocations) which encounters it.
50-
* <p>
51-
* In most cases, the framework transparently takes care of managing the security context
52-
* and authentication objects for you.
53-
*
54-
* @author Ben Alex
22+
* @author Josh Cummings
23+
* @since 7.0
5524
*/
5625
public interface BuildableAuthentication extends Authentication {
5726

@@ -69,118 +38,9 @@ public interface BuildableAuthentication extends Authentication {
6938
* {@link Authentication} interface and that custom information is often contained in
7039
* the {@link Authentication#getPrincipal} value.
7140
* </p>
72-
* @return an {@link Builder} for building a new {@link BuildableAuthentication} based
73-
* on this instance
74-
* @since 7.0
41+
* @return an {@link Builder} for building a new {@link Authentication} based on this
42+
* instance
7543
*/
7644
Builder<?> toBuilder();
7745

78-
/**
79-
* A builder based on a given {@link BuildableAuthentication} instance
80-
*
81-
* @author Josh Cummings
82-
* @since 7.0
83-
*/
84-
interface Builder<B extends Builder<B>> {
85-
86-
/**
87-
* Apply this authentication instance
88-
* <p>
89-
* By default, merges the authorities in the provided {@code authentication} with
90-
* the authentication being built. Only those authorities that haven't already
91-
* been specified to the builder will be added.
92-
* </p>
93-
* @param authentication the {@link Authentication} to appluy
94-
* @return the {@link Builder} for additional configuration
95-
* @see BuildableAuthentication#getAuthorities
96-
*/
97-
default B authentication(Authentication authentication) {
98-
return authorities((a) -> {
99-
Set<String> newAuthorities = a.stream()
100-
.map(GrantedAuthority::getAuthority)
101-
.collect(Collectors.toUnmodifiableSet());
102-
for (GrantedAuthority currentAuthority : authentication.getAuthorities()) {
103-
if (!newAuthorities.contains(currentAuthority.getAuthority())) {
104-
a.add(currentAuthority);
105-
}
106-
}
107-
});
108-
}
109-
110-
/**
111-
* Mutate the authorities with this {@link Consumer}.
112-
* <p>
113-
* Note that since a non-empty set of authorities implies an
114-
* {@link Authentication} is authenticated, this method also marks the
115-
* authentication as {@link #authenticated} by default.
116-
* </p>
117-
* @param authorities a consumer that receives the full set of authorities
118-
* @return the {@link Builder} for additional configuration
119-
* @see Authentication#getAuthorities
120-
*/
121-
B authorities(Consumer<Collection<GrantedAuthority>> authorities);
122-
123-
/**
124-
* Use this credential.
125-
* <p>
126-
* Note that since some credentials are insecure to store, this method is
127-
* implemented as unsupported by default. Only implement or use this method if you
128-
* support secure storage of the credential or if your implementation also
129-
* implements {@link CredentialsContainer} and the credentials are thereby erased.
130-
* </p>
131-
* @param credentials the credentials to use
132-
* @return the {@link Builder} for additional configuration
133-
* @see Authentication#getCredentials
134-
*/
135-
default B credentials(@Nullable Object credentials) {
136-
throw new UnsupportedOperationException(
137-
String.format("%s does not store credentials", this.getClass().getSimpleName()));
138-
}
139-
140-
/**
141-
* Use this details object.
142-
* <p>
143-
* Implementations may choose to use these {@code details} in combination with any
144-
* principal from the pre-existing {@link Authentication} instance.
145-
* </p>
146-
* @param details the details to use
147-
* @return the {@link Builder} for additional configuration
148-
* @see Authentication#getDetails
149-
*/
150-
B details(@Nullable Object details);
151-
152-
/**
153-
* Use this principal.
154-
* <p>
155-
* Note that in many cases, the principal is strongly-typed. Implementations may
156-
* choose to do a type check and are not necessarily expected to allow any object
157-
* as a principal.
158-
* </p>
159-
* <p>
160-
* Implementations may choose to use this {@code principal} in combination with
161-
* any principal from the pre-existing {@link Authentication} instance.
162-
* </p>
163-
* @param principal the principal to use
164-
* @return the {@link Builder} for additional configuration
165-
* @see Authentication#getPrincipal
166-
*/
167-
B principal(@Nullable Object principal);
168-
169-
/**
170-
* Mark this authentication as authenticated or not
171-
* @param authenticated whether this is an authenticated {@link Authentication}
172-
* instance
173-
* @return the {@link Builder} for additional configuration
174-
* @see Authentication#isAuthenticated
175-
*/
176-
B authenticated(boolean authenticated);
177-
178-
/**
179-
* Build an {@link Authentication} instance
180-
* @return the {@link Authentication} instance
181-
*/
182-
Authentication build();
183-
184-
}
185-
18646
}

0 commit comments

Comments
 (0)