Skip to content

Commit c7d6a87

Browse files
committed
Document Authentication.Builder
The commit documents the new Authentication Builder interface and its usage in the security filter chain. Closes gh-17861 Closes gh-17862
1 parent ef5b98c commit c7d6a87

File tree

6 files changed

+92
-2
lines changed

6 files changed

+92
-2
lines changed

docs/modules/ROOT/pages/servlet/authentication/architecture.adoc

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ In many cases, this is cleared after the user is authenticated, to ensure that i
140140
* `authorities`: The <<servlet-authentication-granted-authority,`GrantedAuthority`>> instances are high-level permissions the user is granted.
141141
Two examples are roles and scopes.
142142

143+
It is also equipped with a `Builder` that allows you to mutate an existing `Authentication` instance and potentially merge it with another.
144+
This is useful in scenarios like taking the authorities from one authentication step, like form login, and applying them to another, like one-time-token login, like so:
145+
146+
include-code::./CopyAuthoritiesTests[tag=springSecurity,indent=0]
147+
143148
[[servlet-authentication-granted-authority]]
144149
== GrantedAuthority
145150
javadoc:org.springframework.security.core.GrantedAuthority[] instances are high-level permissions that the user is granted.
@@ -231,8 +236,6 @@ In other cases, a client makes an unauthenticated request to a resource that the
231236
In this case, an implementation of `AuthenticationEntryPoint` is used to request credentials from the client.
232237
The `AuthenticationEntryPoint` implementation might perform a xref:servlet/authentication/passwords/form.adoc#servlet-authentication-form[redirect to a log in page], respond with an xref:servlet/authentication/passwords/basic.adoc#servlet-authentication-basic[WWW-Authenticate] header, or take other action.
233238

234-
235-
236239
// FIXME: authenticationsuccesshandler
237240
// FIXME: authenticationfailurehandler
238241

@@ -266,6 +269,8 @@ image:{icondir}/number_4.png[] If authentication is successful, then __Success__
266269

267270
* `SessionAuthenticationStrategy` is notified of a new login.
268271
See the javadoc:org.springframework.security.web.authentication.session.SessionAuthenticationStrategy[] interface.
272+
* Any already-authenticated `Authentication` in the <<servlet-authentication-securitycontextholder>> is loaded and its
273+
authorities are added to the returned <<servlet-authentication-authentication>>.
269274
* The <<servlet-authentication-authentication>> is set on the <<servlet-authentication-securitycontextholder>>.
270275
Later, if you need to save the `SecurityContext` so that it can be automatically set on future requests, `SecurityContextRepository#saveContext` must be explicitly invoked.
271276
See the javadoc:org.springframework.security.web.context.SecurityContextHolderFilter[] class.

docs/modules/ROOT/pages/servlet/authentication/passwords/basic.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ See the javadoc:org.springframework.security.web.AuthenticationEntryPoint[] inte
5656

5757
image:{icondir}/number_4.png[] If authentication is successful, then __Success__.
5858

59+
* Any already-authenticated `Authentication` in the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] is loaded and its
60+
authorities are added to the returned xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`].
5961
. The xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[Authentication] is set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder].
6062
. `RememberMeServices.loginSuccess` is invoked.
6163
If remember me is not configured, this is a no-op.

docs/modules/ROOT/pages/servlet/oauth2/resource-server/index.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,7 @@ image:{icondir}/number_3.png[] If authentication fails, then __Failure__
5656

5757
image:{icondir}/number_4.png[] If authentication is successful, then __Success__.
5858

59+
* Any already-authenticated `Authentication` in the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] is loaded and its
60+
authorities are added to the returned xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`].
5961
* The xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[Authentication] is set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder].
6062
* The `BearerTokenAuthenticationFilter` invokes `FilterChain.doFilter(request,response)` to continue with the rest of the application logic.

docs/modules/ROOT/pages/whats-new.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Each section that follows will indicate the more notable removals as well as the
1313

1414
* Removed `AuthorizationManager#check` in favor of `AuthorizationManager#authorize`
1515
* Added xref:servlet/authorization/architecture.adoc#authz-authorization-manager-factory[`AuthorizationManagerFactory`] for creating `AuthorizationManager` instances in xref:servlet/authorization/authorize-http-requests.adoc#customizing-authorization-managers[request-based] and xref:servlet/authorization/method-security.adoc#customizing-authorization-managers[method-based] authorization components
16+
* Added `Authentication.Builder` for mutating and merging `Authentication` instances
1617

1718
== Config
1819

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package org.springframework.security.docs.servlet.authentication.servletauthenticationauthentication;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import org.springframework.security.authentication.AuthenticationManager;
6+
import org.springframework.security.authentication.SecurityAssertions;
7+
import org.springframework.security.authentication.TestingAuthenticationToken;
8+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
9+
import org.springframework.security.authentication.ott.OneTimeTokenAuthentication;
10+
import org.springframework.security.core.Authentication;
11+
import org.springframework.security.core.authority.AuthorityUtils;
12+
import org.springframework.security.core.context.SecurityContextHolder;
13+
14+
import static org.mockito.ArgumentMatchers.any;
15+
import static org.mockito.BDDMockito.given;
16+
import static org.mockito.Mockito.mock;
17+
18+
public class CopyAuthoritiesTests {
19+
@Test
20+
void toBuilderWhenApplyThenCopies() {
21+
UsernamePasswordAuthenticationToken previous = new UsernamePasswordAuthenticationToken("alice", "pass",
22+
AuthorityUtils.createAuthorityList("FACTOR_PASSWORD"));
23+
SecurityContextHolder.getContext().setAuthentication(previous);
24+
Authentication latest = new OneTimeTokenAuthentication("bob",
25+
AuthorityUtils.createAuthorityList("FACTOR_OTT"));
26+
AuthenticationManager authenticationManager = mock(AuthenticationManager.class);
27+
given(authenticationManager.authenticate(any())).willReturn(latest);
28+
Authentication authenticationRequest = new TestingAuthenticationToken("user", "pass");
29+
// tag::springSecurity[]
30+
Authentication lastestResult = authenticationManager.authenticate(authenticationRequest);
31+
Authentication previousResult = SecurityContextHolder.getContext().getAuthentication();
32+
if (previousResult != null && previousResult.isAuthenticated()) {
33+
lastestResult = lastestResult.toBuilder()
34+
.authorities((a) -> a.addAll(previous.getAuthorities()))
35+
.build();
36+
}
37+
// end::springSecurity[]
38+
SecurityAssertions.assertThat(lastestResult).hasAuthorities("FACTOR_PASSWORD", "FACTOR_OTT");
39+
SecurityContextHolder.clearContext();
40+
}
41+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.springframework.security.kt.docs.servlet.authentication.servletauthenticationauthentication
2+
3+
import org.junit.jupiter.api.Test
4+
import org.mockito.ArgumentMatchers
5+
import org.mockito.BDDMockito
6+
import org.mockito.Mockito
7+
import org.springframework.security.authentication.AuthenticationManager
8+
import org.springframework.security.authentication.SecurityAssertions
9+
import org.springframework.security.authentication.TestingAuthenticationToken
10+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
11+
import org.springframework.security.authentication.ott.OneTimeTokenAuthentication
12+
import org.springframework.security.core.Authentication
13+
import org.springframework.security.core.authority.AuthorityUtils
14+
import org.springframework.security.core.context.SecurityContextHolder
15+
16+
class CopyAuthoritiesTests {
17+
@Test
18+
fun toBuilderWhenApplyThenCopies() {
19+
val previous: Authentication = UsernamePasswordAuthenticationToken("alice", "pass",
20+
AuthorityUtils.createAuthorityList("FACTOR_PASSWORD"))
21+
SecurityContextHolder.getContext().authentication = previous
22+
var latest: Authentication = OneTimeTokenAuthentication("bob",
23+
AuthorityUtils.createAuthorityList("FACTOR_OTT"))
24+
val authenticationManager: AuthenticationManager = Mockito.mock(AuthenticationManager::class.java)
25+
BDDMockito.given(authenticationManager.authenticate(ArgumentMatchers.any())).willReturn(latest)
26+
val authenticationRequest: Authentication = TestingAuthenticationToken("user", "pass")
27+
// tag::springSecurity[]
28+
var latestResult: Authentication = authenticationManager.authenticate(authenticationRequest)
29+
val previousResult = SecurityContextHolder.getContext().authentication;
30+
if (previousResult?.isAuthenticated == true) {
31+
latestResult = latestResult.toBuilder().authorities { a ->
32+
a.addAll(previousResult.authorities)
33+
}.build()
34+
}
35+
// end::springSecurity[]
36+
SecurityAssertions.assertThat(latestResult).hasAuthorities("FACTOR_PASSWORD", "FACTOR_OTT")
37+
SecurityContextHolder.clearContext()
38+
}
39+
}

0 commit comments

Comments
 (0)