Skip to content

Commit 7e8aade

Browse files
committed
Multi-tenancy for Resource Server
Fixes: gh-5351
1 parent e5249bd commit 7e8aade

File tree

15 files changed

+762
-27
lines changed

15 files changed

+762
-27
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.springframework.core.convert.converter.Converter;
2323
import org.springframework.security.authentication.AbstractAuthenticationToken;
2424
import org.springframework.security.authentication.AuthenticationManager;
25+
import org.springframework.security.authentication.AuthenticationManagerResolver;
2526
import org.springframework.security.authentication.AuthenticationProvider;
2627
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
2728
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
@@ -32,9 +33,9 @@
3233
import org.springframework.security.oauth2.jwt.Jwt;
3334
import org.springframework.security.oauth2.jwt.JwtDecoder;
3435
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
35-
import org.springframework.security.oauth2.server.resource.authentication.OAuth2IntrospectionAuthenticationProvider;
3636
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
3737
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
38+
import org.springframework.security.oauth2.server.resource.authentication.OAuth2IntrospectionAuthenticationProvider;
3839
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
3940
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter;
4041
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
@@ -128,6 +129,7 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
128129

129130
private final ApplicationContext context;
130131

132+
private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
131133
private BearerTokenResolver bearerTokenResolver;
132134

133135
private JwtConfigurer jwtConfigurer;
@@ -154,6 +156,13 @@ public OAuth2ResourceServerConfigurer<H> authenticationEntryPoint(Authentication
154156
return this;
155157
}
156158

159+
public OAuth2ResourceServerConfigurer<H> authenticationManagerResolver
160+
(AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver) {
161+
Assert.notNull(authenticationManagerResolver, "authenticationManagerResolver cannot be null");
162+
this.authenticationManagerResolver = authenticationManagerResolver;
163+
return this;
164+
}
165+
157166
public OAuth2ResourceServerConfigurer<H> bearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
158167
Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null");
159168
this.bearerTokenResolver = bearerTokenResolver;
@@ -188,10 +197,12 @@ public void configure(H http) throws Exception {
188197
BearerTokenResolver bearerTokenResolver = getBearerTokenResolver();
189198
this.requestMatcher.setBearerTokenResolver(bearerTokenResolver);
190199

191-
AuthenticationManager manager = http.getSharedObject(AuthenticationManager.class);
200+
AuthenticationManagerResolver resolver = this.authenticationManagerResolver;
201+
if (resolver == null) {
202+
resolver = request -> http.getSharedObject(AuthenticationManager.class);
203+
}
192204

193-
BearerTokenAuthenticationFilter filter =
194-
new BearerTokenAuthenticationFilter(manager);
205+
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver);
195206
filter.setBearerTokenResolver(bearerTokenResolver);
196207
filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
197208
filter = postProcess(filter);
@@ -203,7 +214,9 @@ public void configure(H http) throws Exception {
203214
"same time");
204215
}
205216

206-
if (this.jwtConfigurer == null && this.opaqueTokenConfigurer == null) {
217+
if (this.jwtConfigurer == null && this.opaqueTokenConfigurer == null &&
218+
this.authenticationManagerResolver == null ) {
219+
207220
throw new IllegalStateException("Jwt and Opaque Token are the only supported formats for bearer tokens " +
208221
"in Spring Security and neither was found. Make sure to configure JWT " +
209222
"via http.oauth2ResourceServer().jwt() or Opaque Tokens via " +

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationProvider.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ public final class JwtAuthenticationProvider implements AuthenticationProvider {
6868

6969
public JwtAuthenticationProvider(JwtDecoder jwtDecoder) {
7070
Assert.notNull(jwtDecoder, "jwtDecoder cannot be null");
71-
7271
this.jwtDecoder = jwtDecoder;
7372
}
7473

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilter.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
2424

2525
import org.springframework.security.authentication.AuthenticationDetailsSource;
2626
import org.springframework.security.authentication.AuthenticationManager;
27+
import org.springframework.security.authentication.AuthenticationManagerResolver;
2728
import org.springframework.security.core.Authentication;
2829
import org.springframework.security.core.AuthenticationException;
2930
import org.springframework.security.core.context.SecurityContext;
@@ -51,7 +52,7 @@
5152
* @see JwtAuthenticationProvider
5253
*/
5354
public final class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
54-
private final AuthenticationManager authenticationManager;
55+
private final AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
5556

5657
private final AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource =
5758
new WebAuthenticationDetailsSource();
@@ -60,13 +61,24 @@ public final class BearerTokenAuthenticationFilter extends OncePerRequestFilter
6061

6162
private AuthenticationEntryPoint authenticationEntryPoint = new BearerTokenAuthenticationEntryPoint();
6263

64+
/**
65+
* Construct a {@code BearerTokenAuthenticationFilter} using the provided parameter(s)
66+
* @param authenticationManagerResolver
67+
*/
68+
public BearerTokenAuthenticationFilter
69+
(AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver) {
70+
71+
Assert.notNull(authenticationManagerResolver, "authenticationManagerResolver cannot be null");
72+
this.authenticationManagerResolver = authenticationManagerResolver;
73+
}
74+
6375
/**
6476
* Construct a {@code BearerTokenAuthenticationFilter} using the provided parameter(s)
6577
* @param authenticationManager
6678
*/
6779
public BearerTokenAuthenticationFilter(AuthenticationManager authenticationManager) {
6880
Assert.notNull(authenticationManager, "authenticationManager cannot be null");
69-
this.authenticationManager = authenticationManager;
81+
this.authenticationManagerResolver = request -> authenticationManager;
7082
}
7183

7284
/**
@@ -104,7 +116,8 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
104116
authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
105117

106118
try {
107-
Authentication authenticationResult = this.authenticationManager.authenticate(authenticationRequest);
119+
AuthenticationManager authenticationManager = this.authenticationManagerResolver.resolve(request);
120+
Authentication authenticationResult = authenticationManager.authenticate(authenticationRequest);
108121

109122
SecurityContext context = SecurityContextHolder.createEmptyContext();
110123
context.setAuthentication(authenticationResult);
@@ -139,5 +152,4 @@ public final void setAuthenticationEntryPoint(final AuthenticationEntryPoint aut
139152
Assert.notNull(authenticationEntryPoint, "authenticationEntryPoint cannot be null");
140153
this.authenticationEntryPoint = authenticationEntryPoint;
141154
}
142-
143155
}

oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilterTests.java

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,12 +17,12 @@
1717

1818
import java.io.IOException;
1919
import javax.servlet.ServletException;
20+
import javax.servlet.http.HttpServletRequest;
2021

2122
import org.junit.Before;
2223
import org.junit.Test;
2324
import org.junit.runner.RunWith;
2425
import org.mockito.ArgumentCaptor;
25-
import org.mockito.InjectMocks;
2626
import org.mockito.Mock;
2727
import org.mockito.junit.MockitoJUnitRunner;
2828

@@ -31,6 +31,7 @@
3131
import org.springframework.mock.web.MockHttpServletRequest;
3232
import org.springframework.mock.web.MockHttpServletResponse;
3333
import org.springframework.security.authentication.AuthenticationManager;
34+
import org.springframework.security.authentication.AuthenticationManagerResolver;
3435
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
3536
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
3637
import org.springframework.security.oauth2.server.resource.BearerTokenError;
@@ -57,6 +58,9 @@ public class BearerTokenAuthenticationFilterTests {
5758
@Mock
5859
AuthenticationManager authenticationManager;
5960

61+
@Mock
62+
AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
63+
6064
@Mock
6165
BearerTokenResolver bearerTokenResolver;
6266

@@ -66,27 +70,38 @@ public class BearerTokenAuthenticationFilterTests {
6670

6771
MockFilterChain filterChain;
6872

69-
@InjectMocks
70-
BearerTokenAuthenticationFilter filter;
71-
7273
@Before
7374
public void httpMocks() {
7475
this.request = new MockHttpServletRequest();
7576
this.response = new MockHttpServletResponse();
7677
this.filterChain = new MockFilterChain();
7778
}
7879

79-
@Before
80-
public void setterMocks() {
81-
this.filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
82-
this.filter.setBearerTokenResolver(this.bearerTokenResolver);
80+
@Test
81+
public void doFilterWhenBearerTokenPresentThenAuthenticates() throws ServletException, IOException {
82+
when(this.bearerTokenResolver.resolve(this.request)).thenReturn("token");
83+
84+
BearerTokenAuthenticationFilter filter =
85+
addMocks(new BearerTokenAuthenticationFilter(this.authenticationManager));
86+
filter.doFilter(this.request, this.response, this.filterChain);
87+
88+
ArgumentCaptor<BearerTokenAuthenticationToken> captor =
89+
ArgumentCaptor.forClass(BearerTokenAuthenticationToken.class);
90+
91+
verify(this.authenticationManager).authenticate(captor.capture());
92+
93+
assertThat(captor.getValue().getPrincipal()).isEqualTo("token");
8394
}
8495

8596
@Test
86-
public void doFilterWhenBearerTokenPresentThenAuthenticates() throws ServletException, IOException {
97+
public void doFilterWhenUsingAuthenticationManagerResolverThenAuthenticates() throws Exception {
98+
BearerTokenAuthenticationFilter filter =
99+
addMocks(new BearerTokenAuthenticationFilter(this.authenticationManagerResolver));
100+
87101
when(this.bearerTokenResolver.resolve(this.request)).thenReturn("token");
102+
when(this.authenticationManagerResolver.resolve(any())).thenReturn(this.authenticationManager);
88103

89-
this.filter.doFilter(this.request, this.response, this.filterChain);
104+
filter.doFilter(this.request, this.response, this.filterChain);
90105

91106
ArgumentCaptor<BearerTokenAuthenticationToken> captor =
92107
ArgumentCaptor.forClass(BearerTokenAuthenticationToken.class);
@@ -137,36 +152,56 @@ public void doFilterWhenAuthenticationFailsThenPropagatesError() throws ServletE
137152
when(this.authenticationManager.authenticate(any(BearerTokenAuthenticationToken.class)))
138153
.thenThrow(exception);
139154

140-
this.filter.doFilter(this.request, this.response, this.filterChain);
155+
BearerTokenAuthenticationFilter filter =
156+
addMocks(new BearerTokenAuthenticationFilter(this.authenticationManager));
157+
filter.doFilter(this.request, this.response, this.filterChain);
141158

142159
verify(this.authenticationEntryPoint).commence(this.request, this.response, exception);
143160
}
144161

145162
@Test
146163
public void setAuthenticationEntryPointWhenNullThenThrowsException() {
147-
assertThatCode(() -> this.filter.setAuthenticationEntryPoint(null))
164+
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager);
165+
assertThatCode(() -> filter.setAuthenticationEntryPoint(null))
148166
.isInstanceOf(IllegalArgumentException.class)
149167
.hasMessageContaining("authenticationEntryPoint cannot be null");
150168
}
151169

152170
@Test
153171
public void setBearerTokenResolverWhenNullThenThrowsException() {
154-
assertThatCode(() -> this.filter.setBearerTokenResolver(null))
172+
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager);
173+
assertThatCode(() -> filter.setBearerTokenResolver(null))
155174
.isInstanceOf(IllegalArgumentException.class)
156175
.hasMessageContaining("bearerTokenResolver cannot be null");
157176
}
158177

159178
@Test
160179
public void constructorWhenNullAuthenticationManagerThenThrowsException() {
161-
assertThatCode(() -> new BearerTokenAuthenticationFilter(null))
180+
assertThatCode(() -> new BearerTokenAuthenticationFilter((AuthenticationManager) null))
162181
.isInstanceOf(IllegalArgumentException.class)
163182
.hasMessageContaining("authenticationManager cannot be null");
164183
}
165184

185+
@Test
186+
public void constructorWhenNullAuthenticationManagerResolverThenThrowsException() {
187+
assertThatCode(() ->
188+
new BearerTokenAuthenticationFilter((AuthenticationManagerResolver<HttpServletRequest>) null))
189+
.isInstanceOf(IllegalArgumentException.class)
190+
.hasMessageContaining("authenticationManagerResolver cannot be null");
191+
}
192+
193+
private BearerTokenAuthenticationFilter addMocks(BearerTokenAuthenticationFilter filter) {
194+
filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
195+
filter.setBearerTokenResolver(this.bearerTokenResolver);
196+
return filter;
197+
}
198+
166199
private void dontAuthenticate()
167200
throws ServletException, IOException {
168201

169-
this.filter.doFilter(this.request, this.response, this.filterChain);
202+
BearerTokenAuthenticationFilter filter =
203+
addMocks(new BearerTokenAuthenticationFilter(this.authenticationManager));
204+
filter.doFilter(this.request, this.response, this.filterChain);
170205

171206
verifyNoMoreInteractions(this.authenticationManager);
172207
}

0 commit comments

Comments
 (0)