Merge Formatting Changes

Issue gh-8945
This commit is contained in:
Rob Winch
2020-08-24 16:38:12 -05:00
2779 changed files with 82438 additions and 91751 deletions

View File

@@ -16,8 +16,16 @@
package org.springframework.security.config.annotation.authentication.ldap;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.Collections;
import java.util.List;
import javax.naming.directory.SearchControls;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
@@ -39,16 +47,14 @@ import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.List;
import javax.naming.directory.SearchControls;
import static java.util.Collections.singleton;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
static Integer port;
@Rule
public final SpringTestRule spring = new SpringTestRule();
@@ -72,37 +78,13 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
assertThat(ReflectionTestUtils.getField(getAuthoritiesMapper(provider), "prefix")).isEqualTo("ROLE_");
}
@EnableWebSecurity
static class DefaultLdapConfig extends BaseLdapProviderConfig {
// @formatter:off
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.contextSource(contextSource())
.userDnPatterns("uid={0},ou=people");
}
// @formatter:on
}
@Test
public void groupRolesCustom() {
this.spring.register(GroupRolesConfig.class).autowire();
LdapAuthenticationProvider provider = ldapProvider();
assertThat(ReflectionTestUtils.getField(getAuthoritiesPopulator(provider), "groupRoleAttribute")).isEqualTo("group");
}
@EnableWebSecurity
static class GroupRolesConfig extends BaseLdapProviderConfig {
// @formatter:off
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.contextSource(contextSource())
.userDnPatterns("uid={0},ou=people")
.groupRoleAttribute("group");
}
// @formatter:on
assertThat(ReflectionTestUtils.getField(getAuthoritiesPopulator(provider), "groupRoleAttribute"))
.isEqualTo("group");
}
@Test
@@ -110,20 +92,8 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
this.spring.register(GroupSearchConfig.class).autowire();
LdapAuthenticationProvider provider = ldapProvider();
assertThat(ReflectionTestUtils.getField(getAuthoritiesPopulator(provider), "groupSearchFilter")).isEqualTo("ou=groupName");
}
@EnableWebSecurity
static class GroupSearchConfig extends BaseLdapProviderConfig {
// @formatter:off
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.contextSource(contextSource())
.userDnPatterns("uid={0},ou=people")
.groupSearchFilter("ou=groupName");
}
// @formatter:on
assertThat(ReflectionTestUtils.getField(getAuthoritiesPopulator(provider), "groupSearchFilter"))
.isEqualTo("ou=groupName");
}
@Test
@@ -135,20 +105,6 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
.extracting("searchScope").isEqualTo(SearchControls.SUBTREE_SCOPE);
}
@EnableWebSecurity
static class GroupSubtreeSearchConfig extends BaseLdapProviderConfig {
// @formatter:off
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.contextSource(contextSource())
.userDnPatterns("uid={0},ou=people")
.groupSearchFilter("ou=groupName")
.groupSearchSubtree(true);
}
// @formatter:on
}
@Test
public void rolePrefixCustom() {
this.spring.register(RolePrefixConfig.class).autowire();
@@ -157,39 +113,13 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
assertThat(ReflectionTestUtils.getField(getAuthoritiesMapper(provider), "prefix")).isEqualTo("role_");
}
@EnableWebSecurity
static class RolePrefixConfig extends BaseLdapProviderConfig {
// @formatter:off
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.contextSource(contextSource())
.userDnPatterns("uid={0},ou=people")
.rolePrefix("role_");
}
// @formatter:on
}
@Test
public void bindAuthentication() throws Exception {
this.spring.register(BindAuthenticationConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
.andExpect(authenticated().withUsername("bob").withAuthorities(singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS"))));
}
@EnableWebSecurity
static class BindAuthenticationConfig extends BaseLdapServerConfig {
// @formatter:off
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.contextSource(contextSource())
.groupSearchBase("ou=groups")
.groupSearchFilter("(member={0})")
.userDnPatterns("uid={0},ou=people");
}
// @formatter:on
.andExpect(authenticated().withUsername("bob")
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS"))));
}
// SEC-2472
@@ -198,26 +128,13 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
this.spring.register(PasswordEncoderConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bcrypt").password("password"))
.andExpect(authenticated().withUsername("bcrypt").withAuthorities(singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS"))));
}
@EnableWebSecurity
static class PasswordEncoderConfig extends BaseLdapServerConfig {
// @formatter:off
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.contextSource(contextSource())
.passwordEncoder(new BCryptPasswordEncoder())
.groupSearchBase("ou=groups")
.groupSearchFilter("(member={0})")
.userDnPatterns("uid={0},ou=people");
}
// @formatter:on
.andExpect(authenticated().withUsername("bcrypt")
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS"))));
}
private LdapAuthenticationProvider ldapProvider() {
return ((List<LdapAuthenticationProvider>) ReflectionTestUtils.getField(authenticationManager, "providers")).get(0);
return ((List<LdapAuthenticationProvider>) ReflectionTestUtils.getField(this.authenticationManager,
"providers")).get(0);
}
private LdapAuthoritiesPopulator getAuthoritiesPopulator(LdapAuthenticationProvider provider) {
@@ -228,23 +145,150 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
return (GrantedAuthoritiesMapper) ReflectionTestUtils.getField(provider, "authoritiesMapper");
}
static int getPort() throws IOException {
if (port == null) {
ServerSocket socket = new ServerSocket(0);
port = socket.getLocalPort();
socket.close();
}
return port;
}
@EnableWebSecurity
static abstract class BaseLdapServerConfig extends BaseLdapProviderConfig {
static class DefaultLdapConfig extends BaseLdapProviderConfig {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.ldapAuthentication()
.contextSource(contextSource())
.userDnPatterns("uid={0},ou=people");
// @formatter:on
}
}
@EnableWebSecurity
static class GroupRolesConfig extends BaseLdapProviderConfig {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.ldapAuthentication()
.contextSource(contextSource())
.userDnPatterns("uid={0},ou=people")
.groupRoleAttribute("group");
// @formatter:on
}
}
@EnableWebSecurity
static class GroupSearchConfig extends BaseLdapProviderConfig {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.ldapAuthentication()
.contextSource(contextSource())
.userDnPatterns("uid={0},ou=people")
.groupSearchFilter("ou=groupName");
// @formatter:on
}
}
@EnableWebSecurity
static class GroupSubtreeSearchConfig extends BaseLdapProviderConfig {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.ldapAuthentication()
.contextSource(contextSource())
.userDnPatterns("uid={0},ou=people")
.groupSearchFilter("ou=groupName")
.groupSearchSubtree(true);
// @formatter:on
}
}
@EnableWebSecurity
static class RolePrefixConfig extends BaseLdapProviderConfig {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.ldapAuthentication()
.contextSource(contextSource())
.userDnPatterns("uid={0},ou=people")
.rolePrefix("role_");
// @formatter:on
}
}
@EnableWebSecurity
static class BindAuthenticationConfig extends BaseLdapServerConfig {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.ldapAuthentication()
.contextSource(contextSource())
.groupSearchBase("ou=groups")
.groupSearchFilter("(member={0})")
.userDnPatterns("uid={0},ou=people");
// @formatter:on
}
}
@EnableWebSecurity
static class PasswordEncoderConfig extends BaseLdapServerConfig {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.ldapAuthentication()
.contextSource(contextSource())
.passwordEncoder(new BCryptPasswordEncoder())
.groupSearchBase("ou=groups")
.groupSearchFilter("(member={0})")
.userDnPatterns("uid={0},ou=people");
// @formatter:on
}
}
@EnableWebSecurity
abstract static class BaseLdapServerConfig extends BaseLdapProviderConfig {
@Bean
public ApacheDSContainer ldapServer() throws Exception {
ApacheDSContainer apacheDSContainer = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
ApacheDSContainer ldapServer() throws Exception {
ApacheDSContainer apacheDSContainer = new ApacheDSContainer("dc=springframework,dc=org",
"classpath:/test-server.ldif");
apacheDSContainer.setPort(getPort());
return apacheDSContainer;
}
}
@EnableWebSecurity
@EnableGlobalAuthentication
@Import(ObjectPostProcessorConfiguration.class)
static abstract class BaseLdapProviderConfig extends WebSecurityConfigurerAdapter {
abstract static class BaseLdapProviderConfig extends WebSecurityConfigurerAdapter {
@Bean
public BaseLdapPathContextSource contextSource() throws Exception {
BaseLdapPathContextSource contextSource() throws Exception {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
"ldap://127.0.0.1:" + getPort() + "/dc=springframework,dc=org");
contextSource.setUserDn("uid=admin,ou=system");
@@ -254,22 +298,14 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationManagerBuilder auth) throws Exception {
AuthenticationManager authenticationManager(AuthenticationManagerBuilder auth) throws Exception {
configure(auth);
return auth.build();
}
abstract protected void configure(AuthenticationManagerBuilder auth) throws Exception;
@Override
protected abstract void configure(AuthenticationManagerBuilder auth) throws Exception;
}
static Integer port;
static int getPort() throws IOException {
if (port == null) {
ServerSocket socket = new ServerSocket(0);
port = socket.getLocalPort();
socket.close();
}
return port;
}
}

View File

@@ -16,6 +16,8 @@
package org.springframework.security.config.annotation.authentication.ldap;
import java.util.Collections;
import org.junit.Rule;
import org.junit.Test;
@@ -27,13 +29,15 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders;
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers;
import org.springframework.test.web.servlet.MockMvc;
import static java.util.Collections.singleton;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
public class LdapAuthenticationProviderConfigurerTests {
@Rule
public final SpringTestRule spring = new SpringTestRule();
@@ -41,7 +45,8 @@ public class LdapAuthenticationProviderConfigurerTests {
private MockMvc mockMvc;
@Test
public void authenticationManagerSupportMultipleDefaultLdapContextsWithPortsDynamicallyAllocated() throws Exception {
public void authenticationManagerSupportMultipleDefaultLdapContextsWithPortsDynamicallyAllocated()
throws Exception {
this.spring.register(MultiLdapAuthenticationProvidersConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
@@ -52,39 +57,68 @@ public class LdapAuthenticationProviderConfigurerTests {
public void authenticationManagerSupportMultipleLdapContextWithDefaultRolePrefix() throws Exception {
this.spring.register(MultiLdapAuthenticationProvidersConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
.andExpect(authenticated().withUsername("bob").withAuthorities(singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS"))));
// @formatter:off
SecurityMockMvcRequestBuilders.FormLoginRequestBuilder request = formLogin()
.user("bob")
.password("bobspassword");
SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
.withUsername("bob")
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS")));
// @formatter:on
this.mockMvc.perform(request).andExpect(expectedUser);
}
@Test
public void authenticationManagerSupportMultipleLdapContextWithCustomRolePrefix() throws Exception {
this.spring.register(MultiLdapWithCustomRolePrefixAuthenticationProvidersConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
.andExpect(authenticated().withUsername("bob").withAuthorities(singleton(new SimpleGrantedAuthority("ROL_DEVELOPERS"))));
// @formatter:off
SecurityMockMvcRequestBuilders.FormLoginRequestBuilder request = formLogin()
.user("bob")
.password("bobspassword");
SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
.withUsername("bob")
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROL_DEVELOPERS")));
// @formatter:on
this.mockMvc.perform(request).andExpect(expectedUser);
}
@Test
public void authenticationManagerWhenPortZeroThenAuthenticates() throws Exception {
this.spring.register(LdapWithRandomPortConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
.andExpect(authenticated().withUsername("bob"));
// @formatter:off
SecurityMockMvcRequestBuilders.FormLoginRequestBuilder request = formLogin()
.user("bob")
.password("bobspassword");
SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
.withUsername("bob");
// @formatter:on
this.mockMvc.perform(request).andExpect(expectedUser);
}
@Test
public void authenticationManagerWhenSearchSubtreeThenNestedGroupFound() throws Exception {
this.spring.register(GroupSubtreeSearchConfig.class).autowire();
this.mockMvc.perform(formLogin().user("ben").password("benspassword"))
.andExpect(authenticated().withUsername("ben").withAuthorities(
AuthorityUtils.createAuthorityList("ROLE_SUBMANAGERS", "ROLE_MANAGERS", "ROLE_DEVELOPERS")));
// @formatter:off
SecurityMockMvcRequestBuilders.FormLoginRequestBuilder request = formLogin()
.user("ben")
.password("benspassword");
SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
.withUsername("ben")
.withAuthorities(
AuthorityUtils.createAuthorityList("ROLE_SUBMANAGERS", "ROLE_MANAGERS", "ROLE_DEVELOPERS"));
// @formatter:on
this.mockMvc.perform(request).andExpect(expectedUser);
}
@EnableWebSecurity
static class MultiLdapAuthenticationProvidersConfig extends WebSecurityConfigurerAdapter {
// @formatter:off
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.ldapAuthentication()
.groupSearchBase("ou=groups")
@@ -95,14 +129,17 @@ public class LdapAuthenticationProviderConfigurerTests {
.groupSearchBase("ou=groups")
.groupSearchFilter("(member={0})")
.userDnPatterns("uid={0},ou=people");
// @formatter:on
}
// @formatter:on
}
@EnableWebSecurity
static class MultiLdapWithCustomRolePrefixAuthenticationProvidersConfig extends WebSecurityConfigurerAdapter {
// @formatter:off
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.ldapAuthentication()
.groupSearchBase("ou=groups")
@@ -115,14 +152,17 @@ public class LdapAuthenticationProviderConfigurerTests {
.groupSearchFilter("(member={0})")
.userDnPatterns("uid={0},ou=people")
.rolePrefix("RUOLO_");
// @formatter:on
}
// @formatter:on
}
@EnableWebSecurity
static class LdapWithRandomPortConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.ldapAuthentication()
.groupSearchBase("ou=groups")
@@ -130,20 +170,26 @@ public class LdapAuthenticationProviderConfigurerTests {
.userDnPatterns("uid={0},ou=people")
.contextSource()
.port(0);
// @formatter:on
}
}
@EnableWebSecurity
static class GroupSubtreeSearchConfig extends BaseLdapProviderConfig {
// @formatter:off
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.ldapAuthentication()
.groupSearchBase("ou=groups")
.groupSearchFilter("(member={0})")
.groupSearchSubtree(true)
.userDnPatterns("uid={0},ou=people");
// @formatter:on
}
// @formatter:on
}
}

View File

@@ -16,8 +16,13 @@
package org.springframework.security.config.annotation.authentication.ldap;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.support.LdapContextSource;
@@ -31,13 +36,11 @@ import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders;
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
@@ -56,22 +59,35 @@ public class NamespaceLdapAuthenticationProviderTests {
public void ldapAuthenticationProvider() throws Exception {
this.spring.register(LdapAuthenticationProviderConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
.andExpect(authenticated().withUsername("bob"));
// @formatter:off
SecurityMockMvcRequestBuilders.FormLoginRequestBuilder request = formLogin()
.user("bob")
.password("bobspassword");
SecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated()
.withUsername("bob");
// @formatter:on
this.mockMvc.perform(request).andExpect(user);
}
@Test
public void ldapAuthenticationProviderCustom() throws Exception {
this.spring.register(CustomLdapAuthenticationProviderConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
.andExpect(authenticated().withAuthorities(Collections.singleton(new SimpleGrantedAuthority("PREFIX_DEVELOPERS"))));
// @formatter:off
SecurityMockMvcRequestBuilders.FormLoginRequestBuilder request = formLogin()
.user("bob")
.password("bobspassword");
SecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated()
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("PREFIX_DEVELOPERS")));
// @formatter:on
this.mockMvc.perform(request).andExpect(user);
}
// SEC-2490
@Test
public void ldapAuthenticationProviderCustomLdapAuthoritiesPopulator() throws Exception {
LdapContextSource contextSource = new DefaultSpringSecurityContextSource("ldap://blah.example.com:789/dc=springframework,dc=org");
LdapContextSource contextSource = new DefaultSpringSecurityContextSource(
"ldap://blah.example.com:789/dc=springframework,dc=org");
CustomAuthoritiesPopulatorConfig.LAP = new DefaultLdapAuthoritiesPopulator(contextSource, null) {
@Override
protected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, String username) {
@@ -81,15 +97,27 @@ public class NamespaceLdapAuthenticationProviderTests {
this.spring.register(CustomAuthoritiesPopulatorConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
.andExpect(authenticated().withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_EXTRA"))));
// @formatter:off
SecurityMockMvcRequestBuilders.FormLoginRequestBuilder request = formLogin()
.user("bob")
.password("bobspassword");
SecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated()
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_EXTRA")));
// @formatter:on
this.mockMvc.perform(request).andExpect(user);
}
@Test
public void ldapAuthenticationProviderPasswordCompare() throws Exception {
this.spring.register(PasswordCompareLdapConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bcrypt").password("password"))
.andExpect(authenticated().withUsername("bcrypt"));
// @formatter:off
SecurityMockMvcRequestBuilders.FormLoginRequestBuilder request = formLogin()
.user("bcrypt")
.password("password");
SecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated().withUsername("bcrypt");
// @formatter:on
this.mockMvc.perform(request).andExpect(user);
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.ldap;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@@ -27,23 +28,28 @@ import org.springframework.security.ldap.userdetails.PersonContextMapper;
*
*/
public class NamespaceLdapAuthenticationProviderTestsConfigs {
@EnableWebSecurity
static class LdapAuthenticationProviderConfig extends WebSecurityConfigurerAdapter {
// @formatter:off
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.ldapAuthentication()
.groupSearchBase("ou=groups")
.userDnPatterns("uid={0},ou=people"); // ldap-server@user-dn-pattern
// @formatter:on
}
// @formatter:on
}
@EnableWebSecurity
static class CustomLdapAuthenticationProviderConfig extends
WebSecurityConfigurerAdapter {
// @formatter:off
static class CustomLdapAuthenticationProviderConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.ldapAuthentication()
.groupRoleAttribute("cn") // ldap-authentication-provider@group-role-attribute
@@ -60,31 +66,36 @@ public class NamespaceLdapAuthenticationProviderTestsConfigs {
.managerDn("uid=admin,ou=system") // ldap-server@manager-dn
.managerPassword("secret") // ldap-server@manager-password
.port(33399) // ldap-server@port
.root("dc=springframework,dc=org") // ldap-server@root
.root("dc=springframework,dc=org"); // ldap-server@root
// .url("ldap://localhost:33389/dc-springframework,dc=org") this overrides root and port and is used for external
;
// @formatter:on
}
// @formatter:on
}
@EnableWebSecurity
static class CustomAuthoritiesPopulatorConfig extends WebSecurityConfigurerAdapter {
static LdapAuthoritiesPopulator LAP;
// @formatter:off
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.ldapAuthentication()
.userSearchFilter("(uid={0})")
.ldapAuthoritiesPopulator(LAP);
// @formatter:on
}
// @formatter:on
}
@EnableWebSecurity
static class PasswordCompareLdapConfig extends WebSecurityConfigurerAdapter {
// @formatter:off
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.ldapAuthentication()
.groupSearchBase("ou=groups")
@@ -92,7 +103,9 @@ public class NamespaceLdapAuthenticationProviderTestsConfigs {
.passwordCompare()
.passwordEncoder(new BCryptPasswordEncoder()) // ldap-authentication-provider/password-compare/password-encoder@ref
.passwordAttribute("userPassword"); // ldap-authentication-provider/password-compare@password-attribute
// @formatter:on
}
// @formatter:on
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.rsocket;
import io.rsocket.ConnectionSetupPayload;
@@ -26,15 +27,15 @@ public class HelloHandler implements SocketAcceptor {
@Override
public Mono<RSocket> accept(ConnectionSetupPayload setup, RSocket sendingSocket) {
return Mono.just(
new RSocket() {
@Override
public Mono<Payload> requestResponse(Payload payload) {
String data = payload.getDataUtf8();
payload.release();
System.out.println("Got " + data);
return Mono.just(ByteBufPayload.create("Hello " + data));
}
});
return Mono.just(new RSocket() {
@Override
public Mono<Payload> requestResponse(Payload payload) {
String data = payload.getDataUtf8();
payload.release();
System.out.println("Got " + data);
return Mono.just(ByteBufPayload.create("Hello " + data));
}
});
}
}

View File

@@ -20,6 +20,7 @@ import java.util.ArrayList;
import java.util.List;
import io.rsocket.core.RSocketServer;
import io.rsocket.exceptions.RejectedSetupException;
import io.rsocket.frame.decoder.PayloadDecoder;
import io.rsocket.transport.netty.server.CloseableChannel;
import io.rsocket.transport.netty.server.TcpServerTransport;
@@ -46,7 +47,7 @@ import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* @author Rob Winch
@@ -54,6 +55,7 @@ import static org.assertj.core.api.Assertions.assertThatCode;
@ContextConfiguration
@RunWith(SpringRunner.class)
public class HelloRSocketITests {
@Autowired
RSocketMessageHandler handler;
@@ -69,14 +71,16 @@ public class HelloRSocketITests {
@Before
public void setup() {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> {
registry.forSocketAcceptor(this.interceptor);
})
.interceptors((registry) ->
registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
.block();
// @formatter:on
}
@After
@@ -88,38 +92,45 @@ public class HelloRSocketITests {
@Test
public void retrieveMonoWhenSecureThenDenied() throws Exception {
// @formatter:off
this.requester = RSocketRequester.builder()
.rsocketStrategies(this.handler.getRSocketStrategies())
.connectTcp("localhost", this.server.address().getPort())
.block();
.rsocketStrategies(this.handler.getRSocketStrategies())
.connectTcp("localhost", this.server.address().getPort())
.block();
// @formatter:on
String data = "rob";
assertThatCode(() -> this.requester.route("secure.retrieve-mono")
.data(data)
.retrieveMono(String.class)
.block()
)
.isNotNull();
// @formatter:off
assertThatExceptionOfType(Exception.class).isThrownBy(
() -> this.requester.route("secure.retrieve-mono")
.data(data)
.retrieveMono(String.class)
.block()
)
.matches((ex) -> ex instanceof RejectedSetupException
|| ex.getClass().toString().contains("ReactiveException"));
// @formatter:on
// FIXME: https://github.com/rsocket/rsocket-java/issues/686
// .isInstanceOf(RejectedSetupException.class);
assertThat(this.controller.payloads).isEmpty();
}
@Test
public void retrieveMonoWhenAuthorizedThenGranted() throws Exception {
UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("rob", "password");
// @formatter:off
this.requester = RSocketRequester.builder()
.setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.rsocketStrategies(this.handler.getRSocketStrategies())
.connectTcp("localhost", this.server.address().getPort())
.block();
.setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.rsocketStrategies(this.handler.getRSocketStrategies())
.connectTcp("localhost", this.server.address().getPort())
.block();
// @formatter:on
String data = "rob";
// @formatter:off
String hiRob = this.requester.route("secure.retrieve-mono")
.metadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.data(data)
.retrieveMono(String.class)
.block();
.metadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.data(data)
.retrieveMono(String.class)
.block();
// @formatter:on
assertThat(hiRob).isEqualTo("Hi rob");
assertThat(this.controller.payloads).containsOnly(data);
}
@@ -129,37 +140,39 @@ public class HelloRSocketITests {
static class Config {
@Bean
public ServerController controller() {
ServerController controller() {
return new ServerController();
}
@Bean
public RSocketMessageHandler messageHandler() {
RSocketMessageHandler messageHandler() {
RSocketMessageHandler handler = new RSocketMessageHandler();
handler.setRSocketStrategies(rsocketStrategies());
return handler;
}
@Bean
public RSocketStrategies rsocketStrategies() {
return RSocketStrategies.builder()
.encoder(new BasicAuthenticationEncoder())
.build();
RSocketStrategies rsocketStrategies() {
return RSocketStrategies.builder().encoder(new BasicAuthenticationEncoder()).build();
}
@Bean
MapReactiveUserDetailsService uds() {
// @formatter:off
UserDetails rob = User.withDefaultPasswordEncoder()
.username("rob")
.password("password")
.roles("USER", "ADMIN")
.build();
// @formatter:on
return new MapReactiveUserDetailsService(rob);
}
}
@Controller
static class ServerController {
private List<String> payloads = new ArrayList<>();
@MessageMapping("**")
@@ -171,6 +184,7 @@ public class HelloRSocketITests {
private void add(String p) {
this.payloads.add(p);
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.rsocket;
import java.util.ArrayList;
@@ -21,6 +22,7 @@ import java.util.List;
import io.rsocket.core.RSocketServer;
import io.rsocket.frame.decoder.PayloadDecoder;
import io.rsocket.metadata.WellKnownMimeType;
import io.rsocket.transport.netty.server.CloseableChannel;
import io.rsocket.transport.netty.server.TcpServerTransport;
import org.junit.After;
@@ -51,11 +53,10 @@ import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
import static io.rsocket.metadata.WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author Rob Winch
@@ -63,6 +64,7 @@ import static org.mockito.Mockito.when;
@ContextConfiguration
@RunWith(SpringRunner.class)
public class JwtITests {
@Autowired
RSocketMessageHandler handler;
@@ -81,14 +83,16 @@ public class JwtITests {
@Before
public void setup() {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> {
registry.forSocketAcceptor(this.interceptor);
})
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
.block();
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) ->
registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
.block();
// @formatter:on
}
@After
@@ -100,88 +104,72 @@ public class JwtITests {
@Test
public void routeWhenBearerThenAuthorized() {
BearerTokenMetadata credentials =
new BearerTokenMetadata("token");
when(this.decoder.decode(any())).thenReturn(Mono.just(jwt()));
BearerTokenMetadata credentials = new BearerTokenMetadata("token");
given(this.decoder.decode(any())).willReturn(Mono.just(jwt()));
// @formatter:off
this.requester = requester()
.setupMetadata(credentials.getToken(), BearerTokenMetadata.BEARER_AUTHENTICATION_MIME_TYPE)
.connectTcp(this.server.address().getHostName(), this.server.address().getPort())
.block();
.setupMetadata(credentials.getToken(), BearerTokenMetadata.BEARER_AUTHENTICATION_MIME_TYPE)
.connectTcp(this.server.address().getHostName(), this.server.address().getPort())
.block();
String hiRob = this.requester.route("secure.retrieve-mono")
.data("rob")
.retrieveMono(String.class)
.block();
.data("rob")
.retrieveMono(String.class)
.block();
// @formatter:on
assertThat(hiRob).isEqualTo("Hi rob");
}
@Test
public void routeWhenAuthenticationBearerThenAuthorized() {
MimeType authenticationMimeType = MimeTypeUtils.parseMimeType(MESSAGE_RSOCKET_AUTHENTICATION.getString());
BearerTokenMetadata credentials =
new BearerTokenMetadata("token");
when(this.decoder.decode(any())).thenReturn(Mono.just(jwt()));
this.requester = requester()
.setupMetadata(credentials, authenticationMimeType)
MimeType authenticationMimeType = MimeTypeUtils
.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
BearerTokenMetadata credentials = new BearerTokenMetadata("token");
given(this.decoder.decode(any())).willReturn(Mono.just(jwt()));
// @formatter:off
this.requester = requester().setupMetadata(credentials, authenticationMimeType)
.connectTcp(this.server.address().getHostName(), this.server.address().getPort())
.block();
String hiRob = this.requester.route("secure.retrieve-mono")
.data("rob")
.retrieveMono(String.class)
.block();
.retrieveMono(String.class).block();
// @formatter:on
assertThat(hiRob).isEqualTo("Hi rob");
}
private Jwt jwt() {
return TestJwts.jwt()
.claim(IdTokenClaimNames.ISS, "https://issuer.example.com")
.claim(IdTokenClaimNames.SUB, "rob")
.claim(IdTokenClaimNames.AUD, Arrays.asList("client-id"))
.build();
return TestJwts.jwt().claim(IdTokenClaimNames.ISS, "https://issuer.example.com")
.claim(IdTokenClaimNames.SUB, "rob").claim(IdTokenClaimNames.AUD, Arrays.asList("client-id")).build();
}
private RSocketRequester.Builder requester() {
return RSocketRequester.builder()
.rsocketStrategies(this.handler.getRSocketStrategies());
return RSocketRequester.builder().rsocketStrategies(this.handler.getRSocketStrategies());
}
@Configuration
@EnableRSocketSecurity
static class Config {
@Bean
public ServerController controller() {
ServerController controller() {
return new ServerController();
}
@Bean
public RSocketMessageHandler messageHandler() {
RSocketMessageHandler messageHandler() {
RSocketMessageHandler handler = new RSocketMessageHandler();
handler.setRSocketStrategies(rsocketStrategies());
return handler;
}
@Bean
public RSocketStrategies rsocketStrategies() {
return RSocketStrategies.builder()
.encoder(new BearerTokenAuthenticationEncoder())
.build();
RSocketStrategies rsocketStrategies() {
return RSocketStrategies.builder().encoder(new BearerTokenAuthenticationEncoder()).build();
}
@Bean
PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
rsocket
.authorizePayload(authorize ->
authorize
.anyRequest().authenticated()
.anyExchange().permitAll()
)
.jwt(Customizer.withDefaults());
rsocket.authorizePayload((authorize) -> authorize.anyRequest().authenticated().anyExchange().permitAll())
.jwt(Customizer.withDefaults());
return rsocket.build();
}
@@ -189,16 +177,19 @@ public class JwtITests {
ReactiveJwtDecoder jwtDecoder() {
return mock(ReactiveJwtDecoder.class);
}
}
@Controller
static class ServerController {
private List<String> payloads = new ArrayList<>();
@MessageMapping("**")
String connect(String payload) {
return "Hi " + payload;
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.rsocket;
import java.util.ArrayList;
@@ -20,6 +21,7 @@ import java.util.List;
import io.rsocket.core.RSocketServer;
import io.rsocket.exceptions.ApplicationErrorException;
import io.rsocket.exceptions.RejectedSetupException;
import io.rsocket.frame.decoder.PayloadDecoder;
import io.rsocket.transport.netty.server.CloseableChannel;
import io.rsocket.transport.netty.server.TcpServerTransport;
@@ -48,7 +50,7 @@ import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* @author Rob Winch
@@ -60,6 +62,7 @@ import static org.assertj.core.api.Assertions.assertThatCode;
@ContextConfiguration
@RunWith(SpringRunner.class)
public class RSocketMessageHandlerConnectionITests {
@Autowired
RSocketMessageHandler handler;
@@ -75,14 +78,16 @@ public class RSocketMessageHandlerConnectionITests {
@Before
public void setup() {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> {
registry.forSocketAcceptor(this.interceptor);
})
.interceptors((registry) ->
registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
.block();
// @formatter:on
}
@After
@@ -94,182 +99,179 @@ public class RSocketMessageHandlerConnectionITests {
@Test
public void routeWhenAuthorized() {
UsernamePasswordMetadata credentials =
new UsernamePasswordMetadata("user", "password");
this.requester = requester()
.setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.connectTcp(this.server.address().getHostName(), this.server.address().getPort())
.block();
UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("user", "password");
// @formatter:off
this.requester = requester().setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.connectTcp(this.server.address().getHostName(), this.server.address().getPort())
.block();
String hiRob = this.requester.route("secure.retrieve-mono")
.data("rob")
.retrieveMono(String.class)
.block();
.data("rob")
.retrieveMono(String.class)
.block();
// @formatter:on
assertThat(hiRob).isEqualTo("Hi rob");
}
@Test
public void routeWhenNotAuthorized() {
UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("user", "password");
this.requester = requester()
.setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.connectTcp(this.server.address().getHostName(), this.server.address().getPort())
.block();
assertThatCode(() -> this.requester.route("secure.admin.retrieve-mono")
// @formatter:off
this.requester = requester().setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.connectTcp(this.server.address().getHostName(), this.server.address().getPort())
.block();
assertThatExceptionOfType(ApplicationErrorException.class).isThrownBy(() -> this.requester
.route("secure.admin.retrieve-mono")
.data("data")
.retrieveMono(String.class)
.block())
.isInstanceOf(ApplicationErrorException.class);
.block()
);
// @formatter:on
}
@Test
public void routeWhenStreamCredentialsAuthorized() {
UsernamePasswordMetadata connectCredentials = new UsernamePasswordMetadata("user", "password");
this.requester = requester()
.setupMetadata(connectCredentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.connectTcp(this.server.address().getHostName(), this.server.address().getPort())
.block();
// @formatter:off
this.requester = requester().setupMetadata(connectCredentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.connectTcp(this.server.address().getHostName(), this.server.address().getPort())
.block();
String hiRob = this.requester.route("secure.admin.retrieve-mono")
.metadata(new UsernamePasswordMetadata("admin", "password"), UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.data("rob")
.retrieveMono(String.class)
.block();
.metadata(new UsernamePasswordMetadata("admin", "password"),
UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.data("rob")
.retrieveMono(String.class)
.block();
// @formatter:on
assertThat(hiRob).isEqualTo("Hi rob");
}
@Test
public void routeWhenStreamCredentialsHaveAuthority() {
UsernamePasswordMetadata connectCredentials = new UsernamePasswordMetadata("user", "password");
this.requester = requester()
.setupMetadata(connectCredentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.connectTcp(this.server.address().getHostName(), this.server.address().getPort())
.block();
// @formatter:off
this.requester = requester().setupMetadata(connectCredentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.connectTcp(this.server.address().getHostName(), this.server.address().getPort())
.block();
String hiUser = this.requester.route("secure.authority.retrieve-mono")
.metadata(new UsernamePasswordMetadata("admin", "password"), UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.data("Felipe")
.retrieveMono(String.class)
.block();
.metadata(new UsernamePasswordMetadata("admin", "password"),
UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.data("Felipe")
.retrieveMono(String.class)
.block();
// @formatter:on
assertThat(hiUser).isEqualTo("Hi Felipe");
}
@Test
public void connectWhenNotAuthenticated() {
this.requester = requester()
.connectTcp(this.server.address().getHostName(), this.server.address().getPort())
// @formatter:off
this.requester = requester().connectTcp(this.server.address().getHostName(), this.server.address().getPort())
.block();
assertThatCode(() -> this.requester.route("retrieve-mono")
.data("data")
.retrieveMono(String.class)
.block())
.isNotNull();
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> this.requester.route("retrieve-mono")
.data("data")
.retrieveMono(String.class)
.block()
)
.matches((ex) -> ex instanceof RejectedSetupException
|| ex.getClass().toString().contains("ReactiveException"));
// @formatter:on
// FIXME: https://github.com/rsocket/rsocket-java/issues/686
// .isInstanceOf(RejectedSetupException.class);
}
@Test
public void connectWhenNotAuthorized() {
UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("evil", "password");
this.requester = requester()
.setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
// @formatter:off
this.requester = requester().setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.connectTcp(this.server.address().getHostName(), this.server.address().getPort())
.block();
assertThatCode(() -> this.requester.route("retrieve-mono")
.data("data")
.retrieveMono(String.class)
.block())
.isNotNull();
// FIXME: https://github.com/rsocket/rsocket-java/issues/686
// .isInstanceOf(RejectedSetupException.class);
assertThatExceptionOfType(Exception.class)
.isThrownBy(() -> this.requester.route("retrieve-mono")
.data("data")
.retrieveMono(String.class)
.block()
)
.matches((ex) -> ex instanceof RejectedSetupException
|| ex.getClass().toString().contains("ReactiveException"));
// @formatter:on
// FIXME: https://github.com/rsocket/rsocket-java/issues/686
}
@Test
public void connectionDenied() {
UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("user", "password");
this.requester = requester()
.setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
// @formatter:off
this.requester = requester().setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.connectTcp(this.server.address().getHostName(), this.server.address().getPort())
.block();
assertThatCode(() -> this.requester.route("prohibit")
.data("data")
.retrieveMono(String.class)
.block())
.isInstanceOf(ApplicationErrorException.class);
assertThatExceptionOfType(ApplicationErrorException.class)
.isThrownBy(() -> this.requester.route("prohibit")
.data("data")
.retrieveMono(String.class)
.block()
);
// @formatter:on
}
@Test
public void connectWithAnyRole() {
UsernamePasswordMetadata credentials =
new UsernamePasswordMetadata("user", "password");
this.requester = requester()
.setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("user", "password");
// @formatter:off
this.requester = requester().setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.connectTcp(this.server.address().getHostName(), this.server.address().getPort())
.block();
String hiRob = this.requester.route("anyroute")
.data("rob")
.retrieveMono(String.class)
.block();
// @formatter:on
assertThat(hiRob).isEqualTo("Hi rob");
}
@Test
public void connectWithAnyAuthority() {
UsernamePasswordMetadata credentials =
new UsernamePasswordMetadata("admin", "password");
this.requester = requester()
.setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("admin", "password");
// @formatter:off
this.requester = requester().setupMetadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.connectTcp(this.server.address().getHostName(), this.server.address().getPort())
.block();
String hiEbert = this.requester.route("management.users")
.data("admin")
.retrieveMono(String.class)
.block();
// @formatter:on
assertThat(hiEbert).isEqualTo("Hi admin");
}
private RSocketRequester.Builder requester() {
return RSocketRequester.builder()
.rsocketStrategies(this.handler.getRSocketStrategies());
return RSocketRequester.builder().rsocketStrategies(this.handler.getRSocketStrategies());
}
@Configuration
@EnableRSocketSecurity
static class Config {
@Bean
public ServerController controller() {
ServerController controller() {
return new ServerController();
}
@Bean
public RSocketMessageHandler messageHandler() {
RSocketMessageHandler messageHandler() {
RSocketMessageHandler handler = new RSocketMessageHandler();
handler.setRSocketStrategies(rsocketStrategies());
return handler;
}
@Bean
public RSocketStrategies rsocketStrategies() {
return RSocketStrategies.builder()
.encoder(new BasicAuthenticationEncoder())
.build();
RSocketStrategies rsocketStrategies() {
return RSocketStrategies.builder().encoder(new BasicAuthenticationEncoder()).build();
}
@Bean
MapReactiveUserDetailsService uds() {
// @formatter:off
UserDetails admin = User.withDefaultPasswordEncoder()
.username("admin")
.password("password")
@@ -280,41 +282,44 @@ public class RSocketMessageHandlerConnectionITests {
.password("password")
.roles("USER", "SETUP")
.build();
UserDetails evil = User.withDefaultPasswordEncoder()
.username("evil")
.password("password")
.roles("EVIL")
.build();
// @formatter:on
return new MapReactiveUserDetailsService(admin, user, evil);
}
@Bean
PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
rsocket
.authorizePayload(authorize ->
authorize
.setup().hasRole("SETUP")
.route("secure.admin.*").hasRole("ADMIN")
.route("secure.**").hasRole("USER")
.route("secure.authority.*").hasAuthority("ROLE_USER")
.route("management.*").hasAnyAuthority("ROLE_ADMIN")
.route("prohibit").denyAll()
.anyRequest().permitAll()
)
.basicAuthentication(Customizer.withDefaults());
// @formatter:off
rsocket.authorizePayload((authorize) -> authorize
.setup().hasRole("SETUP")
.route("secure.admin.*").hasRole("ADMIN")
.route("secure.**").hasRole("USER")
.route("secure.authority.*").hasAuthority("ROLE_USER")
.route("management.*").hasAnyAuthority("ROLE_ADMIN")
.route("prohibit").denyAll()
.anyRequest().permitAll()
)
.basicAuthentication(Customizer.withDefaults());
// @formatter:on
return rsocket.build();
}
}
@Controller
static class ServerController {
private List<String> payloads = new ArrayList<>();
@MessageMapping("**")
String connect(String payload) {
return "Hi " + payload;
}
}
}

View File

@@ -52,7 +52,7 @@ import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* @author Rob Winch
@@ -60,6 +60,7 @@ import static org.assertj.core.api.Assertions.assertThatCode;
@ContextConfiguration
@RunWith(SpringRunner.class)
public class RSocketMessageHandlerITests {
@Autowired
RSocketMessageHandler handler;
@@ -75,20 +76,22 @@ public class RSocketMessageHandlerITests {
@Before
public void setup() {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> {
registry.forSocketAcceptor(this.interceptor);
})
.interceptors((registry) ->
registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
.block();
this.requester = RSocketRequester.builder()
// .rsocketFactory(factory -> factory.addRequesterPlugin(payloadInterceptor))
// .rsocketFactory((factory) ->
// factory.addRequesterPlugin(payloadInterceptor))
.rsocketStrategies(this.handler.getRSocketStrategies())
.connectTcp("localhost", this.server.address().getPort())
.block();
// @formatter:on
}
@After
@@ -101,13 +104,15 @@ public class RSocketMessageHandlerITests {
@Test
public void retrieveMonoWhenSecureThenDenied() throws Exception {
String data = "rob";
assertThatCode(() -> this.requester.route("secure.retrieve-mono")
.data(data)
.retrieveMono(String.class)
.block()
).isInstanceOf(ApplicationErrorException.class)
.hasMessageContaining("Access Denied");
// @formatter:off
assertThatExceptionOfType(ApplicationErrorException.class).isThrownBy(
() -> this.requester.route("secure.retrieve-mono")
.data(data)
.retrieveMono(String.class)
.block()
)
.withMessageContaining("Access Denied");
// @formatter:on
assertThat(this.controller.payloads).isEmpty();
}
@@ -115,14 +120,15 @@ public class RSocketMessageHandlerITests {
public void retrieveMonoWhenAuthenticationFailedThenException() throws Exception {
String data = "rob";
UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("invalid", "password");
assertThatCode(() -> this.requester.route("secure.retrieve-mono")
.metadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.data(data)
// @formatter:off
assertThatExceptionOfType(ApplicationErrorException.class)
.isThrownBy(() -> this.requester.route("secure.retrieve-mono")
.metadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE).data(data)
.retrieveMono(String.class)
.block()
).isInstanceOf(ApplicationErrorException.class)
.hasMessageContaining("Invalid Credentials");
)
.withMessageContaining("Invalid Credentials");
// @formatter:on
assertThat(this.controller.payloads).isEmpty();
}
@@ -130,12 +136,13 @@ public class RSocketMessageHandlerITests {
public void retrieveMonoWhenAuthorizedThenGranted() throws Exception {
String data = "rob";
UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("rob", "password");
// @formatter:off
String hiRob = this.requester.route("secure.retrieve-mono")
.metadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.data(data)
.retrieveMono(String.class)
.block();
.metadata(credentials, UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.data(data)
.retrieveMono(String.class)
.block();
// @formatter:on
assertThat(hiRob).isEqualTo("Hi rob");
assertThat(this.controller.payloads).containsOnly(data);
}
@@ -143,11 +150,12 @@ public class RSocketMessageHandlerITests {
@Test
public void retrieveMonoWhenPublicThenGranted() throws Exception {
String data = "rob";
// @formatter:off
String hiRob = this.requester.route("retrieve-mono")
.data(data)
.retrieveMono(String.class)
.block();
.data(data)
.retrieveMono(String.class)
.block();
// @formatter:on
assertThat(hiRob).isEqualTo("Hi rob");
assertThat(this.controller.payloads).containsOnly(data);
}
@@ -155,26 +163,29 @@ public class RSocketMessageHandlerITests {
@Test
public void retrieveFluxWhenDataFluxAndSecureThenDenied() throws Exception {
Flux<String> data = Flux.just("a", "b", "c");
assertThatCode(() -> this.requester.route("secure.retrieve-flux")
// @formatter:off
assertThatExceptionOfType(ApplicationErrorException.class)
.isThrownBy(() -> this.requester.route("secure.retrieve-flux")
.data(data, String.class)
.retrieveFlux(String.class)
.collectList()
.block()
).isInstanceOf(ApplicationErrorException.class)
.hasMessageContaining("Access Denied");
)
.withMessageContaining("Access Denied");
// @formatter:on
assertThat(this.controller.payloads).isEmpty();
}
@Test
public void retrieveFluxWhenDataFluxAndPublicThenGranted() throws Exception {
Flux<String> data = Flux.just("a", "b", "c");
// @formatter:off
List<String> hi = this.requester.route("retrieve-flux")
.data(data, String.class)
.retrieveFlux(String.class)
.collectList()
.block();
.data(data, String.class)
.retrieveFlux(String.class)
.collectList()
.block();
// @formatter:on
assertThat(hi).containsOnly("hello a", "hello b", "hello c");
assertThat(this.controller.payloads).containsOnlyElementsOf(data.collectList().block());
}
@@ -182,35 +193,33 @@ public class RSocketMessageHandlerITests {
@Test
public void retrieveFluxWhenDataStringAndSecureThenDenied() throws Exception {
String data = "a";
assertThatCode(() -> this.requester.route("secure.hello")
.data(data)
.retrieveFlux(String.class)
.collectList()
.block()
).isInstanceOf(ApplicationErrorException.class)
.hasMessageContaining("Access Denied");
assertThatExceptionOfType(ApplicationErrorException.class).isThrownBy(
() -> this.requester.route("secure.hello").data(data).retrieveFlux(String.class).collectList().block())
.withMessageContaining("Access Denied");
assertThat(this.controller.payloads).isEmpty();
}
@Test
public void sendWhenSecureThenDenied() throws Exception {
String data = "hi";
// @formatter:off
this.requester.route("secure.send")
.data(data)
.send()
.block();
.data(data)
.send()
.block();
// @formatter:on
assertThat(this.controller.payloads).isEmpty();
}
@Test
public void sendWhenPublicThenGranted() throws Exception {
String data = "hi";
// @formatter:off
this.requester.route("send")
.data(data)
.send()
.block();
.data(data)
.send()
.block();
// @formatter:on
assertThat(this.controller.awaitPayloads()).containsOnly("hi");
}
@@ -219,26 +228,25 @@ public class RSocketMessageHandlerITests {
static class Config {
@Bean
public ServerController controller() {
ServerController controller() {
return new ServerController();
}
@Bean
public RSocketMessageHandler messageHandler() {
RSocketMessageHandler messageHandler() {
RSocketMessageHandler handler = new RSocketMessageHandler();
handler.setRSocketStrategies(rsocketStrategies());
return handler;
}
@Bean
public RSocketStrategies rsocketStrategies() {
return RSocketStrategies.builder()
.encoder(new BasicAuthenticationEncoder())
.build();
RSocketStrategies rsocketStrategies() {
return RSocketStrategies.builder().encoder(new BasicAuthenticationEncoder()).build();
}
@Bean
MapReactiveUserDetailsService uds() {
// @formatter:off
UserDetails rob = User.withDefaultPasswordEncoder()
.username("rob")
.password("password")
@@ -249,45 +257,44 @@ public class RSocketMessageHandlerITests {
.password("password")
.roles("USER")
.build();
// @formatter:on
return new MapReactiveUserDetailsService(rob, rossen);
}
@Bean
PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
rsocket
.authorizePayload(authorize -> {
authorize
.route("secure.*").authenticated()
.anyExchange().permitAll();
})
.basicAuthentication(Customizer.withDefaults());
// @formatter:off
rsocket.authorizePayload(
(authorize) -> authorize
.route("secure.*").authenticated()
.anyExchange().permitAll()
)
.basicAuthentication(Customizer.withDefaults());
// @formatter:on
return rsocket.build();
}
}
@Controller
static class ServerController {
private List<String> payloads = new ArrayList<>();
@MessageMapping({"secure.retrieve-mono", "retrieve-mono"})
@MessageMapping({ "secure.retrieve-mono", "retrieve-mono" })
String retrieveMono(String payload) {
add(payload);
return "Hi " + payload;
}
@MessageMapping({"secure.retrieve-flux", "retrieve-flux"})
@MessageMapping({ "secure.retrieve-flux", "retrieve-flux" })
Flux<String> retrieveFlux(Flux<String> payload) {
return payload.doOnNext(this::add)
.map(p -> "hello " + p);
return payload.doOnNext(this::add).map((p) -> "hello " + p);
}
@MessageMapping({"secure.send", "send"})
@MessageMapping({ "secure.send", "send" })
Mono<Void> send(Mono<String> payload) {
return payload
.doOnNext(this::add)
.then(Mono.fromRunnable(() -> {
doNotifyAll();
}));
return payload.doOnNext(this::add).then(Mono.fromRunnable(() -> doNotifyAll()));
}
private synchronized void doNotifyAll() {
@@ -302,6 +309,7 @@ public class RSocketMessageHandlerITests {
private void add(String p) {
this.payloads.add(p);
}
}
}

View File

@@ -22,6 +22,7 @@ import java.util.List;
import io.rsocket.core.RSocketServer;
import io.rsocket.exceptions.ApplicationErrorException;
import io.rsocket.frame.decoder.PayloadDecoder;
import io.rsocket.metadata.WellKnownMimeType;
import io.rsocket.transport.netty.server.CloseableChannel;
import io.rsocket.transport.netty.server.TcpServerTransport;
import org.junit.After;
@@ -50,9 +51,8 @@ import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
import static io.rsocket.metadata.WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* @author Rob Winch
@@ -60,6 +60,7 @@ import static org.assertj.core.api.Assertions.assertThatCode;
@ContextConfiguration
@RunWith(SpringRunner.class)
public class SimpleAuthenticationITests {
@Autowired
RSocketMessageHandler handler;
@@ -75,14 +76,16 @@ public class SimpleAuthenticationITests {
@Before
public void setup() {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> {
registry.forSocketAcceptor(this.interceptor);
})
.interceptors((registry) ->
registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
.block();
// @formatter:on
}
@After
@@ -94,38 +97,42 @@ public class SimpleAuthenticationITests {
@Test
public void retrieveMonoWhenSecureThenDenied() throws Exception {
// @formatter:off
this.requester = RSocketRequester.builder()
.rsocketStrategies(this.handler.getRSocketStrategies())
.connectTcp("localhost", this.server.address().getPort())
.block();
.rsocketStrategies(this.handler.getRSocketStrategies())
.connectTcp("localhost", this.server.address().getPort())
.block();
// @formatter:on
String data = "rob";
assertThatCode(() -> this.requester.route("secure.retrieve-mono")
.data(data)
.retrieveMono(String.class)
// @formatter:off
assertThatExceptionOfType(ApplicationErrorException.class)
.isThrownBy(() -> this.requester.route("secure.retrieve-mono")
.data(data).retrieveMono(String.class)
.block()
)
.isInstanceOf(ApplicationErrorException.class);
);
// @formatter:on
assertThat(this.controller.payloads).isEmpty();
}
@Test
public void retrieveMonoWhenAuthorizedThenGranted() {
MimeType authenticationMimeType = MimeTypeUtils.parseMimeType(MESSAGE_RSOCKET_AUTHENTICATION.getString());
MimeType authenticationMimeType = MimeTypeUtils
.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("rob", "password");
// @formatter:off
this.requester = RSocketRequester.builder()
.setupMetadata(credentials, authenticationMimeType)
.rsocketStrategies(this.handler.getRSocketStrategies())
.connectTcp("localhost", this.server.address().getPort())
.block();
.setupMetadata(credentials, authenticationMimeType)
.rsocketStrategies(this.handler.getRSocketStrategies())
.connectTcp("localhost", this.server.address().getPort())
.block();
// @formatter:on
String data = "rob";
// @formatter:off
String hiRob = this.requester.route("secure.retrieve-mono")
.metadata(credentials, authenticationMimeType)
.data(data)
.retrieveMono(String.class)
.block();
.metadata(credentials, authenticationMimeType)
.data(data).retrieveMono(String.class)
.block();
// @formatter:on
assertThat(hiRob).isEqualTo("Hi rob");
assertThat(this.controller.payloads).containsOnly(data);
}
@@ -135,49 +142,46 @@ public class SimpleAuthenticationITests {
static class Config {
@Bean
public ServerController controller() {
ServerController controller() {
return new ServerController();
}
@Bean
public RSocketMessageHandler messageHandler() {
RSocketMessageHandler messageHandler() {
RSocketMessageHandler handler = new RSocketMessageHandler();
handler.setRSocketStrategies(rsocketStrategies());
return handler;
}
@Bean
public RSocketStrategies rsocketStrategies() {
return RSocketStrategies.builder()
.encoder(new SimpleAuthenticationEncoder())
.build();
RSocketStrategies rsocketStrategies() {
return RSocketStrategies.builder().encoder(new SimpleAuthenticationEncoder()).build();
}
@Bean
PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
rsocket
.authorizePayload(authorize ->
authorize
.anyRequest().authenticated()
.anyExchange().permitAll()
)
rsocket.authorizePayload((authorize) -> authorize.anyRequest().authenticated().anyExchange().permitAll())
.simpleAuthentication(Customizer.withDefaults());
return rsocket.build();
}
@Bean
MapReactiveUserDetailsService uds() {
// @formatter:off
UserDetails rob = User.withDefaultPasswordEncoder()
.username("rob")
.password("password")
.roles("USER", "ADMIN")
.build();
// @formatter:on
return new MapReactiveUserDetailsService(rob);
}
}
@Controller
static class ServerController {
private List<String> payloads = new ArrayList<>();
@MessageMapping("**")
@@ -189,6 +193,7 @@ public class SimpleAuthenticationITests {
private void add(String p) {
this.payloads.add(p);
}
}
}

View File

@@ -35,84 +35,74 @@ import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;
import static org.assertj.core.api.Assertions.assertThat;
public class LdapProviderBeanDefinitionParserTests {
InMemoryXmlApplicationContext appCtx;
@After
public void closeAppContext() {
if (appCtx != null) {
appCtx.close();
appCtx = null;
if (this.appCtx != null) {
this.appCtx.close();
this.appCtx = null;
}
}
@Test
public void simpleProviderAuthenticatesCorrectly() {
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='0'/>"
+ "<authentication-manager>"
+ " <ldap-authentication-provider group-search-filter='member={0}' />"
+ "</authentication-manager>"
);
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='0'/>"
+ "<authentication-manager>" + " <ldap-authentication-provider group-search-filter='member={0}' />"
+ "</authentication-manager>");
AuthenticationManager authenticationManager = appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, AuthenticationManager.class);
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"));
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
AuthenticationManager.class);
Authentication auth = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"));
UserDetails ben = (UserDetails) auth.getPrincipal();
assertThat(ben.getAuthorities()).hasSize(3);
}
@Test
public void multipleProvidersAreSupported() {
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='0'/>"
+ "<authentication-manager>"
+ " <ldap-authentication-provider group-search-filter='member={0}' />"
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='0'/>"
+ "<authentication-manager>" + " <ldap-authentication-provider group-search-filter='member={0}' />"
+ " <ldap-authentication-provider group-search-filter='uniqueMember={0}' />"
+ "</authentication-manager>"
);
+ "</authentication-manager>");
ProviderManager providerManager = appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class);
ProviderManager providerManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class);
assertThat(providerManager.getProviders()).hasSize(2);
assertThat(providerManager.getProviders())
.extracting("authoritiesPopulator.groupSearchFilter")
assertThat(providerManager.getProviders()).extracting("authoritiesPopulator.groupSearchFilter")
.containsExactly("member={0}", "uniqueMember={0}");
}
@Test(expected = ApplicationContextException.class)
public void missingServerEltCausesConfigException() {
new InMemoryXmlApplicationContext("<authentication-manager>"
+ " <ldap-authentication-provider />"
+ "</authentication-manager>"
);
new InMemoryXmlApplicationContext(
"<authentication-manager>" + " <ldap-authentication-provider />" + "</authentication-manager>");
}
@Test
public void supportsPasswordComparisonAuthentication() {
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='0'/>"
+ "<authentication-manager>"
+ " <ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>"
+ " <password-compare />"
+ " </ldap-authentication-provider>"
+ "</authentication-manager>"
);
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='0'/>"
+ "<authentication-manager>" + " <ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>"
+ " <password-compare />" + " </ldap-authentication-provider>" + "</authentication-manager>");
AuthenticationManager authenticationManager = appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, AuthenticationManager.class);
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"));
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
AuthenticationManager.class);
Authentication auth = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"));
assertThat(auth).isNotNull();
}
@Test
public void supportsPasswordComparisonAuthenticationWithPasswordEncoder() {
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='0'/>"
+ "<authentication-manager>"
+ " <ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>"
+ " <password-compare password-attribute='uid'>"
+ " <password-encoder ref='passwordEncoder' />"
+ " </password-compare>"
+ " </ldap-authentication-provider>"
+ "</authentication-manager>"
+ "<b:bean id='passwordEncoder' class='org.springframework.security.crypto.password.NoOpPasswordEncoder' factory-method='getInstance' />"
);
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='0'/>"
+ "<authentication-manager>" + " <ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>"
+ " <password-compare password-attribute='uid'>" + " <password-encoder ref='passwordEncoder' />"
+ " </password-compare>" + " </ldap-authentication-provider>" + "</authentication-manager>"
+ "<b:bean id='passwordEncoder' class='org.springframework.security.crypto.password.NoOpPasswordEncoder' factory-method='getInstance' />");
AuthenticationManager authenticationManager = appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, AuthenticationManager.class);
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
AuthenticationManager.class);
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("ben", "ben"));
assertThat(auth).isNotNull();
@@ -121,58 +111,52 @@ public class LdapProviderBeanDefinitionParserTests {
// SEC-2472
@Test
public void supportsCryptoPasswordEncoder() {
appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='0'/>"
+ "<authentication-manager>"
+ " <ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>"
+ " <password-compare>"
+ " <password-encoder ref='pe' />"
+ " </password-compare>"
+ " </ldap-authentication-provider>"
+ "</authentication-manager>"
+ "<b:bean id='pe' class='org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder' />"
);
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='0'/>"
+ "<authentication-manager>" + " <ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>"
+ " <password-compare>" + " <password-encoder ref='pe' />" + " </password-compare>"
+ " </ldap-authentication-provider>" + "</authentication-manager>"
+ "<b:bean id='pe' class='org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder' />");
AuthenticationManager authenticationManager = appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, AuthenticationManager.class);
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("bcrypt", "password"));
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
AuthenticationManager.class);
Authentication auth = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken("bcrypt", "password"));
assertThat(auth).isNotNull();
}
@Test
public void inetOrgContextMapperIsSupported() {
appCtx = new InMemoryXmlApplicationContext("<ldap-server url='ldap://127.0.0.1:343/dc=springframework,dc=org' port='0'/>"
+ "<authentication-manager>"
+ " <ldap-authentication-provider user-details-class='inetOrgPerson' />"
+ "</authentication-manager>"
);
this.appCtx = new InMemoryXmlApplicationContext(
"<ldap-server url='ldap://127.0.0.1:343/dc=springframework,dc=org' port='0'/>"
+ "<authentication-manager>"
+ " <ldap-authentication-provider user-details-class='inetOrgPerson' />"
+ "</authentication-manager>");
ProviderManager providerManager = appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class);
ProviderManager providerManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class);
assertThat(providerManager.getProviders()).hasSize(1);
assertThat(providerManager.getProviders())
.extracting("userDetailsContextMapper")
.allSatisfy(contextMapper -> assertThat(contextMapper).isInstanceOf(InetOrgPersonContextMapper.class));
assertThat(providerManager.getProviders()).extracting("userDetailsContextMapper").allSatisfy(
(contextMapper) -> assertThat(contextMapper).isInstanceOf(InetOrgPersonContextMapper.class));
}
@Test
public void ldapAuthenticationProviderWorksWithPlaceholders() {
System.setProperty("udp", "people");
System.setProperty("gsf", "member");
appCtx = new InMemoryXmlApplicationContext("<ldap-server />"
+ "<authentication-manager>"
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server />" + "<authentication-manager>"
+ " <ldap-authentication-provider user-dn-pattern='uid={0},ou=${udp}' group-search-filter='${gsf}={0}' />"
+ "</authentication-manager>"
+ "<b:bean id='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer' class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer' />"
);
+ "<b:bean id='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer' class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer' />");
ProviderManager providerManager = appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class);
ProviderManager providerManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class);
assertThat(providerManager.getProviders()).hasSize(1);
AuthenticationProvider authenticationProvider = providerManager.getProviders().get(0);
assertThat(authenticationProvider)
.extracting("authenticator.userDnFormat")
.satisfies(messageFormats -> assertThat(messageFormats).isEqualTo(new MessageFormat[]{new MessageFormat("uid={0},ou=people")}));
assertThat(authenticationProvider)
.extracting("authoritiesPopulator.groupSearchFilter")
.satisfies(searchFilter -> assertThat(searchFilter).isEqualTo("member={0}"));
assertThat(authenticationProvider).extracting("authenticator.userDnFormat")
.satisfies((messageFormats) -> assertThat(messageFormats)
.isEqualTo(new MessageFormat[] { new MessageFormat("uid={0},ou=people") }));
assertThat(authenticationProvider).extracting("authoritiesPopulator.groupSearchFilter")
.satisfies((searchFilter) -> assertThat(searchFilter).isEqualTo("member={0}"));
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.ldap;
import java.io.IOException;
@@ -35,22 +36,22 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Rob Winch
*/
public class LdapServerBeanDefinitionParserTests {
InMemoryXmlApplicationContext appCtx;
@After
public void closeAppContext() {
if (appCtx != null) {
appCtx.close();
appCtx = null;
if (this.appCtx != null) {
this.appCtx.close();
this.appCtx = null;
}
}
@Test
public void embeddedServerCreationContainsExpectedContextSourceAndData() {
appCtx = new InMemoryXmlApplicationContext(
"<ldap-server ldif='classpath:test-server.ldif' port='0'/>");
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='0'/>");
DefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) appCtx
DefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) this.appCtx
.getBean(BeanIds.CONTEXT_SOURCE);
// Check data is loaded
@@ -62,17 +63,14 @@ public class LdapServerBeanDefinitionParserTests {
public void useOfUrlAttributeCreatesCorrectContextSource() throws Exception {
int port = getDefaultPort();
// Create second "server" with a url pointing at embedded one
appCtx = new InMemoryXmlApplicationContext(
"<ldap-server ldif='classpath:test-server.ldif' port='"
+ port
+ "'/>"
+ "<ldap-server ldif='classpath:test-server.ldif' id='blah' url='ldap://127.0.0.1:"
+ port + "/dc=springframework,dc=org' />");
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server ldif='classpath:test-server.ldif' port='" + port
+ "'/>" + "<ldap-server ldif='classpath:test-server.ldif' id='blah' url='ldap://127.0.0.1:" + port
+ "/dc=springframework,dc=org' />");
// Check the default context source is still there.
appCtx.getBean(BeanIds.CONTEXT_SOURCE);
this.appCtx.getBean(BeanIds.CONTEXT_SOURCE);
DefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) appCtx
DefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) this.appCtx
.getBean("blah");
// Check data is loaded as before
@@ -82,9 +80,9 @@ public class LdapServerBeanDefinitionParserTests {
@Test
public void loadingSpecificLdifFileIsSuccessful() {
appCtx = new InMemoryXmlApplicationContext(
this.appCtx = new InMemoryXmlApplicationContext(
"<ldap-server ldif='classpath*:test-server2.xldif' root='dc=monkeymachine,dc=co,dc=uk' port='0'/>");
DefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) appCtx
DefaultSpringSecurityContextSource contextSource = (DefaultSpringSecurityContextSource) this.appCtx
.getBean(BeanIds.CONTEXT_SOURCE);
LdapTemplate template = new LdapTemplate(contextSource);
@@ -93,8 +91,8 @@ public class LdapServerBeanDefinitionParserTests {
@Test
public void defaultLdifFileIsSuccessful() {
appCtx = new InMemoryXmlApplicationContext("<ldap-server/>");
ApacheDSContainer dsContainer = appCtx.getBean(ApacheDSContainer.class);
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server/>");
ApacheDSContainer dsContainer = this.appCtx.getBean(ApacheDSContainer.class);
assertThat(ReflectionTestUtils.getField(dsContainer, "ldifResources")).isEqualTo("classpath*:*.ldif");
}
@@ -104,4 +102,5 @@ public class LdapServerBeanDefinitionParserTests {
return server.getLocalPort();
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.ldap;
import java.util.Set;
@@ -36,11 +37,6 @@ import org.springframework.security.ldap.userdetails.PersonContextMapper;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.springframework.security.config.ldap.LdapUserServiceBeanDefinitionParser.INET_ORG_PERSON_MAPPER_CLASS;
import static org.springframework.security.config.ldap.LdapUserServiceBeanDefinitionParser.LDAP_AUTHORITIES_POPULATOR_CLASS;
import static org.springframework.security.config.ldap.LdapUserServiceBeanDefinitionParser.LDAP_SEARCH_CLASS;
import static org.springframework.security.config.ldap.LdapUserServiceBeanDefinitionParser.LDAP_USER_MAPPER_CLASS;
import static org.springframework.security.config.ldap.LdapUserServiceBeanDefinitionParser.PERSON_MAPPER_CLASS;
/**
* @author Luke Taylor
@@ -48,36 +44,45 @@ import static org.springframework.security.config.ldap.LdapUserServiceBeanDefini
* @author Eddú Meléndez
*/
public class LdapUserServiceBeanDefinitionParserTests {
private InMemoryXmlApplicationContext appCtx;
@After
public void closeAppContext() {
if (appCtx != null) {
appCtx.close();
appCtx = null;
if (this.appCtx != null) {
this.appCtx.close();
this.appCtx = null;
}
}
@Test
public void beanClassNamesAreCorrect() {
assertThat(FilterBasedLdapUserSearch.class.getName()).isEqualTo(LDAP_SEARCH_CLASS);
assertThat(PersonContextMapper.class.getName()).isEqualTo(PERSON_MAPPER_CLASS);
assertThat(InetOrgPersonContextMapper.class.getName()).isEqualTo(INET_ORG_PERSON_MAPPER_CLASS);
assertThat(LdapUserDetailsMapper.class.getName()).isEqualTo(LDAP_USER_MAPPER_CLASS);
assertThat(DefaultLdapAuthoritiesPopulator.class.getName()).isEqualTo(LDAP_AUTHORITIES_POPULATOR_CLASS);
assertThat(new LdapUserServiceBeanDefinitionParser().getBeanClassName(mock(Element.class))).isEqualTo(LdapUserDetailsService.class.getName());
assertThat(FilterBasedLdapUserSearch.class.getName())
.isEqualTo(LdapUserServiceBeanDefinitionParser.LDAP_SEARCH_CLASS);
assertThat(PersonContextMapper.class.getName())
.isEqualTo(LdapUserServiceBeanDefinitionParser.PERSON_MAPPER_CLASS);
assertThat(InetOrgPersonContextMapper.class.getName())
.isEqualTo(LdapUserServiceBeanDefinitionParser.INET_ORG_PERSON_MAPPER_CLASS);
assertThat(LdapUserDetailsMapper.class.getName())
.isEqualTo(LdapUserServiceBeanDefinitionParser.LDAP_USER_MAPPER_CLASS);
assertThat(DefaultLdapAuthoritiesPopulator.class.getName())
.isEqualTo(LdapUserServiceBeanDefinitionParser.LDAP_AUTHORITIES_POPULATOR_CLASS);
assertThat(new LdapUserServiceBeanDefinitionParser().getBeanClassName(mock(Element.class)))
.isEqualTo(LdapUserDetailsService.class.getName());
}
@Test
public void minimalConfigurationIsParsedOk() {
setContext("<ldap-user-service user-search-filter='(uid={0})' /><ldap-server ldif='classpath:test-server.ldif' url='ldap://127.0.0.1:343/dc=springframework,dc=org' />");
setContext(
"<ldap-user-service user-search-filter='(uid={0})' /><ldap-server ldif='classpath:test-server.ldif' url='ldap://127.0.0.1:343/dc=springframework,dc=org' />");
}
@Test
public void userServiceReturnsExpectedData() {
setContext("<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' group-search-filter='member={0}' /><ldap-server ldif='classpath:test-server.ldif'/>");
setContext(
"<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' group-search-filter='member={0}' /><ldap-server ldif='classpath:test-server.ldif'/>");
UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS");
UserDetailsService uds = (UserDetailsService) this.appCtx.getBean("ldapUDS");
UserDetails ben = uds.loadUserByUsername("ben");
Set<String> authorities = AuthorityUtils.authorityListToSet(ben.getAuthorities());
@@ -87,12 +92,11 @@ public class LdapUserServiceBeanDefinitionParserTests {
@Test
public void differentUserSearchBaseWorksAsExpected() {
setContext("<ldap-user-service id='ldapUDS' "
+ " user-search-base='ou=otherpeople' "
setContext("<ldap-user-service id='ldapUDS' " + " user-search-base='ou=otherpeople' "
+ " user-search-filter='(cn={0})' "
+ " group-search-filter='member={0}' /><ldap-server ldif='classpath:test-server.ldif'/>");
UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS");
UserDetailsService uds = (UserDetailsService) this.appCtx.getBean("ldapUDS");
UserDetails joe = uds.loadUserByUsername("Joe Smeth");
assertThat(joe.getUsername()).isEqualTo("Joe Smeth");
@@ -100,27 +104,26 @@ public class LdapUserServiceBeanDefinitionParserTests {
@Test
public void rolePrefixIsSupported() {
setContext("<ldap-user-service id='ldapUDS' "
+ " user-search-filter='(uid={0})' "
setContext("<ldap-user-service id='ldapUDS' " + " user-search-filter='(uid={0})' "
+ " group-search-filter='member={0}' role-prefix='PREFIX_'/>"
+ "<ldap-user-service id='ldapUDSNoPrefix' "
+ " user-search-filter='(uid={0})' "
+ "<ldap-user-service id='ldapUDSNoPrefix' " + " user-search-filter='(uid={0})' "
+ " group-search-filter='member={0}' role-prefix='none'/><ldap-server ldif='classpath:test-server.ldif'/>");
UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS");
UserDetailsService uds = (UserDetailsService) this.appCtx.getBean("ldapUDS");
UserDetails ben = uds.loadUserByUsername("ben");
assertThat(AuthorityUtils.authorityListToSet(ben.getAuthorities())).contains("PREFIX_DEVELOPERS");
uds = (UserDetailsService) appCtx.getBean("ldapUDSNoPrefix");
uds = (UserDetailsService) this.appCtx.getBean("ldapUDSNoPrefix");
ben = uds.loadUserByUsername("ben");
assertThat(AuthorityUtils.authorityListToSet(ben.getAuthorities())).contains("DEVELOPERS");
}
@Test
public void differentGroupRoleAttributeWorksAsExpected() {
setContext("<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' group-role-attribute='ou' group-search-filter='member={0}' /><ldap-server ldif='classpath:test-server.ldif'/>");
setContext(
"<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' group-role-attribute='ou' group-search-filter='member={0}' /><ldap-server ldif='classpath:test-server.ldif'/>");
UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS");
UserDetailsService uds = (UserDetailsService) this.appCtx.getBean("ldapUDS");
UserDetails ben = uds.loadUserByUsername("ben");
Set<String> authorities = AuthorityUtils.authorityListToSet(ben.getAuthorities());
@@ -131,18 +134,18 @@ public class LdapUserServiceBeanDefinitionParserTests {
@Test
public void isSupportedByAuthenticationProviderElement() {
setContext("<ldap-server url='ldap://127.0.0.1:343/dc=springframework,dc=org' ldif='classpath:test-server.ldif'/>"
+ "<authentication-manager>"
+ " <authentication-provider>"
+ " <ldap-user-service user-search-filter='(uid={0})' />"
+ " </authentication-provider>" + "</authentication-manager>");
setContext(
"<ldap-server url='ldap://127.0.0.1:343/dc=springframework,dc=org' ldif='classpath:test-server.ldif'/>"
+ "<authentication-manager>" + " <authentication-provider>"
+ " <ldap-user-service user-search-filter='(uid={0})' />" + " </authentication-provider>"
+ "</authentication-manager>");
}
@Test
public void personContextMapperIsSupported() {
setContext("<ldap-server ldif='classpath:test-server.ldif'/>"
+ "<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' user-details-class='person'/>");
UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS");
UserDetailsService uds = (UserDetailsService) this.appCtx.getBean("ldapUDS");
UserDetails ben = uds.loadUserByUsername("ben");
assertThat(ben instanceof Person).isTrue();
}
@@ -151,7 +154,7 @@ public class LdapUserServiceBeanDefinitionParserTests {
public void inetOrgContextMapperIsSupported() {
setContext("<ldap-server id='someServer' ldif='classpath:test-server.ldif'/>"
+ "<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' user-details-class='inetOrgPerson'/>");
UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS");
UserDetailsService uds = (UserDetailsService) this.appCtx.getBean("ldapUDS");
UserDetails ben = uds.loadUserByUsername("ben");
assertThat(ben instanceof InetOrgPerson).isTrue();
}
@@ -160,15 +163,15 @@ public class LdapUserServiceBeanDefinitionParserTests {
public void externalContextMapperIsSupported() {
setContext("<ldap-server id='someServer' ldif='classpath:test-server.ldif'/>"
+ "<ldap-user-service id='ldapUDS' user-search-filter='(uid={0})' user-context-mapper-ref='mapper'/>"
+ "<b:bean id='mapper' class='"
+ InetOrgPersonContextMapper.class.getName() + "'/>");
+ "<b:bean id='mapper' class='" + InetOrgPersonContextMapper.class.getName() + "'/>");
UserDetailsService uds = (UserDetailsService) appCtx.getBean("ldapUDS");
UserDetailsService uds = (UserDetailsService) this.appCtx.getBean("ldapUDS");
UserDetails ben = uds.loadUserByUsername("ben");
assertThat(ben instanceof InetOrgPerson).isTrue();
}
private void setContext(String context) {
appCtx = new InMemoryXmlApplicationContext(context);
this.appCtx = new InMemoryXmlApplicationContext(context);
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config;
/**
@@ -25,6 +26,7 @@ package org.springframework.security.config;
* @author Luke Taylor
*/
public abstract class BeanIds {
private static final String PREFIX = "org.springframework.security.";
/**
@@ -33,29 +35,31 @@ public abstract class BeanIds {
*/
public static final String AUTHENTICATION_MANAGER = PREFIX + "authenticationManager";
/** External alias for FilterChainProxy bean, for use in web.xml files */
/**
* External alias for FilterChainProxy bean, for use in web.xml files
*/
public static final String SPRING_SECURITY_FILTER_CHAIN = "springSecurityFilterChain";
public static final String CONTEXT_SOURCE_SETTING_POST_PROCESSOR = PREFIX
+ "contextSettingPostProcessor";
public static final String CONTEXT_SOURCE_SETTING_POST_PROCESSOR = PREFIX + "contextSettingPostProcessor";
public static final String USER_DETAILS_SERVICE = PREFIX + "userDetailsService";
public static final String USER_DETAILS_SERVICE_FACTORY = PREFIX
+ "userDetailsServiceFactory";
public static final String METHOD_ACCESS_MANAGER = PREFIX
+ "defaultMethodAccessManager";
public static final String USER_DETAILS_SERVICE_FACTORY = PREFIX + "userDetailsServiceFactory";
public static final String METHOD_ACCESS_MANAGER = PREFIX + "defaultMethodAccessManager";
public static final String FILTER_CHAIN_PROXY = PREFIX + "filterChainProxy";
public static final String FILTER_CHAINS = PREFIX + "filterChains";
public static final String METHOD_SECURITY_METADATA_SOURCE_ADVISOR = PREFIX
+ "methodSecurityMetadataSourceAdvisor";
public static final String EMBEDDED_APACHE_DS = PREFIX
+ "apacheDirectoryServerContainer";
public static final String EMBEDDED_UNBOUNDID = PREFIX
+ "unboundidServerContainer";
public static final String METHOD_SECURITY_METADATA_SOURCE_ADVISOR = PREFIX + "methodSecurityMetadataSourceAdvisor";
public static final String EMBEDDED_APACHE_DS = PREFIX + "apacheDirectoryServerContainer";
public static final String EMBEDDED_UNBOUNDID = PREFIX + "unboundidServerContainer";
public static final String CONTEXT_SOURCE = PREFIX + "securityContextSource";
public static final String DEBUG_FILTER = PREFIX + "debugFilter";
}

View File

@@ -28,17 +28,17 @@ public interface Customizer<T> {
/**
* Performs the customizations on the input argument.
*
* @param t the input argument
*/
void customize(T t);
/**
* Returns a {@link Customizer} that does not alter the input argument.
*
* @return a {@link Customizer} that does not alter the input argument.
*/
static <T> Customizer<T> withDefaults() {
return t -> {};
return (t) -> {
};
}
}

View File

@@ -13,24 +13,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.config.debug.SecurityDebugBeanFactoryPostProcessor;
import org.w3c.dom.Element;
/**
* @author Luke Taylor
*/
public class DebugBeanDefinitionParser implements BeanDefinitionParser {
public BeanDefinition parse(Element element, ParserContext parserContext) {
RootBeanDefinition debugPP = new RootBeanDefinition(
SecurityDebugBeanFactoryPostProcessor.class);
parserContext.getReaderContext().registerWithGeneratedName(debugPP);
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
RootBeanDefinition debugPP = new RootBeanDefinition(SecurityDebugBeanFactoryPostProcessor.class);
parserContext.getReaderContext().registerWithGeneratedName(debugPP);
return null;
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config;
/**
@@ -23,61 +24,113 @@ package org.springframework.security.config;
public abstract class Elements {
public static final String ACCESS_DENIED_HANDLER = "access-denied-handler";
public static final String AUTHENTICATION_MANAGER = "authentication-manager";
public static final String AFTER_INVOCATION_PROVIDER = "after-invocation-provider";
public static final String USER_SERVICE = "user-service";
public static final String JDBC_USER_SERVICE = "jdbc-user-service";
public static final String FILTER_CHAIN_MAP = "filter-chain-map";
public static final String INTERCEPT_METHODS = "intercept-methods";
public static final String INTERCEPT_URL = "intercept-url";
public static final String AUTHENTICATION_PROVIDER = "authentication-provider";
public static final String HTTP = "http";
public static final String LDAP_PROVIDER = "ldap-authentication-provider";
public static final String LDAP_SERVER = "ldap-server";
public static final String LDAP_USER_SERVICE = "ldap-user-service";
public static final String PROTECT_POINTCUT = "protect-pointcut";
public static final String EXPRESSION_HANDLER = "expression-handler";
public static final String INVOCATION_HANDLING = "pre-post-annotation-handling";
public static final String INVOCATION_ATTRIBUTE_FACTORY = "invocation-attribute-factory";
public static final String PRE_INVOCATION_ADVICE = "pre-invocation-advice";
public static final String POST_INVOCATION_ADVICE = "post-invocation-advice";
public static final String PROTECT = "protect";
public static final String SESSION_MANAGEMENT = "session-management";
public static final String CONCURRENT_SESSIONS = "concurrency-control";
public static final String LOGOUT = "logout";
public static final String FORM_LOGIN = "form-login";
public static final String OPENID_LOGIN = "openid-login";
public static final String OPENID_ATTRIBUTE_EXCHANGE = "attribute-exchange";
public static final String OPENID_ATTRIBUTE = "openid-attribute";
public static final String BASIC_AUTH = "http-basic";
public static final String REMEMBER_ME = "remember-me";
public static final String ANONYMOUS = "anonymous";
public static final String FILTER_CHAIN = "filter-chain";
public static final String GLOBAL_METHOD_SECURITY = "global-method-security";
public static final String PASSWORD_ENCODER = "password-encoder";
public static final String PORT_MAPPINGS = "port-mappings";
public static final String PORT_MAPPING = "port-mapping";
public static final String CUSTOM_FILTER = "custom-filter";
public static final String REQUEST_CACHE = "request-cache";
public static final String X509 = "x509";
public static final String JEE = "jee";
public static final String FILTER_SECURITY_METADATA_SOURCE = "filter-security-metadata-source";
public static final String METHOD_SECURITY_METADATA_SOURCE = "method-security-metadata-source";
public static final String LDAP_PASSWORD_COMPARE = "password-compare";
public static final String DEBUG = "debug";
public static final String HTTP_FIREWALL = "http-firewall";
public static final String HEADERS = "headers";
public static final String CORS = "cors";
public static final String CSRF = "csrf";
public static final String OAUTH2_RESOURCE_SERVER = "oauth2-resource-server";
public static final String JWT = "jwt";
public static final String OPAQUE_TOKEN = "opaque-token";
public static final String WEBSOCKET_MESSAGE_BROKER = "websocket-message-broker";
public static final String INTERCEPT_MESSAGE = "intercept-message";
public static final String OAUTH2_LOGIN = "oauth2-login";
public static final String OAUTH2_CLIENT = "oauth2-client";
public static final String CLIENT_REGISTRATIONS = "client-registrations";
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config;
import java.util.HashMap;
@@ -59,150 +60,140 @@ import org.springframework.util.ClassUtils;
* @since 2.0
*/
public final class SecurityNamespaceHandler implements NamespaceHandler {
private static final String FILTER_CHAIN_PROXY_CLASSNAME = "org.springframework.security.web.FilterChainProxy";
private static final String MESSAGE_CLASSNAME = "org.springframework.messaging.Message";
private final Log logger = LogFactory.getLog(getClass());
private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();
private final BeanDefinitionDecorator interceptMethodsBDD = new InterceptMethodsBeanDefinitionDecorator();
private BeanDefinitionDecorator filterChainMapBDD;
public SecurityNamespaceHandler() {
String coreVersion = SpringSecurityCoreVersion.getVersion();
Package pkg = SpringSecurityCoreVersion.class.getPackage();
if (pkg == null || coreVersion == null) {
logger.info("Couldn't determine package version information.");
this.logger.info("Couldn't determine package version information.");
return;
}
String version = pkg.getImplementationVersion();
logger.info("Spring Security 'config' module version is " + version);
this.logger.info("Spring Security 'config' module version is " + version);
if (version.compareTo(coreVersion) != 0) {
logger.error("You are running with different versions of the Spring Security 'core' and 'config' modules");
this.logger.error(
"You are running with different versions of the Spring Security 'core' and 'config' modules");
}
}
@Override
public BeanDefinition parse(Element element, ParserContext pc) {
if (!namespaceMatchesVersion(element)) {
pc.getReaderContext()
.fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or spring-security-3.1.xsd schema or spring-security-3.2.xsd schema or spring-security-4.0.xsd schema "
+ "with Spring Security 5.4. Please update your schema declarations to the 5.4 schema.",
element);
pc.getReaderContext().fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or "
+ "spring-security-3.1.xsd schema or spring-security-3.2.xsd schema or spring-security-4.0.xsd schema "
+ "with Spring Security 5.4. Please update your schema declarations to the 5.4 schema.", element);
}
String name = pc.getDelegate().getLocalName(element);
BeanDefinitionParser parser = parsers.get(name);
BeanDefinitionParser parser = this.parsers.get(name);
if (parser == null) {
// SEC-1455. Load parsers when required, not just on init().
loadParsers();
}
if (parser == null) {
if (Elements.HTTP.equals(name)
|| Elements.FILTER_SECURITY_METADATA_SOURCE.equals(name)
|| Elements.FILTER_CHAIN_MAP.equals(name)
|| Elements.FILTER_CHAIN.equals(name)) {
reportMissingWebClasses(name, pc, element);
}
else {
reportUnsupportedNodeType(name, pc, element);
}
return null;
if (parser != null) {
return parser.parse(element, pc);
}
return parser.parse(element, pc);
if (Elements.HTTP.equals(name) || Elements.FILTER_SECURITY_METADATA_SOURCE.equals(name)
|| Elements.FILTER_CHAIN_MAP.equals(name) || Elements.FILTER_CHAIN.equals(name)) {
reportMissingWebClasses(name, pc, element);
}
else {
reportUnsupportedNodeType(name, pc, element);
}
return null;
}
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition,
ParserContext pc) {
@Override
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext pc) {
String name = pc.getDelegate().getLocalName(node);
// We only handle elements
if (node instanceof Element) {
// We only handle elements
if (Elements.INTERCEPT_METHODS.equals(name)) {
return interceptMethodsBDD.decorate(node, definition, pc);
return this.interceptMethodsBDD.decorate(node, definition, pc);
}
if (Elements.FILTER_CHAIN_MAP.equals(name)) {
if (filterChainMapBDD == null) {
if (this.filterChainMapBDD == null) {
loadParsers();
}
if (filterChainMapBDD == null) {
if (this.filterChainMapBDD == null) {
reportMissingWebClasses(name, pc, node);
}
return filterChainMapBDD.decorate(node, definition, pc);
return this.filterChainMapBDD.decorate(node, definition, pc);
}
}
reportUnsupportedNodeType(name, pc, node);
return null;
}
private void reportUnsupportedNodeType(String name, ParserContext pc, Node node) {
pc.getReaderContext().fatal(
"Security namespace does not support decoration of "
+ (node instanceof Element ? "element" : "attribute") + " ["
+ name + "]", node);
pc.getReaderContext().fatal("Security namespace does not support decoration of "
+ ((node instanceof Element) ? "element" : "attribute") + " [" + name + "]", node);
}
private void reportMissingWebClasses(String nodeName, ParserContext pc, Node node) {
String errorMessage = "The classes from the spring-security-web jar "
+ "(or one of its dependencies) are not available. You need these to use <"
+ nodeName + ">";
+ "(or one of its dependencies) are not available. You need these to use <" + nodeName + ">";
try {
ClassUtils.forName(FILTER_CHAIN_PROXY_CLASSNAME, getClass().getClassLoader());
// no details available
pc.getReaderContext().fatal(errorMessage, node);
}
catch (Throwable cause) {
catch (Throwable ex) {
// provide details on why it could not be loaded
pc.getReaderContext().fatal(errorMessage, node, cause);
pc.getReaderContext().fatal(errorMessage, node, ex);
}
}
@Override
public void init() {
loadParsers();
}
private void loadParsers() {
// Parsers
parsers.put(Elements.LDAP_PROVIDER, new LdapProviderBeanDefinitionParser());
parsers.put(Elements.LDAP_SERVER, new LdapServerBeanDefinitionParser());
parsers.put(Elements.LDAP_USER_SERVICE, new LdapUserServiceBeanDefinitionParser());
parsers.put(Elements.USER_SERVICE, new UserServiceBeanDefinitionParser());
parsers.put(Elements.JDBC_USER_SERVICE, new JdbcUserServiceBeanDefinitionParser());
parsers.put(Elements.AUTHENTICATION_PROVIDER,
new AuthenticationProviderBeanDefinitionParser());
parsers.put(Elements.GLOBAL_METHOD_SECURITY,
new GlobalMethodSecurityBeanDefinitionParser());
parsers.put(Elements.AUTHENTICATION_MANAGER,
new AuthenticationManagerBeanDefinitionParser());
parsers.put(Elements.METHOD_SECURITY_METADATA_SOURCE,
this.parsers.put(Elements.LDAP_PROVIDER, new LdapProviderBeanDefinitionParser());
this.parsers.put(Elements.LDAP_SERVER, new LdapServerBeanDefinitionParser());
this.parsers.put(Elements.LDAP_USER_SERVICE, new LdapUserServiceBeanDefinitionParser());
this.parsers.put(Elements.USER_SERVICE, new UserServiceBeanDefinitionParser());
this.parsers.put(Elements.JDBC_USER_SERVICE, new JdbcUserServiceBeanDefinitionParser());
this.parsers.put(Elements.AUTHENTICATION_PROVIDER, new AuthenticationProviderBeanDefinitionParser());
this.parsers.put(Elements.GLOBAL_METHOD_SECURITY, new GlobalMethodSecurityBeanDefinitionParser());
this.parsers.put(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser());
this.parsers.put(Elements.METHOD_SECURITY_METADATA_SOURCE,
new MethodSecurityMetadataSourceBeanDefinitionParser());
// Only load the web-namespace parsers if the web classes are available
if (ClassUtils.isPresent(FILTER_CHAIN_PROXY_CLASSNAME, getClass()
.getClassLoader())) {
parsers.put(Elements.DEBUG, new DebugBeanDefinitionParser());
parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());
parsers.put(Elements.HTTP_FIREWALL, new HttpFirewallBeanDefinitionParser());
parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE,
new FilterInvocationSecurityMetadataSourceParser());
parsers.put(Elements.FILTER_CHAIN, new FilterChainBeanDefinitionParser());
filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();
parsers.put(Elements.CLIENT_REGISTRATIONS, new ClientRegistrationsBeanDefinitionParser());
if (ClassUtils.isPresent(FILTER_CHAIN_PROXY_CLASSNAME, getClass().getClassLoader())) {
loadWebParsers();
}
if (ClassUtils.isPresent(MESSAGE_CLASSNAME, getClass().getClassLoader())) {
parsers.put(Elements.WEBSOCKET_MESSAGE_BROKER,
new WebSocketMessageBrokerSecurityBeanDefinitionParser());
loadWebSocketParsers();
}
}
private void loadWebParsers() {
this.parsers.put(Elements.DEBUG, new DebugBeanDefinitionParser());
this.parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser());
this.parsers.put(Elements.HTTP_FIREWALL, new HttpFirewallBeanDefinitionParser());
this.parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
this.parsers.put(Elements.FILTER_CHAIN, new FilterChainBeanDefinitionParser());
this.filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();
this.parsers.put(Elements.CLIENT_REGISTRATIONS, new ClientRegistrationsBeanDefinitionParser());
}
private void loadWebSocketParsers() {
this.parsers.put(Elements.WEBSOCKET_MESSAGE_BROKER, new WebSocketMessageBrokerSecurityBeanDefinitionParser());
}
/**
* Check that the schema location declared in the source file being parsed matches the
* Spring Security version. The old 2.0 schema is not compatible with the 3.1 parser,
@@ -212,7 +203,6 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
* using 3.0 as an error too. It might be an error to declare spring-security.xsd as
* an alias, but you are only going to find that out when one of the sub parsers
* breaks.
*
* @param element the element that is to be parsed next
* @return true if we find a schema declaration that matches
*/
@@ -222,8 +212,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
}
private boolean matchesVersionInternal(Element element) {
String schemaLocation = element.getAttributeNS(
"http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
return schemaLocation.matches("(?m).*spring-security-5\\.4.*.xsd.*")
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|| !schemaLocation.matches("(?m).*spring-security.*");

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation;
import java.util.ArrayList;
@@ -44,18 +45,18 @@ import org.springframework.web.filter.DelegatingFilterProxy;
* filters necessary for session management, form based login, authorization, etc.
* </p>
*
* @see WebSecurity
*
* @author Rob Winch
*
* @param <O> The object that this builder returns
* @param <B> The type of this builder (that is returned by the base class)
* @author Rob Winch
* @see WebSecurity
*/
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
extends AbstractSecurityBuilder<O> {
private final Log logger = LogFactory.getLog(getClass());
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
private final Map<Class<?>, Object> sharedObjects = new HashMap<>();
@@ -70,11 +71,9 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
* Creates a new instance with the provided {@link ObjectPostProcessor}. This post
* processor must support Object since there are many types of objects that may be
* post processed.
*
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
*/
protected AbstractConfiguredSecurityBuilder(
ObjectPostProcessor<Object> objectPostProcessor) {
protected AbstractConfiguredSecurityBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
this(objectPostProcessor, false);
}
@@ -82,13 +81,11 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
* Creates a new instance with the provided {@link ObjectPostProcessor}. This post
* processor must support Object since there are many types of objects that may be
* post processed.
*
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
* @param allowConfigurersOfSameType if true, will not override other
* {@link SecurityConfigurer}'s when performing apply
*/
protected AbstractConfiguredSecurityBuilder(
ObjectPostProcessor<Object> objectPostProcessor,
protected AbstractConfiguredSecurityBuilder(ObjectPostProcessor<Object> objectPostProcessor,
boolean allowConfigurersOfSameType) {
Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");
this.objectPostProcessor = objectPostProcessor;
@@ -98,37 +95,32 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
/**
* Similar to {@link #build()} and {@link #getObject()} but checks the state to
* determine if {@link #build()} needs to be called first.
*
* @return the result of {@link #build()} or {@link #getObject()}. If an error occurs
* while building, returns null.
*/
public O getOrBuild() {
if (isUnbuilt()) {
try {
return build();
}
catch (Exception e) {
logger.debug("Failed to perform build. Returning null", e);
return null;
}
}
else {
if (!isUnbuilt()) {
return getObject();
}
try {
return build();
}
catch (Exception ex) {
this.logger.debug("Failed to perform build. Returning null", ex);
return null;
}
}
/**
* Applies a {@link SecurityConfigurerAdapter} to this {@link SecurityBuilder} and
* invokes {@link SecurityConfigurerAdapter#setBuilder(SecurityBuilder)}.
*
* @param configurer
* @return the {@link SecurityConfigurerAdapter} for further customizations
* @throws Exception
*/
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer)
throws Exception {
configurer.addObjectPostProcessor(objectPostProcessor);
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception {
configurer.addObjectPostProcessor(this.objectPostProcessor);
configurer.setBuilder((B) this);
add(configurer);
return configurer;
@@ -138,7 +130,6 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
* Applies a {@link SecurityConfigurer} to this {@link SecurityBuilder} overriding any
* {@link SecurityConfigurer} of the exact same class. Note that object hierarchies
* are not considered.
*
* @param configurer
* @return the {@link SecurityConfigurerAdapter} for further customizations
* @throws Exception
@@ -150,7 +141,6 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
/**
* Sets an object that is shared by multiple {@link SecurityConfigurer}.
*
* @param sharedType the Class to key the shared object by.
* @param object the Object to store
*/
@@ -161,7 +151,6 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
/**
* Gets a shared Object. Note that object heirarchies are not considered.
*
* @param sharedType the type of the shared Object
* @return the shared Object or null if it is not found
*/
@@ -181,28 +170,25 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
/**
* Adds {@link SecurityConfigurer} ensuring that it is allowed and invoking
* {@link SecurityConfigurer#init(SecurityBuilder)} immediately if necessary.
*
* @param configurer the {@link SecurityConfigurer} to add
*/
@SuppressWarnings("unchecked")
private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
Assert.notNull(configurer, "configurer cannot be null");
Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
.getClass();
synchronized (configurers) {
if (buildState.isConfigured()) {
throw new IllegalStateException("Cannot apply " + configurer
+ " to already built object");
synchronized (this.configurers) {
if (this.buildState.isConfigured()) {
throw new IllegalStateException("Cannot apply " + configurer + " to already built object");
}
List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
.get(clazz) : null;
if (configs == null) {
configs = new ArrayList<>(1);
List<SecurityConfigurer<O, B>> configs = null;
if (this.allowConfigurersOfSameType) {
configs = this.configurers.get(clazz);
}
configs = (configs != null) ? configs : new ArrayList<>(1);
configs.add(configurer);
this.configurers.put(clazz, configs);
if (buildState.isInitializing()) {
if (this.buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
@@ -211,7 +197,6 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
/**
* Gets all the {@link SecurityConfigurer} instances by its class name or an empty
* List if not found. Note that object hierarchies are not considered.
*
* @param clazz the {@link SecurityConfigurer} class to look for
* @return a list of {@link SecurityConfigurer}s for further customization
*/
@@ -227,7 +212,6 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
/**
* Removes all the {@link SecurityConfigurer} instances by its class name or an empty
* List if not found. Note that object hierarchies are not considered.
*
* @param clazz the {@link SecurityConfigurer} class to look for
* @return a list of {@link SecurityConfigurer}s for further customization
*/
@@ -243,7 +227,6 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
/**
* Gets the {@link SecurityConfigurer} by its class name or <code>null</code> if not
* found. Note that object hierarchies are not considered.
*
* @param clazz
* @return the {@link SecurityConfigurer} for further customizations
*/
@@ -253,17 +236,14 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
if (configs == null) {
return null;
}
if (configs.size() != 1) {
throw new IllegalStateException("Only one configurer expected for type "
+ clazz + ", but got " + configs);
}
Assert.state(configs.size() == 1,
() -> "Only one configurer expected for type " + clazz + ", but got " + configs);
return (C) configs.get(0);
}
/**
* Removes and returns the {@link SecurityConfigurer} by its class name or
* <code>null</code> if not found. Note that object hierarchies are not considered.
*
* @param clazz
* @return
*/
@@ -273,10 +253,8 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
if (configs == null) {
return null;
}
if (configs.size() != 1) {
throw new IllegalStateException("Only one configurer expected for type "
+ clazz + ", but got " + configs);
}
Assert.state(configs.size() == 1,
() -> "Only one configurer expected for type " + clazz + ", but got " + configs);
return (C) configs.get(0);
}
@@ -295,7 +273,6 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
/**
* Performs post processing of an object. The default is to delegate to the
* {@link ObjectPostProcessor}.
*
* @param object the Object to post process
* @return the possibly modified Object to use
*/
@@ -317,23 +294,16 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
*/
@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
synchronized (this.configurers) {
this.buildState = BuildState.INITIALIZING;
beforeInit();
init();
buildState = BuildState.CONFIGURING;
this.buildState = BuildState.CONFIGURING;
beforeConfigure();
configure();
buildState = BuildState.BUILDING;
this.buildState = BuildState.BUILDING;
O result = performBuild();
buildState = BuildState.BUILT;
this.buildState = BuildState.BUILT;
return result;
}
}
@@ -357,7 +327,6 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
/**
* Subclasses must implement this method to build the object that is being returned.
*
* @return the Object to be buit or null if the implementation allows it
*/
protected abstract O performBuild() throws Exception;
@@ -365,12 +334,10 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
@SuppressWarnings("unchecked")
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
}
for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
for (SecurityConfigurer<O, B> configurer : this.configurersAddedInInitializing) {
configurer.init((B) this);
}
}
@@ -378,7 +345,6 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
@SuppressWarnings("unchecked")
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
@@ -397,8 +363,8 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
* @return true, if unbuilt else false
*/
private boolean isUnbuilt() {
synchronized (configurers) {
return buildState == BuildState.UNBUILT;
synchronized (this.configurers) {
return this.buildState == BuildState.UNBUILT;
}
}
@@ -409,6 +375,7 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
* @since 3.2
*/
private enum BuildState {
/**
* This is the state before the {@link Builder#build()} is invoked
*/
@@ -447,7 +414,7 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
}
public boolean isInitializing() {
return INITIALIZING.order == order;
return INITIALIZING.order == this.order;
}
/**
@@ -455,7 +422,9 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
* @return
*/
public boolean isConfigured() {
return order >= CONFIGURING.order;
return this.order >= CONFIGURING.order;
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -22,20 +23,16 @@ import java.util.concurrent.atomic.AtomicBoolean;
* time.
*
* @param <O> the type of Object that is being built
*
* @author Rob Winch
*
*/
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
private AtomicBoolean building = new AtomicBoolean();
private O object;
/*
* (non-Javadoc)
*
* @see org.springframework.security.config.annotation.SecurityBuilder#build()
*/
@Override
public final O build() throws Exception {
if (this.building.compareAndSet(false, true)) {
this.object = doBuild();
@@ -47,7 +44,6 @@ public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
/**
* Gets the object that was built. If it has not been built yet an Exception is
* thrown.
*
* @return the Object that was built
*/
public final O getObject() {
@@ -59,10 +55,9 @@ public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
/**
* Subclasses should implement this to perform the build.
*
* @return the object that should be returned by {@link #build()}.
*
* @throws Exception if an error occurs
*/
protected abstract O doBuild() throws Exception;
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation;
/**
@@ -28,4 +29,5 @@ public class AlreadyBuiltException extends IllegalStateException {
}
private static final long serialVersionUID = -5891004752785553015L;
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation;
import org.springframework.beans.factory.Aware;
@@ -25,7 +26,6 @@ import org.springframework.beans.factory.InitializingBean;
* {@link DisposableBean#destroy()} has been invoked.
*
* @param <T> the bound of the types of Objects this {@link ObjectPostProcessor} supports.
*
* @author Rob Winch
* @since 3.2
*/
@@ -34,9 +34,9 @@ public interface ObjectPostProcessor<T> {
/**
* Initialize the object possibly returning a modified instance that should be used
* instead.
*
* @param object the object to initialize
* @return the initialized version of the object
*/
<O extends T> O postProcess(O object);
}
}

View File

@@ -13,23 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation;
/**
* Interface for building an Object
*
* @param <O> The type of the Object being built
* @author Rob Winch
* @since 3.2
*
* @param <O> The type of the Object being built
*/
public interface SecurityBuilder<O> {
/**
* Builds the object and returns it or null.
*
* @return the Object to be built or null if the implementation allows it.
* @throws Exception if an error occurred when building the Object
*/
O build() throws Exception;
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation;
/**
@@ -21,21 +22,19 @@ package org.springframework.security.config.annotation;
* {@link #init(SecurityBuilder)} methods have been invoked, each
* {@link #configure(SecurityBuilder)} method is invoked.
*
* @see AbstractConfiguredSecurityBuilder
*
* @author Rob Winch
*
* @param <O> The object being built by the {@link SecurityBuilder} B
* @param <B> The {@link SecurityBuilder} that builds objects of type O. This is also the
* {@link SecurityBuilder} that is being configured.
* @author Rob Winch
* @see AbstractConfiguredSecurityBuilder
*/
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
/**
* Initialize the {@link SecurityBuilder}. Here only shared state should be created
* and modified, but not properties on the {@link SecurityBuilder} used for building
* the object. This ensures that the {@link #configure(SecurityBuilder)} method uses
* the correct shared objects when building. Configurers should be applied here.
*
* @param builder
* @throws Exception
*/
@@ -44,9 +43,9 @@ public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
/**
* Configure the {@link SecurityBuilder} by setting the necessary properties on the
* {@link SecurityBuilder}.
*
* @param builder
* @throws Exception
*/
void configure(B builder) throws Exception;
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation;
import java.util.ArrayList;
@@ -20,6 +21,7 @@ import java.util.List;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.util.Assert;
/**
* A base class for {@link SecurityConfigurer} that allows subclasses to only implement
@@ -27,29 +29,29 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator;
* {@link SecurityConfigurer} and when done gaining access to the {@link SecurityBuilder}
* that is being configured.
*
* @author Rob Winch
* @author Wallace Wadge
*
* @param <O> The Object being built by B
* @param <B> The Builder that is building O and is configured by
* {@link SecurityConfigurerAdapter}
* @author Rob Winch
* @author Wallace Wadge
*/
public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>>
implements SecurityConfigurer<O, B> {
public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>> implements SecurityConfigurer<O, B> {
private B securityBuilder;
private CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor();
@Override
public void init(B builder) throws Exception {
}
@Override
public void configure(B builder) throws Exception {
}
/**
* Return the {@link SecurityBuilder} when done using the {@link SecurityConfigurer}.
* This is useful for method chaining.
*
* @return the {@link SecurityBuilder} for further customizations
*/
public B and() {
@@ -58,21 +60,17 @@ public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>>
/**
* Gets the {@link SecurityBuilder}. Cannot be null.
*
* @return the {@link SecurityBuilder}
* @throws IllegalStateException if {@link SecurityBuilder} is null
*/
protected final B getBuilder() {
if (securityBuilder == null) {
throw new IllegalStateException("securityBuilder cannot be null");
}
return securityBuilder;
Assert.state(this.securityBuilder != null, "securityBuilder cannot be null");
return this.securityBuilder;
}
/**
* Performs post processing of an object. The default is to delegate to the
* {@link ObjectPostProcessor}.
*
* @param object the Object to post process
* @return the possibly modified Object to use
*/
@@ -85,7 +83,6 @@ public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>>
* Adds an {@link ObjectPostProcessor} to be used for this
* {@link SecurityConfigurerAdapter}. The default implementation does nothing to the
* object.
*
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
*/
public void addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
@@ -95,7 +92,6 @@ public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>>
/**
* Sets the {@link SecurityBuilder} to be used. This is automatically set when using
* {@link AbstractConfiguredSecurityBuilder#apply(SecurityConfigurerAdapter)}
*
* @param builder the {@link SecurityBuilder} to set
*/
public void setBuilder(B builder) {
@@ -108,16 +104,16 @@ public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>>
*
* @author Rob Winch
*/
private static final class CompositeObjectPostProcessor implements
ObjectPostProcessor<Object> {
private static final class CompositeObjectPostProcessor implements ObjectPostProcessor<Object> {
private List<ObjectPostProcessor<?>> postProcessors = new ArrayList<>();
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public Object postProcess(Object object) {
for (ObjectPostProcessor opp : postProcessors) {
for (ObjectPostProcessor opp : this.postProcessors) {
Class<?> oppClass = opp.getClass();
Class<?> oppType = GenericTypeResolver.resolveTypeArgument(oppClass,
ObjectPostProcessor.class);
Class<?> oppType = GenericTypeResolver.resolveTypeArgument(oppClass, ObjectPostProcessor.class);
if (oppType == null || oppType.isAssignableFrom(object.getClass())) {
object = opp.postProcess(object);
}
@@ -130,11 +126,12 @@ public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>>
* @param objectPostProcessor the {@link ObjectPostProcessor} to add
* @return true if the {@link ObjectPostProcessor} was added, else false
*/
private boolean addObjectPostProcessor(
ObjectPostProcessor<?> objectPostProcessor) {
private boolean addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
boolean result = this.postProcessors.add(objectPostProcessor);
postProcessors.sort(AnnotationAwareOrderComparator.INSTANCE);
this.postProcessors.sort(AnnotationAwareOrderComparator.INSTANCE);
return result;
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication;
import org.springframework.security.authentication.AuthenticationManager;
@@ -23,12 +24,11 @@ import org.springframework.security.config.annotation.SecurityBuilder;
/**
* Interface for operating on a SecurityBuilder that creates a {@link ProviderManager}
*
* @author Rob Winch
*
* @param <B> the type of the {@link SecurityBuilder}
* @author Rob Winch
*/
public interface ProviderManagerBuilder<B extends ProviderManagerBuilder<B>> extends
SecurityBuilder<AuthenticationManager> {
public interface ProviderManagerBuilder<B extends ProviderManagerBuilder<B>>
extends SecurityBuilder<AuthenticationManager> {
/**
* Add authentication based upon the custom {@link AuthenticationProvider} that is
@@ -36,10 +36,11 @@ public interface ProviderManagerBuilder<B extends ProviderManagerBuilder<B>> ext
* customizations must be done externally and the {@link ProviderManagerBuilder} is
* returned immediately.
*
* Note that an Exception is thrown if an error occurs when adding the {@link AuthenticationProvider}.
*
* Note that an Exception is thrown if an error occurs when adding the
* {@link AuthenticationProvider}.
* @return a {@link ProviderManagerBuilder} to allow further authentication to be
* provided to the {@link ProviderManagerBuilder}
*/
B authenticationProvider(AuthenticationProvider authenticationProvider);
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.builders;
import java.util.ArrayList;
@@ -20,6 +21,7 @@ import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
@@ -48,15 +50,19 @@ import org.springframework.util.Assert;
* @since 3.2
*/
public class AuthenticationManagerBuilder
extends
AbstractConfiguredSecurityBuilder<AuthenticationManager, AuthenticationManagerBuilder>
extends AbstractConfiguredSecurityBuilder<AuthenticationManager, AuthenticationManagerBuilder>
implements ProviderManagerBuilder<AuthenticationManagerBuilder> {
private final Log logger = LogFactory.getLog(getClass());
private AuthenticationManager parentAuthenticationManager;
private List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
private UserDetailsService defaultUserDetailsService;
private Boolean eraseCredentials;
private AuthenticationEventPublisher eventPublisher;
/**
@@ -71,18 +77,15 @@ public class AuthenticationManagerBuilder
* Allows providing a parent {@link AuthenticationManager} that will be tried if this
* {@link AuthenticationManager} was unable to attempt to authenticate the provided
* {@link Authentication}.
*
* @param authenticationManager the {@link AuthenticationManager} that should be used
* if the current {@link AuthenticationManager} was unable to attempt to authenticate
* the provided {@link Authentication}.
* @return the {@link AuthenticationManagerBuilder} for further adding types of
* authentication
*/
public AuthenticationManagerBuilder parentAuthenticationManager(
AuthenticationManager authenticationManager) {
public AuthenticationManagerBuilder parentAuthenticationManager(AuthenticationManager authenticationManager) {
if (authenticationManager instanceof ProviderManager) {
eraseCredentials(((ProviderManager) authenticationManager)
.isEraseCredentialsAfterAuthentication());
eraseCredentials(((ProviderManager) authenticationManager).isEraseCredentialsAfterAuthentication());
}
this.parentAuthenticationManager = authenticationManager;
return this;
@@ -90,20 +93,16 @@ public class AuthenticationManagerBuilder
/**
* Sets the {@link AuthenticationEventPublisher}
*
* @param eventPublisher the {@link AuthenticationEventPublisher} to use
* @return the {@link AuthenticationManagerBuilder} for further customizations
*/
public AuthenticationManagerBuilder authenticationEventPublisher(
AuthenticationEventPublisher eventPublisher) {
public AuthenticationManagerBuilder authenticationEventPublisher(AuthenticationEventPublisher eventPublisher) {
Assert.notNull(eventPublisher, "AuthenticationEventPublisher cannot be null");
this.eventPublisher = eventPublisher;
return this;
}
/**
*
*
* @param eraseCredentials true if {@link AuthenticationManager} should clear the
* credentials from the {@link Authentication} object after authenticating
* @return the {@link AuthenticationManagerBuilder} for further customizations
@@ -124,7 +123,6 @@ public class AuthenticationManagerBuilder
* {@link UserDetailsService}'s may override this {@link UserDetailsService} as the
* default.
* </p>
*
* @return a {@link InMemoryUserDetailsManagerConfigurer} to allow customization of
* the in memory authentication
* @throws Exception if an error occurs when adding the in memory authentication
@@ -141,8 +139,8 @@ public class AuthenticationManagerBuilder
*
* <p>
* When using with a persistent data store, it is best to add users external of
* configuration using something like <a href="https://flywaydb.org/">Flyway</a> or <a
* href="https://www.liquibase.org/">Liquibase</a> to create the schema and adding
* configuration using something like <a href="https://flywaydb.org/">Flyway</a> or
* <a href="https://www.liquibase.org/">Liquibase</a> to create the schema and adding
* users to ensure these steps are only done once and that the optimal SQL is used.
* </p>
*
@@ -154,13 +152,11 @@ public class AuthenticationManagerBuilder
* "https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#user-schema"
* >User Schema</a> section of the reference for the default schema.
* </p>
*
* @return a {@link JdbcUserDetailsManagerConfigurer} to allow customization of the
* JDBC authentication
* @throws Exception if an error occurs when adding the JDBC authentication
*/
public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication()
throws Exception {
public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication() throws Exception {
return apply(new JdbcUserDetailsManagerConfigurer<>());
}
@@ -175,7 +171,6 @@ public class AuthenticationManagerBuilder
* {@link UserDetailsService}'s may override this {@link UserDetailsService} as the
* default.
* </p>
*
* @return a {@link DaoAuthenticationConfigurer} to allow customization of the DAO
* authentication
* @throws Exception if an error occurs when adding the {@link UserDetailsService}
@@ -184,8 +179,7 @@ public class AuthenticationManagerBuilder
public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService(
T userDetailsService) throws Exception {
this.defaultUserDetailsService = userDetailsService;
return apply(new DaoAuthenticationConfigurer<>(
userDetailsService));
return apply(new DaoAuthenticationConfigurer<>(userDetailsService));
}
/**
@@ -196,13 +190,11 @@ public class AuthenticationManagerBuilder
* <p>
* This method <b>does NOT</b> ensure that a {@link UserDetailsService} is available
* for the {@link #getDefaultUserDetailsService()} method.
*
* @return a {@link LdapAuthenticationProviderConfigurer} to allow customization of
* the LDAP authentication
* @throws Exception if an error occurs when adding the LDAP authentication
*/
public LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthentication()
throws Exception {
public LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthentication() throws Exception {
return apply(new LdapAuthenticationProviderConfigurer<>());
}
@@ -216,13 +208,13 @@ public class AuthenticationManagerBuilder
* This method <b>does NOT</b> ensure that the {@link UserDetailsService} is available
* for the {@link #getDefaultUserDetailsService()} method.
*
* Note that an {@link Exception} might be thrown if an error occurs when adding the {@link AuthenticationProvider}.
*
* Note that an {@link Exception} might be thrown if an error occurs when adding the
* {@link AuthenticationProvider}.
* @return a {@link AuthenticationManagerBuilder} to allow further authentication to
* be provided to the {@link AuthenticationManagerBuilder}
*/
public AuthenticationManagerBuilder authenticationProvider(
AuthenticationProvider authenticationProvider) {
@Override
public AuthenticationManagerBuilder authenticationProvider(AuthenticationProvider authenticationProvider) {
this.authenticationProviders.add(authenticationProvider);
return this;
}
@@ -230,16 +222,16 @@ public class AuthenticationManagerBuilder
@Override
protected ProviderManager performBuild() throws Exception {
if (!isConfigured()) {
logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
this.logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
return null;
}
ProviderManager providerManager = new ProviderManager(authenticationProviders,
parentAuthenticationManager);
if (eraseCredentials != null) {
providerManager.setEraseCredentialsAfterAuthentication(eraseCredentials);
ProviderManager providerManager = new ProviderManager(this.authenticationProviders,
this.parentAuthenticationManager);
if (this.eraseCredentials != null) {
providerManager.setEraseCredentialsAfterAuthentication(this.eraseCredentials);
}
if (eventPublisher != null) {
providerManager.setAuthenticationEventPublisher(eventPublisher);
if (this.eventPublisher != null) {
providerManager.setAuthenticationEventPublisher(this.eventPublisher);
}
providerManager = postProcess(providerManager);
return providerManager;
@@ -257,17 +249,16 @@ public class AuthenticationManagerBuilder
* {@link SecurityConfigurer} that is last could check this method and provide a
* default configuration in the {@link SecurityConfigurer#configure(SecurityBuilder)}
* method.
*
* @return true, if {@link AuthenticationManagerBuilder} is configured, otherwise false
* @return true, if {@link AuthenticationManagerBuilder} is configured, otherwise
* false
*/
public boolean isConfigured() {
return !authenticationProviders.isEmpty() || parentAuthenticationManager != null;
return !this.authenticationProviders.isEmpty() || this.parentAuthenticationManager != null;
}
/**
* Gets the default {@link UserDetailsService} for the
* {@link AuthenticationManagerBuilder}. The result may be null in some circumstances.
*
* @return the default {@link UserDetailsService} for the
* {@link AuthenticationManagerBuilder}
*/
@@ -278,7 +269,6 @@ public class AuthenticationManagerBuilder
/**
* Captures the {@link UserDetailsService} from any {@link UserDetailsAwareConfigurer}
* .
*
* @param configurer the {@link UserDetailsAwareConfigurer} to capture the
* {@link UserDetailsService} from.
* @return the {@link UserDetailsAwareConfigurer} for further customizations
@@ -289,4 +279,5 @@ public class AuthenticationManagerBuilder
this.defaultUserDetailsService = configurer.getUserDetailsService();
return super.apply(configurer);
}
}

View File

@@ -13,10 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configuration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.target.LazyInitTargetSource;
import org.springframework.beans.factory.BeanFactoryUtils;
@@ -28,6 +36,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.log.LogMessage;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
@@ -43,12 +52,6 @@ import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.Assert;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Exports the authentication {@link Configuration}
*
@@ -68,18 +71,18 @@ public class AuthenticationConfiguration {
private boolean authenticationManagerInitialized;
private List<GlobalAuthenticationConfigurerAdapter> globalAuthConfigurers = Collections
.emptyList();
private List<GlobalAuthenticationConfigurerAdapter> globalAuthConfigurers = Collections.emptyList();
private ObjectPostProcessor<Object> objectPostProcessor;
@Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(
ObjectPostProcessor<Object> objectPostProcessor, ApplicationContext context) {
public AuthenticationManagerBuilder authenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,
ApplicationContext context) {
LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
AuthenticationEventPublisher authenticationEventPublisher = getBeanOrNull(context, AuthenticationEventPublisher.class);
DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);
AuthenticationEventPublisher authenticationEventPublisher = getBeanOrNull(context,
AuthenticationEventPublisher.class);
DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(
objectPostProcessor, defaultPasswordEncoder);
if (authenticationEventPublisher != null) {
result.authenticationEventPublisher(authenticationEventPublisher);
}
@@ -93,12 +96,14 @@ public class AuthenticationConfiguration {
}
@Bean
public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {
public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(
ApplicationContext context) {
return new InitializeUserDetailsBeanManagerConfigurer(context);
}
@Bean
public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(ApplicationContext context) {
public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(
ApplicationContext context) {
return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
}
@@ -110,24 +115,19 @@ public class AuthenticationConfiguration {
if (this.buildingAuthenticationManager.getAndSet(true)) {
return new AuthenticationManagerDelegator(authBuilder);
}
for (GlobalAuthenticationConfigurerAdapter config : globalAuthConfigurers) {
for (GlobalAuthenticationConfigurerAdapter config : this.globalAuthConfigurers) {
authBuilder.apply(config);
}
authenticationManager = authBuilder.build();
if (authenticationManager == null) {
authenticationManager = getAuthenticationManagerBean();
this.authenticationManager = authBuilder.build();
if (this.authenticationManager == null) {
this.authenticationManager = getAuthenticationManagerBean();
}
this.authenticationManagerInitialized = true;
return authenticationManager;
return this.authenticationManager;
}
@Autowired(required = false)
public void setGlobalAuthenticationConfigurers(
List<GlobalAuthenticationConfigurerAdapter> configurers) {
public void setGlobalAuthenticationConfigurers(List<GlobalAuthenticationConfigurerAdapter> configurers) {
configurers.sort(AnnotationAwareOrderComparator.INSTANCE);
this.globalAuthConfigurers = configurers;
}
@@ -145,40 +145,40 @@ public class AuthenticationConfiguration {
@SuppressWarnings("unchecked")
private <T> T lazyBean(Class<T> interfaceName) {
LazyInitTargetSource lazyTargetSource = new LazyInitTargetSource();
String[] beanNamesForType = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
applicationContext, interfaceName);
String[] beanNamesForType = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.applicationContext,
interfaceName);
if (beanNamesForType.length == 0) {
return null;
}
String beanName;
if (beanNamesForType.length > 1) {
List<String> primaryBeanNames = getPrimaryBeanNames(beanNamesForType);
Assert.isTrue(primaryBeanNames.size() != 0, () -> "Found " + beanNamesForType.length
+ " beans for type " + interfaceName + ", but none marked as primary");
Assert.isTrue(primaryBeanNames.size() == 1, () -> "Found " + primaryBeanNames.size()
+ " beans for type " + interfaceName + " marked as primary");
beanName = primaryBeanNames.get(0);
} else {
beanName = beanNamesForType[0];
}
String beanName = getBeanName(interfaceName, beanNamesForType);
lazyTargetSource.setTargetBeanName(beanName);
lazyTargetSource.setBeanFactory(applicationContext);
lazyTargetSource.setBeanFactory(this.applicationContext);
ProxyFactoryBean proxyFactory = new ProxyFactoryBean();
proxyFactory = objectPostProcessor.postProcess(proxyFactory);
proxyFactory = this.objectPostProcessor.postProcess(proxyFactory);
proxyFactory.setTargetSource(lazyTargetSource);
return (T) proxyFactory.getObject();
}
private <T> String getBeanName(Class<T> interfaceName, String[] beanNamesForType) {
if (beanNamesForType.length == 1) {
return beanNamesForType[0];
}
List<String> primaryBeanNames = getPrimaryBeanNames(beanNamesForType);
Assert.isTrue(primaryBeanNames.size() != 0, () -> "Found " + beanNamesForType.length + " beans for type "
+ interfaceName + ", but none marked as primary");
Assert.isTrue(primaryBeanNames.size() == 1,
() -> "Found " + primaryBeanNames.size() + " beans for type " + interfaceName + " marked as primary");
return primaryBeanNames.get(0);
}
private List<String> getPrimaryBeanNames(String[] beanNamesForType) {
List<String> list = new ArrayList<>();
if (!(applicationContext instanceof ConfigurableApplicationContext)) {
if (!(this.applicationContext instanceof ConfigurableApplicationContext)) {
return Collections.emptyList();
}
for (String beanName : beanNamesForType) {
if (((ConfigurableApplicationContext) applicationContext).getBeanFactory()
.getBeanDefinition(beanName).isPrimary()) {
if (((ConfigurableApplicationContext) this.applicationContext).getBeanFactory().getBeanDefinition(beanName)
.isPrimary()) {
list.add(beanName);
}
}
@@ -192,16 +192,17 @@ public class AuthenticationConfiguration {
private static <T> T getBeanOrNull(ApplicationContext applicationContext, Class<T> type) {
try {
return applicationContext.getBean(type);
} catch(NoSuchBeanDefinitionException notFound) {
}
catch (NoSuchBeanDefinitionException notFound) {
return null;
}
}
private static class EnableGlobalAuthenticationAutowiredConfigurer extends
GlobalAuthenticationConfigurerAdapter {
private static class EnableGlobalAuthenticationAutowiredConfigurer extends GlobalAuthenticationConfigurerAdapter {
private final ApplicationContext context;
private static final Log logger = LogFactory
.getLog(EnableGlobalAuthenticationAutowiredConfigurer.class);
private static final Log logger = LogFactory.getLog(EnableGlobalAuthenticationAutowiredConfigurer.class);
EnableGlobalAuthenticationAutowiredConfigurer(ApplicationContext context) {
this.context = context;
@@ -209,12 +210,11 @@ public class AuthenticationConfiguration {
@Override
public void init(AuthenticationManagerBuilder auth) {
Map<String, Object> beansWithAnnotation = context
Map<String, Object> beansWithAnnotation = this.context
.getBeansWithAnnotation(EnableGlobalAuthentication.class);
if (logger.isDebugEnabled()) {
logger.debug("Eagerly initializing " + beansWithAnnotation);
}
logger.debug(LogMessage.format("Eagerly initializing %s", beansWithAnnotation));
}
}
/**
@@ -225,8 +225,11 @@ public class AuthenticationConfiguration {
* @since 4.1.1
*/
static final class AuthenticationManagerDelegator implements AuthenticationManager {
private AuthenticationManagerBuilder delegateBuilder;
private AuthenticationManager delegate;
private final Object delegateMonitor = new Object();
AuthenticationManagerDelegator(AuthenticationManagerBuilder delegateBuilder) {
@@ -235,19 +238,16 @@ public class AuthenticationConfiguration {
}
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (this.delegate != null) {
return this.delegate.authenticate(authentication);
}
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
this.delegate = this.delegateBuilder.getObject();
this.delegateBuilder = null;
}
}
return this.delegate.authenticate(authentication);
}
@@ -255,46 +255,46 @@ public class AuthenticationConfiguration {
public String toString() {
return "AuthenticationManagerDelegator [delegate=" + this.delegate + "]";
}
}
static class DefaultPasswordEncoderAuthenticationManagerBuilder extends AuthenticationManagerBuilder {
private PasswordEncoder defaultPasswordEncoder;
/**
* Creates a new instance
*
* @param objectPostProcessor the {@link ObjectPostProcessor} instance to use.
*/
DefaultPasswordEncoderAuthenticationManagerBuilder(
ObjectPostProcessor<Object> objectPostProcessor, PasswordEncoder defaultPasswordEncoder) {
DefaultPasswordEncoderAuthenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,
PasswordEncoder defaultPasswordEncoder) {
super(objectPostProcessor);
this.defaultPasswordEncoder = defaultPasswordEncoder;
}
@Override
public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()
throws Exception {
return super.inMemoryAuthentication()
.passwordEncoder(this.defaultPasswordEncoder);
throws Exception {
return super.inMemoryAuthentication().passwordEncoder(this.defaultPasswordEncoder);
}
@Override
public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication()
throws Exception {
return super.jdbcAuthentication()
.passwordEncoder(this.defaultPasswordEncoder);
public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication() throws Exception {
return super.jdbcAuthentication().passwordEncoder(this.defaultPasswordEncoder);
}
@Override
public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService(
T userDetailsService) throws Exception {
return super.userDetailsService(userDetailsService)
.passwordEncoder(this.defaultPasswordEncoder);
T userDetailsService) throws Exception {
return super.userDetailsService(userDetailsService).passwordEncoder(this.defaultPasswordEncoder);
}
}
static class LazyPasswordEncoder implements PasswordEncoder {
private ApplicationContext applicationContext;
private PasswordEncoder passwordEncoder;
LazyPasswordEncoder(ApplicationContext applicationContext) {
@@ -307,8 +307,7 @@ public class AuthenticationConfiguration {
}
@Override
public boolean matches(CharSequence rawPassword,
String encodedPassword) {
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return getPasswordEncoder().matches(rawPassword, encodedPassword);
}
@@ -333,5 +332,7 @@ public class AuthenticationConfiguration {
public String toString() {
return getPasswordEncoder().toString();
}
}
}

View File

@@ -13,10 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configuration;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration;
@@ -81,10 +84,11 @@ import org.springframework.security.config.annotation.web.servlet.configuration.
* @author Rob Winch
*
*/
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(AuthenticationConfiguration.class)
@Configuration
public @interface EnableGlobalAuthentication {
}

View File

@@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
/**
* A {@link SecurityConfigurer} that can be exposed as a bean to configure the global
@@ -31,12 +31,15 @@ import org.springframework.security.config.annotation.authentication.configurati
* @author Rob Winch
*/
@Order(100)
public abstract class GlobalAuthenticationConfigurerAdapter implements
SecurityConfigurer<AuthenticationManager, AuthenticationManagerBuilder> {
public abstract class GlobalAuthenticationConfigurerAdapter
implements SecurityConfigurer<AuthenticationManager, AuthenticationManagerBuilder> {
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configuration;
import org.springframework.context.ApplicationContext;
@@ -21,26 +22,23 @@ import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
/**
* Lazily initializes the global authentication with an {@link AuthenticationProvider} if it is
* not yet configured and there is only a single Bean of that type.
* Lazily initializes the global authentication with an {@link AuthenticationProvider} if
* it is not yet configured and there is only a single Bean of that type.
*
* @author Rob Winch
* @since 4.1
*/
@Order(InitializeAuthenticationProviderBeanManagerConfigurer.DEFAULT_ORDER)
class InitializeAuthenticationProviderBeanManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
class InitializeAuthenticationProviderBeanManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {
static final int DEFAULT_ORDER = InitializeUserDetailsBeanManagerConfigurer.DEFAULT_ORDER
- 100;
static final int DEFAULT_ORDER = InitializeUserDetailsBeanManagerConfigurer.DEFAULT_ORDER - 100;
private final ApplicationContext context;
/**
* @param context the ApplicationContext to look up beans.
*/
InitializeAuthenticationProviderBeanManagerConfigurer(
ApplicationContext context) {
InitializeAuthenticationProviderBeanManagerConfigurer(ApplicationContext context) {
this.context = context;
}
@@ -49,25 +47,23 @@ class InitializeAuthenticationProviderBeanManagerConfigurer
auth.apply(new InitializeAuthenticationProviderManagerConfigurer());
}
class InitializeAuthenticationProviderManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
class InitializeAuthenticationProviderManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth) {
if (auth.isConfigured()) {
return;
}
AuthenticationProvider authenticationProvider = getBeanOrNull(
AuthenticationProvider.class);
AuthenticationProvider authenticationProvider = getBeanOrNull(AuthenticationProvider.class);
if (authenticationProvider == null) {
return;
}
auth.authenticationProvider(authenticationProvider);
}
/**
* @return a bean of the requested class if there's just a single registered component, null otherwise.
* @return a bean of the requested class if there's just a single registered
* component, null otherwise.
*/
private <T> T getBeanOrNull(Class<T> type) {
String[] beanNames = InitializeAuthenticationProviderBeanManagerConfigurer.this.context
@@ -75,9 +71,9 @@ class InitializeAuthenticationProviderBeanManagerConfigurer
if (beanNames.length != 1) {
return null;
}
return InitializeAuthenticationProviderBeanManagerConfigurer.this.context
.getBean(beanNames[0], type);
return InitializeAuthenticationProviderBeanManagerConfigurer.this.context.getBean(beanNames[0], type);
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configuration;
import org.springframework.context.ApplicationContext;
@@ -20,9 +21,9 @@ import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
/**
* Lazily initializes the global authentication with a {@link UserDetailsService} if it is
@@ -33,8 +34,7 @@ import org.springframework.security.core.userdetails.UserDetailsPasswordService;
* @since 4.1
*/
@Order(InitializeUserDetailsBeanManagerConfigurer.DEFAULT_ORDER)
class InitializeUserDetailsBeanManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
class InitializeUserDetailsBeanManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {
static final int DEFAULT_ORDER = Ordered.LOWEST_PRECEDENCE - 5000;
@@ -52,22 +52,19 @@ class InitializeUserDetailsBeanManagerConfigurer
auth.apply(new InitializeUserDetailsManagerConfigurer());
}
class InitializeUserDetailsManagerConfigurer
extends GlobalAuthenticationConfigurerAdapter {
class InitializeUserDetailsManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
if (auth.isConfigured()) {
return;
}
UserDetailsService userDetailsService = getBeanOrNull(
UserDetailsService.class);
UserDetailsService userDetailsService = getBeanOrNull(UserDetailsService.class);
if (userDetailsService == null) {
return;
}
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
if (passwordEncoder != null) {
@@ -77,22 +74,21 @@ class InitializeUserDetailsBeanManagerConfigurer
provider.setUserDetailsPasswordService(passwordManager);
}
provider.afterPropertiesSet();
auth.authenticationProvider(provider);
}
/**
* @return a bean of the requested class if there's just a single registered component, null otherwise.
* @return a bean of the requested class if there's just a single registered
* component, null otherwise.
*/
private <T> T getBeanOrNull(Class<T> type) {
String[] beanNames = InitializeUserDetailsBeanManagerConfigurer.this.context
.getBeanNamesForType(type);
String[] beanNames = InitializeUserDetailsBeanManagerConfigurer.this.context.getBeanNamesForType(type);
if (beanNames.length != 1) {
return null;
}
return InitializeUserDetailsBeanManagerConfigurer.this.context
.getBean(beanNames[0], type);
return InitializeUserDetailsBeanManagerConfigurer.this.context.getBean(beanNames[0], type);
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers.ldap;
import java.io.IOException;
@@ -52,48 +53,58 @@ import org.springframework.util.ClassUtils;
* Configures LDAP {@link AuthenticationProvider} in the {@link ProviderManagerBuilder}.
*
* @param <B> the {@link ProviderManagerBuilder} type that this is configuring.
*
* @author Rob Winch
* @author Eddú Meléndez
* @since 3.2
*/
public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuilder<B>>
extends SecurityConfigurerAdapter<AuthenticationManager, B> {
private String groupRoleAttribute = "cn";
private String groupSearchBase = "";
private boolean groupSearchSubtree = false;
private String groupSearchFilter = "(uniqueMember={0})";
private String rolePrefix = "ROLE_";
private String userSearchBase = ""; // only for search
private String userSearchFilter = null; // "uid={0}"; // only for search
private String[] userDnPatterns;
private BaseLdapPathContextSource contextSource;
private ContextSourceBuilder contextSourceBuilder = new ContextSourceBuilder();
private UserDetailsContextMapper userDetailsContextMapper;
private PasswordEncoder passwordEncoder;
private String passwordAttribute;
private LdapAuthoritiesPopulator ldapAuthoritiesPopulator;
private GrantedAuthoritiesMapper authoritiesMapper;
private LdapAuthenticationProvider build() throws Exception {
BaseLdapPathContextSource contextSource = getContextSource();
LdapAuthenticator ldapAuthenticator = createLdapAuthenticator(contextSource);
LdapAuthoritiesPopulator authoritiesPopulator = getLdapAuthoritiesPopulator();
LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(
ldapAuthenticator, authoritiesPopulator);
LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(ldapAuthenticator,
authoritiesPopulator);
ldapAuthenticationProvider.setAuthoritiesMapper(getAuthoritiesMapper());
if (userDetailsContextMapper != null) {
ldapAuthenticationProvider
.setUserDetailsContextMapper(userDetailsContextMapper);
if (this.userDetailsContextMapper != null) {
ldapAuthenticationProvider.setUserDetailsContextMapper(this.userDetailsContextMapper);
}
return ldapAuthenticationProvider;
}
/**
* Specifies the {@link LdapAuthoritiesPopulator}.
*
* @param ldapAuthoritiesPopulator the {@link LdapAuthoritiesPopulator} the default is
* {@link DefaultLdapAuthoritiesPopulator}
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
@@ -106,12 +117,10 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
/**
* Adds an {@link ObjectPostProcessor} for this class.
*
* @param objectPostProcessor
* @return the {@link ChannelSecurityConfigurer} for further customizations
*/
public LdapAuthenticationProviderConfigurer<B> withObjectPostProcessor(
ObjectPostProcessor<?> objectPostProcessor) {
public LdapAuthenticationProviderConfigurer<B> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return this;
}
@@ -119,51 +128,47 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
/**
* Gets the {@link LdapAuthoritiesPopulator} and defaults to
* {@link DefaultLdapAuthoritiesPopulator}
*
* @return the {@link LdapAuthoritiesPopulator}
*/
private LdapAuthoritiesPopulator getLdapAuthoritiesPopulator() {
if (ldapAuthoritiesPopulator != null) {
return ldapAuthoritiesPopulator;
if (this.ldapAuthoritiesPopulator != null) {
return this.ldapAuthoritiesPopulator;
}
DefaultLdapAuthoritiesPopulator defaultAuthoritiesPopulator = new DefaultLdapAuthoritiesPopulator(
contextSource, groupSearchBase);
defaultAuthoritiesPopulator.setGroupRoleAttribute(groupRoleAttribute);
defaultAuthoritiesPopulator.setGroupSearchFilter(groupSearchFilter);
defaultAuthoritiesPopulator.setSearchSubtree(groupSearchSubtree);
this.contextSource, this.groupSearchBase);
defaultAuthoritiesPopulator.setGroupRoleAttribute(this.groupRoleAttribute);
defaultAuthoritiesPopulator.setGroupSearchFilter(this.groupSearchFilter);
defaultAuthoritiesPopulator.setSearchSubtree(this.groupSearchSubtree);
defaultAuthoritiesPopulator.setRolePrefix(this.rolePrefix);
this.ldapAuthoritiesPopulator = defaultAuthoritiesPopulator;
return defaultAuthoritiesPopulator;
}
/**
* Specifies the {@link GrantedAuthoritiesMapper}.
*
* @param grantedAuthoritiesMapper the {@link GrantedAuthoritiesMapper} the default is {@link SimpleAuthorityMapper}
* @param grantedAuthoritiesMapper the {@link GrantedAuthoritiesMapper} the default is
* {@link SimpleAuthorityMapper}
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
*
* @author Tony Dalbrekt
* @since 4.1.1
*/
public LdapAuthenticationProviderConfigurer<B> authoritiesMapper(GrantedAuthoritiesMapper grantedAuthoritiesMapper) {
public LdapAuthenticationProviderConfigurer<B> authoritiesMapper(
GrantedAuthoritiesMapper grantedAuthoritiesMapper) {
this.authoritiesMapper = grantedAuthoritiesMapper;
return this;
}
/**
* Gets the {@link GrantedAuthoritiesMapper} and defaults to {@link SimpleAuthorityMapper}.
*
* Gets the {@link GrantedAuthoritiesMapper} and defaults to
* {@link SimpleAuthorityMapper}.
* @return the {@link GrantedAuthoritiesMapper}
* @throws Exception if errors in {@link SimpleAuthorityMapper#afterPropertiesSet()}
*/
protected GrantedAuthoritiesMapper getAuthoritiesMapper() throws Exception {
if (authoritiesMapper != null) {
return authoritiesMapper;
if (this.authoritiesMapper != null) {
return this.authoritiesMapper;
}
SimpleAuthorityMapper simpleAuthorityMapper = new SimpleAuthorityMapper();
simpleAuthorityMapper.setPrefix(this.rolePrefix);
simpleAuthorityMapper.afterPropertiesSet();
@@ -173,70 +178,61 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
/**
* Creates the {@link LdapAuthenticator} to use
*
* @param contextSource the {@link BaseLdapPathContextSource} to use
* @return the {@link LdapAuthenticator} to use
*/
private LdapAuthenticator createLdapAuthenticator(
BaseLdapPathContextSource contextSource) {
AbstractLdapAuthenticator ldapAuthenticator = passwordEncoder == null ? createBindAuthenticator(contextSource)
: createPasswordCompareAuthenticator(contextSource);
private LdapAuthenticator createLdapAuthenticator(BaseLdapPathContextSource contextSource) {
AbstractLdapAuthenticator ldapAuthenticator = (this.passwordEncoder != null)
? createPasswordCompareAuthenticator(contextSource) : createBindAuthenticator(contextSource);
LdapUserSearch userSearch = createUserSearch();
if (userSearch != null) {
ldapAuthenticator.setUserSearch(userSearch);
}
if (userDnPatterns != null && userDnPatterns.length > 0) {
ldapAuthenticator.setUserDnPatterns(userDnPatterns);
if (this.userDnPatterns != null && this.userDnPatterns.length > 0) {
ldapAuthenticator.setUserDnPatterns(this.userDnPatterns);
}
return postProcess(ldapAuthenticator);
}
/**
* Creates {@link PasswordComparisonAuthenticator}
*
* @param contextSource the {@link BaseLdapPathContextSource} to use
* @return
*/
private PasswordComparisonAuthenticator createPasswordCompareAuthenticator(
BaseLdapPathContextSource contextSource) {
PasswordComparisonAuthenticator ldapAuthenticator = new PasswordComparisonAuthenticator(
contextSource);
if (passwordAttribute != null) {
ldapAuthenticator.setPasswordAttributeName(passwordAttribute);
PasswordComparisonAuthenticator ldapAuthenticator = new PasswordComparisonAuthenticator(contextSource);
if (this.passwordAttribute != null) {
ldapAuthenticator.setPasswordAttributeName(this.passwordAttribute);
}
ldapAuthenticator.setPasswordEncoder(passwordEncoder);
ldapAuthenticator.setPasswordEncoder(this.passwordEncoder);
return ldapAuthenticator;
}
/**
* Creates a {@link BindAuthenticator}
*
* @param contextSource the {@link BaseLdapPathContextSource} to use
* @return the {@link BindAuthenticator} to use
*/
private BindAuthenticator createBindAuthenticator(
BaseLdapPathContextSource contextSource) {
private BindAuthenticator createBindAuthenticator(BaseLdapPathContextSource contextSource) {
return new BindAuthenticator(contextSource);
}
private LdapUserSearch createUserSearch() {
if (userSearchFilter == null) {
if (this.userSearchFilter == null) {
return null;
}
return new FilterBasedLdapUserSearch(userSearchBase, userSearchFilter,
contextSource);
return new FilterBasedLdapUserSearch(this.userSearchBase, this.userSearchFilter, this.contextSource);
}
/**
* Specifies the {@link BaseLdapPathContextSource} to be used. If not specified, an
* embedded LDAP server will be created using {@link #contextSource()}.
*
* @param contextSource the {@link BaseLdapPathContextSource} to use
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
* @see #contextSource()
*/
public LdapAuthenticationProviderConfigurer<B> contextSource(
BaseLdapPathContextSource contextSource) {
public LdapAuthenticationProviderConfigurer<B> contextSource(BaseLdapPathContextSource contextSource) {
this.contextSource = contextSource;
return this;
}
@@ -244,17 +240,15 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
/**
* Allows easily configuring of a {@link BaseLdapPathContextSource} with defaults
* pointing to an embedded LDAP server that is created.
*
* @return the {@link ContextSourceBuilder} for further customizations
*/
public ContextSourceBuilder contextSource() {
return contextSourceBuilder;
return this.contextSourceBuilder;
}
/**
* Specifies the {@link org.springframework.security.crypto.password.PasswordEncoder}
* to be used when authenticating with password comparison.
*
* @param passwordEncoder the
* {@link org.springframework.security.crypto.password.PasswordEncoder} to use
* @return the {@link LdapAuthenticationProviderConfigurer} for further customization
@@ -273,12 +267,10 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
* property of AbstractLdapAuthenticator. The value is a specific pattern used to
* build the user's DN, for example "uid={0},ou=people". The key "{0}" must be present
* and will be substituted with the username.
*
* @param userDnPatterns the LDAP patterns for finding the usernames
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
*/
public LdapAuthenticationProviderConfigurer<B> userDnPatterns(
String... userDnPatterns) {
public LdapAuthenticationProviderConfigurer<B> userDnPatterns(String... userDnPatterns) {
this.userDnPatterns = userDnPatterns;
return this;
}
@@ -287,7 +279,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
* Allows explicit customization of the loaded user object by specifying a
* UserDetailsContextMapper bean which will be called with the context information
* from the user's directory entry.
*
* @param userDetailsContextMapper the {@link UserDetailsContextMapper} to use
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
*
@@ -306,8 +297,7 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
* @param groupRoleAttribute the attribute name that maps a group to a role.
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
*/
public LdapAuthenticationProviderConfigurer<B> groupRoleAttribute(
String groupRoleAttribute) {
public LdapAuthenticationProviderConfigurer<B> groupRoleAttribute(String groupRoleAttribute) {
this.groupRoleAttribute = groupRoleAttribute;
return this;
}
@@ -323,11 +313,10 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
}
/**
* If set to true, a subtree scope search will be performed for group membership. If false a
* single-level search is used.
*
* If set to true, a subtree scope search will be performed for group membership. If
* false a single-level search is used.
* @param searchSubtree set to true to enable searching of the entire tree below the
* <tt>groupSearchBase</tt>.
* <tt>groupSearchBase</tt>.
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
*/
public LdapAuthenticationProviderConfigurer<B> groupSearchSubtree(boolean groupSearchSubtree) {
@@ -338,12 +327,10 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
/**
* The LDAP filter to search for groups. Defaults to "(uniqueMember={0})". The
* substituted parameter is the DN of the user.
*
* @param groupSearchFilter the LDAP filter to search for groups
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
*/
public LdapAuthenticationProviderConfigurer<B> groupSearchFilter(
String groupSearchFilter) {
public LdapAuthenticationProviderConfigurer<B> groupSearchFilter(String groupSearchFilter) {
this.groupSearchFilter = groupSearchFilter;
return this;
}
@@ -351,7 +338,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
/**
* A non-empty string prefix that will be added as a prefix to the existing roles. The
* default is "ROLE_".
*
* @param rolePrefix the prefix to be added to the roles that are loaded.
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
* @see SimpleAuthorityMapper#setPrefix(String)
@@ -364,7 +350,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
/**
* Search base for user searches. Defaults to "". Only used with
* {@link #userSearchFilter(String)}.
*
* @param userSearchBase search base for user searches
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
*/
@@ -376,12 +361,10 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
/**
* The LDAP filter used to search for users (optional). For example "(uid={0})". The
* substituted parameter is the user's login name.
*
* @param userSearchFilter the LDAP filter used to search for users
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
*/
public LdapAuthenticationProviderConfigurer<B> userSearchFilter(
String userSearchFilter) {
public LdapAuthenticationProviderConfigurer<B> userSearchFilter(String userSearchFilter) {
this.userSearchFilter = userSearchFilter;
return this;
}
@@ -392,6 +375,21 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
builder.authenticationProvider(provider);
}
private BaseLdapPathContextSource getContextSource() throws Exception {
if (this.contextSource == null) {
this.contextSource = this.contextSourceBuilder.build();
}
return this.contextSource;
}
/**
* @return the {@link PasswordCompareConfigurer} for further customizations
*/
public PasswordCompareConfigurer passwordCompare() {
return new PasswordCompareConfigurer().passwordAttribute("password")
.passwordEncoder(NoOpPasswordEncoder.getInstance());
}
/**
* Sets up Password based comparison
*
@@ -413,7 +411,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
/**
* The attribute in the directory which contains the user password. Defaults to
* "userPassword".
*
* @param passwordAttribute the attribute in the directory which contains the user
* password
* @return the {@link PasswordCompareConfigurer} for further customizations
@@ -426,7 +423,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
/**
* Allows obtaining a reference to the
* {@link LdapAuthenticationProviderConfigurer} for further customizations
*
* @return attribute in the directory which contains the user password
*/
public LdapAuthenticationProviderConfigurer<B> and() {
@@ -435,6 +431,7 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
private PasswordCompareConfigurer() {
}
}
/**
@@ -446,23 +443,30 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
* @since 3.2
*/
public final class ContextSourceBuilder {
private static final String APACHEDS_CLASSNAME = "org.apache.directory.server.core.DefaultDirectoryService";
private static final String UNBOUNDID_CLASSNAME = "com.unboundid.ldap.listener.InMemoryDirectoryServer";
private static final int DEFAULT_PORT = 33389;
private static final int RANDOM_PORT = 0;
private String ldif = "classpath*:*.ldif";
private String managerPassword;
private String managerDn;
private Integer port;
private String root = "dc=springframework,dc=org";
private String url;
/**
* Specifies an ldif to load at startup for an embedded LDAP server. This only
* loads if using an embedded instance. The default is "classpath*:*.ldif".
*
* @param ldif the ldif to load at startup for an embedded LDAP server.
* @return the {@link ContextSourceBuilder} for further customization
*/
@@ -475,7 +479,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
* Username (DN) of the "manager" user identity (i.e. "uid=admin,ou=system") which
* will be used to authenticate to a (non-embedded) LDAP server. If omitted,
* anonymous access will be used.
*
* @param managerDn the username (DN) of the "manager" user identity used to
* authenticate to a LDAP server.
* @return the {@link ContextSourceBuilder} for further customization
@@ -500,8 +503,8 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
* The port to connect to LDAP to (the default is 33389 or random available port
* if unavailable).
*
* Supplying 0 as the port indicates that a random available port should be selected.
*
* Supplying 0 as the port indicates that a random available port should be
* selected.
* @param port the port to connect to
* @return the {@link ContextSourceBuilder} for further customization
*/
@@ -513,7 +516,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
/**
* Optional root suffix for the embedded LDAP server. Default is
* "dc=springframework,dc=org"
*
* @param root root suffix for the embedded LDAP server
* @return the {@link ContextSourceBuilder} for further customization
*/
@@ -525,7 +527,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
/**
* Specifies the ldap server URL when not using the embedded LDAP server. For
* example, "ldaps://ldap.example.com:33389/dc=myco,dc=org".
*
* @param url the ldap server URL
* @return the {@link ContextSourceBuilder} for further customization
*/
@@ -537,7 +538,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
/**
* Gets the {@link LdapAuthenticationProviderConfigurer} for further
* customizations
*
* @return the {@link LdapAuthenticationProviderConfigurer} for further
* customizations
*/
@@ -549,16 +549,13 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
if (this.url == null) {
startEmbeddedLdapServer();
}
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
getProviderUrl());
if (managerDn != null) {
contextSource.setUserDn(managerDn);
if (managerPassword == null) {
throw new IllegalStateException(
"managerPassword is required if managerDn is supplied");
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(getProviderUrl());
if (this.managerDn != null) {
contextSource.setUserDn(this.managerDn);
if (this.managerPassword == null) {
throw new IllegalStateException("managerPassword is required if managerDn is supplied");
}
contextSource.setPassword(managerPassword);
contextSource.setPassword(this.managerPassword);
}
contextSource = postProcess(contextSource);
return contextSource;
@@ -583,43 +580,31 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
}
private int getPort() {
if (port == null) {
port = getDefaultPort();
if (this.port == null) {
this.port = getDefaultPort();
}
return port;
return this.port;
}
private int getDefaultPort() {
try (ServerSocket serverSocket = new ServerSocket(DEFAULT_PORT)) {
return serverSocket.getLocalPort();
} catch (IOException e) {
}
catch (IOException ex) {
return RANDOM_PORT;
}
}
private String getProviderUrl() {
if (url == null) {
return "ldap://127.0.0.1:" + getPort() + "/" + root;
if (this.url == null) {
return "ldap://127.0.0.1:" + getPort() + "/" + this.root;
}
return url;
return this.url;
}
private ContextSourceBuilder() {
}
}
private BaseLdapPathContextSource getContextSource() throws Exception {
if (contextSource == null) {
contextSource = contextSourceBuilder.build();
}
return contextSource;
}
/**
* @return the {@link PasswordCompareConfigurer} for further customizations
*/
public PasswordCompareConfigurer passwordCompare() {
return new PasswordCompareConfigurer().passwordAttribute("password")
.passwordEncoder(NoOpPasswordEncoder.getInstance());
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers.provisioning;
import java.util.ArrayList;
@@ -27,7 +28,6 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager;
* authentication.
*
* @param <B> the type of the {@link ProviderManagerBuilder} that is being configured
*
* @author Rob Winch
* @since 3.2
*/
@@ -40,4 +40,5 @@ public class InMemoryUserDetailsManagerConfigurer<B extends ProviderManagerBuild
public InMemoryUserDetailsManagerConfigurer() {
super(new InMemoryUserDetailsManager(new ArrayList<>()));
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers.provisioning;
import java.util.ArrayList;
@@ -40,7 +41,6 @@ import org.springframework.security.provisioning.JdbcUserDetailsManager;
* methods have reasonable defaults.
*
* @param <B> the type of the {@link ProviderManagerBuilder} that is being configured
*
* @author Rob Winch
* @since 3.2
*/
@@ -61,9 +61,9 @@ public class JdbcUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B
/**
* Populates the {@link DataSource} to be used. This is the only required attribute.
*
* @param dataSource the {@link DataSource} to be used. Cannot be null.
* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional customizations
* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional
* customizations
*/
public JdbcUserDetailsManagerConfigurer<B> dataSource(DataSource dataSource) {
this.dataSource = dataSource;
@@ -94,7 +94,6 @@ public class JdbcUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B
* <code>
* select username,authority from authorities where username = ?
* </code>
*
* @param query The query to use for selecting the username, authority by username.
* Must contain a single parameter for the username.
* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional
@@ -116,7 +115,6 @@ public class JdbcUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B
* where
* gm.username = ? and g.id = ga.group_id and g.id = gm.group_id
* </code>
*
* @param query The query to use for selecting the authorities by group. Must contain
* a single parameter for the username.
* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional
@@ -132,9 +130,9 @@ public class JdbcUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B
/**
* A non-empty string prefix that will be added to role strings loaded from persistent
* storage (default is "").
*
* @param rolePrefix
* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional customizations
* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional
* customizations
*/
public JdbcUserDetailsManagerConfigurer<B> rolePrefix(String rolePrefix) {
getUserDetailsService().setRolePrefix(rolePrefix);
@@ -143,7 +141,6 @@ public class JdbcUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B
/**
* Defines the {@link UserCache} to use
*
* @param userCache the {@link UserCache} to use
* @return the {@link JdbcUserDetailsManagerConfigurer} for further customizations
*/
@@ -154,7 +151,7 @@ public class JdbcUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B
@Override
protected void initUserDetailsService() throws Exception {
if (!initScripts.isEmpty()) {
if (!this.initScripts.isEmpty()) {
getDataSourceInit().afterPropertiesSet();
}
super.initUserDetailsService();
@@ -167,26 +164,25 @@ public class JdbcUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B
/**
* Populates the default schema that allows users and authorities to be stored.
*
* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional
* customizations
*/
public JdbcUserDetailsManagerConfigurer<B> withDefaultSchema() {
this.initScripts.add(new ClassPathResource(
"org/springframework/security/core/userdetails/jdbc/users.ddl"));
this.initScripts.add(new ClassPathResource("org/springframework/security/core/userdetails/jdbc/users.ddl"));
return this;
}
protected DatabasePopulator getDatabasePopulator() {
ResourceDatabasePopulator dbp = new ResourceDatabasePopulator();
dbp.setScripts(initScripts.toArray(new Resource[0]));
dbp.setScripts(this.initScripts.toArray(new Resource[0]));
return dbp;
}
private DataSourceInitializer getDataSourceInit() {
DataSourceInitializer dsi = new DataSourceInitializer();
dsi.setDatabasePopulator(getDatabasePopulator());
dsi.setDataSource(dataSource);
dsi.setDataSource(this.dataSource);
return dsi;
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers.provisioning;
import java.util.ArrayList;
@@ -34,7 +35,6 @@ import org.springframework.security.provisioning.UserDetailsManager;
*
* @param <B> the type of the {@link SecurityBuilder} that is being configured
* @param <C> the type of {@link UserDetailsManagerConfigurer}
*
* @author Rob Winch
* @since 3.2
*/
@@ -51,12 +51,11 @@ public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C
/**
* Populates the users that have been added.
*
* @throws Exception
*/
@Override
protected void initUserDetailsService() throws Exception {
for (UserDetailsBuilder userBuilder : userBuilders) {
for (UserDetailsBuilder userBuilder : this.userBuilders) {
getUserDetailsService().createUser(userBuilder.build());
}
for (UserDetails userDetails : this.users) {
@@ -67,7 +66,6 @@ public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C
/**
* Allows adding a user to the {@link UserDetailsManager} that is being created. This
* method can be invoked multiple times to add multiple users.
*
* @param userDetails the user to add. Cannot be null.
* @return the {@link UserDetailsBuilder} for further customizations
*/
@@ -80,7 +78,6 @@ public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C
/**
* Allows adding a user to the {@link UserDetailsManager} that is being created. This
* method can be invoked multiple times to add multiple users.
*
* @param userBuilder the user to add. Cannot be null.
* @return the {@link UserDetailsBuilder} for further customizations
*/
@@ -93,7 +90,6 @@ public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C
/**
* Allows adding a user to the {@link UserDetailsManager} that is being created. This
* method can be invoked multiple times to add multiple users.
*
* @param username the username for the user being added. Cannot be null.
* @return the {@link UserDetailsBuilder} for further customizations
*/
@@ -109,8 +105,10 @@ public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C
* Builds the user to be added. At minimum the username, password, and authorities
* should provided. The remaining attributes have reasonable defaults.
*/
public class UserDetailsBuilder {
public final class UserDetailsBuilder {
private UserBuilder user;
private final C builder;
/**
@@ -122,18 +120,16 @@ public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C
}
/**
* Returns the {@link UserDetailsManagerConfigurer} for method chaining (i.e. to add
* another user)
*
* Returns the {@link UserDetailsManagerConfigurer} for method chaining (i.e. to
* add another user)
* @return the {@link UserDetailsManagerConfigurer} for method chaining
*/
public C and() {
return builder;
return this.builder;
}
/**
* Populates the username. This attribute is required.
*
* @param username the username. Cannot be null.
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
@@ -145,7 +141,6 @@ public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C
/**
* Populates the password. This attribute is required.
*
* @param password the password. Cannot be null.
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
@@ -174,7 +169,6 @@ public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C
* This attribute is required, but can also be populated with
* {@link #authorities(String...)}.
* </p>
*
* @param roles the roles for this user (i.e. USER, ADMIN, etc). Cannot be null,
* contain null values or start with "ROLE_"
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
@@ -187,7 +181,6 @@ public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C
/**
* Populates the authorities. This attribute is required.
*
* @param authorities the authorities for this user. Cannot be null, or contain
* null values
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
@@ -201,7 +194,6 @@ public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C
/**
* Populates the authorities. This attribute is required.
*
* @param authorities the authorities for this user. Cannot be null, or contain
* null values
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
@@ -215,7 +207,6 @@ public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C
/**
* Populates the authorities. This attribute is required.
*
* @param authorities the authorities for this user (i.e. ROLE_USER, ROLE_ADMIN,
* etc). Cannot be null, or contain null values
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
@@ -229,7 +220,6 @@ public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C
/**
* Defines if the account is expired or not. Default is false.
*
* @param accountExpired true if the account is expired, false otherwise
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
@@ -241,7 +231,6 @@ public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C
/**
* Defines if the account is locked or not. Default is false.
*
* @param accountLocked true if the account is locked, false otherwise
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
@@ -253,7 +242,6 @@ public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C
/**
* Defines if the credentials are expired or not. Default is false.
*
* @param credentialsExpired true if the credentials are expired, false otherwise
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
@@ -265,7 +253,6 @@ public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C
/**
* Defines if the account is disabled or not. Default is false.
*
* @param disabled true if the account is disabled, false otherwise
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
@@ -278,5 +265,7 @@ public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C
UserDetails build() {
return this.user.build();
}
}
}

View File

@@ -13,40 +13,40 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers.userdetails;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
/**
* Allows configuring a {@link DaoAuthenticationProvider}
*
* @author Rob Winch
* @since 3.2
*
* @param <B> the type of the {@link SecurityBuilder}
* @param <C> the type of {@link AbstractDaoAuthenticationConfigurer} this is
* @param <U> The type of {@link UserDetailsService} that is being used
*
* @author Rob Winch
* @since 3.2
*/
abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, C extends AbstractDaoAuthenticationConfigurer<B, C, U>, U extends UserDetailsService>
public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, C extends AbstractDaoAuthenticationConfigurer<B, C, U>, U extends UserDetailsService>
extends UserDetailsAwareConfigurer<B, U> {
private DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
private final U userDetailsService;
/**
* Creates a new instance
*
* @param userDetailsService
*/
protected AbstractDaoAuthenticationConfigurer(U userDetailsService) {
AbstractDaoAuthenticationConfigurer(U userDetailsService) {
this.userDetailsService = userDetailsService;
provider.setUserDetailsService(userDetailsService);
this.provider.setUserDetailsService(userDetailsService);
if (userDetailsService instanceof UserDetailsPasswordService) {
this.provider.setUserDetailsPasswordService((UserDetailsPasswordService) userDetailsService);
}
@@ -54,7 +54,6 @@ abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuil
/**
* Adds an {@link ObjectPostProcessor} for this class.
*
* @param objectPostProcessor
* @return the {@link AbstractDaoAuthenticationConfigurer} for further customizations
*/
@@ -67,35 +66,35 @@ abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuil
/**
* Allows specifying the {@link PasswordEncoder} to use with the
* {@link DaoAuthenticationProvider}. The default is to use plain text.
*
* @param passwordEncoder The {@link PasswordEncoder} to use.
* @return the {@link AbstractDaoAuthenticationConfigurer} for further customizations
*/
@SuppressWarnings("unchecked")
public C passwordEncoder(PasswordEncoder passwordEncoder) {
provider.setPasswordEncoder(passwordEncoder);
this.provider.setPasswordEncoder(passwordEncoder);
return (C) this;
}
public C userDetailsPasswordManager(UserDetailsPasswordService passwordManager) {
provider.setUserDetailsPasswordService(passwordManager);
this.provider.setUserDetailsPasswordService(passwordManager);
return (C) this;
}
@Override
public void configure(B builder) throws Exception {
provider = postProcess(provider);
builder.authenticationProvider(provider);
this.provider = postProcess(this.provider);
builder.authenticationProvider(this.provider);
}
/**
* Gets the {@link UserDetailsService} that is used with the
* {@link DaoAuthenticationProvider}
*
* @return the {@link UserDetailsService} that is used with the
* {@link DaoAuthenticationProvider}
*/
@Override
public U getUserDetailsService() {
return userDetailsService;
return this.userDetailsService;
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers.userdetails;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
@@ -22,16 +23,13 @@ import org.springframework.security.core.userdetails.UserDetailsService;
/**
* Allows configuring a {@link DaoAuthenticationProvider}
*
* @author Rob Winch
* @since 3.2
*
* @param <B> The type of {@link ProviderManagerBuilder} this is
* @param <U> The type of {@link UserDetailsService} that is being used
*
* @author Rob Winch
* @since 3.2
*/
public class DaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, U extends UserDetailsService>
extends
AbstractDaoAuthenticationConfigurer<B, DaoAuthenticationConfigurer<B, U>, U> {
extends AbstractDaoAuthenticationConfigurer<B, DaoAuthenticationConfigurer<B, U>, U> {
/**
* Creates a new instance
@@ -40,4 +38,5 @@ public class DaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, U
public DaoAuthenticationConfigurer(U userDetailsService) {
super(userDetailsService);
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers.userdetails;
import org.springframework.security.authentication.AuthenticationManager;
@@ -25,10 +26,9 @@ import org.springframework.security.core.userdetails.UserDetailsService;
* Base class that allows access to the {@link UserDetailsService} for using as a default
* value with {@link AuthenticationManagerBuilder}.
*
* @author Rob Winch
*
* @param <B> the type of the {@link ProviderManagerBuilder}
* @param <U> the type of {@link UserDetailsService}
* @author Rob Winch
*/
public abstract class UserDetailsAwareConfigurer<B extends ProviderManagerBuilder<B>, U extends UserDetailsService>
extends SecurityConfigurerAdapter<AuthenticationManager, B> {
@@ -38,4 +38,5 @@ public abstract class UserDetailsAwareConfigurer<B extends ProviderManagerBuilde
* @return the {@link UserDetailsService} or null if it is not available
*/
public abstract U getUserDetailsService();
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers.userdetails;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
@@ -23,13 +24,12 @@ import org.springframework.security.core.userdetails.UserDetailsService;
* Allows configuring a {@link UserDetailsService} within a
* {@link AuthenticationManagerBuilder}.
*
* @author Rob Winch
* @since 3.2
*
* @param <B> the type of the {@link ProviderManagerBuilder}
* @param <C> the {@link UserDetailsServiceConfigurer} (or this)
* @param <U> the type of UserDetailsService being used to allow for returning the
* concrete UserDetailsService.
* @author Rob Winch
* @since 3.2
*/
public class UserDetailsServiceConfigurer<B extends ProviderManagerBuilder<B>, C extends UserDetailsServiceConfigurer<B, C, U>, U extends UserDetailsService>
extends AbstractDaoAuthenticationConfigurer<B, C, U> {
@@ -45,7 +45,6 @@ public class UserDetailsServiceConfigurer<B extends ProviderManagerBuilder<B>, C
@Override
public void configure(B builder) throws Exception {
initUserDetailsService();
super.configure(builder);
}
@@ -55,4 +54,5 @@ public class UserDetailsServiceConfigurer<B extends ProviderManagerBuilder<B>, C
*/
protected void initUserDetailsService() throws Exception {
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.configuration;
import java.util.ArrayList;
@@ -39,24 +40,21 @@ import org.springframework.util.Assert;
*/
final class AutowireBeanFactoryObjectPostProcessor
implements ObjectPostProcessor<Object>, DisposableBean, SmartInitializingSingleton {
private final Log logger = LogFactory.getLog(getClass());
private final AutowireCapableBeanFactory autowireBeanFactory;
private final List<DisposableBean> disposableBeans = new ArrayList<>();
private final List<SmartInitializingSingleton> smartSingletons = new ArrayList<>();
AutowireBeanFactoryObjectPostProcessor(
AutowireCapableBeanFactory autowireBeanFactory) {
AutowireBeanFactoryObjectPostProcessor(AutowireCapableBeanFactory autowireBeanFactory) {
Assert.notNull(autowireBeanFactory, "autowireBeanFactory cannot be null");
this.autowireBeanFactory = autowireBeanFactory;
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.security.config.annotation.web.Initializer#initialize(java.
* lang.Object)
*/
@Override
@SuppressWarnings("unchecked")
public <T> T postProcess(T object) {
if (object == null) {
@@ -64,13 +62,11 @@ final class AutowireBeanFactoryObjectPostProcessor
}
T result = null;
try {
result = (T) this.autowireBeanFactory.initializeBean(object,
object.toString());
result = (T) this.autowireBeanFactory.initializeBean(object, object.toString());
}
catch (RuntimeException e) {
catch (RuntimeException ex) {
Class<?> type = object.getClass();
throw new RuntimeException(
"Could not postProcess " + object + " of type " + type, e);
throw new RuntimeException("Could not postProcess " + object + " of type " + type, ex);
}
this.autowireBeanFactory.autowireBean(object);
if (result instanceof DisposableBean) {
@@ -82,28 +78,21 @@ final class AutowireBeanFactoryObjectPostProcessor
return result;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.SmartInitializingSingleton#afterSingletonsInstantiated()
*/
@Override
public void afterSingletonsInstantiated() {
for (SmartInitializingSingleton singleton : smartSingletons) {
for (SmartInitializingSingleton singleton : this.smartSingletons) {
singleton.afterSingletonsInstantiated();
}
}
/*
* (non-Javadoc)
*
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
@Override
public void destroy() {
for (DisposableBean disposable : this.disposableBeans) {
try {
disposable.destroy();
}
catch (Exception error) {
this.logger.error(error);
catch (Exception ex) {
this.logger.error(ex);
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.configuration;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
@@ -31,7 +32,6 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
*
* @see EnableWebSecurity
* @see EnableGlobalMethodSecurity
*
* @author Rob Winch
* @since 3.2
*/
@@ -41,8 +41,8 @@ public class ObjectPostProcessorConfiguration {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public ObjectPostProcessor<Object> objectPostProcessor(
AutowireCapableBeanFactory beanFactory) {
public ObjectPostProcessor<Object> objectPostProcessor(AutowireCapableBeanFactory beanFactory) {
return new AutowireBeanFactoryObjectPostProcessor(beanFactory);
}
}

View File

@@ -13,10 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.AdviceMode;
@@ -28,8 +31,8 @@ import org.springframework.security.config.annotation.authentication.configurati
/**
* <p>
* Enables Spring Security global method security similar to the &lt;global-method-security&gt;
* xml support.
* Enables Spring Security global method security similar to the
* &lt;global-method-security&gt; xml support.
*
* <p>
* More advanced configurations may wish to extend
@@ -41,8 +44,8 @@ import org.springframework.security.config.annotation.authentication.configurati
* @author Rob Winch
* @since 3.2
*/
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({ GlobalMethodSecuritySelector.class })
@EnableGlobalAuthentication
@@ -82,7 +85,6 @@ public @interface EnableGlobalMethodSecurity {
* annotation will be upgraded to subclass proxying at the same time. This approach
* has no negative impact in practice unless one is explicitly expecting one type of
* proxy vs another, e.g. in tests.
*
* @return true if CGILIB proxies should be created instead of interface based
* proxies, else false
*/
@@ -92,7 +94,6 @@ public @interface EnableGlobalMethodSecurity {
* Indicate how security advice should be applied. The default is
* {@link AdviceMode#PROXY}.
* @see AdviceMode
*
* @return the {@link AdviceMode} to use
*/
AdviceMode mode() default AdviceMode.PROXY;
@@ -101,8 +102,8 @@ public @interface EnableGlobalMethodSecurity {
* Indicate the ordering of the execution of the security advisor when multiple
* advices are applied at a specific joinpoint. The default is
* {@link Ordered#LOWEST_PRECEDENCE}.
*
* @return the order the security advisor should be applied
*/
int order() default Ordered.LOWEST_PRECEDENCE;
}

View File

@@ -16,36 +16,40 @@
package org.springframework.security.config.annotation.method.configuration;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
*
* @author Rob Winch
* @since 5.0
*/
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({ ReactiveMethodSecuritySelector.class })
@Import(ReactiveMethodSecuritySelector.class)
@Configuration
public @interface EnableReactiveMethodSecurity {
/**
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
* to standard Java interface-based proxies. The default is {@code false}. <strong>
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed to
* standard Java interface-based proxies. The default is {@code false}. <strong>
* Applicable only if {@link #mode()} is set to {@link AdviceMode#PROXY}</strong>.
* <p>Note that setting this attribute to {@code true} will affect <em>all</em>
* Spring-managed beans requiring proxying, not just those marked with {@code @Cacheable}.
* For example, other beans marked with Spring's {@code @Transactional} annotation will
* be upgraded to subclass proxying at the same time. This approach has no negative
* impact in practice unless one is explicitly expecting one type of proxy vs another,
* e.g. in tests.
* <p>
* Note that setting this attribute to {@code true} will affect <em>all</em>
* Spring-managed beans requiring proxying, not just those marked with
* {@code @Cacheable}. For example, other beans marked with Spring's
* {@code @Transactional} annotation will be upgraded to subclass proxying at the same
* time. This approach has no negative impact in practice unless one is explicitly
* expecting one type of proxy vs another, e.g. in tests.
*/
boolean proxyTargetClass() default false;
@@ -53,7 +57,6 @@ public @interface EnableReactiveMethodSecurity {
* Indicate how security advice should be applied. The default is
* {@link AdviceMode#PROXY}.
* @see AdviceMode
*
* @return the {@link AdviceMode} to use
*/
AdviceMode mode() default AdviceMode.PROXY;
@@ -62,8 +65,8 @@ public @interface EnableReactiveMethodSecurity {
* Indicate the ordering of the execution of the security advisor when multiple
* advices are applied at a specific joinpoint. The default is
* {@link Ordered#LOWEST_PRECEDENCE}.
*
* @return the order the security advisor should be applied
*/
int order() default Ordered.LOWEST_PRECEDENCE;
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import org.springframework.beans.factory.config.BeanDefinition;
@@ -36,28 +37,22 @@ import org.springframework.core.type.AnnotationMetadata;
* @author Rob Winch
* @since 3.2
*/
class GlobalMethodSecurityAspectJAutoProxyRegistrar implements
ImportBeanDefinitionRegistrar {
class GlobalMethodSecurityAspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
/**
* Register, escalate, and configure the AspectJ auto proxy creator based on the value
* of the @{@link EnableGlobalMethodSecurity#proxyTargetClass()} attribute on the
* importing {@code @Configuration} class.
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
BeanDefinition interceptor = registry
.getBeanDefinition("methodSecurityInterceptor");
BeanDefinitionBuilder aspect = BeanDefinitionBuilder
.rootBeanDefinition("org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect");
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinition interceptor = registry.getBeanDefinition("methodSecurityInterceptor");
BeanDefinitionBuilder aspect = BeanDefinitionBuilder.rootBeanDefinition(
"org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect");
aspect.setFactoryMethod("aspectOf");
aspect.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
aspect.addPropertyValue("securityInterceptor", interceptor);
registry.registerBeanDefinition("annotationSecurityAspect$0",
aspect.getBeanDefinition());
registry.registerBeanDefinition("annotationSecurityAspect$0", aspect.getBeanDefinition());
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import java.util.ArrayList;
@@ -30,7 +31,11 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.*;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.context.annotation.Role;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.AnnotationMetadata;
@@ -82,24 +87,34 @@ import org.springframework.util.Assert;
*/
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class GlobalMethodSecurityConfiguration
implements ImportAware, SmartInitializingSingleton, BeanFactoryAware {
private static final Log logger = LogFactory
.getLog(GlobalMethodSecurityConfiguration.class);
public class GlobalMethodSecurityConfiguration implements ImportAware, SmartInitializingSingleton, BeanFactoryAware {
private static final Log logger = LogFactory.getLog(GlobalMethodSecurityConfiguration.class);
private ObjectPostProcessor<Object> objectPostProcessor = new ObjectPostProcessor<Object>() {
@Override
public <T> T postProcess(T object) {
throw new IllegalStateException(ObjectPostProcessor.class.getName()
+ " is a required bean. Ensure you have used @"
+ EnableGlobalMethodSecurity.class.getName());
+ " is a required bean. Ensure you have used @" + EnableGlobalMethodSecurity.class.getName());
}
};
private DefaultMethodSecurityExpressionHandler defaultMethodExpressionHandler = new DefaultMethodSecurityExpressionHandler();
private AuthenticationManager authenticationManager;
private AuthenticationManagerBuilder auth;
private boolean disableAuthenticationRegistry;
private AnnotationAttributes enableMethodSecurity;
private BeanFactory context;
private MethodSecurityExpressionHandler expressionHandler;
private MethodSecurityInterceptor methodSecurityInterceptor;
/**
@@ -117,72 +132,56 @@ public class GlobalMethodSecurityConfiguration
* Subclasses can override this method to provide a different
* {@link MethodInterceptor}.
* </p>
* @param methodSecurityMetadataSource the default {@link MethodSecurityMetadataSource}.
*
* @param methodSecurityMetadataSource the default
* {@link MethodSecurityMetadataSource}.
* @return the {@link MethodInterceptor}.
*/
@Bean
public MethodInterceptor methodSecurityInterceptor(MethodSecurityMetadataSource methodSecurityMetadataSource) {
this.methodSecurityInterceptor = isAspectJ()
? new AspectJMethodSecurityInterceptor()
this.methodSecurityInterceptor = isAspectJ() ? new AspectJMethodSecurityInterceptor()
: new MethodSecurityInterceptor();
methodSecurityInterceptor.setAccessDecisionManager(accessDecisionManager());
methodSecurityInterceptor.setAfterInvocationManager(afterInvocationManager());
methodSecurityInterceptor
.setSecurityMetadataSource(methodSecurityMetadataSource);
this.methodSecurityInterceptor.setAccessDecisionManager(accessDecisionManager());
this.methodSecurityInterceptor.setAfterInvocationManager(afterInvocationManager());
this.methodSecurityInterceptor.setSecurityMetadataSource(methodSecurityMetadataSource);
RunAsManager runAsManager = runAsManager();
if (runAsManager != null) {
methodSecurityInterceptor.setRunAsManager(runAsManager);
this.methodSecurityInterceptor.setRunAsManager(runAsManager);
}
return this.methodSecurityInterceptor;
}
/*
* (non-Javadoc)
*
* @see org.springframework.beans.factory.SmartInitializingSingleton#
* afterSingletonsInstantiated()
*/
@Override
public void afterSingletonsInstantiated() {
try {
initializeMethodSecurityInterceptor();
}
catch (Exception e) {
throw new RuntimeException(e);
catch (Exception ex) {
throw new RuntimeException(ex);
}
PermissionEvaluator permissionEvaluator = getSingleBeanOrNull(
PermissionEvaluator.class);
PermissionEvaluator permissionEvaluator = getSingleBeanOrNull(PermissionEvaluator.class);
if (permissionEvaluator != null) {
this.defaultMethodExpressionHandler
.setPermissionEvaluator(permissionEvaluator);
this.defaultMethodExpressionHandler.setPermissionEvaluator(permissionEvaluator);
}
RoleHierarchy roleHierarchy = getSingleBeanOrNull(RoleHierarchy.class);
if (roleHierarchy != null) {
this.defaultMethodExpressionHandler.setRoleHierarchy(roleHierarchy);
}
AuthenticationTrustResolver trustResolver = getSingleBeanOrNull(
AuthenticationTrustResolver.class);
AuthenticationTrustResolver trustResolver = getSingleBeanOrNull(AuthenticationTrustResolver.class);
if (trustResolver != null) {
this.defaultMethodExpressionHandler.setTrustResolver(trustResolver);
}
GrantedAuthorityDefaults grantedAuthorityDefaults = getSingleBeanOrNull(
GrantedAuthorityDefaults.class);
GrantedAuthorityDefaults grantedAuthorityDefaults = getSingleBeanOrNull(GrantedAuthorityDefaults.class);
if (grantedAuthorityDefaults != null) {
this.defaultMethodExpressionHandler.setDefaultRolePrefix(
grantedAuthorityDefaults.getRolePrefix());
this.defaultMethodExpressionHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
}
}
private <T> T getSingleBeanOrNull(Class<T> type) {
try {
return context.getBean(type);
} catch (NoSuchBeanDefinitionException e) {}
return this.context.getBean(type);
}
catch (NoSuchBeanDefinitionException ex) {
}
return null;
}
@@ -195,14 +194,14 @@ public class GlobalMethodSecurityConfiguration
/**
* Provide a custom {@link AfterInvocationManager} for the default implementation of
* {@link #methodSecurityInterceptor(MethodSecurityMetadataSource)}. The default is null
* if pre post is not enabled. Otherwise, it returns a {@link AfterInvocationProviderManager}.
* {@link #methodSecurityInterceptor(MethodSecurityMetadataSource)}. The default is
* null if pre post is not enabled. Otherwise, it returns a
* {@link AfterInvocationProviderManager}.
*
* <p>
* Subclasses should override this method to provide a custom
* {@link AfterInvocationManager}
* </p>
*
* @return the {@link AfterInvocationManager} to use
*/
protected AfterInvocationManager afterInvocationManager() {
@@ -210,8 +209,7 @@ public class GlobalMethodSecurityConfiguration
AfterInvocationProviderManager invocationProviderManager = new AfterInvocationProviderManager();
ExpressionBasedPostInvocationAdvice postAdvice = new ExpressionBasedPostInvocationAdvice(
getExpressionHandler());
PostInvocationAdviceProvider postInvocationAdviceProvider = new PostInvocationAdviceProvider(
postAdvice);
PostInvocationAdviceProvider postInvocationAdviceProvider = new PostInvocationAdviceProvider(postAdvice);
List<AfterInvocationProvider> afterInvocationProviders = new ArrayList<>();
afterInvocationProviders.add(postInvocationAdviceProvider);
invocationProviderManager.setProviders(afterInvocationProviders);
@@ -222,8 +220,8 @@ public class GlobalMethodSecurityConfiguration
/**
* Provide a custom {@link RunAsManager} for the default implementation of
* {@link #methodSecurityInterceptor(MethodSecurityMetadataSource)}. The default is null.
*
* {@link #methodSecurityInterceptor(MethodSecurityMetadataSource)}. The default is
* null.
* @return the {@link RunAsManager} to use
*/
protected RunAsManager runAsManager() {
@@ -239,24 +237,20 @@ public class GlobalMethodSecurityConfiguration
* <li>{@link RoleVoter}</li>
* <li>{@link AuthenticatedVoter}</li>
* </ul>
*
* @return the {@link AccessDecisionManager} to use
*/
protected AccessDecisionManager accessDecisionManager() {
List<AccessDecisionVoter<?>> decisionVoters = new ArrayList<>();
if (prePostEnabled()) {
ExpressionBasedPreInvocationAdvice expressionAdvice =
new ExpressionBasedPreInvocationAdvice();
ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
expressionAdvice.setExpressionHandler(getExpressionHandler());
decisionVoters
.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
decisionVoters.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
}
if (jsr250Enabled()) {
decisionVoters.add(new Jsr250Voter());
}
RoleVoter roleVoter = new RoleVoter();
GrantedAuthorityDefaults grantedAuthorityDefaults =
getSingleBeanOrNull(GrantedAuthorityDefaults.class);
GrantedAuthorityDefaults grantedAuthorityDefaults = getSingleBeanOrNull(GrantedAuthorityDefaults.class);
if (grantedAuthorityDefaults != null) {
roleVoter.setRolePrefix(grantedAuthorityDefaults.getRolePrefix());
}
@@ -275,30 +269,27 @@ public class GlobalMethodSecurityConfiguration
* Subclasses may override this method to provide a custom
* {@link MethodSecurityExpressionHandler}
* </p>
*
* @return the {@link MethodSecurityExpressionHandler} to use
*/
protected MethodSecurityExpressionHandler createExpressionHandler() {
return defaultMethodExpressionHandler;
return this.defaultMethodExpressionHandler;
}
/**
* Gets the {@link MethodSecurityExpressionHandler} or creates it using
* {@link #expressionHandler}.
*
* @return a non {@code null} {@link MethodSecurityExpressionHandler}
*/
protected final MethodSecurityExpressionHandler getExpressionHandler() {
if (expressionHandler == null) {
expressionHandler = createExpressionHandler();
if (this.expressionHandler == null) {
this.expressionHandler = createExpressionHandler();
}
return expressionHandler;
return this.expressionHandler;
}
/**
* Provides a custom {@link MethodSecurityMetadataSource} that is registered with the
* {@link #methodSecurityMetadataSource()}. Default is null.
*
* @return a custom {@link MethodSecurityMetadataSource} that is registered with the
* {@link #methodSecurityMetadataSource()}
*/
@@ -312,32 +303,25 @@ public class GlobalMethodSecurityConfiguration
* {@link #configure(AuthenticationManagerBuilder)}. If
* {@link #configure(AuthenticationManagerBuilder)} was not overridden, then an
* {@link AuthenticationManager} is attempted to be autowired by type.
*
* @return the {@link AuthenticationManager} to use
*/
protected AuthenticationManager authenticationManager() throws Exception {
if (authenticationManager == null) {
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
if (this.authenticationManager == null) {
DefaultAuthenticationEventPublisher eventPublisher = this.objectPostProcessor
.postProcess(new DefaultAuthenticationEventPublisher());
auth = new AuthenticationManagerBuilder(objectPostProcessor);
auth.authenticationEventPublisher(eventPublisher);
configure(auth);
if (disableAuthenticationRegistry) {
authenticationManager = getAuthenticationConfiguration()
.getAuthenticationManager();
}
else {
authenticationManager = auth.build();
}
this.auth = new AuthenticationManagerBuilder(this.objectPostProcessor);
this.auth.authenticationEventPublisher(eventPublisher);
configure(this.auth);
this.authenticationManager = (this.disableAuthenticationRegistry)
? getAuthenticationConfiguration().getAuthenticationManager() : this.auth.build();
}
return authenticationManager;
return this.authenticationManager;
}
/**
* Sub classes can override this method to register different types of authentication.
* If not overridden, {@link #configure(AuthenticationManagerBuilder)} will attempt to
* autowire by type.
*
* @param auth the {@link AuthenticationManagerBuilder} used to register different
* authentication mechanisms for the global method security.
* @throws Exception
@@ -351,7 +335,6 @@ public class GlobalMethodSecurityConfiguration
* creates a {@link DelegatingMethodSecurityMetadataSource} based upon
* {@link #customMethodSecurityMetadataSource()} and the attributes on
* {@link EnableGlobalMethodSecurity}.
*
* @return the {@link MethodSecurityMetadataSource}
*/
@Bean
@@ -363,17 +346,13 @@ public class GlobalMethodSecurityConfiguration
if (customMethodSecurityMetadataSource != null) {
sources.add(customMethodSecurityMetadataSource);
}
boolean hasCustom = customMethodSecurityMetadataSource != null;
boolean isPrePostEnabled = prePostEnabled();
boolean isSecuredEnabled = securedEnabled();
boolean isJsr250Enabled = jsr250Enabled();
if (!isPrePostEnabled && !isSecuredEnabled && !isJsr250Enabled && !hasCustom) {
throw new IllegalStateException("In the composition of all global method configuration, " +
"no annotation support was actually activated");
}
Assert.state(isPrePostEnabled || isSecuredEnabled || isJsr250Enabled || hasCustom,
"In the composition of all global method configuration, "
+ "no annotation support was actually activated");
if (isPrePostEnabled) {
sources.add(new PrePostAnnotationSecurityMetadataSource(attributeFactory));
}
@@ -381,12 +360,11 @@ public class GlobalMethodSecurityConfiguration
sources.add(new SecuredAnnotationSecurityMetadataSource());
}
if (isJsr250Enabled) {
GrantedAuthorityDefaults grantedAuthorityDefaults =
getSingleBeanOrNull(GrantedAuthorityDefaults.class);
Jsr250MethodSecurityMetadataSource jsr250MethodSecurityMetadataSource = this.context.getBean(Jsr250MethodSecurityMetadataSource.class);
GrantedAuthorityDefaults grantedAuthorityDefaults = getSingleBeanOrNull(GrantedAuthorityDefaults.class);
Jsr250MethodSecurityMetadataSource jsr250MethodSecurityMetadataSource = this.context
.getBean(Jsr250MethodSecurityMetadataSource.class);
if (grantedAuthorityDefaults != null) {
jsr250MethodSecurityMetadataSource.setDefaultRolePrefix(
grantedAuthorityDefaults.getRolePrefix());
jsr250MethodSecurityMetadataSource.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
}
sources.add(jsr250MethodSecurityMetadataSource);
}
@@ -396,7 +374,6 @@ public class GlobalMethodSecurityConfiguration
/**
* Creates the {@link PreInvocationAuthorizationAdvice} to be used. The default is
* {@link ExpressionBasedPreInvocationAdvice}.
*
* @return the {@link PreInvocationAuthorizationAdvice}
*/
@Bean
@@ -410,25 +387,23 @@ public class GlobalMethodSecurityConfiguration
* Obtains the attributes from {@link EnableGlobalMethodSecurity} if this class was
* imported using the {@link EnableGlobalMethodSecurity} annotation.
*/
@Override
public final void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> annotationAttributes = importMetadata
.getAnnotationAttributes(EnableGlobalMethodSecurity.class.getName());
enableMethodSecurity = AnnotationAttributes.fromMap(annotationAttributes);
this.enableMethodSecurity = AnnotationAttributes.fromMap(annotationAttributes);
}
@Autowired(required = false)
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
this.objectPostProcessor = objectPostProcessor;
this.defaultMethodExpressionHandler = objectPostProcessor
.postProcess(defaultMethodExpressionHandler);
this.defaultMethodExpressionHandler = objectPostProcessor.postProcess(this.defaultMethodExpressionHandler);
}
@Autowired(required = false)
public void setMethodSecurityExpressionHandler(
List<MethodSecurityExpressionHandler> handlers) {
public void setMethodSecurityExpressionHandler(List<MethodSecurityExpressionHandler> handlers) {
if (handlers.size() != 1) {
logger.debug("Not autowiring MethodSecurityExpressionHandler since size != 1. Got "
+ handlers);
logger.debug("Not autowiring MethodSecurityExpressionHandler since size != 1. Got " + handlers);
return;
}
this.expressionHandler = handlers.get(0);
@@ -440,7 +415,7 @@ public class GlobalMethodSecurityConfiguration
}
private AuthenticationConfiguration getAuthenticationConfiguration() {
return context.getBean(AuthenticationConfiguration.class);
return this.context.getBean(AuthenticationConfiguration.class);
}
private boolean prePostEnabled() {
@@ -455,25 +430,20 @@ public class GlobalMethodSecurityConfiguration
return enableMethodSecurity().getBoolean("jsr250Enabled");
}
private int order() {
return (Integer) enableMethodSecurity().get("order");
}
private boolean isAspectJ() {
return enableMethodSecurity().getEnum("mode") == AdviceMode.ASPECTJ;
}
private AnnotationAttributes enableMethodSecurity() {
if (enableMethodSecurity == null) {
if (this.enableMethodSecurity == null) {
// if it is null look at this instance (i.e. a subclass was used)
EnableGlobalMethodSecurity methodSecurityAnnotation = AnnotationUtils
.findAnnotation(getClass(), EnableGlobalMethodSecurity.class);
Assert.notNull(methodSecurityAnnotation,
() -> EnableGlobalMethodSecurity.class.getName() + " is required");
Map<String, Object> methodSecurityAttrs = AnnotationUtils
.getAnnotationAttributes(methodSecurityAnnotation);
EnableGlobalMethodSecurity methodSecurityAnnotation = AnnotationUtils.findAnnotation(getClass(),
EnableGlobalMethodSecurity.class);
Assert.notNull(methodSecurityAnnotation, () -> EnableGlobalMethodSecurity.class.getName() + " is required");
Map<String, Object> methodSecurityAttrs = AnnotationUtils.getAnnotationAttributes(methodSecurityAnnotation);
this.enableMethodSecurity = AnnotationAttributes.fromMap(methodSecurityAttrs);
}
return this.enableMethodSecurity;
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import java.util.ArrayList;
@@ -36,46 +37,36 @@ import org.springframework.util.ClassUtils;
*/
final class GlobalMethodSecuritySelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
Class<EnableGlobalMethodSecurity> annoType = EnableGlobalMethodSecurity.class;
Map<String, Object> annotationAttributes = importingClassMetadata
.getAnnotationAttributes(annoType.getName(), false);
AnnotationAttributes attributes = AnnotationAttributes
.fromMap(annotationAttributes);
Assert.notNull(attributes, () -> String.format(
"@%s is not present on importing class '%s' as expected",
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(annoType.getName(),
false);
AnnotationAttributes attributes = AnnotationAttributes.fromMap(annotationAttributes);
Assert.notNull(attributes, () -> String.format("@%s is not present on importing class '%s' as expected",
annoType.getSimpleName(), importingClassMetadata.getClassName()));
// TODO would be nice if could use BeanClassLoaderAware (does not work)
Class<?> importingClass = ClassUtils
.resolveClassName(importingClassMetadata.getClassName(),
ClassUtils.getDefaultClassLoader());
Class<?> importingClass = ClassUtils.resolveClassName(importingClassMetadata.getClassName(),
ClassUtils.getDefaultClassLoader());
boolean skipMethodSecurityConfiguration = GlobalMethodSecurityConfiguration.class
.isAssignableFrom(importingClass);
AdviceMode mode = attributes.getEnum("mode");
boolean isProxy = AdviceMode.PROXY == mode;
String autoProxyClassName = isProxy ? AutoProxyRegistrar.class
.getName() : GlobalMethodSecurityAspectJAutoProxyRegistrar.class
.getName();
String autoProxyClassName = isProxy ? AutoProxyRegistrar.class.getName()
: GlobalMethodSecurityAspectJAutoProxyRegistrar.class.getName();
boolean jsr250Enabled = attributes.getBoolean("jsr250Enabled");
List<String> classNames = new ArrayList<>(4);
if (isProxy) {
classNames.add(MethodSecurityMetadataSourceAdvisorRegistrar.class.getName());
}
classNames.add(autoProxyClassName);
if (!skipMethodSecurityConfiguration) {
classNames.add(GlobalMethodSecurityConfiguration.class.getName());
}
if (jsr250Enabled) {
classNames.add(Jsr250MetadataSourceConfiguration.class.getName());
}
return classNames.toArray(new String[0]);
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import org.springframework.beans.factory.config.BeanDefinition;
@@ -27,7 +28,8 @@ class Jsr250MetadataSourceConfiguration {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public Jsr250MethodSecurityMetadataSource jsr250MethodSecurityMetadataSource() {
Jsr250MethodSecurityMetadataSource jsr250MethodSecurityMetadataSource() {
return new Jsr250MethodSecurityMetadataSource();
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.method.configuration;
import org.springframework.beans.factory.config.BeanDefinition;
@@ -24,39 +25,36 @@ import org.springframework.security.access.intercept.aopalliance.MethodSecurityM
import org.springframework.util.MultiValueMap;
/**
* Creates Spring Security's MethodSecurityMetadataSourceAdvisor only when
* using proxy based method security (i.e. do not do it when using ASPECTJ).
* The conditional logic is controlled through {@link GlobalMethodSecuritySelector}.
* Creates Spring Security's MethodSecurityMetadataSourceAdvisor only when using proxy
* based method security (i.e. do not do it when using ASPECTJ). The conditional logic is
* controlled through {@link GlobalMethodSecuritySelector}.
*
* @author Rob Winch
* @since 4.0.2
* @see GlobalMethodSecuritySelector
*/
class MethodSecurityMetadataSourceAdvisorRegistrar implements
ImportBeanDefinitionRegistrar {
class MethodSecurityMetadataSourceAdvisorRegistrar implements ImportBeanDefinitionRegistrar {
/**
* Register, escalate, and configure the AspectJ auto proxy creator based on the value
* of the @{@link EnableGlobalMethodSecurity#proxyTargetClass()} attribute on the
* importing {@code @Configuration} class.
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinitionBuilder advisor = BeanDefinitionBuilder
.rootBeanDefinition(MethodSecurityMetadataSourceAdvisor.class);
advisor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
advisor.addConstructorArgValue("methodSecurityInterceptor");
advisor.addConstructorArgReference("methodSecurityMetadataSource");
advisor.addConstructorArgValue("methodSecurityMetadataSource");
MultiValueMap<String, Object> attributes = importingClassMetadata.getAllAnnotationAttributes(EnableGlobalMethodSecurity.class.getName());
MultiValueMap<String, Object> attributes = importingClassMetadata
.getAllAnnotationAttributes(EnableGlobalMethodSecurity.class.getName());
Integer order = (Integer) attributes.getFirst("order");
if (order != null) {
advisor.addPropertyValue("order", order);
}
registry.registerBeanDefinition("metaDataSourceAdvisor",
advisor.getBeanDefinition());
registry.registerBeanDefinition("metaDataSourceAdvisor", advisor.getBeanDefinition());
}
}

View File

@@ -16,6 +16,8 @@
package org.springframework.security.config.annotation.method.configuration;
import java.util.Arrays;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
@@ -23,7 +25,11 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.context.annotation.Role;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.access.expression.method.*;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.ExpressionBasedAnnotationAttributeFactory;
import org.springframework.security.access.expression.method.ExpressionBasedPostInvocationAdvice;
import org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor;
import org.springframework.security.access.method.AbstractMethodSecurityMetadataSource;
import org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource;
@@ -31,8 +37,6 @@ import org.springframework.security.access.prepost.PrePostAdviceReactiveMethodIn
import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import java.util.Arrays;
/**
* @author Rob Winch
* @author Tadaya Tsuyukubo
@@ -40,43 +44,43 @@ import java.util.Arrays;
*/
@Configuration(proxyBeanMethods = false)
class ReactiveMethodSecurityConfiguration implements ImportAware {
private int advisorOrder;
private GrantedAuthorityDefaults grantedAuthorityDefaults;
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public MethodSecurityMetadataSourceAdvisor methodSecurityInterceptor(AbstractMethodSecurityMetadataSource source) {
MethodSecurityMetadataSourceAdvisor methodSecurityInterceptor(AbstractMethodSecurityMetadataSource source) {
MethodSecurityMetadataSourceAdvisor advisor = new MethodSecurityMetadataSourceAdvisor(
"securityMethodInterceptor", source, "methodMetadataSource");
advisor.setOrder(advisorOrder);
"securityMethodInterceptor", source, "methodMetadataSource");
advisor.setOrder(this.advisorOrder);
return advisor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public DelegatingMethodSecurityMetadataSource methodMetadataSource(MethodSecurityExpressionHandler methodSecurityExpressionHandler) {
DelegatingMethodSecurityMetadataSource methodMetadataSource(
MethodSecurityExpressionHandler methodSecurityExpressionHandler) {
ExpressionBasedAnnotationAttributeFactory attributeFactory = new ExpressionBasedAnnotationAttributeFactory(
methodSecurityExpressionHandler);
PrePostAnnotationSecurityMetadataSource prePostSource = new PrePostAnnotationSecurityMetadataSource(
attributeFactory);
attributeFactory);
return new DelegatingMethodSecurityMetadataSource(Arrays.asList(prePostSource));
}
@Bean
public PrePostAdviceReactiveMethodInterceptor securityMethodInterceptor(AbstractMethodSecurityMetadataSource source, MethodSecurityExpressionHandler handler) {
ExpressionBasedPostInvocationAdvice postAdvice = new ExpressionBasedPostInvocationAdvice(
handler);
PrePostAdviceReactiveMethodInterceptor securityMethodInterceptor(AbstractMethodSecurityMetadataSource source,
MethodSecurityExpressionHandler handler) {
ExpressionBasedPostInvocationAdvice postAdvice = new ExpressionBasedPostInvocationAdvice(handler);
ExpressionBasedPreInvocationAdvice preAdvice = new ExpressionBasedPreInvocationAdvice();
preAdvice.setExpressionHandler(handler);
return new PrePostAdviceReactiveMethodInterceptor(source, preAdvice, postAdvice);
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler() {
DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler() {
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
if (this.grantedAuthorityDefaults != null) {
handler.setDefaultRolePrefix(this.grantedAuthorityDefaults.getRolePrefix());
@@ -86,7 +90,8 @@ class ReactiveMethodSecurityConfiguration implements ImportAware {
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.advisorOrder = (int) importMetadata.getAnnotationAttributes(EnableReactiveMethodSecurity.class.getName()).get("order");
this.advisorOrder = (int) importMetadata.getAnnotationAttributes(EnableReactiveMethodSecurity.class.getName())
.get("order");
}
@Autowired(required = false)

View File

@@ -16,33 +16,32 @@
package org.springframework.security.config.annotation.method.configuration;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AdviceModeImportSelector;
import org.springframework.context.annotation.AutoProxyRegistrar;
import java.util.ArrayList;
import java.util.List;
/**
* @author Rob Winch
* @since 5.0
*/
class ReactiveMethodSecuritySelector extends
AdviceModeImportSelector<EnableReactiveMethodSecurity> {
class ReactiveMethodSecuritySelector extends AdviceModeImportSelector<EnableReactiveMethodSecurity> {
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return getProxyImports();
default:
throw new IllegalStateException("AdviceMode " + adviceMode + " is not supported");
if (adviceMode == AdviceMode.PROXY) {
return getProxyImports();
}
throw new IllegalStateException("AdviceMode " + adviceMode + " is not supported");
}
/**
* Return the imports to use if the {@link AdviceMode} is set to {@link AdviceMode#PROXY}.
* <p>Take care of adding the necessary JSR-107 import if it is available.
* Return the imports to use if the {@link AdviceMode} is set to
* {@link AdviceMode#PROXY}.
* <p>
* Take care of adding the necessary JSR-107 import if it is available.
*/
private String[] getProxyImports() {
List<String> result = new ArrayList<>();
@@ -50,4 +49,5 @@ class ReactiveMethodSecuritySelector extends
result.add(ReactiveMethodSecurityConfiguration.class.getName());
return result.toArray(new String[0]);
}
}

View File

@@ -16,14 +16,14 @@
package org.springframework.security.config.annotation.rsocket;
import org.springframework.context.annotation.Import;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
/**
* Add this annotation to a {@code Configuration} class to have Spring Security
* {@link RSocketSecurity} support added.
@@ -36,4 +36,6 @@ import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({ RSocketSecurityConfiguration.class, SecuritySocketAcceptorInterceptorConfiguration.class })
public @interface EnableRSocketSecurity { }
public @interface EnableRSocketSecurity {
}

View File

@@ -21,14 +21,15 @@ import org.springframework.security.config.Customizer;
import org.springframework.security.rsocket.api.PayloadInterceptor;
/**
* The standard order for {@link PayloadInterceptor} to be
* sorted. The actual values might change, so users should use the {@link #getOrder()} method to
* calculate the position dynamically rather than copy values.
* The standard order for {@link PayloadInterceptor} to be sorted. The actual values might
* change, so users should use the {@link #getOrder()} method to calculate the position
* dynamically rather than copy values.
*
* @author Rob Winch
* @since 5.2
*/
public enum PayloadInterceptorOrder implements Ordered {
/**
* Where basic authentication is placed.
* @see RSocketSecurity#basicAuthentication(Customizer)
@@ -62,7 +63,9 @@ public enum PayloadInterceptorOrder implements Ordered {
this.order = ordinal() * INTERVAL;
}
@Override
public int getOrder() {
return this.order;
}
}

View File

@@ -16,6 +16,12 @@
package org.springframework.security.config.annotation.rsocket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import reactor.core.publisher.Mono;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.core.ResolvableType;
@@ -30,23 +36,18 @@ import org.springframework.security.config.Customizer;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.JwtReactiveAuthenticationManager;
import org.springframework.security.rsocket.api.PayloadInterceptor;
import org.springframework.security.rsocket.authentication.AuthenticationPayloadExchangeConverter;
import org.springframework.security.rsocket.core.PayloadSocketAcceptorInterceptor;
import org.springframework.security.rsocket.authentication.AnonymousPayloadInterceptor;
import org.springframework.security.rsocket.authentication.AuthenticationPayloadExchangeConverter;
import org.springframework.security.rsocket.authentication.AuthenticationPayloadInterceptor;
import org.springframework.security.rsocket.authentication.BearerPayloadExchangeConverter;
import org.springframework.security.rsocket.authorization.AuthorizationPayloadInterceptor;
import org.springframework.security.rsocket.authorization.PayloadExchangeMatcherReactiveAuthorizationManager;
import org.springframework.security.rsocket.core.PayloadSocketAcceptorInterceptor;
import org.springframework.security.rsocket.util.matcher.PayloadExchangeAuthorizationContext;
import org.springframework.security.rsocket.util.matcher.PayloadExchangeMatcher;
import org.springframework.security.rsocket.util.matcher.PayloadExchangeMatcherEntry;
import org.springframework.security.rsocket.util.matcher.PayloadExchangeMatchers;
import org.springframework.security.rsocket.util.matcher.RoutePayloadExchangeMatcher;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Allows configuring RSocket based security.
@@ -56,19 +57,16 @@ import java.util.List;
* <pre class="code">
* &#064;EnableRSocketSecurity
* public class SecurityConfig {
* // @formatter:off
* &#064;Bean
* PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
* rsocket
* .authorizePayload(authorize ->
* .authorizePayload((authorize) ->
* authorize
* .anyRequest().authenticated()
* );
* return rsocket.build();
* }
* // @formatter:on
*
* // @formatter:off
* &#064;Bean
* public MapReactiveUserDetailsService userDetailsService() {
* UserDetails user = User.withDefaultPasswordEncoder()
@@ -78,7 +76,6 @@ import java.util.List;
* .build();
* return new MapReactiveUserDetailsService(user);
* }
* // @formatter:on
* }
* </pre>
*
@@ -87,11 +84,10 @@ import java.util.List;
* <pre class="code">
* &#064;EnableRSocketSecurity
* public class SecurityConfig {
* // @formatter:off
* &#064;Bean
* PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
* rsocket
* .authorizePayload(authorize ->
* .authorizePayload((authorize) ->
* authorize
* // must have ROLE_SETUP to make connection
* .setup().hasRole("SETUP")
@@ -102,9 +98,9 @@ import java.util.List;
* );
* return rsocket.build();
* }
* // @formatter:on
* }
* </pre>
*
* @author Rob Winch
* @author Jesús Ascama Arias
* @author Luis Felipe Vega
@@ -129,12 +125,12 @@ public class RSocketSecurity {
private ReactiveAuthenticationManager authenticationManager;
/**
* Adds a {@link PayloadInterceptor} to be used. This is typically only used
* when using the DSL does not meet a users needs. In order to ensure the
* {@link PayloadInterceptor} is done in the proper order the {@link PayloadInterceptor} should
* either implement {@link org.springframework.core.Ordered} or be annotated with
* Adds a {@link PayloadInterceptor} to be used. This is typically only used when
* using the DSL does not meet a users needs. In order to ensure the
* {@link PayloadInterceptor} is done in the proper order the
* {@link PayloadInterceptor} should either implement
* {@link org.springframework.core.Ordered} or be annotated with
* {@link org.springframework.core.annotation.Order}.
*
* @param interceptor
* @return the builder for additional customizations
* @see PayloadInterceptorOrder
@@ -150,8 +146,9 @@ public class RSocketSecurity {
}
/**
* Adds support for validating a username and password using
* <a href="https://github.com/rsocket/rsocket/blob/5920ed374d008abb712cb1fd7c9d91778b2f4a68/Extensions/Security/Simple.md">Simple Authentication</a>
* Adds support for validating a username and password using <a href=
* "https://github.com/rsocket/rsocket/blob/5920ed374d008abb712cb1fd7c9d91778b2f4a68/Extensions/Security/Simple.md">Simple
* Authentication</a>
* @param simple a customizer
* @return RSocketSecurity for additional configuration
* @since 5.3
@@ -164,12 +161,106 @@ public class RSocketSecurity {
return this;
}
/**
* Adds authentication with BasicAuthenticationPayloadExchangeConverter.
* @param basic
* @return this instance
* @deprecated Use {@link #simpleAuthentication(Customizer)}
*/
@Deprecated
public RSocketSecurity basicAuthentication(Customizer<BasicAuthenticationSpec> basic) {
if (this.basicAuthSpec == null) {
this.basicAuthSpec = new BasicAuthenticationSpec();
}
basic.customize(this.basicAuthSpec);
return this;
}
public RSocketSecurity jwt(Customizer<JwtSpec> jwt) {
if (this.jwtSpec == null) {
this.jwtSpec = new JwtSpec();
}
jwt.customize(this.jwtSpec);
return this;
}
public RSocketSecurity authorizePayload(Customizer<AuthorizePayloadsSpec> authorize) {
if (this.authorizePayload == null) {
this.authorizePayload = new AuthorizePayloadsSpec();
}
authorize.customize(this.authorizePayload);
return this;
}
public PayloadSocketAcceptorInterceptor build() {
PayloadSocketAcceptorInterceptor interceptor = new PayloadSocketAcceptorInterceptor(payloadInterceptors());
RSocketMessageHandler handler = getBean(RSocketMessageHandler.class);
interceptor.setDefaultDataMimeType(handler.getDefaultDataMimeType());
interceptor.setDefaultMetadataMimeType(handler.getDefaultMetadataMimeType());
return interceptor;
}
private List<PayloadInterceptor> payloadInterceptors() {
List<PayloadInterceptor> result = new ArrayList<>(this.payloadInterceptors);
if (this.basicAuthSpec != null) {
result.add(this.basicAuthSpec.build());
}
if (this.simpleAuthSpec != null) {
result.add(this.simpleAuthSpec.build());
}
if (this.jwtSpec != null) {
result.addAll(this.jwtSpec.build());
}
result.add(anonymous());
if (this.authorizePayload != null) {
result.add(this.authorizePayload.build());
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
private AnonymousPayloadInterceptor anonymous() {
AnonymousPayloadInterceptor result = new AnonymousPayloadInterceptor("anonymousUser");
result.setOrder(PayloadInterceptorOrder.ANONYMOUS.getOrder());
return result;
}
private <T> T getBean(Class<T> beanClass) {
if (this.context == null) {
return null;
}
return this.context.getBean(beanClass);
}
private <T> T getBeanOrNull(Class<T> beanClass) {
return getBeanOrNull(ResolvableType.forClass(beanClass));
}
private <T> T getBeanOrNull(ResolvableType type) {
if (this.context == null) {
return null;
}
String[] names = this.context.getBeanNamesForType(type);
if (names.length == 1) {
return (T) this.context.getBean(names[0]);
}
return null;
}
protected void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
/**
* @since 5.3
*/
public class SimpleAuthenticationSpec {
public final class SimpleAuthenticationSpec {
private ReactiveAuthenticationManager authenticationManager;
private SimpleAuthenticationSpec() {
}
public SimpleAuthenticationSpec authenticationManager(ReactiveAuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
return this;
@@ -190,28 +281,15 @@ public class RSocketSecurity {
return result;
}
private SimpleAuthenticationSpec() {}
}
/**
* Adds authentication with BasicAuthenticationPayloadExchangeConverter.
*
* @param basic
* @return
* @deprecated Use {@link #simpleAuthentication(Customizer)}
*/
@Deprecated
public RSocketSecurity basicAuthentication(Customizer<BasicAuthenticationSpec> basic) {
if (this.basicAuthSpec == null) {
this.basicAuthSpec = new BasicAuthenticationSpec();
}
basic.customize(this.basicAuthSpec);
return this;
}
public final class BasicAuthenticationSpec {
public class BasicAuthenticationSpec {
private ReactiveAuthenticationManager authenticationManager;
private BasicAuthenticationSpec() {
}
public BasicAuthenticationSpec authenticationManager(ReactiveAuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
return this;
@@ -231,20 +309,15 @@ public class RSocketSecurity {
return result;
}
private BasicAuthenticationSpec() {}
}
public RSocketSecurity jwt(Customizer<JwtSpec> jwt) {
if (this.jwtSpec == null) {
this.jwtSpec = new JwtSpec();
}
jwt.customize(this.jwtSpec);
return this;
}
public final class JwtSpec {
public class JwtSpec {
private ReactiveAuthenticationManager authenticationManager;
private JwtSpec() {
}
public JwtSpec authenticationManager(ReactiveAuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
return this;
@@ -267,73 +340,27 @@ public class RSocketSecurity {
AuthenticationPayloadInterceptor legacy = new AuthenticationPayloadInterceptor(manager);
legacy.setAuthenticationConverter(new BearerPayloadExchangeConverter());
legacy.setOrder(PayloadInterceptorOrder.AUTHENTICATION.getOrder());
AuthenticationPayloadInterceptor standard = new AuthenticationPayloadInterceptor(manager);
standard.setAuthenticationConverter(new AuthenticationPayloadExchangeConverter());
standard.setOrder(PayloadInterceptorOrder.AUTHENTICATION.getOrder());
return Arrays.asList(standard, legacy);
}
private JwtSpec() {}
}
public RSocketSecurity authorizePayload(Customizer<AuthorizePayloadsSpec> authorize) {
if (this.authorizePayload == null) {
this.authorizePayload = new AuthorizePayloadsSpec();
}
authorize.customize(this.authorizePayload);
return this;
}
public PayloadSocketAcceptorInterceptor build() {
PayloadSocketAcceptorInterceptor interceptor = new PayloadSocketAcceptorInterceptor(
payloadInterceptors());
RSocketMessageHandler handler = getBean(RSocketMessageHandler.class);
interceptor.setDefaultDataMimeType(handler.getDefaultDataMimeType());
interceptor.setDefaultMetadataMimeType(handler.getDefaultMetadataMimeType());
return interceptor;
}
private List<PayloadInterceptor> payloadInterceptors() {
List<PayloadInterceptor> result = new ArrayList<>(this.payloadInterceptors);
if (this.basicAuthSpec != null) {
result.add(this.basicAuthSpec.build());
}
if (this.simpleAuthSpec != null) {
result.add(this.simpleAuthSpec.build());
}
if (this.jwtSpec != null) {
result.addAll(this.jwtSpec.build());
}
result.add(anonymous());
if (this.authorizePayload != null) {
result.add(this.authorizePayload.build());
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
private AnonymousPayloadInterceptor anonymous() {
AnonymousPayloadInterceptor result = new AnonymousPayloadInterceptor("anonymousUser");
result.setOrder(PayloadInterceptorOrder.ANONYMOUS.getOrder());
return result;
}
public class AuthorizePayloadsSpec {
private PayloadExchangeMatcherReactiveAuthorizationManager.Builder authzBuilder =
PayloadExchangeMatcherReactiveAuthorizationManager.builder();
private PayloadExchangeMatcherReactiveAuthorizationManager.Builder authzBuilder = PayloadExchangeMatcherReactiveAuthorizationManager
.builder();
public Access setup() {
return matcher(PayloadExchangeMatchers.setup());
}
/**
* Matches if {@link org.springframework.security.rsocket.api.PayloadExchangeType#isRequest()} is true, else
* not a match
* Matches if
* {@link org.springframework.security.rsocket.api.PayloadExchangeType#isRequest()}
* is true, else not a match
* @return the Access to set up the authorization rule.
*/
public Access anyRequest() {
@@ -356,10 +383,8 @@ public class RSocketSecurity {
public Access route(String pattern) {
RSocketMessageHandler handler = getBean(RSocketMessageHandler.class);
PayloadExchangeMatcher matcher = new RoutePayloadExchangeMatcher(
handler.getMetadataExtractor(),
handler.getRouteMatcher(),
pattern);
PayloadExchangeMatcher matcher = new RoutePayloadExchangeMatcher(handler.getMetadataExtractor(),
handler.getRouteMatcher(), pattern);
return matcher(matcher);
}
@@ -367,7 +392,7 @@ public class RSocketSecurity {
return new Access(matcher);
}
public class Access {
public final class Access {
private final PayloadExchangeMatcher matcher;
@@ -392,8 +417,7 @@ public class RSocketSecurity {
}
public AuthorizePayloadsSpec permitAll() {
return access((a, ctx) -> Mono
.just(new AuthorizationDecision(true)));
return access((a, ctx) -> Mono.just(new AuthorizationDecision(true)));
}
public AuthorizePayloadsSpec hasAnyAuthority(String... authorities) {
@@ -402,41 +426,17 @@ public class RSocketSecurity {
public AuthorizePayloadsSpec access(
ReactiveAuthorizationManager<PayloadExchangeAuthorizationContext> authorization) {
AuthorizePayloadsSpec.this.authzBuilder.add(new PayloadExchangeMatcherEntry<>(this.matcher, authorization));
AuthorizePayloadsSpec.this.authzBuilder
.add(new PayloadExchangeMatcherEntry<>(this.matcher, authorization));
return AuthorizePayloadsSpec.this;
}
public AuthorizePayloadsSpec denyAll() {
return access((a, ctx) -> Mono
.just(new AuthorizationDecision(false)));
return access((a, ctx) -> Mono.just(new AuthorizationDecision(false)));
}
}
}
private <T> T getBean(Class<T> beanClass) {
if (this.context == null) {
return null;
}
return this.context.getBean(beanClass);
}
private <T> T getBeanOrNull(Class<T> beanClass) {
return getBeanOrNull(ResolvableType.forClass(beanClass));
}
private <T> T getBeanOrNull(ResolvableType type) {
if (this.context == null) {
return null;
}
String[] names = this.context.getBeanNamesForType(type);
if (names.length == 1) {
return (T) this.context.getBean(names[0]);
}
return null;
}
protected void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.context = applicationContext;
}
}

View File

@@ -34,6 +34,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
class RSocketSecurityConfiguration {
private static final String BEAN_NAME_PREFIX = "org.springframework.security.config.annotation.rsocket.RSocketSecurityConfiguration.";
private static final String RSOCKET_SECURITY_BEAN_NAME = BEAN_NAME_PREFIX + "rsocketSecurity";
private ReactiveAuthenticationManager authenticationManager;
@@ -43,8 +44,7 @@ class RSocketSecurityConfiguration {
private PasswordEncoder passwordEncoder;
@Autowired(required = false)
void setAuthenticationManager(
ReactiveAuthenticationManager authenticationManager) {
void setAuthenticationManager(ReactiveAuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@@ -60,9 +60,8 @@ class RSocketSecurityConfiguration {
@Bean(name = RSOCKET_SECURITY_BEAN_NAME)
@Scope("prototype")
public RSocketSecurity rsocketSecurity(ApplicationContext context) {
RSocketSecurity security = new RSocketSecurity()
.authenticationManager(authenticationManager());
RSocketSecurity rsocketSecurity(ApplicationContext context) {
RSocketSecurity security = new RSocketSecurity().authenticationManager(authenticationManager());
security.setApplicationContext(context);
return security;
}
@@ -72,8 +71,8 @@ class RSocketSecurityConfiguration {
return this.authenticationManager;
}
if (this.reactiveUserDetailsService != null) {
UserDetailsRepositoryReactiveAuthenticationManager manager =
new UserDetailsRepositoryReactiveAuthenticationManager(this.reactiveUserDetailsService);
UserDetailsRepositoryReactiveAuthenticationManager manager = new UserDetailsRepositoryReactiveAuthenticationManager(
this.reactiveUserDetailsService);
if (this.passwordEncoder != null) {
manager.setPasswordEncoder(this.passwordEncoder);
}
@@ -81,4 +80,5 @@ class RSocketSecurityConfiguration {
}
return null;
}
}

View File

@@ -31,29 +31,31 @@ import org.springframework.security.rsocket.util.matcher.PayloadExchangeMatcher.
*/
@Configuration(proxyBeanMethods = false)
class SecuritySocketAcceptorInterceptorConfiguration {
@Bean
SecuritySocketAcceptorInterceptor securitySocketAcceptorInterceptor(
ObjectProvider<PayloadSocketAcceptorInterceptor> rsocketInterceptor, ObjectProvider<RSocketSecurity> rsocketSecurity) {
ObjectProvider<PayloadSocketAcceptorInterceptor> rsocketInterceptor,
ObjectProvider<RSocketSecurity> rsocketSecurity) {
PayloadSocketAcceptorInterceptor delegate = rsocketInterceptor
.getIfAvailable(() -> defaultInterceptor(rsocketSecurity));
return new SecuritySocketAcceptorInterceptor(delegate);
}
private PayloadSocketAcceptorInterceptor defaultInterceptor(
ObjectProvider<RSocketSecurity> rsocketSecurity) {
private PayloadSocketAcceptorInterceptor defaultInterceptor(ObjectProvider<RSocketSecurity> rsocketSecurity) {
RSocketSecurity rsocket = rsocketSecurity.getIfAvailable();
if (rsocket == null) {
throw new NoSuchBeanDefinitionException("No RSocketSecurity defined");
}
rsocket
.basicAuthentication(Customizer.withDefaults())
// @formatter:off
rsocket.basicAuthentication(Customizer.withDefaults())
.simpleAuthentication(Customizer.withDefaults())
.authorizePayload(authz ->
authz
.setup().authenticated()
.anyRequest().authenticated()
.matcher(e -> MatchResult.match()).permitAll()
.authorizePayload((authz) -> authz
.setup().authenticated()
.anyRequest().authenticated()
.matcher((e) -> MatchResult.match()).permitAll()
);
// @formatter:on
return rsocket.build();
}
}

View File

@@ -13,8 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
@@ -28,22 +33,17 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* A base class for registering {@link RequestMatcher}'s. For example, it might allow for
* specifying which {@link RequestMatcher} require a certain level of authorization.
*
*
* @param <C> The object that is returned or Chained after creating the RequestMatcher
*
* @author Rob Winch
* @author Ankur Pathak
* @since 3.2
*/
public abstract class AbstractRequestMatcherRegistry<C> {
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
private static final RequestMatcher ANY_REQUEST = AnyRequestMatcher.INSTANCE;
@@ -58,7 +58,6 @@ public abstract class AbstractRequestMatcherRegistry<C> {
/**
* Gets the {@link ApplicationContext}
*
* @return the {@link ApplicationContext}
*/
protected final ApplicationContext getApplicationContext() {
@@ -67,7 +66,6 @@ public abstract class AbstractRequestMatcherRegistry<C> {
/**
* Maps any request.
*
* @return the object that is chained after creating the {@link RequestMatcher}
*/
public C anyRequest() {
@@ -81,10 +79,7 @@ public abstract class AbstractRequestMatcherRegistry<C> {
* Maps a {@link List} of
* {@link org.springframework.security.web.util.matcher.AntPathRequestMatcher}
* instances.
*
* @param method the {@link HttpMethod} to use for any
* {@link HttpMethod}.
*
* @param method the {@link HttpMethod} to use for any {@link HttpMethod}.
* @return the object that is chained after creating the {@link RequestMatcher}
*/
public C antMatchers(HttpMethod method) {
@@ -95,12 +90,11 @@ public abstract class AbstractRequestMatcherRegistry<C> {
* Maps a {@link List} of
* {@link org.springframework.security.web.util.matcher.AntPathRequestMatcher}
* instances.
*
* @param method the {@link HttpMethod} to use or {@code null} for any
* {@link HttpMethod}.
* @param antPatterns the ant patterns to create. If {@code null} or empty, then matches on nothing.
* @param antPatterns the ant patterns to create. If {@code null} or empty, then
* matches on nothing.
* {@link org.springframework.security.web.util.matcher.AntPathRequestMatcher} from
*
* @return the object that is chained after creating the {@link RequestMatcher}
*/
public C antMatchers(HttpMethod method, String... antPatterns) {
@@ -112,10 +106,8 @@ public abstract class AbstractRequestMatcherRegistry<C> {
* Maps a {@link List} of
* {@link org.springframework.security.web.util.matcher.AntPathRequestMatcher}
* instances that do not care which {@link HttpMethod} is used.
*
* @param antPatterns the ant patterns to create
* {@link org.springframework.security.web.util.matcher.AntPathRequestMatcher} from
*
* @return the object that is chained after creating the {@link RequestMatcher}
*/
public C antMatchers(String... antPatterns) {
@@ -134,7 +126,6 @@ public abstract class AbstractRequestMatcherRegistry<C> {
* If the current request will not be processed by Spring MVC, a reasonable default
* using the pattern as a ant pattern will be used.
* </p>
*
* @param mvcPatterns the patterns to match on. The rules for matching are defined by
* Spring MVC
* @return the object that is chained after creating the {@link RequestMatcher}.
@@ -152,7 +143,6 @@ public abstract class AbstractRequestMatcherRegistry<C> {
* If the current request will not be processed by Spring MVC, a reasonable default
* using the pattern as a ant pattern will be used.
* </p>
*
* @param method the HTTP method to match on
* @param mvcPatterns the patterns to match on. The rules for matching are defined by
* Spring MVC
@@ -162,27 +152,24 @@ public abstract class AbstractRequestMatcherRegistry<C> {
/**
* Creates {@link MvcRequestMatcher} instances for the method and patterns passed in
*
* @param method the HTTP method to use or null if any should be used
* @param mvcPatterns the Spring MVC patterns to match on
* @return a List of {@link MvcRequestMatcher} instances
*/
protected final List<MvcRequestMatcher> createMvcMatchers(HttpMethod method,
String... mvcPatterns) {
protected final List<MvcRequestMatcher> createMvcMatchers(HttpMethod method, String... mvcPatterns) {
Assert.state(!this.anyRequestConfigured, "Can't configure mvcMatchers after anyRequest");
ObjectPostProcessor<Object> opp = this.context.getBean(ObjectPostProcessor.class);
if (!this.context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
throw new NoSuchBeanDefinitionException("A Bean named " + HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME +" of type " + HandlerMappingIntrospector.class.getName()
+ " is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.");
throw new NoSuchBeanDefinitionException("A Bean named " + HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME
+ " of type " + HandlerMappingIntrospector.class.getName()
+ " is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.");
}
HandlerMappingIntrospector introspector = this.context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME,
HandlerMappingIntrospector.class);
List<MvcRequestMatcher> matchers = new ArrayList<>(
mvcPatterns.length);
HandlerMappingIntrospector.class);
List<MvcRequestMatcher> matchers = new ArrayList<>(mvcPatterns.length);
for (String mvcPattern : mvcPatterns) {
MvcRequestMatcher matcher = new MvcRequestMatcher(introspector, mvcPattern);
opp.postProcess(matcher);
if (method != null) {
matcher.setMethod(method);
}
@@ -195,12 +182,10 @@ public abstract class AbstractRequestMatcherRegistry<C> {
* Maps a {@link List} of
* {@link org.springframework.security.web.util.matcher.RegexRequestMatcher}
* instances.
*
* @param method the {@link HttpMethod} to use or {@code null} for any
* {@link HttpMethod}.
* @param regexPatterns the regular expressions to create
* {@link org.springframework.security.web.util.matcher.RegexRequestMatcher} from
*
* @return the object that is chained after creating the {@link RequestMatcher}
*/
public C regexMatchers(HttpMethod method, String... regexPatterns) {
@@ -212,10 +197,8 @@ public abstract class AbstractRequestMatcherRegistry<C> {
* Create a {@link List} of
* {@link org.springframework.security.web.util.matcher.RegexRequestMatcher} instances
* that do not specify an {@link HttpMethod}.
*
* @param regexPatterns the regular expressions to create
* {@link org.springframework.security.web.util.matcher.RegexRequestMatcher} from
*
* @return the object that is chained after creating the {@link RequestMatcher}
*/
public C regexMatchers(String... regexPatterns) {
@@ -226,9 +209,7 @@ public abstract class AbstractRequestMatcherRegistry<C> {
/**
* Associates a list of {@link RequestMatcher} instances with the
* {@link AbstractConfigAttributeRequestMatcherRegistry}
*
* @param requestMatchers the {@link RequestMatcher} instances
*
* @return the object that is chained after creating the {@link RequestMatcher}
*/
public C requestMatchers(RequestMatcher... requestMatchers) {
@@ -239,7 +220,6 @@ public abstract class AbstractRequestMatcherRegistry<C> {
/**
* Subclasses should implement this method for returning the object that is chained to
* the creation of the {@link RequestMatcher} instances.
*
* @param requestMatchers the {@link RequestMatcher} instances that were created
* @return the chained Object for the subclass which allows association of something
* else to the {@link RequestMatcher}
@@ -254,19 +234,19 @@ public abstract class AbstractRequestMatcherRegistry<C> {
*/
private static final class RequestMatchers {
private RequestMatchers() {
}
/**
* Create a {@link List} of {@link AntPathRequestMatcher} instances.
*
* @param httpMethod the {@link HttpMethod} to use or {@code null} for any
* {@link HttpMethod}.
* @param antPatterns the ant patterns to create {@link AntPathRequestMatcher}
* from
*
* @return a {@link List} of {@link AntPathRequestMatcher} instances
*/
public static List<RequestMatcher> antMatchers(HttpMethod httpMethod,
String... antPatterns) {
String method = httpMethod == null ? null : httpMethod.toString();
static List<RequestMatcher> antMatchers(HttpMethod httpMethod, String... antPatterns) {
String method = (httpMethod != null) ? httpMethod.toString() : null;
List<RequestMatcher> matchers = new ArrayList<>();
for (String pattern : antPatterns) {
matchers.add(new AntPathRequestMatcher(pattern, method));
@@ -277,29 +257,24 @@ public abstract class AbstractRequestMatcherRegistry<C> {
/**
* Create a {@link List} of {@link AntPathRequestMatcher} instances that do not
* specify an {@link HttpMethod}.
*
* @param antPatterns the ant patterns to create {@link AntPathRequestMatcher}
* from
*
* @return a {@link List} of {@link AntPathRequestMatcher} instances
*/
public static List<RequestMatcher> antMatchers(String... antPatterns) {
static List<RequestMatcher> antMatchers(String... antPatterns) {
return antMatchers(null, antPatterns);
}
/**
* Create a {@link List} of {@link RegexRequestMatcher} instances.
*
* @param httpMethod the {@link HttpMethod} to use or {@code null} for any
* {@link HttpMethod}.
* @param regexPatterns the regular expressions to create
* {@link RegexRequestMatcher} from
*
* @return a {@link List} of {@link RegexRequestMatcher} instances
*/
public static List<RequestMatcher> regexMatchers(HttpMethod httpMethod,
String... regexPatterns) {
String method = httpMethod == null ? null : httpMethod.toString();
static List<RequestMatcher> regexMatchers(HttpMethod httpMethod, String... regexPatterns) {
String method = (httpMethod != null) ? httpMethod.toString() : null;
List<RequestMatcher> matchers = new ArrayList<>();
for (String pattern : regexPatterns) {
matchers.add(new RegexRequestMatcher(pattern, method));
@@ -310,18 +285,14 @@ public abstract class AbstractRequestMatcherRegistry<C> {
/**
* Create a {@link List} of {@link RegexRequestMatcher} instances that do not
* specify an {@link HttpMethod}.
*
* @param regexPatterns the regular expressions to create
* {@link RegexRequestMatcher} from
*
* @return a {@link List} of {@link RegexRequestMatcher} instances
*/
public static List<RequestMatcher> regexMatchers(String... regexPatterns) {
static List<RequestMatcher> regexMatchers(String... regexPatterns) {
return regexMatchers(null, regexPatterns);
}
private RequestMatchers() {
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web;
import javax.servlet.Filter;
@@ -44,36 +45,29 @@ import org.springframework.security.web.session.ConcurrentSessionFilter;
import org.springframework.security.web.session.SessionManagementFilter;
/**
*
* @author Rob Winch
*
* @param <H>
* @author Rob Winch
*/
public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends
SecurityBuilder<DefaultSecurityFilterChain> {
public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>>
extends SecurityBuilder<DefaultSecurityFilterChain> {
/**
* Gets the {@link SecurityConfigurer} by its class name or <code>null</code> if not
* found. Note that object hierarchies are not considered.
*
* @param clazz the Class of the {@link SecurityConfigurer} to attempt to get.
*/
<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C getConfigurer(
Class<C> clazz);
<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C getConfigurer(Class<C> clazz);
/**
* Removes the {@link SecurityConfigurer} by its class name or <code>null</code> if
* not found. Note that object hierarchies are not considered.
*
* @param clazz the Class of the {@link SecurityConfigurer} to attempt to remove.
* @return the {@link SecurityConfigurer} that was removed or null if not found
*/
<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C removeConfigurer(
Class<C> clazz);
<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C removeConfigurer(Class<C> clazz);
/**
* Sets an object that is shared by multiple {@link SecurityConfigurer}.
*
* @param sharedType the Class to key the shared object by.
* @param object the Object to store
*/
@@ -81,7 +75,6 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends
/**
* Gets a shared Object. Note that object heirarchies are not considered.
*
* @param sharedType the type of the shared Object
* @return the shared Object or null if it is not found
*/
@@ -89,7 +82,6 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends
/**
* Allows adding an additional {@link AuthenticationProvider} to be used
*
* @param authenticationProvider the {@link AuthenticationProvider} to be added
* @return the {@link HttpSecurity} for further customizations
*/
@@ -97,7 +89,6 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends
/**
* Allows adding an additional {@link UserDetailsService} to be used
*
* @param userDetailsService the {@link UserDetailsService} to be added
* @return the {@link HttpSecurity} for further customizations
*/
@@ -108,7 +99,6 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends
* known {@link Filter} instances are either a {@link Filter} listed in
* {@link #addFilter(Filter)} or a {@link Filter} that has already been added using
* {@link #addFilterAfter(Filter, Class)} or {@link #addFilterBefore(Filter, Class)}.
*
* @param filter the {@link Filter} to register after the type {@code afterFilter}
* @param afterFilter the Class of the known {@link Filter}.
* @return the {@link HttpSecurity} for further customizations
@@ -120,7 +110,6 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends
* known {@link Filter} instances are either a {@link Filter} listed in
* {@link #addFilter(Filter)} or a {@link Filter} that has already been added using
* {@link #addFilterAfter(Filter, Class)} or {@link #addFilterBefore(Filter, Class)}.
*
* @param filter the {@link Filter} to register before the type {@code beforeFilter}
* @param beforeFilter the Class of the known {@link Filter}.
* @return the {@link HttpSecurity} for further customizations
@@ -140,7 +129,8 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends
* <li>{@link LogoutFilter}</li>
* <li>{@link X509AuthenticationFilter}</li>
* <li>{@link AbstractPreAuthenticatedProcessingFilter}</li>
* <li><a href="{@docRoot}/org/springframework/security/cas/web/CasAuthenticationFilter.html">CasAuthenticationFilter</a></li>
* <li><a href="
* {@docRoot}/org/springframework/security/cas/web/CasAuthenticationFilter.html">CasAuthenticationFilter</a></li>
* <li>{@link UsernamePasswordAuthenticationFilter}</li>
* <li>{@link OpenIDAuthenticationFilter}</li>
* <li>{@link org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter}</li>
@@ -159,9 +149,9 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends
* <li>{@link FilterSecurityInterceptor}</li>
* <li>{@link SwitchUserFilter}</li>
* </ul>
*
* @param filter the {@link Filter} to add
* @return the {@link HttpSecurity} for further customizations
*/
H addFilter(Filter filter);
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web;
import javax.servlet.Filter;
@@ -28,17 +29,15 @@ import org.springframework.security.web.SecurityFilterChain;
/**
* Allows customization to the {@link WebSecurity}. In most instances users will use
* {@link EnableWebSecurity} and either create a {@link Configuration} that extends
* {@link WebSecurityConfigurerAdapter} or expose a {@link SecurityFilterChain} bean.
* Both will automatically be applied to the {@link WebSecurity} by the
* {@link WebSecurityConfigurerAdapter} or expose a {@link SecurityFilterChain} bean. Both
* will automatically be applied to the {@link WebSecurity} by the
* {@link EnableWebSecurity} annotation.
*
* @see WebSecurityConfigurerAdapter
* @see SecurityFilterChain
*
* @author Rob Winch
* @since 3.2
* @see WebSecurityConfigurerAdapter
* @see SecurityFilterChain
*/
public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends
SecurityConfigurer<Filter, T> {
public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends SecurityConfigurer<Filter, T> {
}

View File

@@ -13,12 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.builders;
import java.io.Serializable;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import org.springframework.security.web.access.ExceptionTranslationFilter;
@@ -44,6 +46,7 @@ import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
import org.springframework.security.web.session.ConcurrentSessionFilter;
import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.util.Assert;
import org.springframework.web.filter.CorsFilter;
/**
@@ -56,8 +59,11 @@ import org.springframework.web.filter.CorsFilter;
@SuppressWarnings("serial")
final class FilterComparator implements Comparator<Filter>, Serializable {
private static final int INITIAL_ORDER = 100;
private static final int ORDER_STEP = 100;
private final Map<String, Integer> filterToOrder = new HashMap<>();
FilterComparator() {
@@ -70,40 +76,37 @@ final class FilterComparator implements Comparator<Filter>, Serializable {
put(CorsFilter.class, order.next());
put(CsrfFilter.class, order.next());
put(LogoutFilter.class, order.next());
filterToOrder.put(
"org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter",
this.filterToOrder.put(
"org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter",
order.next());
filterToOrder.put(
this.filterToOrder.put(
"org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationRequestFilter",
order.next());
put(X509AuthenticationFilter.class, order.next());
put(AbstractPreAuthenticatedProcessingFilter.class, order.next());
filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter",
this.filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter", order.next());
this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter",
order.next());
filterToOrder.put(
"org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter",
order.next());
filterToOrder.put(
this.filterToOrder.put(
"org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter",
order.next());
put(UsernamePasswordAuthenticationFilter.class, order.next());
order.next(); // gh-8105
filterToOrder.put(
"org.springframework.security.openid.OpenIDAuthenticationFilter", order.next());
this.filterToOrder.put("org.springframework.security.openid.OpenIDAuthenticationFilter", order.next());
put(DefaultLoginPageGeneratingFilter.class, order.next());
put(DefaultLogoutPageGeneratingFilter.class, order.next());
put(ConcurrentSessionFilter.class, order.next());
put(DigestAuthenticationFilter.class, order.next());
filterToOrder.put(
"org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter", order.next());
this.filterToOrder.put(
"org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter",
order.next());
put(BasicAuthenticationFilter.class, order.next());
put(RequestCacheAwareFilter.class, order.next());
put(SecurityContextHolderAwareRequestFilter.class, order.next());
put(JaasApiIntegrationFilter.class, order.next());
put(RememberMeAuthenticationFilter.class, order.next());
put(AnonymousAuthenticationFilter.class, order.next());
filterToOrder.put(
"org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter",
this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter",
order.next());
put(SessionManagementFilter.class, order.next());
put(ExceptionTranslationFilter.class, order.next());
@@ -111,6 +114,7 @@ final class FilterComparator implements Comparator<Filter>, Serializable {
put(SwitchUserFilter.class, order.next());
}
@Override
public int compare(Filter lhs, Filter rhs) {
Integer left = getOrder(lhs.getClass());
Integer right = getOrder(rhs.getClass());
@@ -119,11 +123,10 @@ final class FilterComparator implements Comparator<Filter>, Serializable {
/**
* Determines if a particular {@link Filter} is registered to be sorted
*
* @param filter
* @return
*/
public boolean isRegistered(Class<? extends Filter> filter) {
boolean isRegistered(Class<? extends Filter> filter) {
return getOrder(filter) != null;
}
@@ -134,14 +137,9 @@ final class FilterComparator implements Comparator<Filter>, Serializable {
* @param afterFilter the {@link Filter} that is already registered and that
* {@code filter} should be placed after.
*/
public void registerAfter(Class<? extends Filter> filter,
Class<? extends Filter> afterFilter) {
void registerAfter(Class<? extends Filter> filter, Class<? extends Filter> afterFilter) {
Integer position = getOrder(afterFilter);
if (position == null) {
throw new IllegalArgumentException(
"Cannot register after unregistered Filter " + afterFilter);
}
Assert.notNull(position, () -> "Cannot register after unregistered Filter " + afterFilter);
put(filter, position + 1);
}
@@ -151,14 +149,9 @@ final class FilterComparator implements Comparator<Filter>, Serializable {
* @param atFilter the {@link Filter} that is already registered and that
* {@code filter} should be placed at.
*/
public void registerAt(Class<? extends Filter> filter,
Class<? extends Filter> atFilter) {
void registerAt(Class<? extends Filter> filter, Class<? extends Filter> atFilter) {
Integer position = getOrder(atFilter);
if (position == null) {
throw new IllegalArgumentException(
"Cannot register after unregistered Filter " + atFilter);
}
Assert.notNull(position, () -> "Cannot register after unregistered Filter " + atFilter);
put(filter, position);
}
@@ -169,32 +162,26 @@ final class FilterComparator implements Comparator<Filter>, Serializable {
* @param beforeFilter the {@link Filter} that is already registered and that
* {@code filter} should be placed before.
*/
public void registerBefore(Class<? extends Filter> filter,
Class<? extends Filter> beforeFilter) {
void registerBefore(Class<? extends Filter> filter, Class<? extends Filter> beforeFilter) {
Integer position = getOrder(beforeFilter);
if (position == null) {
throw new IllegalArgumentException(
"Cannot register after unregistered Filter " + beforeFilter);
}
Assert.notNull(position, () -> "Cannot register after unregistered Filter " + beforeFilter);
put(filter, position - 1);
}
private void put(Class<? extends Filter> filter, int position) {
String className = filter.getName();
filterToOrder.put(className, position);
this.filterToOrder.put(className, position);
}
/**
* Gets the order of a particular {@link Filter} class taking into consideration
* superclasses.
*
* @param clazz the {@link Filter} class to determine the sort order
* @return the sort order or null if not defined
*/
private Integer getOrder(Class<?> clazz) {
while (clazz != null) {
Integer result = filterToOrder.get(clazz.getName());
Integer result = this.filterToOrder.get(clazz.getName());
if (result != null) {
return result;
}
@@ -206,6 +193,7 @@ final class FilterComparator implements Comparator<Filter>, Serializable {
private static class Step {
private int value;
private final int stepSize;
Step(int initialValue, int stepSize) {

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.builders;
import java.util.ArrayList;
@@ -72,16 +73,15 @@ import org.springframework.web.filter.DelegatingFilterProxy;
* {@link WebSecurityConfigurerAdapter}.
* </p>
*
* @see EnableWebSecurity
* @see WebSecurityConfiguration
*
* @author Rob Winch
* @author Evgeniy Cheban
* @since 3.2
* @see EnableWebSecurity
* @see WebSecurityConfiguration
*/
public final class WebSecurity extends
AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements
SecurityBuilder<Filter>, ApplicationContextAware {
public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
implements SecurityBuilder<Filter>, ApplicationContextAware {
private final Log logger = LogFactory.getLog(getClass());
private final List<RequestMatcher> ignoredRequests = new ArrayList<>();
@@ -102,7 +102,7 @@ public final class WebSecurity extends
private DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
private SecurityExpressionHandler<FilterInvocation> expressionHandler = defaultWebSecurityExpressionHandler;
private SecurityExpressionHandler<FilterInvocation> expressionHandler = this.defaultWebSecurityExpressionHandler;
private Runnable postBuildAction = () -> {
};
@@ -118,12 +118,11 @@ public final class WebSecurity extends
/**
* <p>
* Allows adding {@link RequestMatcher} instances that Spring Security
* should ignore. Web Security provided by Spring Security (including the
* {@link SecurityContext}) will not be available on {@link HttpServletRequest} that
* match. Typically the requests that are registered should be that of only static
* resources. For requests that are dynamic, consider mapping the request to allow all
* users instead.
* Allows adding {@link RequestMatcher} instances that Spring Security should ignore.
* Web Security provided by Spring Security (including the {@link SecurityContext})
* will not be available on {@link HttpServletRequest} that match. Typically the
* requests that are registered should be that of only static resources. For requests
* that are dynamic, consider mapping the request to allow all users instead.
* </p>
*
* Example Usage:
@@ -154,18 +153,16 @@ public final class WebSecurity extends
* .antMatchers(&quot;/static/**&quot;);
* // now both URLs that start with /resources/ and /static/ will be ignored
* </pre>
*
* @return the {@link IgnoredRequestConfigurer} to use for registering request that
* should be ignored
*/
public IgnoredRequestConfigurer ignoring() {
return ignoredRequestRegistry;
return this.ignoredRequestRegistry;
}
/**
* Allows customizing the {@link HttpFirewall}. The default is
* {@link StrictHttpFirewall}.
*
* @param httpFirewall the custom {@link HttpFirewall}
* @return the {@link WebSecurity} for further customizations
*/
@@ -176,10 +173,8 @@ public final class WebSecurity extends
/**
* Controls debugging support for Spring Security.
*
* @param debugEnabled if true, enables debug support with Spring Security. Default is
* false.
*
* @return the {@link WebSecurity} for further customization.
* @see EnableWebSecurity#debug()
*/
@@ -197,7 +192,6 @@ public final class WebSecurity extends
* Typically this method is invoked automatically within the framework from
* {@link WebSecurityConfigurerAdapter#init(WebSecurity)}
* </p>
*
* @param securityFilterChainBuilder the builder to use to create the
* {@link SecurityFilterChain} instances
* @return the {@link WebSecurity} for further customizations
@@ -209,15 +203,13 @@ public final class WebSecurity extends
}
/**
* Set the {@link WebInvocationPrivilegeEvaluator} to be used. If this is not specified,
* then a {@link DefaultWebInvocationPrivilegeEvaluator} will be created when
* {@link #securityInterceptor(FilterSecurityInterceptor)} is non null.
*
* Set the {@link WebInvocationPrivilegeEvaluator} to be used. If this is not
* specified, then a {@link DefaultWebInvocationPrivilegeEvaluator} will be created
* when {@link #securityInterceptor(FilterSecurityInterceptor)} is non null.
* @param privilegeEvaluator the {@link WebInvocationPrivilegeEvaluator} to use
* @return the {@link WebSecurity} for further customizations
*/
public WebSecurity privilegeEvaluator(
WebInvocationPrivilegeEvaluator privilegeEvaluator) {
public WebSecurity privilegeEvaluator(WebInvocationPrivilegeEvaluator privilegeEvaluator) {
this.privilegeEvaluator = privilegeEvaluator;
return this;
}
@@ -225,12 +217,10 @@ public final class WebSecurity extends
/**
* Set the {@link SecurityExpressionHandler} to be used. If this is not specified,
* then a {@link DefaultWebSecurityExpressionHandler} will be used.
*
* @param expressionHandler the {@link SecurityExpressionHandler} to use
* @return the {@link WebSecurity} for further customizations
*/
public WebSecurity expressionHandler(
SecurityExpressionHandler<FilterInvocation> expressionHandler) {
public WebSecurity expressionHandler(SecurityExpressionHandler<FilterInvocation> expressionHandler) {
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
this.expressionHandler = expressionHandler;
return this;
@@ -241,7 +231,7 @@ public final class WebSecurity extends
* @return the {@link SecurityExpressionHandler} for further customizations
*/
public SecurityExpressionHandler<FilterInvocation> getExpressionHandler() {
return expressionHandler;
return this.expressionHandler;
}
/**
@@ -249,11 +239,11 @@ public final class WebSecurity extends
* @return the {@link WebInvocationPrivilegeEvaluator} for further customizations
*/
public WebInvocationPrivilegeEvaluator getPrivilegeEvaluator() {
if (privilegeEvaluator != null) {
return privilegeEvaluator;
if (this.privilegeEvaluator != null) {
return this.privilegeEvaluator;
}
return filterSecurityInterceptor == null ? null
: new DefaultWebInvocationPrivilegeEvaluator(filterSecurityInterceptor);
return (this.filterSecurityInterceptor != null)
? new DefaultWebInvocationPrivilegeEvaluator(this.filterSecurityInterceptor) : null;
}
/**
@@ -269,7 +259,6 @@ public final class WebSecurity extends
/**
* Executes the Runnable immediately after the build takes place
*
* @param postBuildAction
* @return the {@link WebSecurity} for further customizations
*/
@@ -280,58 +269,80 @@ public final class WebSecurity extends
@Override
protected Filter performBuild() throws Exception {
Assert.state(
!securityFilterChainBuilders.isEmpty(),
Assert.state(!this.securityFilterChainBuilders.isEmpty(),
() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
+ "Typically this is done by exposing a SecurityFilterChain bean "
+ "or by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
+ "More advanced users can invoke "
+ WebSecurity.class.getSimpleName()
+ "More advanced users can invoke " + WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
chainSize);
for (RequestMatcher ignoredRequest : ignoredRequests) {
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
for (RequestMatcher ignoredRequest : this.ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (httpFirewall != null) {
filterChainProxy.setFirewall(httpFirewall);
if (this.httpFirewall != null) {
filterChainProxy.setFirewall(this.httpFirewall);
}
if (requestRejectedHandler != null) {
filterChainProxy.setRequestRejectedHandler(requestRejectedHandler);
if (this.requestRejectedHandler != null) {
filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
if (debugEnabled) {
logger.warn("\n\n"
+ "********************************************************************\n"
if (this.debugEnabled) {
this.logger.warn("\n\n" + "********************************************************************\n"
+ "********** Security debugging is enabled. *************\n"
+ "********** This may include sensitive information. *************\n"
+ "********** Do not use in a production system! *************\n"
+ "********************************************************************\n\n");
result = new DebugFilter(filterChainProxy);
}
postBuildAction.run();
this.postBuildAction.run();
return result;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.defaultWebSecurityExpressionHandler.setApplicationContext(applicationContext);
try {
this.defaultWebSecurityExpressionHandler.setRoleHierarchy(applicationContext.getBean(RoleHierarchy.class));
}
catch (NoSuchBeanDefinitionException ex) {
}
try {
this.defaultWebSecurityExpressionHandler
.setPermissionEvaluator(applicationContext.getBean(PermissionEvaluator.class));
}
catch (NoSuchBeanDefinitionException ex) {
}
this.ignoredRequestRegistry = new IgnoredRequestConfigurer(applicationContext);
try {
this.httpFirewall = applicationContext.getBean(HttpFirewall.class);
}
catch (NoSuchBeanDefinitionException ex) {
}
try {
this.requestRejectedHandler = applicationContext.getBean(RequestRejectedHandler.class);
}
catch (NoSuchBeanDefinitionException ex) {
}
}
/**
* An {@link IgnoredRequestConfigurer} that allows optionally configuring the
* {@link MvcRequestMatcher#setMethod(HttpMethod)}
*
* @author Rob Winch
*/
public final class MvcMatchersIgnoredRequestConfigurer
extends IgnoredRequestConfigurer {
public final class MvcMatchersIgnoredRequestConfigurer extends IgnoredRequestConfigurer {
private final List<MvcRequestMatcher> mvcMatchers;
private MvcMatchersIgnoredRequestConfigurer(ApplicationContext context,
List<MvcRequestMatcher> mvcMatchers) {
private MvcMatchersIgnoredRequestConfigurer(ApplicationContext context, List<MvcRequestMatcher> mvcMatchers) {
super(context);
this.mvcMatchers = mvcMatchers;
}
@@ -342,6 +353,7 @@ public final class WebSecurity extends
}
return this;
}
}
/**
@@ -351,20 +363,17 @@ public final class WebSecurity extends
* @author Rob Winch
* @since 3.2
*/
public class IgnoredRequestConfigurer
extends AbstractRequestMatcherRegistry<IgnoredRequestConfigurer> {
public class IgnoredRequestConfigurer extends AbstractRequestMatcherRegistry<IgnoredRequestConfigurer> {
private IgnoredRequestConfigurer(ApplicationContext context) {
IgnoredRequestConfigurer(ApplicationContext context) {
setApplicationContext(context);
}
@Override
public MvcMatchersIgnoredRequestConfigurer mvcMatchers(HttpMethod method,
String... mvcPatterns) {
public MvcMatchersIgnoredRequestConfigurer mvcMatchers(HttpMethod method, String... mvcPatterns) {
List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
WebSecurity.this.ignoredRequests.addAll(mvcMatchers);
return new MvcMatchersIgnoredRequestConfigurer(getApplicationContext(),
mvcMatchers);
return new MvcMatchersIgnoredRequestConfigurer(getApplicationContext(), mvcMatchers);
}
@Override
@@ -373,8 +382,7 @@ public final class WebSecurity extends
}
@Override
protected IgnoredRequestConfigurer chainRequestMatchers(
List<RequestMatcher> requestMatchers) {
protected IgnoredRequestConfigurer chainRequestMatchers(List<RequestMatcher> requestMatchers) {
WebSecurity.this.ignoredRequests.addAll(requestMatchers);
return this;
}
@@ -385,29 +393,7 @@ public final class WebSecurity extends
public WebSecurity and() {
return WebSecurity.this;
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.defaultWebSecurityExpressionHandler
.setApplicationContext(applicationContext);
try {
this.defaultWebSecurityExpressionHandler.setRoleHierarchy(applicationContext.getBean(RoleHierarchy.class));
} catch (NoSuchBeanDefinitionException e) {}
try {
this.defaultWebSecurityExpressionHandler.setPermissionEvaluator(applicationContext.getBean(
PermissionEvaluator.class));
} catch(NoSuchBeanDefinitionException e) {}
this.ignoredRequestRegistry = new IgnoredRequestConfigurer(applicationContext);
try {
this.httpFirewall = applicationContext.getBean(HttpFirewall.class);
} catch(NoSuchBeanDefinitionException e) {}
try {
this.requestRejectedHandler = applicationContext.getBean(RequestRejectedHandler.class);
} catch(NoSuchBeanDefinitionException e) {}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configuration;
import java.util.ArrayList;
@@ -34,14 +35,12 @@ import org.springframework.util.Assert;
* {@link ApplicationContext} but ignoring the parent.
*
* @author Rob Winch
*
*/
final class AutowiredWebSecurityConfigurersIgnoreParents {
public final class AutowiredWebSecurityConfigurersIgnoreParents {
private final ConfigurableListableBeanFactory beanFactory;
AutowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
AutowiredWebSecurityConfigurersIgnoreParents(ConfigurableListableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "beanFactory cannot be null");
this.beanFactory = beanFactory;
}
@@ -49,11 +48,11 @@ final class AutowiredWebSecurityConfigurersIgnoreParents {
@SuppressWarnings({ "rawtypes", "unchecked" })
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<>();
Map<String, WebSecurityConfigurer> beansOfType = beanFactory
.getBeansOfType(WebSecurityConfigurer.class);
Map<String, WebSecurityConfigurer> beansOfType = this.beanFactory.getBeansOfType(WebSecurityConfigurer.class);
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
}

View File

@@ -13,10 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configuration;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration;
@@ -69,13 +72,11 @@ import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
* @author Rob Winch
* @since 3.2
*/
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.class,
OAuth2ImportSelector.class,
HttpSecurityConfiguration.class})
@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,
HttpSecurityConfiguration.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
@@ -85,4 +86,5 @@ public @interface EnableWebSecurity {
* @return if true, enables debug support with Spring Security
*/
boolean debug() default false;
}

View File

@@ -16,6 +16,9 @@
package org.springframework.security.config.annotation.web.configuration;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
@@ -29,9 +32,6 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
import java.util.HashMap;
import java.util.Map;
import static org.springframework.security.config.Customizer.withDefaults;
/**
@@ -42,7 +42,9 @@ import static org.springframework.security.config.Customizer.withDefaults;
*/
@Configuration(proxyBeanMethods = false)
class HttpSecurityConfiguration {
private static final String BEAN_NAME_PREFIX = "org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.";
private static final String HTTPSECURITY_BEAN_NAME = BEAN_NAME_PREFIX + "httpSecurity";
private ObjectPostProcessor<Object> objectPostProcessor;
@@ -54,7 +56,7 @@ class HttpSecurityConfiguration {
private ApplicationContext context;
@Autowired
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
this.objectPostProcessor = objectPostProcessor;
}
@@ -64,54 +66,50 @@ class HttpSecurityConfiguration {
}
@Autowired
public void setAuthenticationConfiguration(
AuthenticationConfiguration authenticationConfiguration) {
void setAuthenticationConfiguration(AuthenticationConfiguration authenticationConfiguration) {
this.authenticationConfiguration = authenticationConfiguration;
}
@Autowired
public void setApplicationContext(ApplicationContext context) {
void setApplicationContext(ApplicationContext context) {
this.context = context;
}
@Bean(HTTPSECURITY_BEAN_NAME)
@Scope("prototype")
public HttpSecurity httpSecurity() throws Exception {
WebSecurityConfigurerAdapter.LazyPasswordEncoder passwordEncoder =
new WebSecurityConfigurerAdapter.LazyPasswordEncoder(this.context);
AuthenticationManagerBuilder authenticationBuilder =
new WebSecurityConfigurerAdapter.DefaultPasswordEncoderAuthenticationManagerBuilder(this.objectPostProcessor, passwordEncoder);
HttpSecurity httpSecurity() throws Exception {
WebSecurityConfigurerAdapter.LazyPasswordEncoder passwordEncoder = new WebSecurityConfigurerAdapter.LazyPasswordEncoder(
this.context);
AuthenticationManagerBuilder authenticationBuilder = new WebSecurityConfigurerAdapter.DefaultPasswordEncoderAuthenticationManagerBuilder(
this.objectPostProcessor, passwordEncoder);
authenticationBuilder.parentAuthenticationManager(authenticationManager());
HttpSecurity http = new HttpSecurity(objectPostProcessor, authenticationBuilder, createSharedObjects());
HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());
// @formatter:off
http
.csrf(withDefaults())
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling(withDefaults())
.headers(withDefaults())
.sessionManagement(withDefaults())
.securityContext(withDefaults())
.requestCache(withDefaults())
.anonymous(withDefaults())
.servletApi(withDefaults())
.logout(withDefaults())
.apply(new DefaultLoginPageConfigurer<>());
.csrf(withDefaults())
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling(withDefaults())
.headers(withDefaults())
.sessionManagement(withDefaults())
.securityContext(withDefaults())
.requestCache(withDefaults())
.anonymous(withDefaults())
.servletApi(withDefaults())
.logout(withDefaults())
.apply(new DefaultLoginPageConfigurer<>());
// @formatter:on
return http;
}
private AuthenticationManager authenticationManager() throws Exception {
if (this.authenticationManager != null) {
return this.authenticationManager;
} else {
return this.authenticationConfiguration.getAuthenticationManager();
}
return (this.authenticationManager != null) ? this.authenticationManager
: this.authenticationConfiguration.getAuthenticationManager();
}
private Map<Class<?>, Object> createSharedObjects() {
Map<Class<?>, Object> sharedObjects = new HashMap<>();
sharedObjects.put(ApplicationContext.class, context);
sharedObjects.put(ApplicationContext.class, this.context);
return sharedObjects;
}
}

View File

@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configuration;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@@ -33,8 +36,6 @@ import org.springframework.util.ClassUtils;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* {@link Configuration} for OAuth 2.0 Client support.
*
@@ -53,20 +54,25 @@ final class OAuth2ClientConfiguration {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
boolean webmvcPresent = ClassUtils.isPresent(
"org.springframework.web.servlet.DispatcherServlet", getClass().getClassLoader());
return webmvcPresent ?
new String[] { "org.springframework.security.config.annotation.web.configuration.OAuth2ClientConfiguration.OAuth2ClientWebMvcSecurityConfiguration" } :
new String[] {};
if (!ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet",
getClass().getClassLoader())) {
return new String[0];
}
return new String[] { "org.springframework.security.config.annotation.web.configuration."
+ "OAuth2ClientConfiguration.OAuth2ClientWebMvcSecurityConfiguration" };
}
}
@Configuration(proxyBeanMethods = false)
static class OAuth2ClientWebMvcSecurityConfiguration implements WebMvcConfigurer {
private ClientRegistrationRepository clientRegistrationRepository;
private OAuth2AuthorizedClientRepository authorizedClientRepository;
private OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient;
private OAuth2AuthorizedClientManager authorizedClientManager;
@Override
@@ -92,7 +98,8 @@ final class OAuth2ClientConfiguration {
}
@Autowired(required = false)
void setAccessTokenResponseClient(OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient) {
void setAccessTokenResponseClient(
OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient) {
this.accessTokenResponseClient = accessTokenResponseClient;
}
@@ -107,29 +114,31 @@ final class OAuth2ClientConfiguration {
if (this.authorizedClientManager != null) {
return this.authorizedClientManager;
}
OAuth2AuthorizedClientManager authorizedClientManager = null;
if (this.clientRegistrationRepository != null && this.authorizedClientRepository != null) {
if (this.accessTokenResponseClient != null) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.clientCredentials(configurer ->
configurer.accessTokenResponseClient(this.accessTokenResponseClient))
.password()
.build();
DefaultOAuth2AuthorizedClientManager defaultAuthorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
this.clientRegistrationRepository, this.authorizedClientRepository);
// @formatter:off
OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder
.builder()
.authorizationCode()
.refreshToken()
.clientCredentials((configurer) -> configurer.accessTokenResponseClient(this.accessTokenResponseClient))
.password()
.build();
// @formatter:on
DefaultOAuth2AuthorizedClientManager defaultAuthorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
this.clientRegistrationRepository, this.authorizedClientRepository);
defaultAuthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
authorizedClientManager = defaultAuthorizedClientManager;
} else {
}
else {
authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
this.clientRegistrationRepository, this.authorizedClientRepository);
}
}
return authorizedClientManager;
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configuration;
import java.util.LinkedHashSet;
@@ -21,15 +22,17 @@ import java.util.Set;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Used by {@link EnableWebSecurity} to conditionally import:
*
* <ul>
* <li>{@link OAuth2ClientConfiguration} when the {@code spring-security-oauth2-client} module is present on the classpath</li>
* <li>{@link SecurityReactorContextConfiguration} when either the {@code spring-security-oauth2-client} or
* {@code spring-security-oauth2-resource-server} module as well as the {@code spring-webflux} module
* are present on the classpath</li>
* <li>{@link OAuth2ClientConfiguration} when the {@code spring-security-oauth2-client}
* module is present on the classpath</li>
* <li>{@link SecurityReactorContextConfiguration} when either the
* {@code spring-security-oauth2-client} or {@code spring-security-oauth2-resource-server}
* module as well as the {@code spring-webflux} module are present on the classpath</li>
* </ul>
*
* @author Joe Grandja
@@ -43,25 +46,25 @@ final class OAuth2ImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
Set<String> imports = new LinkedHashSet<>();
boolean oauth2ClientPresent = ClassUtils.isPresent(
"org.springframework.security.oauth2.client.registration.ClientRegistration", getClass().getClassLoader());
ClassLoader classLoader = getClass().getClassLoader();
boolean oauth2ClientPresent = ClassUtils
.isPresent("org.springframework.security.oauth2.client.registration.ClientRegistration", classLoader);
boolean webfluxPresent = ClassUtils
.isPresent("org.springframework.web.reactive.function.client.ExchangeFilterFunction", classLoader);
boolean oauth2ResourceServerPresent = ClassUtils
.isPresent("org.springframework.security.oauth2.server.resource.BearerTokenError", classLoader);
if (oauth2ClientPresent) {
imports.add("org.springframework.security.config.annotation.web.configuration.OAuth2ClientConfiguration");
}
boolean webfluxPresent = ClassUtils.isPresent(
"org.springframework.web.reactive.function.client.ExchangeFilterFunction", getClass().getClassLoader());
if (webfluxPresent && oauth2ClientPresent) {
imports.add("org.springframework.security.config.annotation.web.configuration.SecurityReactorContextConfiguration");
imports.add(
"org.springframework.security.config.annotation.web.configuration.SecurityReactorContextConfiguration");
}
boolean oauth2ResourceServerPresent = ClassUtils.isPresent(
"org.springframework.security.oauth2.server.resource.BearerTokenError", getClass().getClassLoader());
if (webfluxPresent && oauth2ResourceServerPresent) {
imports.add("org.springframework.security.config.annotation.web.configuration.SecurityReactorContextConfiguration");
imports.add(
"org.springframework.security.config.annotation.web.configuration.SecurityReactorContextConfiguration");
}
return imports.toArray(new String[0]);
return StringUtils.toStringArray(imports);
}
}

View File

@@ -13,10 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configuration;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.core.publisher.Hooks;
import reactor.core.publisher.Operators;
import reactor.util.context.Context;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Bean;
@@ -26,28 +40,15 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import reactor.core.CoreSubscriber;
import reactor.core.publisher.Hooks;
import reactor.core.publisher.Operators;
import reactor.util.context.Context;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import static org.springframework.security.config.annotation.web.configuration.SecurityReactorContextConfiguration.SecurityReactorContextSubscriber.SECURITY_CONTEXT_ATTRIBUTES;
/**
* {@link Configuration} that (potentially) adds a "decorating" {@code Publisher}
* for the last operator created in every {@code Mono} or {@code Flux}.
* {@link Configuration} that (potentially) adds a "decorating" {@code Publisher} for the
* last operator created in every {@code Mono} or {@code Flux}.
*
* <p>
* The {@code Publisher} is solely responsible for adding
* the current {@code HttpServletRequest}, {@code HttpServletResponse} and {@code Authentication}
* to the Reactor {@code Context} so that it's accessible in every flow, if required.
* The {@code Publisher} is solely responsible for adding the current
* {@code HttpServletRequest}, {@code HttpServletResponse} and {@code Authentication} to
* the Reactor {@code Context} so that it's accessible in every flow, if required.
*
* @author Joe Grandja
* @author Roman Matiushchenko
@@ -63,14 +64,14 @@ class SecurityReactorContextConfiguration {
}
static class SecurityReactorContextSubscriberRegistrar implements InitializingBean, DisposableBean {
private static final String SECURITY_REACTOR_CONTEXT_OPERATOR_KEY = "org.springframework.security.SECURITY_REACTOR_CONTEXT_OPERATOR";
@Override
public void afterPropertiesSet() throws Exception {
Function<? super Publisher<Object>, ? extends Publisher<Object>> lifter =
Operators.liftPublisher((pub, sub) -> createSubscriberIfNecessary(sub));
Hooks.onLastOperator(SECURITY_REACTOR_CONTEXT_OPERATOR_KEY, pub -> {
Function<? super Publisher<Object>, ? extends Publisher<Object>> lifter = Operators
.liftPublisher((pub, sub) -> createSubscriberIfNecessary(sub));
Hooks.onLastOperator(SECURITY_REACTOR_CONTEXT_OPERATOR_KEY, (pub) -> {
if (!contextAttributesAvailable()) {
// No need to decorate so return original Publisher
return pub;
@@ -85,7 +86,7 @@ class SecurityReactorContextConfiguration {
}
<T> CoreSubscriber<T> createSubscriberIfNecessary(CoreSubscriber<T> delegate) {
if (delegate.currentContext().hasKey(SECURITY_CONTEXT_ATTRIBUTES)) {
if (delegate.currentContext().hasKey(SecurityReactorContextSubscriber.SECURITY_CONTEXT_ATTRIBUTES)) {
// Already enriched. No need to create Subscriber so return original
return delegate;
}
@@ -93,8 +94,8 @@ class SecurityReactorContextConfiguration {
}
private static boolean contextAttributesAvailable() {
return SecurityContextHolder.getContext().getAuthentication() != null ||
RequestContextHolder.getRequestAttributes() instanceof ServletRequestAttributes;
return SecurityContextHolder.getContext().getAuthentication() != null
|| RequestContextHolder.getRequestAttributes() instanceof ServletRequestAttributes;
}
private static Map<Object, Object> getContextAttributes() {
@@ -104,13 +105,12 @@ class SecurityReactorContextConfiguration {
if (requestAttributes instanceof ServletRequestAttributes) {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
servletRequest = servletRequestAttributes.getRequest();
servletResponse = servletRequestAttributes.getResponse(); // possible null
servletResponse = servletRequestAttributes.getResponse(); // possible null
}
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null && servletRequest == null) {
return Collections.emptyMap();
}
Map<Object, Object> contextAttributes = new HashMap<>();
if (servletRequest != null) {
contextAttributes.put(HttpServletRequest.class, servletRequest);
@@ -124,25 +124,30 @@ class SecurityReactorContextConfiguration {
return contextAttributes;
}
}
static class SecurityReactorContextSubscriber<T> implements CoreSubscriber<T> {
static final String SECURITY_CONTEXT_ATTRIBUTES = "org.springframework.security.SECURITY_CONTEXT_ATTRIBUTES";
private final CoreSubscriber<T> delegate;
private final Context context;
SecurityReactorContextSubscriber(CoreSubscriber<T> delegate, Map<Object, Object> attributes) {
this.delegate = delegate;
Context currentContext = this.delegate.currentContext();
Context context;
if (currentContext.hasKey(SECURITY_CONTEXT_ATTRIBUTES)) {
context = currentContext;
} else {
context = currentContext.put(SECURITY_CONTEXT_ATTRIBUTES, attributes);
}
Context context = getOrPutContext(attributes, this.delegate.currentContext());
this.context = context;
}
private Context getOrPutContext(Map<Object, Object> attributes, Context currentContext) {
if (currentContext.hasKey(SECURITY_CONTEXT_ATTRIBUTES)) {
return currentContext;
}
return currentContext.put(SECURITY_CONTEXT_ATTRIBUTES, attributes);
}
@Override
public Context currentContext() {
return this.context;
@@ -159,13 +164,15 @@ class SecurityReactorContextConfiguration {
}
@Override
public void onError(Throwable t) {
this.delegate.onError(t);
public void onError(Throwable ex) {
this.delegate.onError(ex);
}
@Override
public void onComplete() {
this.delegate.onComplete();
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configuration;
import org.springframework.context.annotation.ImportSelector;
@@ -29,19 +30,13 @@ import org.springframework.util.ClassUtils;
*/
class SpringWebMvcImportSelector implements ImportSelector {
/*
* (non-Javadoc)
*
* @see org.springframework.context.annotation.ImportSelector#selectImports(org.
* springframework .core.type.AnnotationMetadata)
*/
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
boolean webmvcPresent = ClassUtils.isPresent(
"org.springframework.web.servlet.DispatcherServlet",
getClass().getClassLoader());
return webmvcPresent
? new String[] {
"org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration" }
: new String[] {};
if (!ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", getClass().getClassLoader())) {
return new String[0];
}
return new String[] {
"org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration" };
}
}

View File

@@ -13,30 +13,33 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configuration;
import java.util.List;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.BeanResolver;
import org.springframework.security.web.method.annotation.CurrentSecurityContextArgumentResolver;
import org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver;
import org.springframework.security.web.method.annotation.CsrfTokenArgumentResolver;
import org.springframework.security.web.method.annotation.CurrentSecurityContextArgumentResolver;
import org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.support.RequestDataValueProcessor;
import java.util.List;
/**
* Used to add a {@link RequestDataValueProcessor} for Spring MVC and Spring Security CSRF
* integration. This configuration is added whenever {@link EnableWebMvc} is added by
* <a href="{@docRoot}/org/springframework/security/config/annotation/web/configuration/SpringWebMvcImportSelector.html">SpringWebMvcImportSelector</a> and the DispatcherServlet is present on the
* classpath. It also adds the {@link AuthenticationPrincipalArgumentResolver} as a
* <a href="
* {@docRoot}/org/springframework/security/config/annotation/web/configuration/SpringWebMvcImportSelector.html">SpringWebMvcImportSelector</a>
* and the DispatcherServlet is present on the classpath. It also adds the
* {@link AuthenticationPrincipalArgumentResolver} as a
* {@link HandlerMethodArgumentResolver}.
*
* @author Rob Winch
@@ -44,25 +47,25 @@ import java.util.List;
* @since 3.2
*/
class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContextAware {
private BeanResolver beanResolver;
@Override
@SuppressWarnings("deprecation")
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
AuthenticationPrincipalArgumentResolver authenticationPrincipalResolver = new AuthenticationPrincipalArgumentResolver();
authenticationPrincipalResolver.setBeanResolver(beanResolver);
authenticationPrincipalResolver.setBeanResolver(this.beanResolver);
argumentResolvers.add(authenticationPrincipalResolver);
argumentResolvers
.add(new org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver());
CurrentSecurityContextArgumentResolver currentSecurityContextArgumentResolver = new CurrentSecurityContextArgumentResolver();
currentSecurityContextArgumentResolver.setBeanResolver(beanResolver);
currentSecurityContextArgumentResolver.setBeanResolver(this.beanResolver);
argumentResolvers.add(currentSecurityContextArgumentResolver);
argumentResolvers.add(new CsrfTokenArgumentResolver());
}
@Bean
public RequestDataValueProcessor requestDataValueProcessor() {
RequestDataValueProcessor requestDataValueProcessor() {
return new CsrfRequestDataValueProcessor();
}
@@ -70,4 +73,5 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.beanResolver = new BeanFactoryResolver(applicationContext.getAutowireCapableBeanFactory());
}
}

View File

@@ -13,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configuration;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import org.springframework.beans.factory.BeanClassLoaderAware;
@@ -48,7 +50,7 @@ import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
import org.springframework.util.Assert;
/**
* Uses a {@link WebSecurity} to create the {@link FilterChainProxy} that performs the web
@@ -60,13 +62,13 @@ import org.springframework.security.web.context.AbstractSecurityWebApplicationIn
*
* @see EnableWebSecurity
* @see WebSecurity
*
* @author Rob Winch
* @author Keesun Baik
* @since 3.2
*/
@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
private WebSecurity webSecurity;
private Boolean debugEnabled;
@@ -88,7 +90,7 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
@Bean
@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {
return webSecurity.getExpressionHandler();
return this.webSecurity.getExpressionHandler();
}
/**
@@ -98,30 +100,26 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
*/
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
boolean hasFilterChain = !securityFilterChains.isEmpty();
if (hasConfigurers && hasFilterChain) {
throw new IllegalStateException(
"Found WebSecurityConfigurerAdapter as well as SecurityFilterChain." +
"Please select just one.");
}
boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
boolean hasFilterChain = !this.securityFilterChains.isEmpty();
Assert.state(!(hasConfigurers && hasFilterChain),
"Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.");
if (!hasConfigurers && !hasFilterChain) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
WebSecurityConfigurerAdapter adapter = this.objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
webSecurity.apply(adapter);
this.webSecurity.apply(adapter);
}
for (SecurityFilterChain securityFilterChain : securityFilterChains) {
webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
for (Filter filter : securityFilterChain.getFilters()) {
if (filter instanceof FilterSecurityInterceptor) {
webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
break;
}
}
}
return webSecurity.build();
return this.webSecurity.build();
}
/**
@@ -132,13 +130,12 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
@Bean
@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public WebInvocationPrivilegeEvaluator privilegeEvaluator() {
return webSecurity.getPrivilegeEvaluator();
return this.webSecurity.getPrivilegeEvaluator();
}
/**
* Sets the {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>}
* instances used to create the web configuration.
*
* @param objectPostProcessor the {@link ObjectPostProcessor} used to create a
* {@link WebSecurity} instance
* @param webSecurityConfigurers the
@@ -147,33 +144,27 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
* @throws Exception
*/
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
if (debugEnabled != null) {
webSecurity.debug(debugEnabled);
this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
if (this.debugEnabled != null) {
this.webSecurity.debug(this.debugEnabled);
}
webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);
Integer previousOrder = null;
Object previousConfig = null;
for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
if (previousOrder != null && previousOrder.equals(order)) {
throw new IllegalStateException(
"@Order on WebSecurityConfigurers must be unique. Order of "
+ order + " was already used on " + previousConfig + ", so it cannot be used on "
+ config + " too.");
throw new IllegalStateException("@Order on WebSecurityConfigurers must be unique. Order of " + order
+ " was already used on " + previousConfig + ", so it cannot be used on " + config + " too.");
}
previousOrder = order;
previousConfig = config;
}
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
this.webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
@@ -195,6 +186,22 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> enableWebSecurityAttrMap = importMetadata
.getAnnotationAttributes(EnableWebSecurity.class.getName());
AnnotationAttributes enableWebSecurityAttrs = AnnotationAttributes.fromMap(enableWebSecurityAttrMap);
this.debugEnabled = enableWebSecurityAttrs.getBoolean("debug");
if (this.webSecurity != null) {
this.webSecurity.debug(this.debugEnabled);
}
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
/**
* A custom verision of the Spring provided AnnotationAwareOrderComparator that uses
* {@link AnnotationUtils#findAnnotation(Class, Class)} to look on super class
@@ -204,6 +211,7 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
* @since 3.2
*/
private static class AnnotationAwareOrderComparator extends OrderComparator {
private static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();
@Override
@@ -216,7 +224,7 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
return ((Ordered) obj).getOrder();
}
if (obj != null) {
Class<?> clazz = (obj instanceof Class ? (Class<?>) obj : obj.getClass());
Class<?> clazz = ((obj instanceof Class) ? (Class<?>) obj : obj.getClass());
Order order = AnnotationUtils.findAnnotation(clazz, Order.class);
if (order != null) {
return order.value();
@@ -224,33 +232,7 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
}
return Ordered.LOWEST_PRECEDENCE;
}
}
/*
* (non-Javadoc)
*
* @see org.springframework.context.annotation.ImportAware#setImportMetadata(org.
* springframework.core.type.AnnotationMetadata)
*/
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> enableWebSecurityAttrMap = importMetadata
.getAnnotationAttributes(EnableWebSecurity.class.getName());
AnnotationAttributes enableWebSecurityAttrs = AnnotationAttributes
.fromMap(enableWebSecurityAttrMap);
debugEnabled = enableWebSecurityAttrs.getBoolean("debug");
if (webSecurity != null) {
webSecurity.debug(debugEnabled);
}
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.
* lang.ClassLoader)
*/
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
}

View File

@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configuration;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -69,31 +69,30 @@ import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
/**
* Provides a convenient base class for creating a {@link WebSecurityConfigurer}
* instance. The implementation allows customization by overriding methods.
* Provides a convenient base class for creating a {@link WebSecurityConfigurer} instance.
* The implementation allows customization by overriding methods.
*
* <p>
* Will automatically apply the result of looking up
* {@link AbstractHttpConfigurer} from {@link SpringFactoriesLoader} to allow
* developers to extend the defaults.
* To do this, you must create a class that extends AbstractHttpConfigurer and then create a file in the classpath at "META-INF/spring.factories" that looks something like:
* Will automatically apply the result of looking up {@link AbstractHttpConfigurer} from
* {@link SpringFactoriesLoader} to allow developers to extend the defaults. To do this,
* you must create a class that extends AbstractHttpConfigurer and then create a file in
* the classpath at "META-INF/spring.factories" that looks something like:
* </p>
* <pre>
* org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyClassThatExtendsAbstractHttpConfigurer
* </pre>
* If you have multiple classes that should be added you can use "," to separate the values. For example:
* </pre> If you have multiple classes that should be added you can use "," to separate
* the values. For example:
*
* <pre>
* org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyClassThatExtendsAbstractHttpConfigurer, sample.OtherThatExtendsAbstractHttpConfigurer
* </pre>
*
* @see EnableWebSecurity
*
* @author Rob Winch
*/
@Order(100)
public abstract class WebSecurityConfigurerAdapter implements
WebSecurityConfigurer<WebSecurity> {
public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {
private final Log logger = LogFactory.getLog(WebSecurityConfigurerAdapter.class);
private ApplicationContext context;
@@ -101,21 +100,29 @@ public abstract class WebSecurityConfigurerAdapter implements
private ContentNegotiationStrategy contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
private ObjectPostProcessor<Object> objectPostProcessor = new ObjectPostProcessor<Object>() {
@Override
public <T> T postProcess(T object) {
throw new IllegalStateException(
ObjectPostProcessor.class.getName()
+ " is a required bean. Ensure you have used @EnableWebSecurity and @Configuration");
throw new IllegalStateException(ObjectPostProcessor.class.getName()
+ " is a required bean. Ensure you have used @EnableWebSecurity and @Configuration");
}
};
private AuthenticationConfiguration authenticationConfiguration;
private AuthenticationManagerBuilder authenticationBuilder;
private AuthenticationManagerBuilder localConfigureAuthenticationBldr;
private boolean disableLocalConfigureAuthenticationBldr;
private boolean authenticationManagerInitialized;
private AuthenticationManager authenticationManager;
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
private HttpSecurity http;
private boolean disableDefaults;
/**
@@ -129,7 +136,6 @@ public abstract class WebSecurityConfigurerAdapter implements
* Creates an instance which allows specifying if the default configuration should be
* enabled. Disabling the default configuration should be considered more advanced
* usage as it requires more understanding of how the framework is implemented.
*
* @param disableDefaults true if the default configuration should be disabled, else
* false
*/
@@ -176,7 +182,6 @@ public abstract class WebSecurityConfigurerAdapter implements
* }
*
* </pre>
*
* @param auth the {@link AuthenticationManagerBuilder} to use
* @throws Exception
*/
@@ -186,50 +191,45 @@ public abstract class WebSecurityConfigurerAdapter implements
/**
* Creates the {@link HttpSecurity} or returns the current instance
*
* @return the {@link HttpSecurity}
* @throws Exception
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected final HttpSecurity getHttp() throws Exception {
if (http != null) {
return http;
if (this.http != null) {
return this.http;
}
AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
this.localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
this.authenticationBuilder.parentAuthenticationManager(authenticationManager);
Map<Class<?>, Object> sharedObjects = createSharedObjects();
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
if (!disableDefaults) {
// @formatter:off
http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<>()).and()
.logout();
// @formatter:on
this.http = new HttpSecurity(this.objectPostProcessor, this.authenticationBuilder, sharedObjects);
if (!this.disableDefaults) {
applyDefaultConfiguration(this.http);
ClassLoader classLoader = this.context.getClassLoader();
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader
.loadFactories(AbstractHttpConfigurer.class, classLoader);
for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
this.http.apply(configurer);
}
}
configure(http);
return http;
configure(this.http);
return this.http;
}
private void applyDefaultConfiguration(HttpSecurity http) throws Exception {
http.csrf();
http.addFilter(new WebAsyncManagerIntegrationFilter());
http.exceptionHandling();
http.headers();
http.sessionManagement();
http.securityContext();
http.requestCache();
http.anonymous();
http.servletApi();
http.apply(new DefaultLoginPageConfigurer<>());
http.logout();
}
/**
@@ -244,12 +244,11 @@ public abstract class WebSecurityConfigurerAdapter implements
* return super.authenticationManagerBean();
* }
* </pre>
*
* @return the {@link AuthenticationManager}
* @throws Exception
*/
public AuthenticationManager authenticationManagerBean() throws Exception {
return new AuthenticationManagerDelegator(authenticationBuilder, context);
return new AuthenticationManagerDelegator(this.authenticationBuilder, this.context);
}
/**
@@ -257,23 +256,21 @@ public abstract class WebSecurityConfigurerAdapter implements
* {@link #configure(AuthenticationManagerBuilder)} method is overridden to use the
* {@link AuthenticationManagerBuilder} that was passed in. Otherwise, autowire the
* {@link AuthenticationManager} by type.
*
* @return the {@link AuthenticationManager} to use
* @throws Exception
*/
protected AuthenticationManager authenticationManager() throws Exception {
if (!authenticationManagerInitialized) {
configure(localConfigureAuthenticationBldr);
if (disableLocalConfigureAuthenticationBldr) {
authenticationManager = authenticationConfiguration
.getAuthenticationManager();
if (!this.authenticationManagerInitialized) {
configure(this.localConfigureAuthenticationBldr);
if (this.disableLocalConfigureAuthenticationBldr) {
this.authenticationManager = this.authenticationConfiguration.getAuthenticationManager();
}
else {
authenticationManager = localConfigureAuthenticationBldr.build();
this.authenticationManager = this.localConfigureAuthenticationBldr.build();
}
authenticationManagerInitialized = true;
this.authenticationManagerInitialized = true;
}
return authenticationManager;
return this.authenticationManager;
}
/**
@@ -297,10 +294,8 @@ public abstract class WebSecurityConfigurerAdapter implements
* @see #userDetailsService()
*/
public UserDetailsService userDetailsServiceBean() throws Exception {
AuthenticationManagerBuilder globalAuthBuilder = context
.getBean(AuthenticationManagerBuilder.class);
return new UserDetailsServiceDelegator(Arrays.asList(
localConfigureAuthenticationBldr, globalAuthBuilder));
AuthenticationManagerBuilder globalAuthBuilder = this.context.getBean(AuthenticationManagerBuilder.class);
return new UserDetailsServiceDelegator(Arrays.asList(this.localConfigureAuthenticationBldr, globalAuthBuilder));
}
/**
@@ -308,21 +303,18 @@ public abstract class WebSecurityConfigurerAdapter implements
* {@link #userDetailsServiceBean()} without interacting with the
* {@link ApplicationContext}. Developers should override this method when changing
* the instance of {@link #userDetailsServiceBean()}.
*
* @return the {@link UserDetailsService} to use
*/
protected UserDetailsService userDetailsService() {
AuthenticationManagerBuilder globalAuthBuilder = context
.getBean(AuthenticationManagerBuilder.class);
return new UserDetailsServiceDelegator(Arrays.asList(
localConfigureAuthenticationBldr, globalAuthBuilder));
AuthenticationManagerBuilder globalAuthBuilder = this.context.getBean(AuthenticationManagerBuilder.class);
return new UserDetailsServiceDelegator(Arrays.asList(this.localConfigureAuthenticationBldr, globalAuthBuilder));
}
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
@Override
public void init(WebSecurity web) throws Exception {
HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
});
}
@@ -338,6 +330,7 @@ public abstract class WebSecurityConfigurerAdapter implements
* {@link #configure(HttpSecurity)} and the {@link HttpSecurity#authorizeRequests}
* configuration method.
*/
@Override
public void configure(WebSecurity web) throws Exception {
}
@@ -350,25 +343,19 @@ public abstract class WebSecurityConfigurerAdapter implements
* http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
* </pre>
*
* Any endpoint that requires defense against common vulnerabilities can be specified here, including public ones.
* See {@link HttpSecurity#authorizeRequests} and the `permitAll()` authorization rule
* for more details on public endpoints.
*
* Any endpoint that requires defense against common vulnerabilities can be specified
* here, including public ones. See {@link HttpSecurity#authorizeRequests} and the
* `permitAll()` authorization rule for more details on public endpoints.
* @param http the {@link HttpSecurity} to modify
* @throws Exception if an error occurs
*/
// @formatter:off
protected void configure(HttpSecurity http) throws Exception {
logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
this.logger.debug("Using default configure(HttpSecurity). "
+ "If subclassed this will potentially override subclass configure(HttpSecurity).");
http.authorizeRequests((requests) -> requests.anyRequest().authenticated());
http.formLogin();
http.httpBasic();
}
// @formatter:on
/**
* Gets the ApplicationContext
@@ -381,23 +368,26 @@ public abstract class WebSecurityConfigurerAdapter implements
@Autowired
public void setApplicationContext(ApplicationContext context) {
this.context = context;
ObjectPostProcessor<Object> objectPostProcessor = context.getBean(ObjectPostProcessor.class);
LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(context);
this.authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor,
passwordEncoder);
this.localConfigureAuthenticationBldr = new DefaultPasswordEncoderAuthenticationManagerBuilder(
objectPostProcessor, passwordEncoder) {
authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder);
localConfigureAuthenticationBldr = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder) {
@Override
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
authenticationBuilder.eraseCredentials(eraseCredentials);
WebSecurityConfigurerAdapter.this.authenticationBuilder.eraseCredentials(eraseCredentials);
return super.eraseCredentials(eraseCredentials);
}
@Override
public AuthenticationManagerBuilder authenticationEventPublisher(AuthenticationEventPublisher eventPublisher) {
authenticationBuilder.authenticationEventPublisher(eventPublisher);
public AuthenticationManagerBuilder authenticationEventPublisher(
AuthenticationEventPublisher eventPublisher) {
WebSecurityConfigurerAdapter.this.authenticationBuilder.authenticationEventPublisher(eventPublisher);
return super.authenticationEventPublisher(eventPublisher);
}
};
}
@@ -407,8 +397,7 @@ public abstract class WebSecurityConfigurerAdapter implements
}
@Autowired(required = false)
public void setContentNegotationStrategy(
ContentNegotiationStrategy contentNegotiationStrategy) {
public void setContentNegotationStrategy(ContentNegotiationStrategy contentNegotiationStrategy) {
this.contentNegotiationStrategy = contentNegotiationStrategy;
}
@@ -418,8 +407,7 @@ public abstract class WebSecurityConfigurerAdapter implements
}
@Autowired
public void setAuthenticationConfiguration(
AuthenticationConfiguration authenticationConfiguration) {
public void setAuthenticationConfiguration(AuthenticationConfiguration authenticationConfiguration) {
this.authenticationConfiguration = authenticationConfiguration;
}
@@ -432,16 +420,15 @@ public abstract class WebSecurityConfigurerAdapter implements
/**
* Creates the shared objects
*
* @return the shared Objects
*/
private Map<Class<?>, Object> createSharedObjects() {
Map<Class<?>, Object> sharedObjects = new HashMap<>();
sharedObjects.putAll(localConfigureAuthenticationBldr.getSharedObjects());
sharedObjects.putAll(this.localConfigureAuthenticationBldr.getSharedObjects());
sharedObjects.put(UserDetailsService.class, userDetailsService());
sharedObjects.put(ApplicationContext.class, context);
sharedObjects.put(ContentNegotiationStrategy.class, contentNegotiationStrategy);
sharedObjects.put(AuthenticationTrustResolver.class, trustResolver);
sharedObjects.put(ApplicationContext.class, this.context);
sharedObjects.put(ContentNegotiationStrategy.class, this.contentNegotiationStrategy);
sharedObjects.put(AuthenticationTrustResolver.class, this.trustResolver);
return sharedObjects;
}
@@ -453,43 +440,41 @@ public abstract class WebSecurityConfigurerAdapter implements
* @since 3.2
*/
static final class UserDetailsServiceDelegator implements UserDetailsService {
private List<AuthenticationManagerBuilder> delegateBuilders;
private UserDetailsService delegate;
private final Object delegateMonitor = new Object();
UserDetailsServiceDelegator(List<AuthenticationManagerBuilder> delegateBuilders) {
if (delegateBuilders.contains(null)) {
throw new IllegalArgumentException(
"delegateBuilders cannot contain null values. Got "
+ delegateBuilders);
}
Assert.isTrue(!delegateBuilders.contains(null),
() -> "delegateBuilders cannot contain null values. Got " + delegateBuilders);
this.delegateBuilders = delegateBuilders;
}
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
if (delegate != null) {
return delegate.loadUserByUsername(username);
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (this.delegate != null) {
return this.delegate.loadUserByUsername(username);
}
synchronized (delegateMonitor) {
if (delegate == null) {
for (AuthenticationManagerBuilder delegateBuilder : delegateBuilders) {
delegate = delegateBuilder.getDefaultUserDetailsService();
if (delegate != null) {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
for (AuthenticationManagerBuilder delegateBuilder : this.delegateBuilders) {
this.delegate = delegateBuilder.getDefaultUserDetailsService();
if (this.delegate != null) {
break;
}
}
if (delegate == null) {
if (this.delegate == null) {
throw new IllegalStateException("UserDetailsService is required.");
}
this.delegateBuilders = null;
}
}
return delegate.loadUserByUsername(username);
return this.delegate.loadUserByUsername(username);
}
}
/**
@@ -500,104 +485,100 @@ public abstract class WebSecurityConfigurerAdapter implements
* @since 3.2
*/
static final class AuthenticationManagerDelegator implements AuthenticationManager {
private AuthenticationManagerBuilder delegateBuilder;
private AuthenticationManager delegate;
private final Object delegateMonitor = new Object();
private Set<String> beanNames;
AuthenticationManagerDelegator(AuthenticationManagerBuilder delegateBuilder,
ApplicationContext context) {
AuthenticationManagerDelegator(AuthenticationManagerBuilder delegateBuilder, ApplicationContext context) {
Assert.notNull(delegateBuilder, "delegateBuilder cannot be null");
Field parentAuthMgrField = ReflectionUtils.findField(
AuthenticationManagerBuilder.class, "parentAuthenticationManager");
Field parentAuthMgrField = ReflectionUtils.findField(AuthenticationManagerBuilder.class,
"parentAuthenticationManager");
ReflectionUtils.makeAccessible(parentAuthMgrField);
beanNames = getAuthenticationManagerBeanNames(context);
validateBeanCycle(
ReflectionUtils.getField(parentAuthMgrField, delegateBuilder),
beanNames);
this.beanNames = getAuthenticationManagerBeanNames(context);
validateBeanCycle(ReflectionUtils.getField(parentAuthMgrField, delegateBuilder), this.beanNames);
this.delegateBuilder = delegateBuilder;
}
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
if (delegate != null) {
return delegate.authenticate(authentication);
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (this.delegate != null) {
return this.delegate.authenticate(authentication);
}
synchronized (delegateMonitor) {
if (delegate == null) {
delegate = this.delegateBuilder.getObject();
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
this.delegate = this.delegateBuilder.getObject();
this.delegateBuilder = null;
}
}
return delegate.authenticate(authentication);
return this.delegate.authenticate(authentication);
}
private static Set<String> getAuthenticationManagerBeanNames(
ApplicationContext applicationContext) {
String[] beanNamesForType = BeanFactoryUtils
.beanNamesForTypeIncludingAncestors(applicationContext,
AuthenticationManager.class);
private static Set<String> getAuthenticationManagerBeanNames(ApplicationContext applicationContext) {
String[] beanNamesForType = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext,
AuthenticationManager.class);
return new HashSet<>(Arrays.asList(beanNamesForType));
}
private static void validateBeanCycle(Object auth, Set<String> beanNames) {
if (auth != null && !beanNames.isEmpty()) {
if (auth instanceof Advised) {
Advised advised = (Advised) auth;
TargetSource targetSource = advised.getTargetSource();
if (targetSource instanceof LazyInitTargetSource) {
LazyInitTargetSource lits = (LazyInitTargetSource) targetSource;
if (beanNames.contains(lits.getTargetBeanName())) {
throw new FatalBeanException(
"A dependency cycle was detected when trying to resolve the AuthenticationManager. Please ensure you have configured authentication.");
}
}
}
beanNames = Collections.emptySet();
if (auth == null || beanNames.isEmpty() || !(auth instanceof Advised)) {
return;
}
TargetSource targetSource = ((Advised) auth).getTargetSource();
if (!(targetSource instanceof LazyInitTargetSource)) {
return;
}
LazyInitTargetSource lits = (LazyInitTargetSource) targetSource;
if (beanNames.contains(lits.getTargetBeanName())) {
throw new FatalBeanException(
"A dependency cycle was detected when trying to resolve the AuthenticationManager. "
+ "Please ensure you have configured authentication.");
}
}
}
static class DefaultPasswordEncoderAuthenticationManagerBuilder extends AuthenticationManagerBuilder {
private PasswordEncoder defaultPasswordEncoder;
/**
* Creates a new instance
*
* @param objectPostProcessor the {@link ObjectPostProcessor} instance to use.
*/
DefaultPasswordEncoderAuthenticationManagerBuilder(
ObjectPostProcessor<Object> objectPostProcessor, PasswordEncoder defaultPasswordEncoder) {
DefaultPasswordEncoderAuthenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,
PasswordEncoder defaultPasswordEncoder) {
super(objectPostProcessor);
this.defaultPasswordEncoder = defaultPasswordEncoder;
}
@Override
public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()
throws Exception {
return super.inMemoryAuthentication()
.passwordEncoder(this.defaultPasswordEncoder);
throws Exception {
return super.inMemoryAuthentication().passwordEncoder(this.defaultPasswordEncoder);
}
@Override
public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication()
throws Exception {
return super.jdbcAuthentication()
.passwordEncoder(this.defaultPasswordEncoder);
public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication() throws Exception {
return super.jdbcAuthentication().passwordEncoder(this.defaultPasswordEncoder);
}
@Override
public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService(
T userDetailsService) throws Exception {
return super.userDetailsService(userDetailsService)
.passwordEncoder(this.defaultPasswordEncoder);
T userDetailsService) throws Exception {
return super.userDetailsService(userDetailsService).passwordEncoder(this.defaultPasswordEncoder);
}
}
static class LazyPasswordEncoder implements PasswordEncoder {
private ApplicationContext applicationContext;
private PasswordEncoder passwordEncoder;
LazyPasswordEncoder(ApplicationContext applicationContext) {
@@ -610,8 +591,7 @@ public abstract class WebSecurityConfigurerAdapter implements
}
@Override
public boolean matches(CharSequence rawPassword,
String encodedPassword) {
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return getPasswordEncoder().matches(rawPassword, encodedPassword);
}
@@ -635,7 +615,8 @@ public abstract class WebSecurityConfigurerAdapter implements
private <T> T getBeanOrNull(Class<T> type) {
try {
return this.applicationContext.getBean(type);
} catch(NoSuchBeanDefinitionException notFound) {
}
catch (NoSuchBeanDefinitionException ex) {
return null;
}
}
@@ -644,5 +625,7 @@ public abstract class WebSecurityConfigurerAdapter implements
public String toString() {
return getPasswordEncoder().toString();
}
}
}

View File

@@ -13,8 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.Arrays;
import java.util.Collections;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
@@ -41,23 +47,17 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Collections;
/**
* Base class for configuring {@link AbstractAuthenticationFilterConfigurer}. This is
* intended for internal use only.
*
* @see FormLoginConfigurer
* @see OpenIDLoginConfigurer
*
* @param T refers to "this" for returning the current configurer
* @param F refers to the {@link AbstractAuthenticationProcessingFilter} that is being
* built
*
* @author Rob Winch
* @since 3.2
* @see FormLoginConfigurer
* @see OpenIDLoginConfigurer
*/
public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecurityBuilder<B>, T extends AbstractAuthenticationFilterConfigurer<B, T, F>, F extends AbstractAuthenticationProcessingFilter>
extends AbstractHttpConfigurer<T, B> {
@@ -67,12 +67,15 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
private SavedRequestAwareAuthenticationSuccessHandler defaultSuccessHandler = new SavedRequestAwareAuthenticationSuccessHandler();
private AuthenticationSuccessHandler successHandler = this.defaultSuccessHandler;
private LoginUrlAuthenticationEntryPoint authenticationEntryPoint;
private boolean customLoginPage;
private String loginPage;
private String loginProcessingUrl;
private AuthenticationFailureHandler failureHandler;
@@ -95,8 +98,7 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
* @param defaultLoginProcessingUrl the default URL to use for
* {@link #loginProcessingUrl(String)}
*/
protected AbstractAuthenticationFilterConfigurer(F authenticationFilter,
String defaultLoginProcessingUrl) {
protected AbstractAuthenticationFilterConfigurer(F authenticationFilter, String defaultLoginProcessingUrl) {
this();
this.authFilter = authenticationFilter;
if (defaultLoginProcessingUrl != null) {
@@ -105,10 +107,9 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
}
/**
* Specifies where users will be redirected after authenticating successfully if
* they have not visited a secured page prior to authenticating. This is a shortcut
* for calling {@link #defaultSuccessUrl(String, boolean)}.
*
* Specifies where users will be redirected after authenticating successfully if they
* have not visited a secured page prior to authenticating. This is a shortcut for
* calling {@link #defaultSuccessUrl(String, boolean)}.
* @param defaultSuccessUrl the default success url
* @return the {@link FormLoginConfigurer} for additional customization
*/
@@ -117,11 +118,10 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
}
/**
* Specifies where users will be redirected after authenticating successfully if
* they have not visited a secured page prior to authenticating or {@code alwaysUse}
* is true. This is a shortcut for calling
* Specifies where users will be redirected after authenticating successfully if they
* have not visited a secured page prior to authenticating or {@code alwaysUse} is
* true. This is a shortcut for calling
* {@link #successHandler(AuthenticationSuccessHandler)}.
*
* @param defaultSuccessUrl the default success url
* @param alwaysUse true if the {@code defaultSuccesUrl} should be used after
* authentication despite if a protected page had been previously visited
@@ -137,14 +137,12 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
/**
* Specifies the URL to validate the credentials.
*
* @param loginProcessingUrl the URL to validate username and password
* @return the {@link FormLoginConfigurer} for additional customization
*/
public T loginProcessingUrl(String loginProcessingUrl) {
this.loginProcessingUrl = loginProcessingUrl;
authFilter
.setRequiresAuthenticationRequestMatcher(createLoginProcessingUrlMatcher(loginProcessingUrl));
this.authFilter.setRequiresAuthenticationRequestMatcher(createLoginProcessingUrlMatcher(loginProcessingUrl));
return getSelf();
}
@@ -154,13 +152,11 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
* loginProcessingUrl
* @return the {@link RequestMatcher} to use based upon the loginProcessingUrl
*/
protected abstract RequestMatcher createLoginProcessingUrlMatcher(
String loginProcessingUrl);
protected abstract RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl);
/**
* Specifies a custom {@link AuthenticationDetailsSource}. The default is
* {@link WebAuthenticationDetailsSource}.
*
* @param authenticationDetailsSource the custom {@link AuthenticationDetailsSource}
* @return the {@link FormLoginConfigurer} for additional customization
*/
@@ -174,7 +170,6 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
* Specifies the {@link AuthenticationSuccessHandler} to be used. The default is
* {@link SavedRequestAwareAuthenticationSuccessHandler} with no additional properties
* set.
*
* @param successHandler the {@link AuthenticationSuccessHandler}.
* @return the {@link FormLoginConfigurer} for additional customization
*/
@@ -192,9 +187,9 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
}
/**
* Ensures the urls for {@link #failureUrl(String)} as well as for the {@link HttpSecurityBuilder}, the
* {@link #getLoginPage} and {@link #getLoginProcessingUrl} are granted access to any user.
*
* Ensures the urls for {@link #failureUrl(String)} as well as for the
* {@link HttpSecurityBuilder}, the {@link #getLoginPage} and
* {@link #getLoginProcessingUrl} are granted access to any user.
* @param permitAll true to grant access to the URLs false to skip this step
* @return the {@link FormLoginConfigurer} for additional customization
*/
@@ -207,14 +202,12 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
* The URL to send users if authentication fails. This is a shortcut for invoking
* {@link #failureHandler(AuthenticationFailureHandler)}. The default is
* "/login?error".
*
* @param authenticationFailureUrl the URL to send users if authentication fails (i.e.
* "/login?error").
* @return the {@link FormLoginConfigurer} for additional customization
*/
public final T failureUrl(String authenticationFailureUrl) {
T result = failureHandler(new SimpleUrlAuthenticationFailureHandler(
authenticationFailureUrl));
T result = failureHandler(new SimpleUrlAuthenticationFailureHandler(authenticationFailureUrl));
this.failureUrl = authenticationFailureUrl;
return result;
}
@@ -223,13 +216,11 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
* Specifies the {@link AuthenticationFailureHandler} to use when authentication
* fails. The default is redirecting to "/login?error" using
* {@link SimpleUrlAuthenticationFailureHandler}
*
* @param authenticationFailureHandler the {@link AuthenticationFailureHandler} to use
* when authentication fails.
* @return the {@link FormLoginConfigurer} for additional customization
*/
public final T failureHandler(
AuthenticationFailureHandler authenticationFailureHandler) {
public final T failureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
this.failureUrl = null;
this.failureHandler = authenticationFailureHandler;
return getSelf();
@@ -249,30 +240,25 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
@SuppressWarnings("unchecked")
protected final void registerAuthenticationEntryPoint(B http, AuthenticationEntryPoint authenticationEntryPoint) {
ExceptionHandlingConfigurer<B> exceptionHandling = http
.getConfigurer(ExceptionHandlingConfigurer.class);
ExceptionHandlingConfigurer<B> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);
if (exceptionHandling == null) {
return;
}
exceptionHandling.defaultAuthenticationEntryPointFor(
postProcess(authenticationEntryPoint), getAuthenticationEntryPointMatcher(http));
exceptionHandling.defaultAuthenticationEntryPointFor(postProcess(authenticationEntryPoint),
getAuthenticationEntryPointMatcher(http));
}
protected final RequestMatcher getAuthenticationEntryPointMatcher(B http) {
ContentNegotiationStrategy contentNegotiationStrategy = http
.getSharedObject(ContentNegotiationStrategy.class);
ContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);
if (contentNegotiationStrategy == null) {
contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
}
MediaTypeRequestMatcher mediaMatcher = new MediaTypeRequestMatcher(
contentNegotiationStrategy, MediaType.APPLICATION_XHTML_XML,
new MediaType("image", "*"), MediaType.TEXT_HTML, MediaType.TEXT_PLAIN);
MediaTypeRequestMatcher mediaMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,
MediaType.APPLICATION_XHTML_XML, new MediaType("image", "*"), MediaType.TEXT_HTML,
MediaType.TEXT_PLAIN);
mediaMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
return new AndRequestMatcher(Arrays.asList(notXRequestedWith, mediaMatcher));
}
@@ -280,32 +266,28 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
public void configure(B http) throws Exception {
PortMapper portMapper = http.getSharedObject(PortMapper.class);
if (portMapper != null) {
authenticationEntryPoint.setPortMapper(portMapper);
this.authenticationEntryPoint.setPortMapper(portMapper);
}
RequestCache requestCache = http.getSharedObject(RequestCache.class);
if (requestCache != null) {
this.defaultSuccessHandler.setRequestCache(requestCache);
}
authFilter.setAuthenticationManager(http
.getSharedObject(AuthenticationManager.class));
authFilter.setAuthenticationSuccessHandler(successHandler);
authFilter.setAuthenticationFailureHandler(failureHandler);
if (authenticationDetailsSource != null) {
authFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
this.authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
this.authFilter.setAuthenticationSuccessHandler(this.successHandler);
this.authFilter.setAuthenticationFailureHandler(this.failureHandler);
if (this.authenticationDetailsSource != null) {
this.authFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
}
SessionAuthenticationStrategy sessionAuthenticationStrategy = http
.getSharedObject(SessionAuthenticationStrategy.class);
if (sessionAuthenticationStrategy != null) {
authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
this.authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
}
RememberMeServices rememberMeServices = http
.getSharedObject(RememberMeServices.class);
RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
if (rememberMeServices != null) {
authFilter.setRememberMeServices(rememberMeServices);
this.authFilter.setRememberMeServices(rememberMeServices);
}
F filter = postProcess(authFilter);
F filter = postProcess(this.authFilter);
http.addFilter(filter);
}
@@ -330,25 +312,22 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
}
/**
*
* @return true if a custom login page has been specified, else false
*/
public final boolean isCustomLoginPage() {
return customLoginPage;
return this.customLoginPage;
}
/**
* Gets the Authentication Filter
*
* @return the Authentication Filter
*/
protected final F getAuthenticationFilter() {
return authFilter;
return this.authFilter;
}
/**
* Sets the Authentication Filter
*
* @param authFilter the Authentication Filter
*/
protected final void setAuthenticationFilter(F authFilter) {
@@ -357,58 +336,51 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
/**
* Gets the login page
*
* @return the login page
*/
protected final String getLoginPage() {
return loginPage;
return this.loginPage;
}
/**
* Gets the Authentication Entry Point
*
* @return the Authentication Entry Point
*/
protected final AuthenticationEntryPoint getAuthenticationEntryPoint() {
return authenticationEntryPoint;
return this.authenticationEntryPoint;
}
/**
* Gets the URL to submit an authentication request to (i.e. where username/password
* must be submitted)
*
* @return the URL to submit an authentication request to
*/
protected final String getLoginProcessingUrl() {
return loginProcessingUrl;
return this.loginProcessingUrl;
}
/**
* Gets the URL to send users to if authentication fails
*
* @return the URL to send users if authentication fails (e.g. "/login?error").
*/
protected final String getFailureUrl() {
return failureUrl;
return this.failureUrl;
}
/**
* Updates the default values for authentication.
*
* @throws Exception
*/
protected final void updateAuthenticationDefaults() {
if (loginProcessingUrl == null) {
loginProcessingUrl(loginPage);
if (this.loginProcessingUrl == null) {
loginProcessingUrl(this.loginPage);
}
if (failureHandler == null) {
failureUrl(loginPage + "?error");
if (this.failureHandler == null) {
failureUrl(this.loginPage + "?error");
}
final LogoutConfigurer<B> logoutConfigurer = getBuilder().getConfigurer(
LogoutConfigurer.class);
LogoutConfigurer<B> logoutConfigurer = getBuilder().getConfigurer(LogoutConfigurer.class);
if (logoutConfigurer != null && !logoutConfigurer.isCustomLogoutSuccess()) {
logoutConfigurer.logoutSuccessUrl(loginPage + "?logout");
logoutConfigurer.logoutSuccessUrl(this.loginPage + "?logout");
}
}
@@ -416,8 +388,8 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
* Updates the default values for access.
*/
protected final void updateAccessDefaults(B http) {
if (permitAll) {
PermitAllSupport.permitAll(http, loginPage, loginProcessingUrl, failureUrl);
if (this.permitAll) {
PermitAllSupport.permitAll(http, this.loginPage, this.loginProcessingUrl, this.failureUrl);
}
}
@@ -434,4 +406,5 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
private T getSelf() {
return (T) this;
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.ArrayList;
@@ -23,41 +24,39 @@ import java.util.List;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
/**
* A base class for registering {@link RequestMatcher}'s. For example, it might allow for
* specifying which {@link RequestMatcher} require a certain level of authorization.
*
* @param <C> The object that is returned or Chained after creating the RequestMatcher
* @author Rob Winch
* @since 3.2
*
* @param <C> The object that is returned or Chained after creating the RequestMatcher
*
* @see ChannelSecurityConfigurer
* @see UrlAuthorizationConfigurer
* @see ExpressionUrlAuthorizationConfigurer
*/
public abstract class AbstractConfigAttributeRequestMatcherRegistry<C> extends
AbstractRequestMatcherRegistry<C> {
public abstract class AbstractConfigAttributeRequestMatcherRegistry<C> extends AbstractRequestMatcherRegistry<C> {
private List<UrlMapping> urlMappings = new ArrayList<>();
private List<RequestMatcher> unmappedMatchers;
/**
* Gets the {@link UrlMapping} added by subclasses in
* {@link #chainRequestMatchers(java.util.List)}. May be empty.
*
* @return the {@link UrlMapping} added by subclasses in
* {@link #chainRequestMatchers(java.util.List)}
*/
final List<UrlMapping> getUrlMappings() {
return urlMappings;
return this.urlMappings;
}
/**
* Adds a {@link UrlMapping} added by subclasses in
* {@link #chainRequestMatchers(java.util.List)} and resets the unmapped
* {@link RequestMatcher}'s.
*
* @param urlMapping {@link UrlMapping} the mapping to add
*/
final void addMapping(UrlMapping urlMapping) {
@@ -68,11 +67,11 @@ public abstract class AbstractConfigAttributeRequestMatcherRegistry<C> extends
/**
* Marks the {@link RequestMatcher}'s as unmapped and then calls
* {@link #chainRequestMatchersInternal(List)}.
*
* @param requestMatchers the {@link RequestMatcher} instances that were created
* @return the chained Object for the subclass which allows association of something
* else to the {@link RequestMatcher}
*/
@Override
protected final C chainRequestMatchers(List<RequestMatcher> requestMatchers) {
this.unmappedMatchers = requestMatchers;
return chainRequestMatchersInternal(requestMatchers);
@@ -81,7 +80,6 @@ public abstract class AbstractConfigAttributeRequestMatcherRegistry<C> extends
/**
* Subclasses should implement this method for returning the object that is chained to
* the creation of the {@link RequestMatcher} instances.
*
* @param requestMatchers the {@link RequestMatcher} instances that were created
* @return the chained Object for the subclass which allows association of something
* else to the {@link RequestMatcher}
@@ -91,7 +89,6 @@ public abstract class AbstractConfigAttributeRequestMatcherRegistry<C> extends
/**
* Adds a {@link UrlMapping} added by subclasses in
* {@link #chainRequestMatchers(java.util.List)} at a particular index.
*
* @param index the index to add a {@link UrlMapping}
* @param urlMapping {@link UrlMapping} the mapping to add
*/
@@ -102,18 +99,12 @@ public abstract class AbstractConfigAttributeRequestMatcherRegistry<C> extends
/**
* Creates the mapping of {@link RequestMatcher} to {@link Collection} of
* {@link ConfigAttribute} instances
*
* @return the mapping of {@link RequestMatcher} to {@link Collection} of
* {@link ConfigAttribute} instances. Cannot be null.
*/
final LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> createRequestMap() {
if (unmappedMatchers != null) {
throw new IllegalStateException(
"An incomplete mapping was found for "
+ unmappedMatchers
+ ". Try completing it with something like requestUrls().<something>.hasRole('USER')");
}
Assert.state(this.unmappedMatchers == null, () -> "An incomplete mapping was found for " + this.unmappedMatchers
+ ". Try completing it with something like requestUrls().<something>.hasRole('USER')");
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<>();
for (UrlMapping mapping : getUrlMappings()) {
RequestMatcher matcher = mapping.getRequestMatcher();
@@ -128,20 +119,24 @@ public abstract class AbstractConfigAttributeRequestMatcherRegistry<C> extends
* {@link ConfigAttribute} instances
*/
static final class UrlMapping {
private RequestMatcher requestMatcher;
private Collection<ConfigAttribute> configAttrs;
private final RequestMatcher requestMatcher;
private final Collection<ConfigAttribute> configAttrs;
UrlMapping(RequestMatcher requestMatcher, Collection<ConfigAttribute> configAttrs) {
this.requestMatcher = requestMatcher;
this.configAttrs = configAttrs;
}
public RequestMatcher getRequestMatcher() {
return requestMatcher;
RequestMatcher getRequestMatcher() {
return this.requestMatcher;
}
public Collection<ConfigAttribute> getConfigAttrs() {
return configAttrs;
Collection<ConfigAttribute> getConfigAttrs() {
return this.configAttrs;
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import org.springframework.security.config.annotation.ObjectPostProcessor;
@@ -27,7 +28,6 @@ import org.springframework.security.web.DefaultSecurityFilterChain;
* {@link HttpSecurity}.
*
* @author Rob Winch
*
*/
public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>
extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {
@@ -35,7 +35,6 @@ public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T,
/**
* Disables the {@link AbstractHttpConfigurer} by removing it. After doing so a fresh
* version of the configuration can be applied.
*
* @return the {@link HttpSecurityBuilder} for additional customizations
*/
@SuppressWarnings("unchecked")
@@ -49,4 +48,5 @@ public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T,
addObjectPostProcessor(objectPostProcessor);
return (T) this;
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.List;
@@ -50,37 +51,36 @@ import org.springframework.security.web.access.intercept.FilterSecurityIntercept
* The following shared objects are used:
*
* <ul>
* <li>
* {@link AuthenticationManager}
* </li>
* <li>{@link AuthenticationManager}</li>
* </ul>
*
*
* @param <C> the AbstractInterceptUrlConfigurer
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
*
* @author Rob Winch
* @since 3.2
* @see ExpressionUrlAuthorizationConfigurer
* @see UrlAuthorizationConfigurer
*/
abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConfigurer<C, H>, H extends HttpSecurityBuilder<H>>
public abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConfigurer<C, H>, H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<C, H> {
private Boolean filterSecurityInterceptorOncePerRequest;
private AccessDecisionManager accessDecisionManager;
AbstractInterceptUrlConfigurer() {
}
@Override
public void configure(H http) throws Exception {
FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http);
if (metadataSource == null) {
return;
}
FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor(
http, metadataSource, http.getSharedObject(AuthenticationManager.class));
if (filterSecurityInterceptorOncePerRequest != null) {
securityInterceptor
.setObserveOncePerRequest(filterSecurityInterceptorOncePerRequest);
FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor(http, metadataSource,
http.getSharedObject(AuthenticationManager.class));
if (this.filterSecurityInterceptorOncePerRequest != null) {
securityInterceptor.setObserveOncePerRequest(this.filterSecurityInterceptorOncePerRequest);
}
securityInterceptor = postProcess(securityInterceptor);
http.addFilter(securityInterceptor);
@@ -91,9 +91,7 @@ abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConf
* Subclasses should implement this method to provide a
* {@link FilterInvocationSecurityMetadataSource} for the
* {@link FilterSecurityInterceptor}.
*
* @param http the builder to use
*
* @return the {@link FilterInvocationSecurityMetadataSource} to set on the
* {@link FilterSecurityInterceptor}. Cannot be null.
*/
@@ -102,55 +100,12 @@ abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConf
/**
* Subclasses should implement this method to provide the {@link AccessDecisionVoter}
* instances used to create the default {@link AccessDecisionManager}
*
* @param http the builder to use
*
* @return the {@link AccessDecisionVoter} instances used to create the default
* {@link AccessDecisionManager}
*/
abstract List<AccessDecisionVoter<?>> getDecisionVoters(H http);
abstract class AbstractInterceptUrlRegistry<R extends AbstractInterceptUrlRegistry<R, T>, T>
extends AbstractConfigAttributeRequestMatcherRegistry<T> {
/**
* Allows setting the {@link AccessDecisionManager}. If none is provided, a
* default {@link AccessDecisionManager} is created.
*
* @param accessDecisionManager the {@link AccessDecisionManager} to use
* @return the {@link AbstractInterceptUrlConfigurer} for further customization
*/
public R accessDecisionManager(AccessDecisionManager accessDecisionManager) {
AbstractInterceptUrlConfigurer.this.accessDecisionManager = accessDecisionManager;
return getSelf();
}
/**
* Allows setting if the {@link FilterSecurityInterceptor} should be only applied
* once per request (i.e. if the filter intercepts on a forward, should it be
* applied again).
*
* @param filterSecurityInterceptorOncePerRequest if the
* {@link FilterSecurityInterceptor} should be only applied once per request
* @return the {@link AbstractInterceptUrlConfigurer} for further customization
*/
public R filterSecurityInterceptorOncePerRequest(
boolean filterSecurityInterceptorOncePerRequest) {
AbstractInterceptUrlConfigurer.this.filterSecurityInterceptorOncePerRequest = filterSecurityInterceptorOncePerRequest;
return getSelf();
}
/**
* Returns a reference to the current object with a single suppression of the type
*
* @return a reference to the current object
*/
@SuppressWarnings("unchecked")
private R getSelf() {
return (R) this;
}
}
/**
* Creates the default {@code AccessDecisionManager}
* @return the default {@code AccessDecisionManager}
@@ -162,23 +117,20 @@ abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConf
/**
* If currently null, creates a default {@link AccessDecisionManager} using
* {@link #createDefaultAccessDecisionManager(HttpSecurityBuilder)}. Otherwise returns the
* {@link AccessDecisionManager}.
*
* {@link #createDefaultAccessDecisionManager(HttpSecurityBuilder)}. Otherwise returns
* the {@link AccessDecisionManager}.
* @param http the builder to use
*
* @return the {@link AccessDecisionManager} to use
*/
private AccessDecisionManager getAccessDecisionManager(H http) {
if (accessDecisionManager == null) {
accessDecisionManager = createDefaultAccessDecisionManager(http);
if (this.accessDecisionManager == null) {
this.accessDecisionManager = createDefaultAccessDecisionManager(http);
}
return accessDecisionManager;
return this.accessDecisionManager;
}
/**
* Creates the {@link FilterSecurityInterceptor}
*
* @param http the builder to use
* @param metadataSource the {@link FilterInvocationSecurityMetadataSource} to use
* @param authenticationManager the {@link AuthenticationManager} to use
@@ -186,8 +138,8 @@ abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConf
* @throws Exception
*/
private FilterSecurityInterceptor createFilterSecurityInterceptor(H http,
FilterInvocationSecurityMetadataSource metadataSource,
AuthenticationManager authenticationManager) throws Exception {
FilterInvocationSecurityMetadataSource metadataSource, AuthenticationManager authenticationManager)
throws Exception {
FilterSecurityInterceptor securityInterceptor = new FilterSecurityInterceptor();
securityInterceptor.setSecurityMetadataSource(metadataSource);
securityInterceptor.setAccessDecisionManager(getAccessDecisionManager(http));
@@ -195,4 +147,46 @@ abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConf
securityInterceptor.afterPropertiesSet();
return securityInterceptor;
}
public abstract class AbstractInterceptUrlRegistry<R extends AbstractInterceptUrlRegistry<R, T>, T>
extends AbstractConfigAttributeRequestMatcherRegistry<T> {
AbstractInterceptUrlRegistry() {
}
/**
* Allows setting the {@link AccessDecisionManager}. If none is provided, a
* default {@link AccessDecisionManager} is created.
* @param accessDecisionManager the {@link AccessDecisionManager} to use
* @return the {@link AbstractInterceptUrlConfigurer} for further customization
*/
public R accessDecisionManager(AccessDecisionManager accessDecisionManager) {
AbstractInterceptUrlConfigurer.this.accessDecisionManager = accessDecisionManager;
return getSelf();
}
/**
* Allows setting if the {@link FilterSecurityInterceptor} should be only applied
* once per request (i.e. if the filter intercepts on a forward, should it be
* applied again).
* @param filterSecurityInterceptorOncePerRequest if the
* {@link FilterSecurityInterceptor} should be only applied once per request
* @return the {@link AbstractInterceptUrlConfigurer} for further customization
*/
public R filterSecurityInterceptorOncePerRequest(boolean filterSecurityInterceptorOncePerRequest) {
AbstractInterceptUrlConfigurer.this.filterSecurityInterceptorOncePerRequest = filterSecurityInterceptorOncePerRequest;
return getSelf();
}
/**
* Returns a reference to the current object with a single suppression of the type
* @return a reference to the current object
*/
@SuppressWarnings("unchecked")
private R getSelf() {
return (R) this;
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.List;
@@ -39,14 +40,18 @@ import org.springframework.security.web.authentication.AnonymousAuthenticationFi
* @author Rob Winch
* @since 3.2
*/
public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<AnonymousConfigurer<H>, H> {
public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<AnonymousConfigurer<H>, H> {
private String key;
private AuthenticationProvider authenticationProvider;
private AnonymousAuthenticationFilter authenticationFilter;
private Object principal = "anonymousUser";
private List<GrantedAuthority> authorities = AuthorityUtils
.createAuthorityList("ROLE_ANONYMOUS");
private List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS");
/**
* Creates a new instance
@@ -58,7 +63,6 @@ public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>> extends
/**
* Sets the key to identify tokens created for anonymous authentication. Default is a
* secure randomly generated key.
*
* @param key the key to identify tokens created for anonymous authentication. Default
* is a secure randomly generated key.
* @return the {@link AnonymousConfigurer} for further customization of anonymous
@@ -71,7 +75,6 @@ public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>> extends
/**
* Sets the principal for {@link Authentication} objects of anonymous users
*
* @param principal used for the {@link Authentication} object of anonymous users
* @return the {@link AnonymousConfigurer} for further customization of anonymous
* authentication
@@ -84,7 +87,6 @@ public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>> extends
/**
* Sets the {@link org.springframework.security.core.Authentication#getAuthorities()}
* for anonymous users
*
* @param authorities Sets the
* {@link org.springframework.security.core.Authentication#getAuthorities()} for
* anonymous users
@@ -99,7 +101,6 @@ public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>> extends
/**
* Sets the {@link org.springframework.security.core.Authentication#getAuthorities()}
* for anonymous users
*
* @param authorities Sets the
* {@link org.springframework.security.core.Authentication#getAuthorities()} for
* anonymous users (i.e. "ROLE_ANONYMOUS")
@@ -114,15 +115,12 @@ public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>> extends
* Sets the {@link AuthenticationProvider} used to validate an anonymous user. If this
* is set, no attributes on the {@link AnonymousConfigurer} will be set on the
* {@link AuthenticationProvider}.
*
* @param authenticationProvider the {@link AuthenticationProvider} used to validate
* an anonymous user. Default is {@link AnonymousAuthenticationProvider}
*
* @return the {@link AnonymousConfigurer} for further customization of anonymous
* authentication
*/
public AnonymousConfigurer<H> authenticationProvider(
AuthenticationProvider authenticationProvider) {
public AnonymousConfigurer<H> authenticationProvider(AuthenticationProvider authenticationProvider) {
this.authenticationProvider = authenticationProvider;
return this;
}
@@ -131,42 +129,39 @@ public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>> extends
* Sets the {@link AnonymousAuthenticationFilter} used to populate an anonymous user.
* If this is set, no attributes on the {@link AnonymousConfigurer} will be set on the
* {@link AnonymousAuthenticationFilter}.
*
* @param authenticationFilter the {@link AnonymousAuthenticationFilter} used to
* populate an anonymous user.
*
* @return the {@link AnonymousConfigurer} for further customization of anonymous
* authentication
*/
public AnonymousConfigurer<H> authenticationFilter(
AnonymousAuthenticationFilter authenticationFilter) {
public AnonymousConfigurer<H> authenticationFilter(AnonymousAuthenticationFilter authenticationFilter) {
this.authenticationFilter = authenticationFilter;
return this;
}
@Override
public void init(H http) {
if (authenticationProvider == null) {
authenticationProvider = new AnonymousAuthenticationProvider(getKey());
if (this.authenticationProvider == null) {
this.authenticationProvider = new AnonymousAuthenticationProvider(getKey());
}
if (authenticationFilter == null) {
authenticationFilter = new AnonymousAuthenticationFilter(getKey(), principal,
authorities);
if (this.authenticationFilter == null) {
this.authenticationFilter = new AnonymousAuthenticationFilter(getKey(), this.principal, this.authorities);
}
authenticationProvider = postProcess(authenticationProvider);
http.authenticationProvider(authenticationProvider);
this.authenticationProvider = postProcess(this.authenticationProvider);
http.authenticationProvider(this.authenticationProvider);
}
@Override
public void configure(H http) {
authenticationFilter.afterPropertiesSet();
http.addFilter(authenticationFilter);
this.authenticationFilter.afterPropertiesSet();
http.addFilter(this.authenticationFilter);
}
private String getKey() {
if (key == null) {
key = UUID.randomUUID().toString();
if (this.key == null) {
this.key = UUID.randomUUID().toString();
}
return key;
return this.key;
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.Arrays;
@@ -73,14 +74,16 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
* </ul>
*
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
*
* @author Rob Winch
* @since 3.2
*/
public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<ChannelSecurityConfigurer<H>, H> {
public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<ChannelSecurityConfigurer<H>, H> {
private ChannelProcessingFilter channelFilter = new ChannelProcessingFilter();
private LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<>();
private List<ChannelProcessor> channelProcessors;
private final ChannelRequestMatcherRegistry REGISTRY;
@@ -94,7 +97,7 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> e
}
public ChannelRequestMatcherRegistry getRegistry() {
return REGISTRY;
return this.REGISTRY;
}
@Override
@@ -102,49 +105,40 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> e
ChannelDecisionManagerImpl channelDecisionManager = new ChannelDecisionManagerImpl();
channelDecisionManager.setChannelProcessors(getChannelProcessors(http));
channelDecisionManager = postProcess(channelDecisionManager);
channelFilter.setChannelDecisionManager(channelDecisionManager);
this.channelFilter.setChannelDecisionManager(channelDecisionManager);
DefaultFilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource = new DefaultFilterInvocationSecurityMetadataSource(
requestMap);
channelFilter.setSecurityMetadataSource(filterInvocationSecurityMetadataSource);
channelFilter = postProcess(channelFilter);
http.addFilter(channelFilter);
this.requestMap);
this.channelFilter.setSecurityMetadataSource(filterInvocationSecurityMetadataSource);
this.channelFilter = postProcess(this.channelFilter);
http.addFilter(this.channelFilter);
}
private List<ChannelProcessor> getChannelProcessors(H http) {
if (channelProcessors != null) {
return channelProcessors;
if (this.channelProcessors != null) {
return this.channelProcessors;
}
InsecureChannelProcessor insecureChannelProcessor = new InsecureChannelProcessor();
SecureChannelProcessor secureChannelProcessor = new SecureChannelProcessor();
PortMapper portMapper = http.getSharedObject(PortMapper.class);
if (portMapper != null) {
RetryWithHttpEntryPoint httpEntryPoint = new RetryWithHttpEntryPoint();
httpEntryPoint.setPortMapper(portMapper);
insecureChannelProcessor.setEntryPoint(httpEntryPoint);
RetryWithHttpsEntryPoint httpsEntryPoint = new RetryWithHttpsEntryPoint();
httpsEntryPoint.setPortMapper(portMapper);
secureChannelProcessor.setEntryPoint(httpsEntryPoint);
}
insecureChannelProcessor = postProcess(insecureChannelProcessor);
secureChannelProcessor = postProcess(secureChannelProcessor);
return Arrays.<ChannelProcessor> asList(insecureChannelProcessor,
secureChannelProcessor);
return Arrays.asList(insecureChannelProcessor, secureChannelProcessor);
}
private ChannelRequestMatcherRegistry addAttribute(String attribute,
List<? extends RequestMatcher> matchers) {
private ChannelRequestMatcherRegistry addAttribute(String attribute, List<? extends RequestMatcher> matchers) {
for (RequestMatcher matcher : matchers) {
Collection<ConfigAttribute> attrs = Arrays
.<ConfigAttribute> asList(new SecurityConfig(attribute));
requestMap.put(matcher, attrs);
Collection<ConfigAttribute> attrs = Arrays.asList(new SecurityConfig(attribute));
this.requestMap.put(matcher, attrs);
}
return REGISTRY;
return this.REGISTRY;
}
public final class ChannelRequestMatcherRegistry
@@ -155,8 +149,7 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> e
}
@Override
public MvcMatchersRequiresChannelUrl mvcMatchers(HttpMethod method,
String... mvcPatterns) {
public MvcMatchersRequiresChannelUrl mvcMatchers(HttpMethod method, String... mvcPatterns) {
List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
return new MvcMatchersRequiresChannelUrl(mvcMatchers);
}
@@ -167,19 +160,16 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> e
}
@Override
protected RequiresChannelUrl chainRequestMatchersInternal(
List<RequestMatcher> requestMatchers) {
protected RequiresChannelUrl chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
return new RequiresChannelUrl(requestMatchers);
}
/**
* Adds an {@link ObjectPostProcessor} for this class.
*
* @param objectPostProcessor
* @return the {@link ChannelSecurityConfigurer} for further customizations
*/
public ChannelRequestMatcherRegistry withObjectPostProcessor(
ObjectPostProcessor<?> objectPostProcessor) {
public ChannelRequestMatcherRegistry withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return this;
}
@@ -190,8 +180,7 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> e
* @param channelProcessors
* @return the {@link ChannelSecurityConfigurer} for further customizations
*/
public ChannelRequestMatcherRegistry channelProcessors(
List<ChannelProcessor> channelProcessors) {
public ChannelRequestMatcherRegistry channelProcessors(List<ChannelProcessor> channelProcessors) {
ChannelSecurityConfigurer.this.channelProcessors = channelProcessors;
return this;
}
@@ -199,12 +188,12 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> e
/**
* Return the {@link SecurityBuilder} when done using the
* {@link SecurityConfigurer}. This is useful for method chaining.
*
* @return the type of {@link HttpSecurityBuilder} that is being configured
*/
public H and() {
return ChannelSecurityConfigurer.this.and();
}
}
public final class MvcMatchersRequiresChannelUrl extends RequiresChannelUrl {
@@ -219,12 +208,14 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> e
}
return this;
}
}
public class RequiresChannelUrl {
protected List<? extends RequestMatcher> requestMatchers;
private RequiresChannelUrl(List<? extends RequestMatcher> requestMatchers) {
RequiresChannelUrl(List<? extends RequestMatcher> requestMatchers) {
this.requestMatchers = requestMatchers;
}
@@ -237,7 +228,9 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> e
}
public ChannelRequestMatcherRegistry requires(String attribute) {
return addAttribute(attribute, requestMatchers);
return addAttribute(attribute, this.requestMatchers);
}
}
}

View File

@@ -13,12 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
@@ -36,11 +38,12 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
* @author Rob Winch
* @since 4.1.1
*/
public class CorsConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<CorsConfigurer<H>, H> {
public class CorsConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<CorsConfigurer<H>, H> {
private static final String HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector";
private static final String CORS_CONFIGURATION_SOURCE_BEAN_NAME = "corsConfigurationSource";
private static final String CORS_FILTER_BEAN_NAME = "corsFilter";
private CorsConfigurationSource configurationSource;
@@ -53,8 +56,7 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>>
public CorsConfigurer() {
}
public CorsConfigurer<H> configurationSource(
CorsConfigurationSource configurationSource) {
public CorsConfigurer<H> configurationSource(CorsConfigurationSource configurationSource) {
this.configurationSource = configurationSource;
return this;
}
@@ -62,13 +64,9 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>>
@Override
public void configure(H http) {
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
CorsFilter corsFilter = getCorsFilter(context);
if (corsFilter == null) {
throw new IllegalStateException(
"Please configure either a " + CORS_FILTER_BEAN_NAME + " bean or a "
+ CORS_CONFIGURATION_SOURCE_BEAN_NAME + "bean.");
}
Assert.state(corsFilter != null, () -> "Please configure either a " + CORS_FILTER_BEAN_NAME + " bean or a "
+ CORS_CONFIGURATION_SOURCE_BEAN_NAME + "bean.");
http.addFilter(corsFilter);
}
@@ -76,32 +74,27 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>>
if (this.configurationSource != null) {
return new CorsFilter(this.configurationSource);
}
boolean containsCorsFilter = context
.containsBeanDefinition(CORS_FILTER_BEAN_NAME);
boolean containsCorsFilter = context.containsBeanDefinition(CORS_FILTER_BEAN_NAME);
if (containsCorsFilter) {
return context.getBean(CORS_FILTER_BEAN_NAME, CorsFilter.class);
}
boolean containsCorsSource = context
.containsBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME);
boolean containsCorsSource = context.containsBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME);
if (containsCorsSource) {
CorsConfigurationSource configurationSource = context.getBean(
CORS_CONFIGURATION_SOURCE_BEAN_NAME, CorsConfigurationSource.class);
CorsConfigurationSource configurationSource = context.getBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME,
CorsConfigurationSource.class);
return new CorsFilter(configurationSource);
}
boolean mvcPresent = ClassUtils.isPresent(HANDLER_MAPPING_INTROSPECTOR,
context.getClassLoader());
boolean mvcPresent = ClassUtils.isPresent(HANDLER_MAPPING_INTROSPECTOR, context.getClassLoader());
if (mvcPresent) {
return MvcCorsFilter.getMvcCorsFilter(context);
}
return null;
}
static class MvcCorsFilter {
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
/**
* This needs to be isolated into a separate class as Spring MVC is an optional
* dependency and will potentially cause ClassLoading issues
@@ -110,11 +103,16 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>>
*/
private static CorsFilter getMvcCorsFilter(ApplicationContext context) {
if (!context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
throw new NoSuchBeanDefinitionException(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, "A Bean named " + HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME +" of type " + HandlerMappingIntrospector.class.getName()
throw new NoSuchBeanDefinitionException(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, "A Bean named "
+ HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + " of type "
+ HandlerMappingIntrospector.class.getName()
+ " is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.");
}
HandlerMappingIntrospector mappingIntrospector = context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, HandlerMappingIntrospector.class);
HandlerMappingIntrospector mappingIntrospector = context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME,
HandlerMappingIntrospector.class);
return new CorsFilter(mappingIntrospector);
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.ArrayList;
@@ -74,15 +75,20 @@ import org.springframework.util.Assert;
* </ul>
*
* @author Rob Winch
* @author Michael Vitz
* @since 3.2
*/
public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<CsrfConfigurer<H>, H> {
private CsrfTokenRepository csrfTokenRepository = new LazyCsrfTokenRepository(
new HttpSessionCsrfTokenRepository());
private CsrfTokenRepository csrfTokenRepository = new LazyCsrfTokenRepository(new HttpSessionCsrfTokenRepository());
private RequestMatcher requireCsrfProtectionMatcher = CsrfFilter.DEFAULT_CSRF_MATCHER;
private List<RequestMatcher> ignoredCsrfProtectionMatchers = new ArrayList<>();
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
private final ApplicationContext context;
/**
@@ -96,12 +102,10 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Specify the {@link CsrfTokenRepository} to use. The default is an
* {@link HttpSessionCsrfTokenRepository} wrapped by {@link LazyCsrfTokenRepository}.
*
* @param csrfTokenRepository the {@link CsrfTokenRepository} to use
* @return the {@link CsrfConfigurer} for further customizations
*/
public CsrfConfigurer<H> csrfTokenRepository(
CsrfTokenRepository csrfTokenRepository) {
public CsrfConfigurer<H> csrfTokenRepository(CsrfTokenRepository csrfTokenRepository) {
Assert.notNull(csrfTokenRepository, "csrfTokenRepository cannot be null");
this.csrfTokenRepository = csrfTokenRepository;
return this;
@@ -111,14 +115,11 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
* Specify the {@link RequestMatcher} to use for determining when CSRF should be
* applied. The default is to ignore GET, HEAD, TRACE, OPTIONS and process all other
* requests.
*
* @param requireCsrfProtectionMatcher the {@link RequestMatcher} to use
* @return the {@link CsrfConfigurer} for further customizations
*/
public CsrfConfigurer<H> requireCsrfProtectionMatcher(
RequestMatcher requireCsrfProtectionMatcher) {
Assert.notNull(requireCsrfProtectionMatcher,
"requireCsrfProtectionMatcher cannot be null");
public CsrfConfigurer<H> requireCsrfProtectionMatcher(RequestMatcher requireCsrfProtectionMatcher) {
Assert.notNull(requireCsrfProtectionMatcher, "requireCsrfProtectionMatcher cannot be null");
this.requireCsrfProtectionMatcher = requireCsrfProtectionMatcher;
return this;
}
@@ -148,8 +149,7 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
* @since 4.0
*/
public CsrfConfigurer<H> ignoringAntMatchers(String... antPatterns) {
return new IgnoreCsrfProtectionRegistry(this.context).antMatchers(antPatterns)
.and();
return new IgnoreCsrfProtectionRegistry(this.context).antMatchers(antPatterns).and();
}
/**
@@ -163,13 +163,14 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
* </p>
* <ul>
* <li>Any GET, HEAD, TRACE, OPTIONS (this is the default)</li>
* <li>We also explicitly state to ignore any request that has a "X-Requested-With: XMLHttpRequest" header</li>
* <li>We also explicitly state to ignore any request that has a "X-Requested-With:
* XMLHttpRequest" header</li>
* </ul>
*
* <pre>
* http
* .csrf()
* .ignoringRequestMatchers(request -> "XMLHttpRequest".equals(request.getHeader("X-Requested-With")))
* .ignoringRequestMatchers((request) -> "XMLHttpRequest".equals(request.getHeader("X-Requested-With")))
* .and()
* ...
* </pre>
@@ -177,8 +178,7 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
* @since 5.1
*/
public CsrfConfigurer<H> ignoringRequestMatchers(RequestMatcher... requestMatchers) {
return new IgnoreCsrfProtectionRegistry(this.context).requestMatchers(requestMatchers)
.and();
return new IgnoreCsrfProtectionRegistry(this.context).requestMatchers(requestMatchers).and();
}
/**
@@ -186,17 +186,14 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
* Specify the {@link SessionAuthenticationStrategy} to use. The default is a
* {@link CsrfAuthenticationStrategy}.
* </p>
*
* @author Michael Vitz
* @since 5.2
*
* @param sessionAuthenticationStrategy the {@link SessionAuthenticationStrategy} to use
* @param sessionAuthenticationStrategy the {@link SessionAuthenticationStrategy} to
* use
* @return the {@link CsrfConfigurer} for further customizations
* @since 5.2
*/
public CsrfConfigurer<H> sessionAuthenticationStrategy(
SessionAuthenticationStrategy sessionAuthenticationStrategy) {
Assert.notNull(sessionAuthenticationStrategy,
"sessionAuthenticationStrategy cannot be null");
Assert.notNull(sessionAuthenticationStrategy, "sessionAuthenticationStrategy cannot be null");
this.sessionAuthenticationStrategy = sessionAuthenticationStrategy;
return this;
}
@@ -215,14 +212,11 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
}
LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
if (logoutConfigurer != null) {
logoutConfigurer
.addLogoutHandler(new CsrfLogoutHandler(this.csrfTokenRepository));
logoutConfigurer.addLogoutHandler(new CsrfLogoutHandler(this.csrfTokenRepository));
}
SessionManagementConfigurer<H> sessionConfigurer = http
.getConfigurer(SessionManagementConfigurer.class);
SessionManagementConfigurer<H> sessionConfigurer = http.getConfigurer(SessionManagementConfigurer.class);
if (sessionConfigurer != null) {
sessionConfigurer.addSessionAuthenticationStrategy(
getSessionAuthenticationStrategy());
sessionConfigurer.addSessionAuthenticationStrategy(getSessionAuthenticationStrategy());
}
filter = postProcess(filter);
http.addFilter(filter);
@@ -231,7 +225,6 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Gets the final {@link RequestMatcher} to use by combining the
* {@link #requireCsrfProtectionMatcher(RequestMatcher)} and any {@link #ignore()}.
*
* @return the {@link RequestMatcher} to use
*/
private RequestMatcher getRequireCsrfProtectionMatcher() {
@@ -239,22 +232,19 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
return this.requireCsrfProtectionMatcher;
}
return new AndRequestMatcher(this.requireCsrfProtectionMatcher,
new NegatedRequestMatcher(
new OrRequestMatcher(this.ignoredCsrfProtectionMatchers)));
new NegatedRequestMatcher(new OrRequestMatcher(this.ignoredCsrfProtectionMatchers)));
}
/**
* Gets the default {@link AccessDeniedHandler} from the
* {@link ExceptionHandlingConfigurer#getAccessDeniedHandler()} or create a
* {@link AccessDeniedHandlerImpl} if not available.
*
* @param http the {@link HttpSecurityBuilder}
* @return the {@link AccessDeniedHandler}
*/
@SuppressWarnings("unchecked")
private AccessDeniedHandler getDefaultAccessDeniedHandler(H http) {
ExceptionHandlingConfigurer<H> exceptionConfig = http
.getConfigurer(ExceptionHandlingConfigurer.class);
ExceptionHandlingConfigurer<H> exceptionConfig = http.getConfigurer(ExceptionHandlingConfigurer.class);
AccessDeniedHandler handler = null;
if (exceptionConfig != null) {
handler = exceptionConfig.getAccessDeniedHandler();
@@ -269,14 +259,12 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
* Gets the default {@link InvalidSessionStrategy} from the
* {@link SessionManagementConfigurer#getInvalidSessionStrategy()} or null if not
* available.
*
* @param http the {@link HttpSecurityBuilder}
* @return the {@link InvalidSessionStrategy}
*/
@SuppressWarnings("unchecked")
private InvalidSessionStrategy getInvalidSessionStrategy(H http) {
SessionManagementConfigurer<H> sessionManagement = http
.getConfigurer(SessionManagementConfigurer.class);
SessionManagementConfigurer<H> sessionManagement = http.getConfigurer(SessionManagementConfigurer.class);
if (sessionManagement == null) {
return null;
}
@@ -292,18 +280,15 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
* {@link InvalidSessionAccessDeniedHandler} and the
* {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)}. Otherwise, only
* {@link #getDefaultAccessDeniedHandler(HttpSecurityBuilder)} is used.
*
* @param http the {@link HttpSecurityBuilder}
* @return the {@link AccessDeniedHandler}
*/
private AccessDeniedHandler createAccessDeniedHandler(H http) {
InvalidSessionStrategy invalidSessionStrategy = getInvalidSessionStrategy(http);
AccessDeniedHandler defaultAccessDeniedHandler = getDefaultAccessDeniedHandler(
http);
AccessDeniedHandler defaultAccessDeniedHandler = getDefaultAccessDeniedHandler(http);
if (invalidSessionStrategy == null) {
return defaultAccessDeniedHandler;
}
InvalidSessionAccessDeniedHandler invalidSessionDeniedHandler = new InvalidSessionAccessDeniedHandler(
invalidSessionStrategy);
LinkedHashMap<Class<? extends AccessDeniedException>, AccessDeniedHandler> handlers = new LinkedHashMap<>();
@@ -312,20 +297,16 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
}
/**
* Gets the {@link SessionAuthenticationStrategy} to use. If none was set by the user a
* {@link CsrfAuthenticationStrategy} is created.
*
* @author Michael Vitz
* @since 5.2
*
* Gets the {@link SessionAuthenticationStrategy} to use. If none was set by the user
* a {@link CsrfAuthenticationStrategy} is created.
* @return the {@link SessionAuthenticationStrategy}
* @since 5.2
*/
private SessionAuthenticationStrategy getSessionAuthenticationStrategy() {
if (sessionAuthenticationStrategy != null) {
return sessionAuthenticationStrategy;
} else {
return new CsrfAuthenticationStrategy(this.csrfTokenRepository);
if (this.sessionAuthenticationStrategy != null) {
return this.sessionAuthenticationStrategy;
}
return new CsrfAuthenticationStrategy(this.csrfTokenRepository);
}
/**
@@ -336,23 +317,17 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
* @author Rob Winch
* @since 4.0
*/
private class IgnoreCsrfProtectionRegistry
extends AbstractRequestMatcherRegistry<IgnoreCsrfProtectionRegistry> {
private class IgnoreCsrfProtectionRegistry extends AbstractRequestMatcherRegistry<IgnoreCsrfProtectionRegistry> {
/**
* @param context
*/
private IgnoreCsrfProtectionRegistry(ApplicationContext context) {
IgnoreCsrfProtectionRegistry(ApplicationContext context) {
setApplicationContext(context);
}
@Override
public MvcMatchersIgnoreCsrfProtectionRegistry mvcMatchers(HttpMethod method,
String... mvcPatterns) {
public MvcMatchersIgnoreCsrfProtectionRegistry mvcMatchers(HttpMethod method, String... mvcPatterns) {
List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
CsrfConfigurer.this.ignoredCsrfProtectionMatchers.addAll(mvcMatchers);
return new MvcMatchersIgnoreCsrfProtectionRegistry(getApplicationContext(),
mvcMatchers);
return new MvcMatchersIgnoreCsrfProtectionRegistry(getApplicationContext(), mvcMatchers);
}
@Override
@@ -360,16 +335,16 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
return mvcMatchers(null, mvcPatterns);
}
public CsrfConfigurer<H> and() {
CsrfConfigurer<H> and() {
return CsrfConfigurer.this;
}
@Override
protected IgnoreCsrfProtectionRegistry chainRequestMatchers(
List<RequestMatcher> requestMatchers) {
protected IgnoreCsrfProtectionRegistry chainRequestMatchers(List<RequestMatcher> requestMatchers) {
CsrfConfigurer.this.ignoredCsrfProtectionMatchers.addAll(requestMatchers);
return this;
}
}
/**
@@ -378,8 +353,8 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
*
* @author Rob Winch
*/
private final class MvcMatchersIgnoreCsrfProtectionRegistry
extends IgnoreCsrfProtectionRegistry {
private final class MvcMatchersIgnoreCsrfProtectionRegistry extends IgnoreCsrfProtectionRegistry {
private final List<MvcRequestMatcher> mvcMatchers;
private MvcMatchersIgnoreCsrfProtectionRegistry(ApplicationContext context,
@@ -388,11 +363,13 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
this.mvcMatchers = mvcMatchers;
}
public IgnoreCsrfProtectionRegistry servletPath(String servletPath) {
IgnoreCsrfProtectionRegistry servletPath(String servletPath) {
for (MvcRequestMatcher matcher : this.mvcMatchers) {
matcher.setServletPath(servletPath);
}
return this;
}
}
}

View File

@@ -13,8 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.Collections;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.AuthenticationEntryPoint;
@@ -22,11 +28,6 @@ import org.springframework.security.web.authentication.ui.DefaultLoginPageGenera
import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
import org.springframework.security.web.csrf.CsrfToken;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
/**
* Adds a Filter that will generate a login page if one is not specified otherwise when
* using {@link WebSecurityConfigurerAdapter}.
@@ -49,7 +50,8 @@ import java.util.function.Function;
*
* <h2>Shared Objects Created</h2>
*
* No shared objects are created. isLogoutRequest <h2>Shared Objects Used</h2>
* No shared objects are created. isLogoutRequest
* <h2>Shared Objects Used</h2>
*
* The following shared objects are used:
*
@@ -60,13 +62,12 @@ import java.util.function.Function;
* {@link DefaultLoginPageConfigurer} should be added and how to configure it.</li>
* </ul>
*
* @see WebSecurityConfigurerAdapter
*
* @author Rob Winch
* @since 3.2
* @see WebSecurityConfigurerAdapter
*/
public final class DefaultLoginPageConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<DefaultLoginPageConfigurer<H>, H> {
public final class DefaultLoginPageConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<DefaultLoginPageConfigurer<H>, H> {
private DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = new DefaultLoginPageGeneratingFilter();
@@ -74,32 +75,28 @@ public final class DefaultLoginPageConfigurer<H extends HttpSecurityBuilder<H>>
@Override
public void init(H http) {
Function<HttpServletRequest, Map<String, String>> hiddenInputs = request -> {
CsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (token == null) {
return Collections.emptyMap();
}
return Collections.singletonMap(token.getParameterName(), token.getToken());
};
this.loginPageGeneratingFilter.setResolveHiddenInputs(hiddenInputs);
this.logoutPageGeneratingFilter.setResolveHiddenInputs(hiddenInputs);
http.setSharedObject(DefaultLoginPageGeneratingFilter.class,
loginPageGeneratingFilter);
this.loginPageGeneratingFilter.setResolveHiddenInputs(DefaultLoginPageConfigurer.this::hiddenInputs);
this.logoutPageGeneratingFilter.setResolveHiddenInputs(DefaultLoginPageConfigurer.this::hiddenInputs);
http.setSharedObject(DefaultLoginPageGeneratingFilter.class, this.loginPageGeneratingFilter);
}
private Map<String, String> hiddenInputs(HttpServletRequest request) {
CsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
return (token != null) ? Collections.singletonMap(token.getParameterName(), token.getToken())
: Collections.emptyMap();
}
@Override
@SuppressWarnings("unchecked")
public void configure(H http) {
AuthenticationEntryPoint authenticationEntryPoint = null;
ExceptionHandlingConfigurer<?> exceptionConf = http
.getConfigurer(ExceptionHandlingConfigurer.class);
ExceptionHandlingConfigurer<?> exceptionConf = http.getConfigurer(ExceptionHandlingConfigurer.class);
if (exceptionConf != null) {
authenticationEntryPoint = exceptionConf.getAuthenticationEntryPoint();
}
if (loginPageGeneratingFilter.isEnabled() && authenticationEntryPoint == null) {
loginPageGeneratingFilter = postProcess(loginPageGeneratingFilter);
http.addFilter(loginPageGeneratingFilter);
if (this.loginPageGeneratingFilter.isEnabled() && authenticationEntryPoint == null) {
this.loginPageGeneratingFilter = postProcess(this.loginPageGeneratingFilter);
http.addFilter(this.loginPageGeneratingFilter);
http.addFilter(this.logoutPageGeneratingFilter);
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.LinkedHashMap;
@@ -62,8 +63,8 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
* @author Rob Winch
* @since 3.2
*/
public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<ExceptionHandlingConfigurer<H>, H> {
public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<ExceptionHandlingConfigurer<H>, H> {
private AuthenticationEntryPoint authenticationEntryPoint;
@@ -83,7 +84,6 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Shortcut to specify the {@link AccessDeniedHandler} to be used is a specific error
* page
*
* @param accessDeniedUrl the URL to the access denied page (i.e. /errors/401)
* @return the {@link ExceptionHandlingConfigurer} for further customization
* @see AccessDeniedHandlerImpl
@@ -97,32 +97,29 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Specifies the {@link AccessDeniedHandler} to be used
*
* @param accessDeniedHandler the {@link AccessDeniedHandler} to be used
* @return the {@link ExceptionHandlingConfigurer} for further customization
*/
public ExceptionHandlingConfigurer<H> accessDeniedHandler(
AccessDeniedHandler accessDeniedHandler) {
public ExceptionHandlingConfigurer<H> accessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {
this.accessDeniedHandler = accessDeniedHandler;
return this;
}
/**
* Sets a default {@link AccessDeniedHandler} to be used which prefers being
* invoked for the provided {@link RequestMatcher}. If only a single default
* {@link AccessDeniedHandler} is specified, it will be what is used for the
* default {@link AccessDeniedHandler}. If multiple default
* {@link AccessDeniedHandler} instances are configured, then a
* Sets a default {@link AccessDeniedHandler} to be used which prefers being invoked
* for the provided {@link RequestMatcher}. If only a single default
* {@link AccessDeniedHandler} is specified, it will be what is used for the default
* {@link AccessDeniedHandler}. If multiple default {@link AccessDeniedHandler}
* instances are configured, then a
* {@link RequestMatcherDelegatingAccessDeniedHandler} will be used.
*
* @param deniedHandler the {@link AccessDeniedHandler} to use
* @param preferredMatcher the {@link RequestMatcher} for this default
* {@link AccessDeniedHandler}
* @return the {@link ExceptionHandlingConfigurer} for further customizations
* @since 5.1
*/
public ExceptionHandlingConfigurer<H> defaultAccessDeniedHandlerFor(
AccessDeniedHandler deniedHandler, RequestMatcher preferredMatcher) {
public ExceptionHandlingConfigurer<H> defaultAccessDeniedHandlerFor(AccessDeniedHandler deniedHandler,
RequestMatcher preferredMatcher) {
this.defaultDeniedHandlerMappings.put(preferredMatcher, deniedHandler);
return this;
}
@@ -141,12 +138,10 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
* <p>
* If that is not provided defaults to {@link Http403ForbiddenEntryPoint}.
* </p>
*
* @param authenticationEntryPoint the {@link AuthenticationEntryPoint} to use
* @return the {@link ExceptionHandlingConfigurer} for further customizations
*/
public ExceptionHandlingConfigurer<H> authenticationEntryPoint(
AuthenticationEntryPoint authenticationEntryPoint) {
public ExceptionHandlingConfigurer<H> authenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
this.authenticationEntryPoint = authenticationEntryPoint;
return this;
}
@@ -158,14 +153,13 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
* default {@link AuthenticationEntryPoint}. If multiple default
* {@link AuthenticationEntryPoint} instances are configured, then a
* {@link DelegatingAuthenticationEntryPoint} will be used.
*
* @param entryPoint the {@link AuthenticationEntryPoint} to use
* @param preferredMatcher the {@link RequestMatcher} for this default
* {@link AuthenticationEntryPoint}
* @return the {@link ExceptionHandlingConfigurer} for further customizations
*/
public ExceptionHandlingConfigurer<H> defaultAuthenticationEntryPointFor(
AuthenticationEntryPoint entryPoint, RequestMatcher preferredMatcher) {
public ExceptionHandlingConfigurer<H> defaultAuthenticationEntryPointFor(AuthenticationEntryPoint entryPoint,
RequestMatcher preferredMatcher) {
this.defaultEntryPointMappings.put(preferredMatcher, entryPoint);
return this;
}
@@ -180,7 +174,6 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Gets the {@link AccessDeniedHandler} that is configured.
*
* @return the {@link AccessDeniedHandler}
*/
AccessDeniedHandler getAccessDeniedHandler() {
@@ -190,8 +183,8 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
@Override
public void configure(H http) {
AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http);
ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(
entryPoint, getRequestCache(http));
ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(entryPoint,
getRequestCache(http));
AccessDeniedHandler deniedHandler = getAccessDeniedHandler(http);
exceptionTranslationFilter.setAccessDeniedHandler(deniedHandler);
exceptionTranslationFilter = postProcess(exceptionTranslationFilter);
@@ -235,8 +228,7 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
if (this.defaultDeniedHandlerMappings.size() == 1) {
return this.defaultDeniedHandlerMappings.values().iterator().next();
}
return new RequestMatcherDelegatingAccessDeniedHandler(
this.defaultDeniedHandlerMappings,
return new RequestMatcherDelegatingAccessDeniedHandler(this.defaultDeniedHandlerMappings,
new AccessDeniedHandlerImpl());
}
@@ -249,8 +241,7 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
}
DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(
this.defaultEntryPointMappings);
entryPoint.setDefaultEntryPoint(this.defaultEntryPointMappings.values().iterator()
.next());
entryPoint.setDefaultEntryPoint(this.defaultEntryPointMappings.values().iterator().next());
return entryPoint;
}
@@ -259,7 +250,6 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
* {@link #requestCache(org.springframework.security.web.savedrequest.RequestCache)},
* then it is used. Otherwise, an attempt to find a {@link RequestCache} shared object
* is made. If that fails, an {@link HttpSessionRequestCache} is used
*
* @param http the {@link HttpSecurity} to attempt to fined the shared object
* @return the {@link RequestCache} to use
*/
@@ -270,4 +260,5 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
}
return new HttpSessionRequestCache();
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.ArrayList;
@@ -46,7 +47,8 @@ import org.springframework.util.StringUtils;
* Adds URL based authorization based upon SpEL expressions to an application. At least
* one {@link org.springframework.web.bind.annotation.RequestMapping} needs to be mapped
* to {@link ConfigAttribute}'s for this {@link SecurityContextConfigurer} to have
* meaning. <h2>Security Filters</h2>
* meaning.
* <h2>Security Filters</h2>
*
* The following Filters are populated
*
@@ -73,19 +75,23 @@ import org.springframework.util.StringUtils;
* </ul>
*
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
*
* @author Rob Winch
* @since 3.2
* @see org.springframework.security.config.annotation.web.builders.HttpSecurity#authorizeRequests()
*/
public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
extends
AbstractInterceptUrlConfigurer<ExpressionUrlAuthorizationConfigurer<H>, H> {
extends AbstractInterceptUrlConfigurer<ExpressionUrlAuthorizationConfigurer<H>, H> {
static final String permitAll = "permitAll";
private static final String denyAll = "denyAll";
private static final String anonymous = "anonymous";
private static final String authenticated = "authenticated";
private static final String fullyAuthenticated = "fullyAuthenticated";
private static final String rememberMe = "rememberMe";
private final ExpressionInterceptUrlRegistry REGISTRY;
@@ -101,73 +107,12 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
}
public ExpressionInterceptUrlRegistry getRegistry() {
return REGISTRY;
}
public class ExpressionInterceptUrlRegistry
extends
ExpressionUrlAuthorizationConfigurer<H>.AbstractInterceptUrlRegistry<ExpressionInterceptUrlRegistry, AuthorizedUrl> {
/**
* @param context
*/
private ExpressionInterceptUrlRegistry(ApplicationContext context) {
setApplicationContext(context);
}
@Override
public MvcMatchersAuthorizedUrl mvcMatchers(HttpMethod method, String... mvcPatterns) {
return new MvcMatchersAuthorizedUrl(createMvcMatchers(method, mvcPatterns));
}
@Override
public MvcMatchersAuthorizedUrl mvcMatchers(String... patterns) {
return mvcMatchers(null, patterns);
}
@Override
protected final AuthorizedUrl chainRequestMatchersInternal(
List<RequestMatcher> requestMatchers) {
return new AuthorizedUrl(requestMatchers);
}
/**
* Allows customization of the {@link SecurityExpressionHandler} to be used. The
* default is {@link DefaultWebSecurityExpressionHandler}
*
* @param expressionHandler the {@link SecurityExpressionHandler} to be used
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization.
*/
public ExpressionInterceptUrlRegistry expressionHandler(
SecurityExpressionHandler<FilterInvocation> expressionHandler) {
ExpressionUrlAuthorizationConfigurer.this.expressionHandler = expressionHandler;
return this;
}
/**
* Adds an {@link ObjectPostProcessor} for this class.
*
* @param objectPostProcessor
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customizations
*/
public ExpressionInterceptUrlRegistry withObjectPostProcessor(
ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return this;
}
public H and() {
return ExpressionUrlAuthorizationConfigurer.this.and();
}
return this.REGISTRY;
}
/**
* Allows registering multiple {@link RequestMatcher} instances to a collection of
* {@link ConfigAttribute} instances
*
* @param requestMatchers the {@link RequestMatcher} instances to register to the
* {@link ConfigAttribute} instances
* @param configAttributes the {@link ConfigAttribute} to be mapped by the
@@ -176,8 +121,8 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
private void interceptUrl(Iterable<? extends RequestMatcher> requestMatchers,
Collection<ConfigAttribute> configAttributes) {
for (RequestMatcher requestMatcher : requestMatchers) {
REGISTRY.addMapping(new AbstractConfigAttributeRequestMatcherRegistry.UrlMapping(
requestMatcher, configAttributes));
this.REGISTRY.addMapping(
new AbstractConfigAttributeRequestMatcherRegistry.UrlMapping(requestMatcher, configAttributes));
}
}
@@ -192,63 +137,54 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
}
@Override
ExpressionBasedFilterInvocationSecurityMetadataSource createMetadataSource(
H http) {
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = REGISTRY
.createRequestMap();
if (requestMap.isEmpty()) {
throw new IllegalStateException(
"At least one mapping is required (i.e. authorizeRequests().anyRequest().authenticated())");
}
return new ExpressionBasedFilterInvocationSecurityMetadataSource(requestMap,
getExpressionHandler(http));
ExpressionBasedFilterInvocationSecurityMetadataSource createMetadataSource(H http) {
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = this.REGISTRY.createRequestMap();
Assert.state(!requestMap.isEmpty(),
"At least one mapping is required (i.e. authorizeRequests().anyRequest().authenticated())");
return new ExpressionBasedFilterInvocationSecurityMetadataSource(requestMap, getExpressionHandler(http));
}
private SecurityExpressionHandler<FilterInvocation> getExpressionHandler(H http) {
if (expressionHandler == null) {
DefaultWebSecurityExpressionHandler defaultHandler = new DefaultWebSecurityExpressionHandler();
AuthenticationTrustResolver trustResolver = http
.getSharedObject(AuthenticationTrustResolver.class);
if (trustResolver != null) {
defaultHandler.setTrustResolver(trustResolver);
}
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
if (context != null) {
String[] roleHiearchyBeanNames = context.getBeanNamesForType(RoleHierarchy.class);
if (roleHiearchyBeanNames.length == 1) {
defaultHandler.setRoleHierarchy(context.getBean(roleHiearchyBeanNames[0], RoleHierarchy.class));
}
String[] grantedAuthorityDefaultsBeanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class);
if (grantedAuthorityDefaultsBeanNames.length == 1) {
GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBean(grantedAuthorityDefaultsBeanNames[0], GrantedAuthorityDefaults.class);
defaultHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
}
String[] permissionEvaluatorBeanNames = context.getBeanNamesForType(PermissionEvaluator.class);
if (permissionEvaluatorBeanNames.length == 1) {
PermissionEvaluator permissionEvaluator = context.getBean(permissionEvaluatorBeanNames[0], PermissionEvaluator.class);
defaultHandler.setPermissionEvaluator(permissionEvaluator);
}
}
expressionHandler = postProcess(defaultHandler);
if (this.expressionHandler != null) {
return this.expressionHandler;
}
return expressionHandler;
DefaultWebSecurityExpressionHandler defaultHandler = new DefaultWebSecurityExpressionHandler();
AuthenticationTrustResolver trustResolver = http.getSharedObject(AuthenticationTrustResolver.class);
if (trustResolver != null) {
defaultHandler.setTrustResolver(trustResolver);
}
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
if (context != null) {
String[] roleHiearchyBeanNames = context.getBeanNamesForType(RoleHierarchy.class);
if (roleHiearchyBeanNames.length == 1) {
defaultHandler.setRoleHierarchy(context.getBean(roleHiearchyBeanNames[0], RoleHierarchy.class));
}
String[] grantedAuthorityDefaultsBeanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class);
if (grantedAuthorityDefaultsBeanNames.length == 1) {
GrantedAuthorityDefaults grantedAuthorityDefaults = context
.getBean(grantedAuthorityDefaultsBeanNames[0], GrantedAuthorityDefaults.class);
defaultHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
}
String[] permissionEvaluatorBeanNames = context.getBeanNamesForType(PermissionEvaluator.class);
if (permissionEvaluatorBeanNames.length == 1) {
PermissionEvaluator permissionEvaluator = context.getBean(permissionEvaluatorBeanNames[0],
PermissionEvaluator.class);
defaultHandler.setPermissionEvaluator(permissionEvaluator);
}
}
this.expressionHandler = postProcess(defaultHandler);
return this.expressionHandler;
}
private static String hasAnyRole(String... authorities) {
String anyAuthorities = StringUtils.arrayToDelimitedString(authorities,
"','ROLE_");
String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','ROLE_");
return "hasAnyRole('ROLE_" + anyAuthorities + "')";
}
private static String hasRole(String role) {
Assert.notNull(role, "role cannot be null");
if (role.startsWith("ROLE_")) {
throw new IllegalArgumentException(
"role should not start with 'ROLE_' since it is automatically inserted. Got '"
+ role + "'");
}
Assert.isTrue(!role.startsWith("ROLE_"),
() -> "role should not start with 'ROLE_' since it is automatically inserted. Got '" + role + "'");
return "hasRole('ROLE_" + role + "')";
}
@@ -265,16 +201,68 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
return "hasIpAddress('" + ipAddressExpression + "')";
}
public final class ExpressionInterceptUrlRegistry extends
ExpressionUrlAuthorizationConfigurer<H>.AbstractInterceptUrlRegistry<ExpressionInterceptUrlRegistry, AuthorizedUrl> {
private ExpressionInterceptUrlRegistry(ApplicationContext context) {
setApplicationContext(context);
}
@Override
public MvcMatchersAuthorizedUrl mvcMatchers(HttpMethod method, String... mvcPatterns) {
return new MvcMatchersAuthorizedUrl(createMvcMatchers(method, mvcPatterns));
}
@Override
public MvcMatchersAuthorizedUrl mvcMatchers(String... patterns) {
return mvcMatchers(null, patterns);
}
@Override
protected AuthorizedUrl chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
return new AuthorizedUrl(requestMatchers);
}
/**
* Allows customization of the {@link SecurityExpressionHandler} to be used. The
* default is {@link DefaultWebSecurityExpressionHandler}
* @param expressionHandler the {@link SecurityExpressionHandler} to be used
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization.
*/
public ExpressionInterceptUrlRegistry expressionHandler(
SecurityExpressionHandler<FilterInvocation> expressionHandler) {
ExpressionUrlAuthorizationConfigurer.this.expressionHandler = expressionHandler;
return this;
}
/**
* Adds an {@link ObjectPostProcessor} for this class.
* @param objectPostProcessor
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customizations
*/
public ExpressionInterceptUrlRegistry withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return this;
}
public H and() {
return ExpressionUrlAuthorizationConfigurer.this.and();
}
}
/**
* An {@link AuthorizedUrl} that allows optionally configuring the
* {@link MvcRequestMatcher#setMethod(HttpMethod)}
*
* @author Rob Winch
*/
public class MvcMatchersAuthorizedUrl extends AuthorizedUrl {
public final class MvcMatchersAuthorizedUrl extends AuthorizedUrl {
/**
* Creates a new instance
*
* @param requestMatchers the {@link RequestMatcher} instances to map
*/
private MvcMatchersAuthorizedUrl(List<MvcRequestMatcher> requestMatchers) {
@@ -287,18 +275,20 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
}
return this;
}
}
public class AuthorizedUrl {
private List<? extends RequestMatcher> requestMatchers;
private boolean not;
/**
* Creates a new instance
*
* @param requestMatchers the {@link RequestMatcher} instances to map
*/
private AuthorizedUrl(List<? extends RequestMatcher> requestMatchers) {
AuthorizedUrl(List<? extends RequestMatcher> requestMatchers) {
this.requestMatchers = requestMatchers;
}
@@ -308,7 +298,6 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
/**
* Negates the following expression.
*
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
@@ -320,7 +309,6 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
/**
* Shortcut for specifying URLs require a particular role. If you do not want to
* have "ROLE_" automatically inserted see {@link #hasAuthority(String)}.
*
* @param role the role to require (i.e. USER, ADMIN, etc). Note, it should not
* start with "ROLE_" as this is automatically inserted.
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
@@ -334,7 +322,6 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
* Shortcut for specifying URLs require any of a number of roles. If you do not
* want to have "ROLE_" automatically inserted see
* {@link #hasAnyAuthority(String...)}
*
* @param roles the roles to require (i.e. USER, ADMIN, etc). Note, it should not
* start with "ROLE_" as this is automatically inserted.
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
@@ -346,7 +333,6 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
/**
* Specify that URLs require a particular authority.
*
* @param authority the authority to require (i.e. ROLE_USER, ROLE_ADMIN, etc).
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
@@ -357,7 +343,6 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
/**
* Specify that URLs requires any of a number authorities.
*
* @param authorities the requests require at least one of the authorities (i.e.
* "ROLE_USER","ROLE_ADMIN" would mean either "ROLE_USER" or "ROLE_ADMIN" is
* required).
@@ -365,28 +350,24 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
* customization
*/
public ExpressionInterceptUrlRegistry hasAnyAuthority(String... authorities) {
return access(ExpressionUrlAuthorizationConfigurer
.hasAnyAuthority(authorities));
return access(ExpressionUrlAuthorizationConfigurer.hasAnyAuthority(authorities));
}
/**
* Specify that URLs requires a specific IP Address or <a href=
* "https://forum.spring.io/showthread.php?102783-How-to-use-hasIpAddress&p=343971#post343971"
* >subnet</a>.
*
* @param ipaddressExpression the ipaddress (i.e. 192.168.1.79) or local subnet
* (i.e. 192.168.0/24)
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
public ExpressionInterceptUrlRegistry hasIpAddress(String ipaddressExpression) {
return access(ExpressionUrlAuthorizationConfigurer
.hasIpAddress(ipaddressExpression));
return access(ExpressionUrlAuthorizationConfigurer.hasIpAddress(ipaddressExpression));
}
/**
* Specify that URLs are allowed by anyone.
*
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
@@ -396,7 +377,6 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
/**
* Specify that URLs are allowed by anonymous users.
*
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
@@ -406,7 +386,6 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
/**
* Specify that URLs are allowed by users that have been remembered.
*
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
* @see RememberMeConfigurer
@@ -417,7 +396,6 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
/**
* Specify that URLs are not allowed by anyone.
*
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
@@ -427,7 +405,6 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
/**
* Specify that URLs are allowed by any authenticated user.
*
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
@@ -438,7 +415,6 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
/**
* Specify that URLs are allowed by users who have authenticated and were not
* "remembered".
*
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
* @see RememberMeConfigurer
@@ -449,18 +425,19 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
/**
* Allows specifying that URLs are secured by an arbitrary expression
*
* @param attribute the expression to secure the URLs (i.e.
* "hasRole('ROLE_USER') and hasRole('ROLE_SUPER')")
* @param attribute the expression to secure the URLs (i.e. "hasRole('ROLE_USER')
* and hasRole('ROLE_SUPER')")
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
public ExpressionInterceptUrlRegistry access(String attribute) {
if (not) {
if (this.not) {
attribute = "!" + attribute;
}
interceptUrl(requestMatchers, SecurityConfig.createList(attribute));
interceptUrl(this.requestMatchers, SecurityConfig.createList(attribute));
return ExpressionUrlAuthorizationConfigurer.this.REGISTRY;
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
@@ -172,8 +173,6 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
* <li>/authenticate?error GET - redirect here for failed authentication attempts</li>
* <li>/authenticate?logout GET - redirect here after successfully logging out</li>
* </ul>
*
*
* @param loginPage the login page to redirect to if authentication is required (i.e.
* "/login")
* @return the {@link FormLoginConfigurer} for additional customization
@@ -186,7 +185,6 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
/**
* The HTTP parameter to look for the username when performing authentication. Default
* is "username".
*
* @param usernameParameter the HTTP parameter to look for the username when
* performing authentication
* @return the {@link FormLoginConfigurer} for additional customization
@@ -199,7 +197,6 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
/**
* The HTTP parameter to look for the password when performing authentication. Default
* is "password".
*
* @param passwordParameter the HTTP parameter to look for the password when
* performing authentication
* @return the {@link FormLoginConfigurer} for additional customization
@@ -211,7 +208,6 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
/**
* Forward Authentication Failure Handler
*
* @param forwardUrl the target URL in case of failure
* @return the {@link FormLoginConfigurer} for additional customization
*/
@@ -222,7 +218,6 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
/**
* Forward Authentication Success Handler
*
* @param forwardUrl the target URL in case of success
* @return the {@link FormLoginConfigurer} for additional customization
*/
@@ -237,13 +232,6 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
initDefaultLoginFilter(http);
}
/*
* (non-Javadoc)
*
* @see org.springframework.security.config.annotation.web.configurers.
* AbstractAuthenticationFilterConfigurer
* #createLoginProcessingUrlMatcher(java.lang.String)
*/
@Override
protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
return new AntPathRequestMatcher(loginProcessingUrl, "POST");
@@ -251,7 +239,6 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
/**
* Gets the HTTP parameter that is used to submit the username.
*
* @return the HTTP parameter that is used to submit the username
*/
private String getUsernameParameter() {
@@ -260,7 +247,6 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
/**
* Gets the HTTP parameter that is used to submit the password.
*
* @return the HTTP parameter that is used to submit the password
*/
private String getPasswordParameter() {
@@ -270,7 +256,6 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
/**
* If available, initializes the {@link DefaultLoginPageGeneratingFilter} shared
* object.
*
* @param http the {@link HttpSecurityBuilder} to use
*/
private void initDefaultLoginFilter(H http) {
@@ -285,4 +270,5 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
loginPageGeneratingFilter.setAuthenticationUrl(getLoginProcessingUrl());
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.Arrays;
@@ -53,8 +54,7 @@ import org.springframework.web.accept.HeaderContentNegotiationStrategy;
* The following Filters are populated
*
* <ul>
* <li>
* {@link BasicAuthenticationFilter}</li>
* <li>{@link BasicAuthenticationFilter}</li>
* </ul>
*
* <h2>Shared Objects Created</h2>
@@ -77,16 +77,18 @@ import org.springframework.web.accept.HeaderContentNegotiationStrategy;
* @author Rob Winch
* @since 3.2
*/
public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
AbstractHttpConfigurer<HttpBasicConfigurer<B>, B> {
public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>>
extends AbstractHttpConfigurer<HttpBasicConfigurer<B>, B> {
private static final RequestHeaderRequestMatcher X_REQUESTED_WITH = new RequestHeaderRequestMatcher("X-Requested-With",
"XMLHttpRequest");
private static final RequestHeaderRequestMatcher X_REQUESTED_WITH = new RequestHeaderRequestMatcher(
"X-Requested-With", "XMLHttpRequest");
private static final String DEFAULT_REALM = "Realm";
private AuthenticationEntryPoint authenticationEntryPoint;
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
private BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint();
/**
@@ -95,12 +97,9 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
*/
public HttpBasicConfigurer() {
realmName(DEFAULT_REALM);
LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints = new LinkedHashMap<>();
entryPoints.put(X_REQUESTED_WITH, new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
DelegatingAuthenticationEntryPoint defaultEntryPoint = new DelegatingAuthenticationEntryPoint(
entryPoints);
DelegatingAuthenticationEntryPoint defaultEntryPoint = new DelegatingAuthenticationEntryPoint(entryPoints);
defaultEntryPoint.setDefaultEntryPoint(this.basicAuthEntryPoint);
this.authenticationEntryPoint = defaultEntryPoint;
}
@@ -109,7 +108,6 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
* Allows easily changing the realm, but leaving the remaining defaults in place. If
* {@link #authenticationEntryPoint(AuthenticationEntryPoint)} has been invoked,
* invoking this method will result in an error.
*
* @param realmName the HTTP Basic realm to use
* @return {@link HttpBasicConfigurer} for additional customization
*/
@@ -122,14 +120,11 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
/**
* The {@link AuthenticationEntryPoint} to be populated on
* {@link BasicAuthenticationFilter} in the event that authentication fails. The
* default to use {@link BasicAuthenticationEntryPoint} with the realm
* "Realm".
*
* default to use {@link BasicAuthenticationEntryPoint} with the realm "Realm".
* @param authenticationEntryPoint the {@link AuthenticationEntryPoint} to use
* @return {@link HttpBasicConfigurer} for additional customization
*/
public HttpBasicConfigurer<B> authenticationEntryPoint(
AuthenticationEntryPoint authenticationEntryPoint) {
public HttpBasicConfigurer<B> authenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
this.authenticationEntryPoint = authenticationEntryPoint;
return this;
}
@@ -137,7 +132,6 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
/**
* Specifies a custom {@link AuthenticationDetailsSource} to use for basic
* authentication. The default is {@link WebAuthenticationDetailsSource}.
*
* @param authenticationDetailsSource the custom {@link AuthenticationDetailsSource}
* to use
* @return {@link HttpBasicConfigurer} for additional customization
@@ -154,47 +148,38 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
}
private void registerDefaults(B http) {
ContentNegotiationStrategy contentNegotiationStrategy = http
.getSharedObject(ContentNegotiationStrategy.class);
ContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);
if (contentNegotiationStrategy == null) {
contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
}
MediaTypeRequestMatcher restMatcher = new MediaTypeRequestMatcher(
contentNegotiationStrategy, MediaType.APPLICATION_ATOM_XML,
MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_XML,
MediaType.MULTIPART_FORM_DATA, MediaType.TEXT_XML);
MediaTypeRequestMatcher restMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,
MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_XML, MediaType.MULTIPART_FORM_DATA,
MediaType.TEXT_XML);
restMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
MediaTypeRequestMatcher allMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.ALL);
allMatcher.setUseEquals(true);
RequestMatcher notHtmlMatcher = new NegatedRequestMatcher(
new MediaTypeRequestMatcher(contentNegotiationStrategy,
MediaType.TEXT_HTML));
new MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.TEXT_HTML));
RequestMatcher restNotHtmlMatcher = new AndRequestMatcher(
Arrays.<RequestMatcher>asList(notHtmlMatcher, restMatcher));
RequestMatcher preferredMatcher = new OrRequestMatcher(Arrays.asList(X_REQUESTED_WITH, restNotHtmlMatcher, allMatcher));
RequestMatcher preferredMatcher = new OrRequestMatcher(
Arrays.asList(X_REQUESTED_WITH, restNotHtmlMatcher, allMatcher));
registerDefaultEntryPoint(http, preferredMatcher);
registerDefaultLogoutSuccessHandler(http, preferredMatcher);
}
private void registerDefaultEntryPoint(B http, RequestMatcher preferredMatcher) {
ExceptionHandlingConfigurer<B> exceptionHandling = http
.getConfigurer(ExceptionHandlingConfigurer.class);
ExceptionHandlingConfigurer<B> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);
if (exceptionHandling == null) {
return;
}
exceptionHandling.defaultAuthenticationEntryPointFor(
postProcess(this.authenticationEntryPoint), preferredMatcher);
exceptionHandling.defaultAuthenticationEntryPointFor(postProcess(this.authenticationEntryPoint),
preferredMatcher);
}
private void registerDefaultLogoutSuccessHandler(B http, RequestMatcher preferredMatcher) {
LogoutConfigurer<B> logout = http
.getConfigurer(LogoutConfigurer.class);
LogoutConfigurer<B> logout = http.getConfigurer(LogoutConfigurer.class);
if (logout == null) {
return;
}
@@ -204,13 +189,11 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
@Override
public void configure(B http) {
AuthenticationManager authenticationManager = http
.getSharedObject(AuthenticationManager.class);
BasicAuthenticationFilter basicAuthenticationFilter = new BasicAuthenticationFilter(
authenticationManager, this.authenticationEntryPoint);
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
BasicAuthenticationFilter basicAuthenticationFilter = new BasicAuthenticationFilter(authenticationManager,
this.authenticationEntryPoint);
if (this.authenticationDetailsSource != null) {
basicAuthenticationFilter
.setAuthenticationDetailsSource(this.authenticationDetailsSource);
basicAuthenticationFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
}
RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
if (rememberMeServices != null) {
@@ -219,4 +202,5 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
basicAuthenticationFilter = postProcess(basicAuthenticationFilter);
http.addFilter(basicAuthenticationFilter);
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.HashSet;
@@ -42,15 +43,13 @@ import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthe
* The following Filters are populated
*
* <ul>
* <li>
* {@link J2eePreAuthenticatedProcessingFilter}</li>
* <li>{@link J2eePreAuthenticatedProcessingFilter}</li>
* </ul>
*
* <h2>Shared Objects Created</h2>
*
* <ul>
* <li>
* {@link AuthenticationEntryPoint} is populated with an
* <li>{@link AuthenticationEntryPoint} is populated with an
* {@link Http403ForbiddenEntryPoint}</li>
* <li>A {@link PreAuthenticatedAuthenticationProvider} is populated into
* {@link HttpSecurity#authenticationProvider(org.springframework.security.authentication.AuthenticationProvider)}
@@ -68,10 +67,12 @@ import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthe
* @author Rob Winch
* @since 3.2
*/
public final class JeeConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<JeeConfigurer<H>, H> {
public final class JeeConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<JeeConfigurer<H>, H> {
private J2eePreAuthenticatedProcessingFilter j2eePreAuthenticatedProcessingFilter;
private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService;
private Set<String> mappableRoles = new HashSet<>();
/**
@@ -91,7 +92,6 @@ public final class JeeConfigurer<H extends HttpSecurityBuilder<H>> extends
* <p>
* There are no default roles that are mapped.
* </p>
*
* @param mappableRoles the roles to attempt to map to the {@link UserDetails} (i.e.
* "ROLE_USER", "ROLE_ADMIN", etc).
* @return the {@link JeeConfigurer} for further customizations
@@ -117,7 +117,6 @@ public final class JeeConfigurer<H extends HttpSecurityBuilder<H>> extends
* <p>
* There are no default roles that are mapped.
* </p>
*
* @param mappableRoles the roles to attempt to map to the {@link UserDetails} (i.e.
* "USER", "ADMIN", etc).
* @return the {@link JeeConfigurer} for further customizations
@@ -142,7 +141,6 @@ public final class JeeConfigurer<H extends HttpSecurityBuilder<H>> extends
* <p>
* There are no default roles that are mapped.
* </p>
*
* @param mappableRoles the roles to attempt to map to the {@link UserDetails}.
* @return the {@link JeeConfigurer} for further customizations
* @see SimpleMappableAttributesRetriever
@@ -156,7 +154,6 @@ public final class JeeConfigurer<H extends HttpSecurityBuilder<H>> extends
* Specifies the {@link AuthenticationUserDetailsService} that is used with the
* {@link PreAuthenticatedAuthenticationProvider}. The default is a
* {@link PreAuthenticatedGrantedAuthoritiesUserDetailsService}.
*
* @param authenticatedUserDetailsService the {@link AuthenticationUserDetailsService}
* to use.
* @return the {@link JeeConfigurer} for further configuration
@@ -172,7 +169,6 @@ public final class JeeConfigurer<H extends HttpSecurityBuilder<H>> extends
* {@link J2eePreAuthenticatedProcessingFilter} is provided, all of its attributes
* must also be configured manually (i.e. all attributes populated in the
* {@link JeeConfigurer} are not used).
*
* @param j2eePreAuthenticatedProcessingFilter the
* {@link J2eePreAuthenticatedProcessingFilter} to use.
* @return the {@link JeeConfigurer} for further configuration
@@ -194,21 +190,15 @@ public final class JeeConfigurer<H extends HttpSecurityBuilder<H>> extends
@Override
public void init(H http) {
PreAuthenticatedAuthenticationProvider authenticationProvider = new PreAuthenticatedAuthenticationProvider();
authenticationProvider
.setPreAuthenticatedUserDetailsService(getUserDetailsService());
authenticationProvider.setPreAuthenticatedUserDetailsService(getUserDetailsService());
authenticationProvider = postProcess(authenticationProvider);
// @formatter:off
http
.authenticationProvider(authenticationProvider)
.setSharedObject(AuthenticationEntryPoint.class, new Http403ForbiddenEntryPoint());
// @formatter:on
http.authenticationProvider(authenticationProvider).setSharedObject(AuthenticationEntryPoint.class,
new Http403ForbiddenEntryPoint());
}
@Override
public void configure(H http) {
J2eePreAuthenticatedProcessingFilter filter = getFilter(http
.getSharedObject(AuthenticationManager.class));
J2eePreAuthenticatedProcessingFilter filter = getFilter(http.getSharedObject(AuthenticationManager.class));
http.addFilter(filter);
}
@@ -218,45 +208,41 @@ public final class JeeConfigurer<H extends HttpSecurityBuilder<H>> extends
* @param authenticationManager the {@link AuthenticationManager} to use.
* @return the {@link J2eePreAuthenticatedProcessingFilter} to use.
*/
private J2eePreAuthenticatedProcessingFilter getFilter(
AuthenticationManager authenticationManager) {
if (j2eePreAuthenticatedProcessingFilter == null) {
j2eePreAuthenticatedProcessingFilter = new J2eePreAuthenticatedProcessingFilter();
j2eePreAuthenticatedProcessingFilter
.setAuthenticationManager(authenticationManager);
j2eePreAuthenticatedProcessingFilter
private J2eePreAuthenticatedProcessingFilter getFilter(AuthenticationManager authenticationManager) {
if (this.j2eePreAuthenticatedProcessingFilter == null) {
this.j2eePreAuthenticatedProcessingFilter = new J2eePreAuthenticatedProcessingFilter();
this.j2eePreAuthenticatedProcessingFilter.setAuthenticationManager(authenticationManager);
this.j2eePreAuthenticatedProcessingFilter
.setAuthenticationDetailsSource(createWebAuthenticationDetailsSource());
j2eePreAuthenticatedProcessingFilter = postProcess(j2eePreAuthenticatedProcessingFilter);
this.j2eePreAuthenticatedProcessingFilter = postProcess(this.j2eePreAuthenticatedProcessingFilter);
}
return j2eePreAuthenticatedProcessingFilter;
return this.j2eePreAuthenticatedProcessingFilter;
}
/**
* Gets the {@link AuthenticationUserDetailsService} that was specified or defaults to
* {@link PreAuthenticatedGrantedAuthoritiesUserDetailsService}.
*
* @return the {@link AuthenticationUserDetailsService} to use
*/
private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> getUserDetailsService() {
return authenticationUserDetailsService == null ? new PreAuthenticatedGrantedAuthoritiesUserDetailsService()
: authenticationUserDetailsService;
return (this.authenticationUserDetailsService != null) ? this.authenticationUserDetailsService
: new PreAuthenticatedGrantedAuthoritiesUserDetailsService();
}
/**
* Creates the {@link J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource} to set
* on the {@link J2eePreAuthenticatedProcessingFilter}. It is populated with a
* {@link SimpleMappableAttributesRetriever}.
*
* @return the {@link J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource} to use.
*/
private J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource createWebAuthenticationDetailsSource() {
J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource detailsSource = new J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource();
SimpleMappableAttributesRetriever rolesRetriever = new SimpleMappableAttributesRetriever();
rolesRetriever.setMappableAttributes(mappableRoles);
rolesRetriever.setMappableAttributes(this.mappableRoles);
detailsSource.setMappableRolesRetriever(rolesRetriever);
detailsSource = postProcess(detailsSource);
return detailsSource;
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.ArrayList;
@@ -41,15 +42,15 @@ import org.springframework.util.Assert;
/**
* Adds logout support. Other {@link SecurityConfigurer} instances may invoke
* {@link #addLogoutHandler(LogoutHandler)} in the {@link #init(HttpSecurityBuilder)} phase.
* {@link #addLogoutHandler(LogoutHandler)} in the {@link #init(HttpSecurityBuilder)}
* phase.
*
* <h2>Security Filters</h2>
*
* The following Filters are populated
*
* <ul>
* <li>
* {@link LogoutFilter}</li>
* <li>{@link LogoutFilter}</li>
* </ul>
*
* <h2>Shared Objects Created</h2>
@@ -65,19 +66,26 @@ import org.springframework.util.Assert;
* @since 3.2
* @see RememberMeConfigurer
*/
public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<LogoutConfigurer<H>, H> {
public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<LogoutConfigurer<H>, H> {
private List<LogoutHandler> logoutHandlers = new ArrayList<>();
private SecurityContextLogoutHandler contextLogoutHandler = new SecurityContextLogoutHandler();
private String logoutSuccessUrl = "/login?logout";
private LogoutSuccessHandler logoutSuccessHandler;
private String logoutUrl = "/logout";
private RequestMatcher logoutRequestMatcher;
private boolean permitAll;
private boolean customLogoutSuccess;
private LinkedHashMap<RequestMatcher, LogoutSuccessHandler> defaultLogoutSuccessHandlerMappings =
new LinkedHashMap<>();
private LinkedHashMap<RequestMatcher, LogoutSuccessHandler> defaultLogoutSuccessHandlerMappings = new LinkedHashMap<>();
/**
* Creates a new instance
@@ -87,10 +95,9 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends
}
/**
* Adds a {@link LogoutHandler}.
* {@link SecurityContextLogoutHandler} and {@link LogoutSuccessEventPublishingLogoutHandler} are added as
* last {@link LogoutHandler} instances by default.
*
* Adds a {@link LogoutHandler}. {@link SecurityContextLogoutHandler} and
* {@link LogoutSuccessEventPublishingLogoutHandler} are added as last
* {@link LogoutHandler} instances by default.
* @param logoutHandler the {@link LogoutHandler} to add
* @return the {@link LogoutConfigurer} for further customization
*/
@@ -101,16 +108,17 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends
}
/**
* Specifies if {@link SecurityContextLogoutHandler} should clear the {@link Authentication} at the time of logout.
* @param clearAuthentication true {@link SecurityContextLogoutHandler} should clear the {@link Authentication} (default), or false otherwise.
* Specifies if {@link SecurityContextLogoutHandler} should clear the
* {@link Authentication} at the time of logout.
* @param clearAuthentication true {@link SecurityContextLogoutHandler} should clear
* the {@link Authentication} (default), or false otherwise.
* @return the {@link LogoutConfigurer} for further customization
*/
public LogoutConfigurer<H> clearAuthentication(boolean clearAuthentication) {
contextLogoutHandler.setClearAuthentication(clearAuthentication);
this.contextLogoutHandler.setClearAuthentication(clearAuthentication);
return this;
}
/**
* Configures {@link SecurityContextLogoutHandler} to invalidate the
* {@link HttpSession} at the time of logout.
@@ -119,7 +127,7 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends
* @return the {@link LogoutConfigurer} for further customization
*/
public LogoutConfigurer<H> invalidateHttpSession(boolean invalidateHttpSession) {
contextLogoutHandler.setInvalidateHttpSession(invalidateHttpSession);
this.contextLogoutHandler.setInvalidateHttpSession(invalidateHttpSession);
return this;
}
@@ -131,17 +139,15 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends
*
* <p>
* It is considered best practice to use an HTTP POST on any action that changes state
* (i.e. log out) to protect against <a
* href="https://en.wikipedia.org/wiki/Cross-site_request_forgery">CSRF attacks</a>. If
* you really want to use an HTTP GET, you can use
* (i.e. log out) to protect against
* <a href="https://en.wikipedia.org/wiki/Cross-site_request_forgery">CSRF
* attacks</a>. If you really want to use an HTTP GET, you can use
* <code>logoutRequestMatcher(new AntPathRequestMatcher(logoutUrl, "GET"));</code>
* </p>
*
* @see #logoutRequestMatcher(RequestMatcher)
* @see HttpSecurity#csrf()
*
* @param logoutUrl the URL that will invoke logout.
* @return the {@link LogoutConfigurer} for further customization
* @see #logoutRequestMatcher(RequestMatcher)
* @see HttpSecurity#csrf()
*/
public LogoutConfigurer<H> logoutUrl(String logoutUrl) {
this.logoutRequestMatcher = null;
@@ -152,12 +158,10 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends
/**
* The RequestMatcher that triggers log out to occur. In most circumstances users will
* use {@link #logoutUrl(String)} which helps enforce good practices.
*
* @see #logoutUrl(String)
*
* @param logoutRequestMatcher the RequestMatcher used to determine if logout should
* occur.
* @return the {@link LogoutConfigurer} for further customization
* @see #logoutUrl(String)
*/
public LogoutConfigurer<H> logoutRequestMatcher(RequestMatcher logoutRequestMatcher) {
this.logoutRequestMatcher = logoutRequestMatcher;
@@ -168,7 +172,6 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends
* The URL to redirect to after logout has occurred. The default is "/login?logout".
* This is a shortcut for invoking {@link #logoutSuccessHandler(LogoutSuccessHandler)}
* with a {@link SimpleUrlLogoutSuccessHandler}.
*
* @param logoutSuccessUrl the URL to redirect to after logout occurred
* @return the {@link LogoutConfigurer} for further customization
*/
@@ -190,7 +193,6 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends
* Allows specifying the names of cookies to be removed on logout success. This is a
* shortcut to easily invoke {@link #addLogoutHandler(LogoutHandler)} with a
* {@link CookieClearingLogoutHandler}.
*
* @param cookieNamesToClear the names of cookies to be removed on logout success.
* @return the {@link LogoutConfigurer} for further customization
*/
@@ -201,13 +203,11 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends
/**
* Sets the {@link LogoutSuccessHandler} to use. If this is specified,
* {@link #logoutSuccessUrl(String)} is ignored.
*
* @param logoutSuccessHandler the {@link LogoutSuccessHandler} to use after a user
* has been logged out.
* @return the {@link LogoutConfigurer} for further customizations
*/
public LogoutConfigurer<H> logoutSuccessHandler(
LogoutSuccessHandler logoutSuccessHandler) {
public LogoutConfigurer<H> logoutSuccessHandler(LogoutSuccessHandler logoutSuccessHandler) {
this.logoutSuccessUrl = null;
this.customLogoutSuccess = true;
this.logoutSuccessHandler = logoutSuccessHandler;
@@ -217,18 +217,17 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends
/**
* Sets a default {@link LogoutSuccessHandler} to be used which prefers being invoked
* for the provided {@link RequestMatcher}. If no {@link LogoutSuccessHandler} is
* specified a {@link SimpleUrlLogoutSuccessHandler} will be used.
* If any default {@link LogoutSuccessHandler} instances are configured, then a
* specified a {@link SimpleUrlLogoutSuccessHandler} will be used. If any default
* {@link LogoutSuccessHandler} instances are configured, then a
* {@link DelegatingLogoutSuccessHandler} will be used that defaults to a
* {@link SimpleUrlLogoutSuccessHandler}.
*
* @param handler the {@link LogoutSuccessHandler} to use
* @param preferredMatcher the {@link RequestMatcher} for this default
* {@link LogoutSuccessHandler}
* @return the {@link LogoutConfigurer} for further customizations
*/
public LogoutConfigurer<H> defaultLogoutSuccessHandlerFor(
LogoutSuccessHandler handler, RequestMatcher preferredMatcher) {
public LogoutConfigurer<H> defaultLogoutSuccessHandlerFor(LogoutSuccessHandler handler,
RequestMatcher preferredMatcher) {
Assert.notNull(handler, "handler cannot be null");
Assert.notNull(preferredMatcher, "preferredMatcher cannot be null");
this.defaultLogoutSuccessHandlerMappings.put(preferredMatcher, handler);
@@ -238,7 +237,6 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends
/**
* Grants access to the {@link #logoutSuccessUrl(String)} and the
* {@link #logoutUrl(String)} for every user.
*
* @param permitAll if true grants access, else nothing is done
* @return the {@link LogoutConfigurer} for further customization.
*/
@@ -250,7 +248,6 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends
/**
* Gets the {@link LogoutSuccessHandler} if not null, otherwise creates a new
* {@link SimpleUrlLogoutSuccessHandler} using the {@link #logoutSuccessUrl(String)}.
*
* @return the {@link LogoutSuccessHandler} to use
*/
private LogoutSuccessHandler getLogoutSuccessHandler() {
@@ -263,22 +260,22 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends
private LogoutSuccessHandler createDefaultSuccessHandler() {
SimpleUrlLogoutSuccessHandler urlLogoutHandler = new SimpleUrlLogoutSuccessHandler();
urlLogoutHandler.setDefaultTargetUrl(logoutSuccessUrl);
if (defaultLogoutSuccessHandlerMappings.isEmpty()) {
urlLogoutHandler.setDefaultTargetUrl(this.logoutSuccessUrl);
if (this.defaultLogoutSuccessHandlerMappings.isEmpty()) {
return urlLogoutHandler;
}
DelegatingLogoutSuccessHandler successHandler = new DelegatingLogoutSuccessHandler(defaultLogoutSuccessHandlerMappings);
DelegatingLogoutSuccessHandler successHandler = new DelegatingLogoutSuccessHandler(
this.defaultLogoutSuccessHandlerMappings);
successHandler.setDefaultLogoutSuccessHandler(urlLogoutHandler);
return successHandler;
}
@Override
public void init(H http) {
if (permitAll) {
if (this.permitAll) {
PermitAllSupport.permitAll(http, this.logoutSuccessUrl);
PermitAllSupport.permitAll(http, this.getLogoutRequestMatcher(http));
}
DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http
.getSharedObject(DefaultLoginPageGeneratingFilter.class);
if (loginPageGeneratingFilter != null && !isCustomLogoutSuccess()) {
@@ -296,21 +293,19 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends
* Returns true if the logout success has been customized via
* {@link #logoutSuccessUrl(String)} or
* {@link #logoutSuccessHandler(LogoutSuccessHandler)}.
*
* @return true if logout success handling has been customized, else false
*/
boolean isCustomLogoutSuccess() {
return customLogoutSuccess;
return this.customLogoutSuccess;
}
/**
* Gets the logoutSuccesUrl or null if a
* {@link #logoutSuccessHandler(LogoutSuccessHandler)} was configured.
*
* @return the logoutSuccessUrl
*/
private String getLogoutSuccessUrl() {
return logoutSuccessUrl;
return this.logoutSuccessUrl;
}
/**
@@ -318,44 +313,48 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends
* @return the {@link LogoutHandler} instances. Cannot be null.
*/
List<LogoutHandler> getLogoutHandlers() {
return logoutHandlers;
return this.logoutHandlers;
}
/**
* Creates the {@link LogoutFilter} using the {@link LogoutHandler} instances, the
* {@link #logoutSuccessHandler(LogoutSuccessHandler)} and the
* {@link #logoutUrl(String)}.
*
* @param http the builder to use
* @return the {@link LogoutFilter} to use.
*/
private LogoutFilter createLogoutFilter(H http) {
logoutHandlers.add(contextLogoutHandler);
logoutHandlers.add(postProcess(new LogoutSuccessEventPublishingLogoutHandler()));
LogoutHandler[] handlers = logoutHandlers
.toArray(new LogoutHandler[0]);
this.logoutHandlers.add(this.contextLogoutHandler);
this.logoutHandlers.add(postProcess(new LogoutSuccessEventPublishingLogoutHandler()));
LogoutHandler[] handlers = this.logoutHandlers.toArray(new LogoutHandler[0]);
LogoutFilter result = new LogoutFilter(getLogoutSuccessHandler(), handlers);
result.setLogoutRequestMatcher(getLogoutRequestMatcher(http));
result = postProcess(result);
return result;
}
@SuppressWarnings("unchecked")
private RequestMatcher getLogoutRequestMatcher(H http) {
if (logoutRequestMatcher != null) {
return logoutRequestMatcher;
}
if (http.getConfigurer(CsrfConfigurer.class) != null) {
this.logoutRequestMatcher = new AntPathRequestMatcher(this.logoutUrl, "POST");
}
else {
this.logoutRequestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(this.logoutUrl, "GET"),
new AntPathRequestMatcher(this.logoutUrl, "POST"),
new AntPathRequestMatcher(this.logoutUrl, "PUT"),
new AntPathRequestMatcher(this.logoutUrl, "DELETE")
);
if (this.logoutRequestMatcher != null) {
return this.logoutRequestMatcher;
}
this.logoutRequestMatcher = createLogoutRequestMatcher(http);
return this.logoutRequestMatcher;
}
@SuppressWarnings("unchecked")
private RequestMatcher createLogoutRequestMatcher(H http) {
RequestMatcher post = createLogoutRequestMatcher("POST");
if (http.getConfigurer(CsrfConfigurer.class) != null) {
return post;
}
RequestMatcher get = createLogoutRequestMatcher("GET");
RequestMatcher put = createLogoutRequestMatcher("PUT");
RequestMatcher delete = createLogoutRequestMatcher("DELETE");
return new OrRequestMatcher(get, post, put, delete);
}
private RequestMatcher createLogoutRequestMatcher(String httpMethod) {
return new AntPathRequestMatcher(this.logoutUrl, httpMethod);
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import javax.servlet.http.HttpServletRequest;
@@ -21,16 +22,20 @@ import org.springframework.security.access.SecurityConfig;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractConfigAttributeRequestMatcherRegistry.UrlMapping;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
/**
* Configures non-null URL's to grant access to every URL
*
* @author Rob Winch
* @since 3.2
*/
final class PermitAllSupport {
public static void permitAll(
HttpSecurityBuilder<? extends HttpSecurityBuilder<?>> http, String... urls) {
private PermitAllSupport() {
}
static void permitAll(HttpSecurityBuilder<? extends HttpSecurityBuilder<?>> http, String... urls) {
for (String url : urls) {
if (url != null) {
permitAll(http, new ExactUrlRequestMatcher(url));
@@ -39,61 +44,47 @@ final class PermitAllSupport {
}
@SuppressWarnings("unchecked")
public static void permitAll(
HttpSecurityBuilder<? extends HttpSecurityBuilder<?>> http,
static void permitAll(HttpSecurityBuilder<? extends HttpSecurityBuilder<?>> http,
RequestMatcher... requestMatchers) {
ExpressionUrlAuthorizationConfigurer<?> configurer = http
.getConfigurer(ExpressionUrlAuthorizationConfigurer.class);
if (configurer == null) {
throw new IllegalStateException(
"permitAll only works with HttpSecurity.authorizeRequests()");
}
Assert.state(configurer != null, "permitAll only works with HttpSecurity.authorizeRequests()");
for (RequestMatcher matcher : requestMatchers) {
if (matcher != null) {
configurer
.getRegistry()
.addMapping(
0,
new UrlMapping(
matcher,
SecurityConfig
.createList(ExpressionUrlAuthorizationConfigurer.permitAll)));
configurer.getRegistry().addMapping(0, new UrlMapping(matcher,
SecurityConfig.createList(ExpressionUrlAuthorizationConfigurer.permitAll)));
}
}
}
private final static class ExactUrlRequestMatcher implements RequestMatcher {
private static final class ExactUrlRequestMatcher implements RequestMatcher {
private String processUrl;
private ExactUrlRequestMatcher(String processUrl) {
this.processUrl = processUrl;
}
@Override
public boolean matches(HttpServletRequest request) {
String uri = request.getRequestURI();
String query = request.getQueryString();
if (query != null) {
uri += "?" + query;
}
if ("".equals(request.getContextPath())) {
return uri.equals(processUrl);
return uri.equals(this.processUrl);
}
return uri.equals(request.getContextPath() + processUrl);
return uri.equals(request.getContextPath() + this.processUrl);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("ExactUrl [processUrl='").append(processUrl).append("']");
sb.append("ExactUrl [processUrl='").append(this.processUrl).append("']");
return sb.toString();
}
}
private PermitAllSupport() {
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.HashMap;
@@ -31,9 +32,11 @@ import org.springframework.security.web.PortMapperImpl;
* @author Rob Winch
* @since 3.2
*/
public final class PortMapperConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<PortMapperConfigurer<H>, H> {
public final class PortMapperConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<PortMapperConfigurer<H>, H> {
private PortMapper portMapper;
private Map<String, String> httpsPortMappings = new HashMap<>();
/**
@@ -70,16 +73,15 @@ public final class PortMapperConfigurer<H extends HttpSecurityBuilder<H>> extend
* Gets the {@link PortMapper} to use. If {@link #portMapper(PortMapper)} was not
* invoked, builds a {@link PortMapperImpl} using the port mappings specified with
* {@link #http(int)}.
*
* @return the {@link PortMapper} to use
*/
private PortMapper getPortMapper() {
if (portMapper == null) {
if (this.portMapper == null) {
PortMapperImpl portMapper = new PortMapperImpl();
portMapper.setPortMappings(httpsPortMappings);
portMapper.setPortMappings(this.httpsPortMappings);
this.portMapper = portMapper;
}
return portMapper;
return this.portMapper;
}
/**
@@ -90,6 +92,7 @@ public final class PortMapperConfigurer<H extends HttpSecurityBuilder<H>> extend
* @since 3.2
*/
public final class HttpPortMapping {
private final int httpPort;
/**
@@ -107,8 +110,10 @@ public final class PortMapperConfigurer<H extends HttpSecurityBuilder<H>> extend
* @return the {@link PortMapperConfigurer} for further customization
*/
public PortMapperConfigurer<H> mapsTo(int httpsPort) {
httpsPortMappings.put(String.valueOf(httpPort), String.valueOf(httpsPort));
PortMapperConfigurer.this.httpsPortMappings.put(String.valueOf(this.httpPort), String.valueOf(httpsPort));
return PortMapperConfigurer.this;
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.UUID;
@@ -34,6 +35,7 @@ import org.springframework.security.web.authentication.rememberme.PersistentToke
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.util.Assert;
/**
* Configures Remember Me authentication. This typically involves the user checking a box
@@ -79,21 +81,34 @@ import org.springframework.security.web.authentication.ui.DefaultLoginPageGenera
*/
public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<RememberMeConfigurer<H>, H> {
/**
* The default name for remember me parameter name and remember me cookie name
*/
private static final String DEFAULT_REMEMBER_ME_NAME = "remember-me";
private AuthenticationSuccessHandler authenticationSuccessHandler;
private String key;
private RememberMeServices rememberMeServices;
private LogoutHandler logoutHandler;
private String rememberMeParameter = DEFAULT_REMEMBER_ME_NAME;
private String rememberMeCookieName = DEFAULT_REMEMBER_ME_NAME;
private String rememberMeCookieDomain;
private PersistentTokenRepository tokenRepository;
private UserDetailsService userDetailsService;
private Integer tokenValiditySeconds;
private Boolean useSecureCookie;
private Boolean alwaysRemember;
/**
@@ -104,7 +119,6 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Allows specifying how long (in seconds) a token is valid for
*
* @param tokenValiditySeconds
* @return {@link RememberMeConfigurer} for further customization
* @see AbstractRememberMeServices#setTokenValiditySeconds(int)
@@ -122,7 +136,6 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
* By default the cookie will be secure if the request is secure. If you only want to
* use remember-me over HTTPS (recommended) you should set this property to
* {@code true}.
*
* @param useSecureCookie set to {@code true} to always user secure cookies,
* {@code false} to disable their use.
* @return the {@link RememberMeConfigurer} for further customization
@@ -140,13 +153,11 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
* {@link HttpSecurity#getSharedObject(Class)} which is set when using
* {@link WebSecurityConfigurerAdapter#configure(AuthenticationManagerBuilder)}.
* Alternatively, one can populate {@link #rememberMeServices(RememberMeServices)}.
*
* @param userDetailsService the {@link UserDetailsService} to configure
* @return the {@link RememberMeConfigurer} for further customization
* @see AbstractRememberMeServices
*/
public RememberMeConfigurer<H> userDetailsService(
UserDetailsService userDetailsService) {
public RememberMeConfigurer<H> userDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
return this;
}
@@ -154,23 +165,19 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Specifies the {@link PersistentTokenRepository} to use. The default is to use
* {@link TokenBasedRememberMeServices} instead.
*
* @param tokenRepository the {@link PersistentTokenRepository} to use
* @return the {@link RememberMeConfigurer} for further customization
*/
public RememberMeConfigurer<H> tokenRepository(
PersistentTokenRepository tokenRepository) {
public RememberMeConfigurer<H> tokenRepository(PersistentTokenRepository tokenRepository) {
this.tokenRepository = tokenRepository;
return this;
}
/**
* Sets the key to identify tokens created for remember me authentication. Default is
* a secure randomly generated key.
* If {@link #rememberMeServices(RememberMeServices)} is specified and is of type
* {@link AbstractRememberMeServices}, then the default is the key set in
* {@link AbstractRememberMeServices}.
*
* a secure randomly generated key. If {@link #rememberMeServices(RememberMeServices)}
* is specified and is of type {@link AbstractRememberMeServices}, then the default is
* the key set in {@link AbstractRememberMeServices}.
* @param key the key to identify tokens created for remember me authentication
* @return the {@link RememberMeConfigurer} for further customization
*/
@@ -181,7 +188,6 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
/**
* The HTTP parameter used to indicate to remember the user at time of login.
*
* @param rememberMeParameter the HTTP parameter used to indicate to remember the user
* @return the {@link RememberMeConfigurer} for further customization
*/
@@ -193,7 +199,6 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
/**
* The name of cookie which store the token for remember me authentication. Defaults
* to 'remember-me'.
*
* @param rememberMeCookieName the name of cookie which store the token for remember
* me authentication
* @return the {@link RememberMeConfigurer} for further customization
@@ -206,7 +211,6 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
/**
* The domain name within which the remember me cookie is visible.
*
* @param rememberMeCookieDomain the domain name within which the remember me cookie
* is visible.
* @return the {@link RememberMeConfigurer} for further customization
@@ -224,7 +228,6 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
* be invoked and the {@code doFilter()} method will return immediately, thus allowing
* the application to redirect the user to a specific URL, regardless of what the
* original request was for.
*
* @param authenticationSuccessHandler the strategy to invoke immediately before
* returning from {@code doFilter()}.
* @return {@link RememberMeConfigurer} for further customization
@@ -242,8 +245,7 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
* @return the {@link RememberMeConfigurer} for further customizations
* @see RememberMeServices
*/
public RememberMeConfigurer<H> rememberMeServices(
RememberMeServices rememberMeServices) {
public RememberMeConfigurer<H> rememberMeServices(RememberMeServices rememberMeServices) {
this.rememberMeServices = rememberMeServices;
return this;
}
@@ -253,7 +255,6 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
* not set.
* <p>
* By default this will be set to {@code false}.
*
* @param alwaysRemember set to {@code true} to always trigger remember me,
* {@code false} to use the remember-me parameter.
* @return the {@link RememberMeConfigurer} for further customization
@@ -275,36 +276,30 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
if (logoutConfigurer != null && this.logoutHandler != null) {
logoutConfigurer.addLogoutHandler(this.logoutHandler);
}
RememberMeAuthenticationProvider authenticationProvider = new RememberMeAuthenticationProvider(
key);
RememberMeAuthenticationProvider authenticationProvider = new RememberMeAuthenticationProvider(key);
authenticationProvider = postProcess(authenticationProvider);
http.authenticationProvider(authenticationProvider);
initDefaultLoginFilter(http);
}
@Override
public void configure(H http) {
RememberMeAuthenticationFilter rememberMeFilter = new RememberMeAuthenticationFilter(
http.getSharedObject(AuthenticationManager.class),
this.rememberMeServices);
http.getSharedObject(AuthenticationManager.class), this.rememberMeServices);
if (this.authenticationSuccessHandler != null) {
rememberMeFilter
.setAuthenticationSuccessHandler(this.authenticationSuccessHandler);
rememberMeFilter.setAuthenticationSuccessHandler(this.authenticationSuccessHandler);
}
rememberMeFilter = postProcess(rememberMeFilter);
http.addFilter(rememberMeFilter);
}
/**
* Validate rememberMeServices and rememberMeCookieName have not been set at
* the same time.
* Validate rememberMeServices and rememberMeCookieName have not been set at the same
* time.
*/
private void validateInput() {
if (this.rememberMeServices != null && this.rememberMeCookieName != DEFAULT_REMEMBER_ME_NAME) {
throw new IllegalArgumentException("Can not set rememberMeCookieName " +
"and custom rememberMeServices.");
if (this.rememberMeServices != null && !DEFAULT_REMEMBER_ME_NAME.equals(this.rememberMeCookieName)) {
throw new IllegalArgumentException("Can not set rememberMeCookieName and custom rememberMeServices.");
}
}
@@ -319,7 +314,6 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
/**
* If available, initializes the {@link DefaultLoginPageGeneratingFilter} shared
* object.
*
* @param http the {@link HttpSecurityBuilder} to use
*/
private void initDefaultLoginFilter(H http) {
@@ -337,17 +331,14 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
* @return the {@link RememberMeServices} to use
* @throws Exception
*/
private RememberMeServices getRememberMeServices(H http, String key)
throws Exception {
private RememberMeServices getRememberMeServices(H http, String key) throws Exception {
if (this.rememberMeServices != null) {
if (this.rememberMeServices instanceof LogoutHandler
&& this.logoutHandler == null) {
if (this.rememberMeServices instanceof LogoutHandler && this.logoutHandler == null) {
this.logoutHandler = (LogoutHandler) this.rememberMeServices;
}
return this.rememberMeServices;
}
AbstractRememberMeServices tokenRememberMeServices = createRememberMeServices(
http, key);
AbstractRememberMeServices tokenRememberMeServices = createRememberMeServices(http, key);
tokenRememberMeServices.setParameter(this.rememberMeParameter);
tokenRememberMeServices.setCookieName(this.rememberMeCookieName);
if (this.rememberMeCookieDomain != null) {
@@ -372,49 +363,41 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
* Creates the {@link RememberMeServices} to use when none is provided. The result is
* either {@link PersistentTokenRepository} (if a {@link PersistentTokenRepository} is
* specified, else {@link TokenBasedRememberMeServices}.
*
* @param http the {@link HttpSecurity} to lookup shared objects
* @param key the {@link #key(String)}
* @return the {@link RememberMeServices} to use
*/
private AbstractRememberMeServices createRememberMeServices(H http, String key) {
return this.tokenRepository == null
? createTokenBasedRememberMeServices(http, key)
: createPersistentRememberMeServices(http, key);
return (this.tokenRepository != null) ? createPersistentRememberMeServices(http, key)
: createTokenBasedRememberMeServices(http, key);
}
/**
* Creates {@link TokenBasedRememberMeServices}
*
* @param http the {@link HttpSecurity} to lookup shared objects
* @param key the {@link #key(String)}
* @return the {@link TokenBasedRememberMeServices}
*/
private AbstractRememberMeServices createTokenBasedRememberMeServices(H http,
String key) {
private AbstractRememberMeServices createTokenBasedRememberMeServices(H http, String key) {
UserDetailsService userDetailsService = getUserDetailsService(http);
return new TokenBasedRememberMeServices(key, userDetailsService);
}
/**
* Creates {@link PersistentTokenBasedRememberMeServices}
*
* @param http the {@link HttpSecurity} to lookup shared objects
* @param key the {@link #key(String)}
* @return the {@link PersistentTokenBasedRememberMeServices}
*/
private AbstractRememberMeServices createPersistentRememberMeServices(H http,
String key) {
private AbstractRememberMeServices createPersistentRememberMeServices(H http, String key) {
UserDetailsService userDetailsService = getUserDetailsService(http);
return new PersistentTokenBasedRememberMeServices(key, userDetailsService,
this.tokenRepository);
return new PersistentTokenBasedRememberMeServices(key, userDetailsService, this.tokenRepository);
}
/**
* Gets the {@link UserDetailsService} to use. Either the explicitly configure
* {@link UserDetailsService} from {@link #userDetailsService(UserDetailsService)} or
* a shared object from {@link HttpSecurity#getSharedObject(Class)}.
*
* @param http {@link HttpSecurity} to get the shared {@link UserDetailsService}
* @return the {@link UserDetailsService} to use
*/
@@ -422,32 +405,30 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
if (this.userDetailsService == null) {
this.userDetailsService = http.getSharedObject(UserDetailsService.class);
}
if (this.userDetailsService == null) {
throw new IllegalStateException("userDetailsService cannot be null. Invoke "
+ RememberMeConfigurer.class.getSimpleName()
+ "#userDetailsService(UserDetailsService) or see its javadoc for alternative approaches.");
}
Assert.state(this.userDetailsService != null,
() -> "userDetailsService cannot be null. Invoke " + RememberMeConfigurer.class.getSimpleName()
+ "#userDetailsService(UserDetailsService) or see its javadoc for alternative approaches.");
return this.userDetailsService;
}
/**
* Gets the key to use for validating remember me tokens. If a value was passed into
* {@link #key(String)}, then that is returned.
* Alternatively, if a key was specified in the
* {@link #rememberMeServices(RememberMeServices)}}, then that is returned.
* If no key was specified in either of those cases, then a secure random string is
* {@link #key(String)}, then that is returned. Alternatively, if a key was specified
* in the {@link #rememberMeServices(RememberMeServices)}}, then that is returned. If
* no key was specified in either of those cases, then a secure random string is
* generated.
*
* @return the remember me key to use
*/
private String getKey() {
if (this.key == null) {
if (this.rememberMeServices instanceof AbstractRememberMeServices) {
this.key = ((AbstractRememberMeServices) rememberMeServices).getKey();
} else {
this.key = ((AbstractRememberMeServices) this.rememberMeServices).getKey();
}
else {
this.key = UUID.randomUUID().toString();
}
}
return this.key;
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.ArrayList;
@@ -69,8 +70,8 @@ import org.springframework.web.accept.HeaderContentNegotiationStrategy;
* @since 3.2
* @see RequestCache
*/
public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<RequestCacheConfigurer<H>, H> {
public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<RequestCacheConfigurer<H>, H> {
public RequestCacheConfigurer() {
}
@@ -79,7 +80,6 @@ public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>> exte
* Allows explicit configuration of the {@link RequestCache} to be used. Defaults to
* try finding a {@link RequestCache} as a shared object. Then falls back to a
* {@link HttpSessionRequestCache}.
*
* @param requestCache the explicit {@link RequestCache} to use
* @return the {@link RequestCacheConfigurer} for further customization
*/
@@ -102,8 +102,7 @@ public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>> exte
@Override
public void configure(H http) {
RequestCache requestCache = getRequestCache(http);
RequestCacheAwareFilter requestCacheFilter = new RequestCacheAwareFilter(
requestCache);
RequestCacheAwareFilter requestCacheFilter = new RequestCacheAwareFilter(requestCache);
requestCacheFilter = postProcess(requestCacheFilter);
http.addFilter(requestCacheFilter);
}
@@ -113,7 +112,6 @@ public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>> exte
* {@link #requestCache(org.springframework.security.web.savedrequest.RequestCache)},
* then it is used. Otherwise, an attempt to find a {@link RequestCache} shared object
* is made. If that fails, an {@link HttpSessionRequestCache} is used
*
* @param http the {@link HttpSecurity} to attempt to fined the shared object
* @return the {@link RequestCache} to use
*/
@@ -138,21 +136,18 @@ public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>> exte
}
try {
return context.getBean(type);
} catch (NoSuchBeanDefinitionException e) {
}
catch (NoSuchBeanDefinitionException ex) {
return null;
}
}
@SuppressWarnings("unchecked")
private RequestMatcher createDefaultSavedRequestMatcher(H http) {
RequestMatcher notFavIcon = new NegatedRequestMatcher(new AntPathRequestMatcher(
"/**/favicon.*"));
RequestMatcher notFavIcon = new NegatedRequestMatcher(new AntPathRequestMatcher("/**/favicon.*"));
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
boolean isCsrfEnabled = http.getConfigurer(CsrfConfigurer.class) != null;
List<RequestMatcher> matchers = new ArrayList<>();
if (isCsrfEnabled) {
RequestMatcher getRequests = new AntPathRequestMatcher("/**", "GET");
@@ -163,7 +158,6 @@ public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>> exte
matchers.add(notXRequestedWith);
matchers.add(notMatchingMediaType(http, MediaType.MULTIPART_FORM_DATA));
matchers.add(notMatchingMediaType(http, MediaType.TEXT_EVENT_STREAM));
return new AndRequestMatcher(matchers);
}
@@ -172,9 +166,9 @@ public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>> exte
if (contentNegotiationStrategy == null) {
contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
}
MediaTypeRequestMatcher mediaRequest = new MediaTypeRequestMatcher(contentNegotiationStrategy, mediaType);
mediaRequest.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
return new NegatedRequestMatcher(mediaRequest);
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
@@ -58,8 +59,8 @@ import org.springframework.security.web.context.SecurityContextRepository;
* @author Rob Winch
* @since 3.2
*/
public final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<SecurityContextConfigurer<H>, H> {
public final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<SecurityContextConfigurer<H>, H> {
/**
* Creates a new instance
@@ -73,32 +74,28 @@ public final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>> e
* @param securityContextRepository the {@link SecurityContextRepository} to use
* @return the {@link HttpSecurity} for further customizations
*/
public SecurityContextConfigurer<H> securityContextRepository(
SecurityContextRepository securityContextRepository) {
getBuilder().setSharedObject(SecurityContextRepository.class,
securityContextRepository);
public SecurityContextConfigurer<H> securityContextRepository(SecurityContextRepository securityContextRepository) {
getBuilder().setSharedObject(SecurityContextRepository.class, securityContextRepository);
return this;
}
@Override
@SuppressWarnings("unchecked")
public void configure(H http) {
SecurityContextRepository securityContextRepository = http
.getSharedObject(SecurityContextRepository.class);
SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
if (securityContextRepository == null) {
securityContextRepository = new HttpSessionSecurityContextRepository();
}
SecurityContextPersistenceFilter securityContextFilter = new SecurityContextPersistenceFilter(
securityContextRepository);
SessionManagementConfigurer<?> sessionManagement = http
.getConfigurer(SessionManagementConfigurer.class);
SessionCreationPolicy sessionCreationPolicy = sessionManagement == null ? null
: sessionManagement.getSessionCreationPolicy();
SessionManagementConfigurer<?> sessionManagement = http.getConfigurer(SessionManagementConfigurer.class);
SessionCreationPolicy sessionCreationPolicy = (sessionManagement != null)
? sessionManagement.getSessionCreationPolicy() : null;
if (SessionCreationPolicy.ALWAYS == sessionCreationPolicy) {
securityContextFilter.setForceEagerSessionCreation(true);
}
securityContextFilter = postProcess(securityContextFilter);
http.addFilter(securityContextFilter);
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.List;
@@ -57,8 +58,9 @@ import org.springframework.security.web.servletapi.SecurityContextHolderAwareReq
* @author Rob Winch
* @since 3.2
*/
public final class ServletApiConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<ServletApiConfigurer<H>, H> {
public final class ServletApiConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<ServletApiConfigurer<H>, H> {
private SecurityContextHolderAwareRequestFilter securityContextRequestFilter = new SecurityContextHolderAwareRequestFilter();
/**
@@ -69,39 +71,36 @@ public final class ServletApiConfigurer<H extends HttpSecurityBuilder<H>> extend
}
public ServletApiConfigurer<H> rolePrefix(String rolePrefix) {
securityContextRequestFilter.setRolePrefix(rolePrefix);
this.securityContextRequestFilter.setRolePrefix(rolePrefix);
return this;
}
@Override
@SuppressWarnings("unchecked")
public void configure(H http) {
securityContextRequestFilter.setAuthenticationManager(http
.getSharedObject(AuthenticationManager.class));
ExceptionHandlingConfigurer<H> exceptionConf = http
.getConfigurer(ExceptionHandlingConfigurer.class);
AuthenticationEntryPoint authenticationEntryPoint = exceptionConf == null ? null
: exceptionConf.getAuthenticationEntryPoint(http);
securityContextRequestFilter
.setAuthenticationEntryPoint(authenticationEntryPoint);
this.securityContextRequestFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
ExceptionHandlingConfigurer<H> exceptionConf = http.getConfigurer(ExceptionHandlingConfigurer.class);
AuthenticationEntryPoint authenticationEntryPoint = (exceptionConf != null)
? exceptionConf.getAuthenticationEntryPoint(http) : null;
this.securityContextRequestFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
LogoutConfigurer<H> logoutConf = http.getConfigurer(LogoutConfigurer.class);
List<LogoutHandler> logoutHandlers = logoutConf == null ? null : logoutConf
.getLogoutHandlers();
securityContextRequestFilter.setLogoutHandlers(logoutHandlers);
AuthenticationTrustResolver trustResolver = http
.getSharedObject(AuthenticationTrustResolver.class);
List<LogoutHandler> logoutHandlers = (logoutConf != null) ? logoutConf.getLogoutHandlers() : null;
this.securityContextRequestFilter.setLogoutHandlers(logoutHandlers);
AuthenticationTrustResolver trustResolver = http.getSharedObject(AuthenticationTrustResolver.class);
if (trustResolver != null) {
securityContextRequestFilter.setTrustResolver(trustResolver);
this.securityContextRequestFilter.setTrustResolver(trustResolver);
}
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
if (context != null) {
String[] grantedAuthorityDefaultsBeanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class);
if (grantedAuthorityDefaultsBeanNames.length == 1) {
GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBean(grantedAuthorityDefaultsBeanNames[0], GrantedAuthorityDefaults.class);
securityContextRequestFilter.setRolePrefix(grantedAuthorityDefaults.getRolePrefix());
GrantedAuthorityDefaults grantedAuthorityDefaults = context
.getBean(grantedAuthorityDefaultsBeanNames[0], GrantedAuthorityDefaults.class);
this.securityContextRequestFilter.setRolePrefix(grantedAuthorityDefaults.getRolePrefix());
}
}
securityContextRequestFilter = postProcess(securityContextRequestFilter);
http.addFilter(securityContextRequestFilter);
this.securityContextRequestFilter = postProcess(this.securityContextRequestFilter);
http.addFilter(this.securityContextRequestFilter);
}
}

View File

@@ -13,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@@ -98,21 +100,37 @@ import org.springframework.util.CollectionUtils;
*/
public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<SessionManagementConfigurer<H>, H> {
private final SessionAuthenticationStrategy DEFAULT_SESSION_FIXATION_STRATEGY = createDefaultSessionFixationProtectionStrategy();
private SessionAuthenticationStrategy sessionFixationAuthenticationStrategy = this.DEFAULT_SESSION_FIXATION_STRATEGY;
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
private SessionAuthenticationStrategy providedSessionAuthenticationStrategy;
private InvalidSessionStrategy invalidSessionStrategy;
private SessionInformationExpiredStrategy expiredSessionStrategy;
private List<SessionAuthenticationStrategy> sessionAuthenticationStrategies = new ArrayList<>();
private SessionRegistry sessionRegistry;
private Integer maximumSessions;
private String expiredUrl;
private boolean maxSessionsPreventsLogin;
private SessionCreationPolicy sessionPolicy;
private boolean enableSessionUrlRewriting;
private String invalidSessionUrl;
private String sessionAuthenticationErrorUrl;
private AuthenticationFailureHandler sessionAuthenticationFailureHandler;
/**
@@ -127,7 +145,6 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
* {@link SimpleRedirectInvalidSessionStrategy} configured with the attribute value.
* When an invalid session ID is submitted, the strategy will be invoked, redirecting
* to the configured URL.
*
* @param invalidSessionUrl the URL to redirect to when an invalid session is detected
* @return the {@link SessionManagementConfigurer} for further customization
*/
@@ -144,8 +161,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
* submitted.
* @return the {@link SessionManagementConfigurer} for further customization
*/
public SessionManagementConfigurer<H> invalidSessionStrategy(
InvalidSessionStrategy invalidSessionStrategy) {
public SessionManagementConfigurer<H> invalidSessionStrategy(InvalidSessionStrategy invalidSessionStrategy) {
Assert.notNull(invalidSessionStrategy, "invalidSessionStrategy");
this.invalidSessionStrategy = invalidSessionStrategy;
return this;
@@ -157,12 +173,10 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
* (402) error code will be returned to the client. Note that this attribute doesn't
* apply if the error occurs during a form-based login, where the URL for
* authentication failure will take precedence.
*
* @param sessionAuthenticationErrorUrl the URL to redirect to
* @return the {@link SessionManagementConfigurer} for further customization
*/
public SessionManagementConfigurer<H> sessionAuthenticationErrorUrl(
String sessionAuthenticationErrorUrl) {
public SessionManagementConfigurer<H> sessionAuthenticationErrorUrl(String sessionAuthenticationErrorUrl) {
this.sessionAuthenticationErrorUrl = sessionAuthenticationErrorUrl;
return this;
}
@@ -173,7 +187,6 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
* (402) error code will be returned to the client. Note that this attribute doesn't
* apply if the error occurs during a form-based login, where the URL for
* authentication failure will take precedence.
*
* @param sessionAuthenticationFailureHandler the handler to use
* @return the {@link SessionManagementConfigurer} for further customization
*/
@@ -188,14 +201,12 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
* {@link HttpServletResponse#encodeRedirectURL(String)} or
* {@link HttpServletResponse#encodeURL(String)}, otherwise disallows HTTP sessions to
* be included in the URL. This prevents leaking information to external domains.
*
* @param enableSessionUrlRewriting true if should allow the JSESSIONID to be
* rewritten into the URLs, else false (default)
* @return the {@link SessionManagementConfigurer} for further customization
* @see HttpSessionSecurityContextRepository#setDisableUrlRewriting(boolean)
*/
public SessionManagementConfigurer<H> enableSessionUrlRewriting(
boolean enableSessionUrlRewriting) {
public SessionManagementConfigurer<H> enableSessionUrlRewriting(boolean enableSessionUrlRewriting) {
this.enableSessionUrlRewriting = enableSessionUrlRewriting;
return this;
}
@@ -205,29 +216,27 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
* @param sessionCreationPolicy the {@link SessionCreationPolicy} to use. Cannot be
* null.
* @return the {@link SessionManagementConfigurer} for further customizations
* @see SessionCreationPolicy
* @throws IllegalArgumentException if {@link SessionCreationPolicy} is null.
* @see SessionCreationPolicy
*/
public SessionManagementConfigurer<H> sessionCreationPolicy(
SessionCreationPolicy sessionCreationPolicy) {
public SessionManagementConfigurer<H> sessionCreationPolicy(SessionCreationPolicy sessionCreationPolicy) {
Assert.notNull(sessionCreationPolicy, "sessionCreationPolicy cannot be null");
this.sessionPolicy = sessionCreationPolicy;
return this;
}
/**
* Allows explicitly specifying the {@link SessionAuthenticationStrategy}.
* The default is to use {@link ChangeSessionIdAuthenticationStrategy}.
* If restricting the maximum number of sessions is configured, then
* Allows explicitly specifying the {@link SessionAuthenticationStrategy}. The default
* is to use {@link ChangeSessionIdAuthenticationStrategy}. If restricting the maximum
* number of sessions is configured, then
* {@link CompositeSessionAuthenticationStrategy} delegating to
* {@link ConcurrentSessionControlAuthenticationStrategy},
* the default OR supplied {@code SessionAuthenticationStrategy} and
* {@link ConcurrentSessionControlAuthenticationStrategy}, the default OR supplied
* {@code SessionAuthenticationStrategy} and
* {@link RegisterSessionAuthenticationStrategy}.
*
* <p>
* NOTE: Supplying a custom {@link SessionAuthenticationStrategy} will override the
* default session fixation strategy.
*
* @param sessionAuthenticationStrategy
* @return the {@link SessionManagementConfigurer} for further customizations
*/
@@ -240,7 +249,6 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Adds an additional {@link SessionAuthenticationStrategy} to be used within the
* {@link CompositeSessionAuthenticationStrategy}.
*
* @param sessionAuthenticationStrategy
* @return the {@link SessionManagementConfigurer} for further customizations
*/
@@ -252,7 +260,6 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Allows changing the default {@link SessionFixationProtectionStrategy}.
*
* @return the {@link SessionFixationConfigurer} for further customizations
*/
public SessionFixationConfigurer sessionFixation() {
@@ -261,12 +268,12 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Allows configuring session fixation protection.
*
* @param sessionFixationCustomizer the {@link Customizer} to provide more options for
* the {@link SessionFixationConfigurer}
* @return the {@link SessionManagementConfigurer} for further customizations
*/
public SessionManagementConfigurer<H> sessionFixation(Customizer<SessionFixationConfigurer> sessionFixationCustomizer) {
public SessionManagementConfigurer<H> sessionFixation(
Customizer<SessionFixationConfigurer> sessionFixationCustomizer) {
sessionFixationCustomizer.customize(new SessionFixationConfigurer());
return this;
}
@@ -285,12 +292,12 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Controls the maximum number of sessions for a user. The default is to allow any
* number of users.
*
* @param sessionConcurrencyCustomizer the {@link Customizer} to provide more options for
* the {@link ConcurrencyControlConfigurer}
* @param sessionConcurrencyCustomizer the {@link Customizer} to provide more options
* for the {@link ConcurrencyControlConfigurer}
* @return the {@link SessionManagementConfigurer} for further customizations
*/
public SessionManagementConfigurer<H> sessionConcurrency(Customizer<ConcurrencyControlConfigurer> sessionConcurrencyCustomizer) {
public SessionManagementConfigurer<H> sessionConcurrency(
Customizer<ConcurrencyControlConfigurer> sessionConcurrencyCustomizer) {
sessionConcurrencyCustomizer.customize(new ConcurrencyControlConfigurer());
return this;
}
@@ -302,207 +309,46 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
*/
private void setSessionFixationAuthenticationStrategy(
SessionAuthenticationStrategy sessionFixationAuthenticationStrategy) {
this.sessionFixationAuthenticationStrategy = postProcess(
sessionFixationAuthenticationStrategy);
}
/**
* Allows configuring SessionFixation protection
*
* @author Rob Winch
*/
public final class SessionFixationConfigurer {
/**
* Specifies that a new session should be created, but the session attributes from
* the original {@link HttpSession} should not be retained.
*
* @return the {@link SessionManagementConfigurer} for further customizations
*/
public SessionManagementConfigurer<H> newSession() {
SessionFixationProtectionStrategy sessionFixationProtectionStrategy = new SessionFixationProtectionStrategy();
sessionFixationProtectionStrategy.setMigrateSessionAttributes(false);
setSessionFixationAuthenticationStrategy(sessionFixationProtectionStrategy);
return SessionManagementConfigurer.this;
}
/**
* Specifies that a new session should be created and the session attributes from
* the original {@link HttpSession} should be retained.
*
* @return the {@link SessionManagementConfigurer} for further customizations
*/
public SessionManagementConfigurer<H> migrateSession() {
setSessionFixationAuthenticationStrategy(
new SessionFixationProtectionStrategy());
return SessionManagementConfigurer.this;
}
/**
* Specifies that the Servlet container-provided session fixation protection
* should be used. When a session authenticates, the Servlet method
* {@code HttpServletRequest#changeSessionId()} is called to change the session ID
* and retain all session attributes.
*
* @return the {@link SessionManagementConfigurer} for further customizations
*/
public SessionManagementConfigurer<H> changeSessionId() {
setSessionFixationAuthenticationStrategy(
new ChangeSessionIdAuthenticationStrategy());
return SessionManagementConfigurer.this;
}
/**
* Specifies that no session fixation protection should be enabled. This may be
* useful when utilizing other mechanisms for protecting against session fixation.
* For example, if application container session fixation protection is already in
* use. Otherwise, this option is not recommended.
*
* @return the {@link SessionManagementConfigurer} for further customizations
*/
public SessionManagementConfigurer<H> none() {
setSessionFixationAuthenticationStrategy(
new NullAuthenticatedSessionStrategy());
return SessionManagementConfigurer.this;
}
}
/**
* Allows configuring controlling of multiple sessions.
*
* @author Rob Winch
*/
public final class ConcurrencyControlConfigurer {
/**
* Controls the maximum number of sessions for a user. The default is to allow any
* number of users.
*
* @param maximumSessions the maximum number of sessions for a user
* @return the {@link ConcurrencyControlConfigurer} for further customizations
*/
public ConcurrencyControlConfigurer maximumSessions(int maximumSessions) {
SessionManagementConfigurer.this.maximumSessions = maximumSessions;
return this;
}
/**
* The URL to redirect to if a user tries to access a resource and their session
* has been expired due to too many sessions for the current user. The default is
* to write a simple error message to the response.
*
* @param expiredUrl the URL to redirect to
* @return the {@link ConcurrencyControlConfigurer} for further customizations
*/
public ConcurrencyControlConfigurer expiredUrl(String expiredUrl) {
SessionManagementConfigurer.this.expiredUrl = expiredUrl;
return this;
}
/**
* Determines the behaviour when an expired session is detected.
*
* @param expiredSessionStrategy the {@link SessionInformationExpiredStrategy} to
* use when an expired session is detected.
* @return the {@link ConcurrencyControlConfigurer} for further customizations
*/
public ConcurrencyControlConfigurer expiredSessionStrategy(
SessionInformationExpiredStrategy expiredSessionStrategy) {
SessionManagementConfigurer.this.expiredSessionStrategy = expiredSessionStrategy;
return this;
}
/**
* If true, prevents a user from authenticating when the
* {@link #maximumSessions(int)} has been reached. Otherwise (default), the user
* who authenticates is allowed access and an existing user's session is expired.
* The user's who's session is forcibly expired is sent to
* {@link #expiredUrl(String)}. The advantage of this approach is if a user
* accidentally does not log out, there is no need for an administrator to
* intervene or wait till their session expires.
*
* @param maxSessionsPreventsLogin true to have an error at time of
* authentication, else false (default)
* @return the {@link ConcurrencyControlConfigurer} for further customizations
*/
public ConcurrencyControlConfigurer maxSessionsPreventsLogin(
boolean maxSessionsPreventsLogin) {
SessionManagementConfigurer.this.maxSessionsPreventsLogin = maxSessionsPreventsLogin;
return this;
}
/**
* Controls the {@link SessionRegistry} implementation used. The default is
* {@link SessionRegistryImpl} which is an in memory implementation.
*
* @param sessionRegistry the {@link SessionRegistry} to use
* @return the {@link ConcurrencyControlConfigurer} for further customizations
*/
public ConcurrencyControlConfigurer sessionRegistry(
SessionRegistry sessionRegistry) {
SessionManagementConfigurer.this.sessionRegistry = sessionRegistry;
return this;
}
/**
* Used to chain back to the {@link SessionManagementConfigurer}
*
* @return the {@link SessionManagementConfigurer} for further customizations
*/
public SessionManagementConfigurer<H> and() {
return SessionManagementConfigurer.this;
}
private ConcurrencyControlConfigurer() {
}
this.sessionFixationAuthenticationStrategy = postProcess(sessionFixationAuthenticationStrategy);
}
@Override
public void init(H http) {
SecurityContextRepository securityContextRepository = http
.getSharedObject(SecurityContextRepository.class);
SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
boolean stateless = isStateless();
if (securityContextRepository == null) {
if (stateless) {
http.setSharedObject(SecurityContextRepository.class,
new NullSecurityContextRepository());
http.setSharedObject(SecurityContextRepository.class, new NullSecurityContextRepository());
}
else {
HttpSessionSecurityContextRepository httpSecurityRepository = new HttpSessionSecurityContextRepository();
httpSecurityRepository
.setDisableUrlRewriting(!this.enableSessionUrlRewriting);
httpSecurityRepository.setDisableUrlRewriting(!this.enableSessionUrlRewriting);
httpSecurityRepository.setAllowSessionCreation(isAllowSessionCreation());
AuthenticationTrustResolver trustResolver = http
.getSharedObject(AuthenticationTrustResolver.class);
AuthenticationTrustResolver trustResolver = http.getSharedObject(AuthenticationTrustResolver.class);
if (trustResolver != null) {
httpSecurityRepository.setTrustResolver(trustResolver);
}
http.setSharedObject(SecurityContextRepository.class,
httpSecurityRepository);
http.setSharedObject(SecurityContextRepository.class, httpSecurityRepository);
}
}
RequestCache requestCache = http.getSharedObject(RequestCache.class);
if (requestCache == null) {
if (stateless) {
http.setSharedObject(RequestCache.class, new NullRequestCache());
}
}
http.setSharedObject(SessionAuthenticationStrategy.class,
getSessionAuthenticationStrategy(http));
http.setSharedObject(SessionAuthenticationStrategy.class, getSessionAuthenticationStrategy(http));
http.setSharedObject(InvalidSessionStrategy.class, getInvalidSessionStrategy());
}
@Override
public void configure(H http) {
SecurityContextRepository securityContextRepository = http
.getSharedObject(SecurityContextRepository.class);
SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(
securityContextRepository, getSessionAuthenticationStrategy(http));
SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(securityContextRepository,
getSessionAuthenticationStrategy(http));
if (this.sessionAuthenticationErrorUrl != null) {
sessionManagementFilter.setAuthenticationFailureHandler(
new SimpleUrlAuthenticationFailureHandler(
this.sessionAuthenticationErrorUrl));
new SimpleUrlAuthenticationFailureHandler(this.sessionAuthenticationErrorUrl));
}
InvalidSessionStrategy strategy = getInvalidSessionStrategy();
if (strategy != null) {
@@ -512,13 +358,11 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
if (failureHandler != null) {
sessionManagementFilter.setAuthenticationFailureHandler(failureHandler);
}
AuthenticationTrustResolver trustResolver = http
.getSharedObject(AuthenticationTrustResolver.class);
AuthenticationTrustResolver trustResolver = http.getSharedObject(AuthenticationTrustResolver.class);
if (trustResolver != null) {
sessionManagementFilter.setTrustResolver(trustResolver);
}
sessionManagementFilter = postProcess(sessionManagementFilter);
http.addFilter(sessionManagementFilter);
if (isConcurrentSessionControlEnabled()) {
ConcurrentSessionFilter concurrentSessionFilter = createConcurrencyFilter(http);
@@ -531,12 +375,9 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
private ConcurrentSessionFilter createConcurrencyFilter(H http) {
SessionInformationExpiredStrategy expireStrategy = getExpiredSessionStrategy();
SessionRegistry sessionRegistry = getSessionRegistry(http);
ConcurrentSessionFilter concurrentSessionFilter;
if (expireStrategy == null) {
concurrentSessionFilter = new ConcurrentSessionFilter(sessionRegistry);
} else {
concurrentSessionFilter = new ConcurrentSessionFilter(sessionRegistry, expireStrategy);
}
ConcurrentSessionFilter concurrentSessionFilter = (expireStrategy != null)
? new ConcurrentSessionFilter(sessionRegistry, expireStrategy)
: new ConcurrentSessionFilter(sessionRegistry);
LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
if (logoutConfigurer != null) {
List<LogoutHandler> logoutHandlers = logoutConfigurer.getLogoutHandlers();
@@ -551,20 +392,16 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
* Gets the {@link InvalidSessionStrategy} to use. If null and
* {@link #invalidSessionUrl} is not null defaults to
* {@link SimpleRedirectInvalidSessionStrategy}.
*
* @return the {@link InvalidSessionStrategy} to use
*/
InvalidSessionStrategy getInvalidSessionStrategy() {
if (this.invalidSessionStrategy != null) {
return this.invalidSessionStrategy;
}
if (this.invalidSessionUrl == null) {
return null;
}
this.invalidSessionStrategy = new SimpleRedirectInvalidSessionStrategy(
this.invalidSessionUrl);
this.invalidSessionStrategy = new SimpleRedirectInvalidSessionStrategy(this.invalidSessionUrl);
return this.invalidSessionStrategy;
}
@@ -572,13 +409,10 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
if (this.expiredSessionStrategy != null) {
return this.expiredSessionStrategy;
}
if (this.expiredUrl == null) {
return null;
}
this.expiredSessionStrategy = new SimpleRedirectSessionInformationExpiredStrategy(
this.expiredUrl);
this.expiredSessionStrategy = new SimpleRedirectSessionInformationExpiredStrategy(this.expiredUrl);
return this.expiredSessionStrategy;
}
@@ -586,11 +420,9 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
if (this.sessionAuthenticationFailureHandler != null) {
return this.sessionAuthenticationFailureHandler;
}
if (this.sessionAuthenticationErrorUrl == null) {
return null;
}
this.sessionAuthenticationFailureHandler = new SimpleUrlAuthenticationFailureHandler(
this.sessionAuthenticationErrorUrl);
return this.sessionAuthenticationFailureHandler;
@@ -604,11 +436,8 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
if (this.sessionPolicy != null) {
return this.sessionPolicy;
}
SessionCreationPolicy sessionPolicy =
getBuilder().getSharedObject(SessionCreationPolicy.class);
return sessionPolicy == null ?
SessionCreationPolicy.IF_REQUIRED : sessionPolicy;
SessionCreationPolicy sessionPolicy = getBuilder().getSharedObject(SessionCreationPolicy.class);
return (sessionPolicy != null) ? sessionPolicy : SessionCreationPolicy.IF_REQUIRED;
}
/**
@@ -618,8 +447,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
*/
private boolean isAllowSessionCreation() {
SessionCreationPolicy sessionPolicy = getSessionCreationPolicy();
return SessionCreationPolicy.ALWAYS == sessionPolicy
|| SessionCreationPolicy.IF_REQUIRED == sessionPolicy;
return SessionCreationPolicy.ALWAYS == sessionPolicy || SessionCreationPolicy.IF_REQUIRED == sessionPolicy;
}
/**
@@ -635,7 +463,6 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
* Gets the customized {@link SessionAuthenticationStrategy} if
* {@link #sessionAuthenticationStrategy(SessionAuthenticationStrategy)} was
* specified. Otherwise creates a default {@link SessionAuthenticationStrategy}.
*
* @return the {@link SessionAuthenticationStrategy} to use
*/
private SessionAuthenticationStrategy getSessionAuthenticationStrategy(H http) {
@@ -647,8 +474,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
if (this.providedSessionAuthenticationStrategy == null) {
// If the user did not provide a SessionAuthenticationStrategy
// then default to sessionFixationAuthenticationStrategy
defaultSessionAuthenticationStrategy = postProcess(
this.sessionFixationAuthenticationStrategy);
defaultSessionAuthenticationStrategy = postProcess(this.sessionFixationAuthenticationStrategy);
}
else {
defaultSessionAuthenticationStrategy = this.providedSessionAuthenticationStrategy;
@@ -658,10 +484,8 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlStrategy = new ConcurrentSessionControlAuthenticationStrategy(
sessionRegistry);
concurrentSessionControlStrategy.setMaximumSessions(this.maximumSessions);
concurrentSessionControlStrategy
.setExceptionIfMaximumExceeded(this.maxSessionsPreventsLogin);
concurrentSessionControlStrategy = postProcess(
concurrentSessionControlStrategy);
concurrentSessionControlStrategy.setExceptionIfMaximumExceeded(this.maxSessionsPreventsLogin);
concurrentSessionControlStrategy = postProcess(concurrentSessionControlStrategy);
RegisterSessionAuthenticationStrategy registerSessionStrategy = new RegisterSessionAuthenticationStrategy(
sessionRegistry);
@@ -690,14 +514,12 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
return this.sessionRegistry;
}
private void registerDelegateApplicationListener(H http,
ApplicationListener<?> delegate) {
private void registerDelegateApplicationListener(H http, ApplicationListener<?> delegate) {
DelegatingApplicationListener delegating = getBeanOrNull(DelegatingApplicationListener.class);
if (delegating == null) {
return;
}
SmartApplicationListener smartListener = new GenericApplicationListenerAdapter(
delegate);
SmartApplicationListener smartListener = new GenericApplicationListenerAdapter(delegate);
delegating.addListener(smartListener);
}
@@ -714,7 +536,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
* @return the default {@link SessionAuthenticationStrategy} for session fixation
*/
private static SessionAuthenticationStrategy createDefaultSessionFixationProtectionStrategy() {
return new ChangeSessionIdAuthenticationStrategy();
return new ChangeSessionIdAuthenticationStrategy();
}
private <T> T getBeanOrNull(Class<T> type) {
@@ -725,8 +547,147 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
try {
return context.getBean(type);
}
catch (NoSuchBeanDefinitionException e) {
catch (NoSuchBeanDefinitionException ex) {
return null;
}
}
/**
* Allows configuring SessionFixation protection
*
* @author Rob Winch
*/
public final class SessionFixationConfigurer {
/**
* Specifies that a new session should be created, but the session attributes from
* the original {@link HttpSession} should not be retained.
* @return the {@link SessionManagementConfigurer} for further customizations
*/
public SessionManagementConfigurer<H> newSession() {
SessionFixationProtectionStrategy sessionFixationProtectionStrategy = new SessionFixationProtectionStrategy();
sessionFixationProtectionStrategy.setMigrateSessionAttributes(false);
setSessionFixationAuthenticationStrategy(sessionFixationProtectionStrategy);
return SessionManagementConfigurer.this;
}
/**
* Specifies that a new session should be created and the session attributes from
* the original {@link HttpSession} should be retained.
* @return the {@link SessionManagementConfigurer} for further customizations
*/
public SessionManagementConfigurer<H> migrateSession() {
setSessionFixationAuthenticationStrategy(new SessionFixationProtectionStrategy());
return SessionManagementConfigurer.this;
}
/**
* Specifies that the Servlet container-provided session fixation protection
* should be used. When a session authenticates, the Servlet method
* {@code HttpServletRequest#changeSessionId()} is called to change the session ID
* and retain all session attributes.
* @return the {@link SessionManagementConfigurer} for further customizations
*/
public SessionManagementConfigurer<H> changeSessionId() {
setSessionFixationAuthenticationStrategy(new ChangeSessionIdAuthenticationStrategy());
return SessionManagementConfigurer.this;
}
/**
* Specifies that no session fixation protection should be enabled. This may be
* useful when utilizing other mechanisms for protecting against session fixation.
* For example, if application container session fixation protection is already in
* use. Otherwise, this option is not recommended.
* @return the {@link SessionManagementConfigurer} for further customizations
*/
public SessionManagementConfigurer<H> none() {
setSessionFixationAuthenticationStrategy(new NullAuthenticatedSessionStrategy());
return SessionManagementConfigurer.this;
}
}
/**
* Allows configuring controlling of multiple sessions.
*
* @author Rob Winch
*/
public final class ConcurrencyControlConfigurer {
private ConcurrencyControlConfigurer() {
}
/**
* Controls the maximum number of sessions for a user. The default is to allow any
* number of users.
* @param maximumSessions the maximum number of sessions for a user
* @return the {@link ConcurrencyControlConfigurer} for further customizations
*/
public ConcurrencyControlConfigurer maximumSessions(int maximumSessions) {
SessionManagementConfigurer.this.maximumSessions = maximumSessions;
return this;
}
/**
* The URL to redirect to if a user tries to access a resource and their session
* has been expired due to too many sessions for the current user. The default is
* to write a simple error message to the response.
* @param expiredUrl the URL to redirect to
* @return the {@link ConcurrencyControlConfigurer} for further customizations
*/
public ConcurrencyControlConfigurer expiredUrl(String expiredUrl) {
SessionManagementConfigurer.this.expiredUrl = expiredUrl;
return this;
}
/**
* Determines the behaviour when an expired session is detected.
* @param expiredSessionStrategy the {@link SessionInformationExpiredStrategy} to
* use when an expired session is detected.
* @return the {@link ConcurrencyControlConfigurer} for further customizations
*/
public ConcurrencyControlConfigurer expiredSessionStrategy(
SessionInformationExpiredStrategy expiredSessionStrategy) {
SessionManagementConfigurer.this.expiredSessionStrategy = expiredSessionStrategy;
return this;
}
/**
* If true, prevents a user from authenticating when the
* {@link #maximumSessions(int)} has been reached. Otherwise (default), the user
* who authenticates is allowed access and an existing user's session is expired.
* The user's who's session is forcibly expired is sent to
* {@link #expiredUrl(String)}. The advantage of this approach is if a user
* accidentally does not log out, there is no need for an administrator to
* intervene or wait till their session expires.
* @param maxSessionsPreventsLogin true to have an error at time of
* authentication, else false (default)
* @return the {@link ConcurrencyControlConfigurer} for further customizations
*/
public ConcurrencyControlConfigurer maxSessionsPreventsLogin(boolean maxSessionsPreventsLogin) {
SessionManagementConfigurer.this.maxSessionsPreventsLogin = maxSessionsPreventsLogin;
return this;
}
/**
* Controls the {@link SessionRegistry} implementation used. The default is
* {@link SessionRegistryImpl} which is an in memory implementation.
* @param sessionRegistry the {@link SessionRegistry} to use
* @return the {@link ConcurrencyControlConfigurer} for further customizations
*/
public ConcurrencyControlConfigurer sessionRegistry(SessionRegistry sessionRegistry) {
SessionManagementConfigurer.this.sessionRegistry = sessionRegistry;
return this;
}
/**
* Used to chain back to the {@link SessionManagementConfigurer}
* @return the {@link SessionManagementConfigurer} for further customizations
*/
public SessionManagementConfigurer<H> and() {
return SessionManagementConfigurer.this;
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.ArrayList;
@@ -77,97 +78,46 @@ import org.springframework.util.Assert;
* The following shared objects are used:
*
* <ul>
* <li>
* AuthenticationManager</li>
* <li>AuthenticationManager</li>
* </ul>
*
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
*
* @author Rob Winch
* @since 3.2
* @see ExpressionUrlAuthorizationConfigurer
*/
public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractInterceptUrlConfigurer<UrlAuthorizationConfigurer<H>, H> {
private final StandardInterceptUrlRegistry REGISTRY;
public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractInterceptUrlConfigurer<UrlAuthorizationConfigurer<H>, H> {
private final StandardInterceptUrlRegistry registry;
public UrlAuthorizationConfigurer(ApplicationContext context) {
this.REGISTRY = new StandardInterceptUrlRegistry(context);
this.registry = new StandardInterceptUrlRegistry(context);
}
/**
* The StandardInterceptUrlRegistry is what users will interact with after applying
* the {@link UrlAuthorizationConfigurer}.
*
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further customizations
*/
public StandardInterceptUrlRegistry getRegistry() {
return REGISTRY;
return this.registry;
}
/**
* Adds an {@link ObjectPostProcessor} for this class.
*
* @param objectPostProcessor
* @return the {@link UrlAuthorizationConfigurer} for further customizations
*/
public UrlAuthorizationConfigurer<H> withObjectPostProcessor(
ObjectPostProcessor<?> objectPostProcessor) {
@Override
public UrlAuthorizationConfigurer<H> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return this;
}
public class StandardInterceptUrlRegistry
extends
ExpressionUrlAuthorizationConfigurer<H>.AbstractInterceptUrlRegistry<StandardInterceptUrlRegistry, AuthorizedUrl> {
/**
* @param context
*/
private StandardInterceptUrlRegistry(ApplicationContext context) {
setApplicationContext(context);
}
@Override
public MvcMatchersAuthorizedUrl mvcMatchers(HttpMethod method,
String... mvcPatterns) {
return new MvcMatchersAuthorizedUrl(createMvcMatchers(method, mvcPatterns));
}
@Override
public MvcMatchersAuthorizedUrl mvcMatchers(String... patterns) {
return mvcMatchers(null, patterns);
}
@Override
protected final AuthorizedUrl chainRequestMatchersInternal(
List<RequestMatcher> requestMatchers) {
return new AuthorizedUrl(requestMatchers);
}
/**
* Adds an {@link ObjectPostProcessor} for this class.
*
* @param objectPostProcessor
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customizations
*/
public StandardInterceptUrlRegistry withObjectPostProcessor(
ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return this;
}
public H and() {
return UrlAuthorizationConfigurer.this.and();
}
}
/**
* Creates the default {@link AccessDecisionVoter} instances used if an
* {@link AccessDecisionManager} was not specified.
*
* @param http the builder to use
*/
@Override
@@ -182,13 +132,11 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Creates the {@link FilterInvocationSecurityMetadataSource} to use. The
* implementation is a {@link DefaultFilterInvocationSecurityMetadataSource}.
*
* @param http the builder to use
*/
@Override
FilterInvocationSecurityMetadataSource createMetadataSource(H http) {
return new DefaultFilterInvocationSecurityMetadataSource(
REGISTRY.createRequestMap());
return new DefaultFilterInvocationSecurityMetadataSource(this.registry.createRequestMap());
}
/**
@@ -200,34 +148,29 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
* by the {@link RequestMatcher} instances
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further customizations
*/
private StandardInterceptUrlRegistry addMapping(
Iterable<? extends RequestMatcher> requestMatchers,
private StandardInterceptUrlRegistry addMapping(Iterable<? extends RequestMatcher> requestMatchers,
Collection<ConfigAttribute> configAttributes) {
for (RequestMatcher requestMatcher : requestMatchers) {
REGISTRY.addMapping(new AbstractConfigAttributeRequestMatcherRegistry.UrlMapping(
requestMatcher, configAttributes));
this.registry.addMapping(
new AbstractConfigAttributeRequestMatcherRegistry.UrlMapping(requestMatcher, configAttributes));
}
return REGISTRY;
return this.registry;
}
/**
* Creates a String for specifying a user requires a role.
*
* @param role the role that should be required which is prepended with ROLE_
* automatically (i.e. USER, ADMIN, etc). It should not start with ROLE_
* @return the {@link ConfigAttribute} expressed as a String
*/
private static String hasRole(String role) {
Assert.isTrue(
!role.startsWith("ROLE_"),
() -> role
+ " should not start with ROLE_ since ROLE_ is automatically prepended when using hasRole. Consider using hasAuthority or access instead.");
Assert.isTrue(!role.startsWith("ROLE_"), () -> role
+ " should not start with ROLE_ since ROLE_ is automatically prepended when using hasRole. Consider using hasAuthority or access instead.");
return "ROLE_" + role;
}
/**
* Creates a String for specifying that a user requires one of many roles.
*
* @param roles the roles that the user should have at least one of (i.e. ADMIN, USER,
* etc). Each role should not start with ROLE_ since it is automatically prepended
* already.
@@ -250,6 +193,45 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
return authorities;
}
public final class StandardInterceptUrlRegistry extends
ExpressionUrlAuthorizationConfigurer<H>.AbstractInterceptUrlRegistry<StandardInterceptUrlRegistry, AuthorizedUrl> {
private StandardInterceptUrlRegistry(ApplicationContext context) {
setApplicationContext(context);
}
@Override
public MvcMatchersAuthorizedUrl mvcMatchers(HttpMethod method, String... mvcPatterns) {
return new MvcMatchersAuthorizedUrl(createMvcMatchers(method, mvcPatterns));
}
@Override
public MvcMatchersAuthorizedUrl mvcMatchers(String... patterns) {
return mvcMatchers(null, patterns);
}
@Override
protected AuthorizedUrl chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
return new AuthorizedUrl(requestMatchers);
}
/**
* Adds an {@link ObjectPostProcessor} for this class.
* @param objectPostProcessor
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customizations
*/
public StandardInterceptUrlRegistry withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return this;
}
public H and() {
return UrlAuthorizationConfigurer.this.and();
}
}
/**
* An {@link AuthorizedUrl} that allows optionally configuring the
* {@link MvcRequestMatcher#setMethod(HttpMethod)}
@@ -257,9 +239,9 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
* @author Rob Winch
*/
public final class MvcMatchersAuthorizedUrl extends AuthorizedUrl {
/**
* Creates a new instance
*
* @param requestMatchers the {@link RequestMatcher} instances to map
*/
private MvcMatchersAuthorizedUrl(List<MvcRequestMatcher> requestMatchers) {
@@ -273,6 +255,7 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
}
return this;
}
}
/**
@@ -283,6 +266,7 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
* @since 3.2
*/
public class AuthorizedUrl {
private final List<? extends RequestMatcher> requestMatchers;
/**
@@ -290,15 +274,13 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
* @param requestMatchers the {@link RequestMatcher} instances to map to some
* {@link ConfigAttribute} instances.
*/
private AuthorizedUrl(List<? extends RequestMatcher> requestMatchers) {
Assert.notEmpty(requestMatchers,
"requestMatchers must contain at least one value");
AuthorizedUrl(List<? extends RequestMatcher> requestMatchers) {
Assert.notEmpty(requestMatchers, "requestMatchers must contain at least one value");
this.requestMatchers = requestMatchers;
}
/**
* Specifies a user requires a role.
*
* @param role the role that should be required which is prepended with ROLE_
* automatically (i.e. USER, ADMIN, etc). It should not start with ROLE_ the
* {@link UrlAuthorizationConfigurer} for further customization
@@ -309,7 +291,6 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Specifies that a user requires one of many roles.
*
* @param roles the roles that the user should have at least one of (i.e. ADMIN,
* USER, etc). Each role should not start with ROLE_ since it is automatically
* prepended already.
@@ -321,7 +302,6 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Specifies a user requires an authority.
*
* @param authority the authority that should be required
* @return the {@link UrlAuthorizationConfigurer} for further customization
*/
@@ -353,12 +333,14 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
* @return the {@link UrlAuthorizationConfigurer} for further customization
*/
public StandardInterceptUrlRegistry access(String... attributes) {
addMapping(requestMatchers, SecurityConfig.createList(attributes));
return UrlAuthorizationConfigurer.this.REGISTRY;
addMapping(this.requestMatchers, SecurityConfig.createList(attributes));
return UrlAuthorizationConfigurer.this.registry;
}
protected List<? extends RequestMatcher> getMatchers() {
return this.requestMatchers;
}
}
}

View File

@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
@@ -32,8 +35,6 @@ import org.springframework.security.web.authentication.preauth.x509.SubjectDnX50
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
import javax.servlet.http.HttpServletRequest;
/**
* Adds X509 based pre authentication to an application. Since validating the certificate
* happens when the client connects, the requesting and validation of the client
@@ -53,8 +54,7 @@ import javax.servlet.http.HttpServletRequest;
* The following shared objects are created
*
* <ul>
* <li>
* {@link AuthenticationEntryPoint} is populated with an
* <li>{@link AuthenticationEntryPoint} is populated with an
* {@link Http403ForbiddenEntryPoint}</li>
* <li>A {@link PreAuthenticatedAuthenticationProvider} is populated into
* {@link HttpSecurity#authenticationProvider(org.springframework.security.authentication.AuthenticationProvider)}
@@ -73,11 +73,15 @@ import javax.servlet.http.HttpServletRequest;
* @author Rob Winch
* @since 3.2
*/
public final class X509Configurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<X509Configurer<H>, H> {
public final class X509Configurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<X509Configurer<H>, H> {
private X509AuthenticationFilter x509AuthenticationFilter;
private X509PrincipalExtractor x509PrincipalExtractor;
private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService;
private AuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails> authenticationDetailsSource;
/**
@@ -92,19 +96,16 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>> extends
* Allows specifying the entire {@link X509AuthenticationFilter}. If this is
* specified, the properties on {@link X509Configurer} will not be populated on the
* {@link X509AuthenticationFilter}.
*
* @param x509AuthenticationFilter the {@link X509AuthenticationFilter} to use
* @return the {@link X509Configurer} for further customizations
*/
public X509Configurer<H> x509AuthenticationFilter(
X509AuthenticationFilter x509AuthenticationFilter) {
public X509Configurer<H> x509AuthenticationFilter(X509AuthenticationFilter x509AuthenticationFilter) {
this.x509AuthenticationFilter = x509AuthenticationFilter;
return this;
}
/**
* Specifies the {@link X509PrincipalExtractor}
*
* @param x509PrincipalExtractor the {@link X509PrincipalExtractor} to use
* @return the {@link X509Configurer} to use
*/
@@ -115,7 +116,6 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>> extends
/**
* Specifies the {@link AuthenticationDetailsSource}
*
* @param authenticationDetailsSource the {@link AuthenticationDetailsSource} to use
* @return the {@link X509Configurer} to use
*/
@@ -129,7 +129,6 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>> extends
* Shortcut for invoking
* {@link #authenticationUserDetailsService(AuthenticationUserDetailsService)} with a
* {@link UserDetailsByNameServiceWrapper}.
*
* @param userDetailsService the {@link UserDetailsService} to use
* @return the {@link X509Configurer} for further customizations
*/
@@ -143,8 +142,8 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>> extends
* Specifies the {@link AuthenticationUserDetailsService} to use. If not specified,
* the shared {@link UserDetailsService} will be used to create a
* {@link UserDetailsByNameServiceWrapper}.
*
* @param authenticationUserDetailsService the {@link AuthenticationUserDetailsService} to use
* @param authenticationUserDetailsService the
* {@link AuthenticationUserDetailsService} to use
* @return the {@link X509Configurer} for further customizations
*/
public X509Configurer<H> authenticationUserDetailsService(
@@ -157,9 +156,8 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>> extends
* Specifies the regex to extract the principal from the certificate. If not
* specified, the default expression from {@link SubjectDnX509PrincipalExtractor} is
* used.
*
* @param subjectPrincipalRegex the regex to extract the user principal from the
* certificate (i.e. "CN=(.*?)(?:,|$)").
* certificate (i.e. "CN=(.*?)(?:,|$)").
* @return the {@link X509Configurer} for further customizations
*/
public X509Configurer<H> subjectPrincipalRegex(String subjectPrincipalRegex) {
@@ -169,48 +167,42 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>> extends
return this;
}
// @formatter:off
@Override
public void init(H http) {
PreAuthenticatedAuthenticationProvider authenticationProvider = new PreAuthenticatedAuthenticationProvider();
authenticationProvider.setPreAuthenticatedUserDetailsService(getAuthenticationUserDetailsService(http));
http
.authenticationProvider(authenticationProvider)
.setSharedObject(AuthenticationEntryPoint.class, new Http403ForbiddenEntryPoint());
http.authenticationProvider(authenticationProvider).setSharedObject(AuthenticationEntryPoint.class,
new Http403ForbiddenEntryPoint());
}
// @formatter:on
@Override
public void configure(H http) {
X509AuthenticationFilter filter = getFilter(http
.getSharedObject(AuthenticationManager.class));
X509AuthenticationFilter filter = getFilter(http.getSharedObject(AuthenticationManager.class));
http.addFilter(filter);
}
private X509AuthenticationFilter getFilter(AuthenticationManager authenticationManager) {
if (x509AuthenticationFilter == null) {
x509AuthenticationFilter = new X509AuthenticationFilter();
x509AuthenticationFilter.setAuthenticationManager(authenticationManager);
if (x509PrincipalExtractor != null) {
x509AuthenticationFilter.setPrincipalExtractor(x509PrincipalExtractor);
if (this.x509AuthenticationFilter == null) {
this.x509AuthenticationFilter = new X509AuthenticationFilter();
this.x509AuthenticationFilter.setAuthenticationManager(authenticationManager);
if (this.x509PrincipalExtractor != null) {
this.x509AuthenticationFilter.setPrincipalExtractor(this.x509PrincipalExtractor);
}
if (authenticationDetailsSource != null) {
x509AuthenticationFilter
.setAuthenticationDetailsSource(authenticationDetailsSource);
if (this.authenticationDetailsSource != null) {
this.x509AuthenticationFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
}
x509AuthenticationFilter = postProcess(x509AuthenticationFilter);
this.x509AuthenticationFilter = postProcess(this.x509AuthenticationFilter);
}
return x509AuthenticationFilter;
return this.x509AuthenticationFilter;
}
private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> getAuthenticationUserDetailsService(
H http) {
if (authenticationUserDetailsService == null) {
if (this.authenticationUserDetailsService == null) {
userDetailsService(http.getSharedObject(UserDetailsService.class));
}
return authenticationUserDetailsService;
return this.authenticationUserDetailsService;
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers.oauth2.client;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
@@ -48,26 +49,26 @@ import org.springframework.util.Assert;
* <li>{@link ClientRegistrationRepository}</li>
* </ul>
*
* @deprecated It is not recommended to use the implicit flow
* due to the inherent risks of returning access tokens in an HTTP redirect
* without any confirmation that it has been received by the client.
* See reference <a target="_blank" href="https://oauth.net/2/grant-types/implicit/">OAuth 2.0 Implicit Grant</a>.
*
* @deprecated It is not recommended to use the implicit flow due to the inherent risks of
* returning access tokens in an HTTP redirect without any confirmation that it has been
* received by the client. See reference
* <a target="_blank" href="https://oauth.net/2/grant-types/implicit/">OAuth 2.0 Implicit
* Grant</a>.
* @author Joe Grandja
* @since 5.0
* @see OAuth2AuthorizationRequestRedirectFilter
* @see ClientRegistrationRepository
*/
@Deprecated
public final class ImplicitGrantConfigurer<B extends HttpSecurityBuilder<B>> extends
AbstractHttpConfigurer<ImplicitGrantConfigurer<B>, B> {
public final class ImplicitGrantConfigurer<B extends HttpSecurityBuilder<B>>
extends AbstractHttpConfigurer<ImplicitGrantConfigurer<B>, B> {
private String authorizationRequestBaseUri;
/**
* Sets the base {@code URI} used for authorization requests.
*
* @param authorizationRequestBaseUri the base {@code URI} used for authorization requests
* @param authorizationRequestBaseUri the base {@code URI} used for authorization
* requests
* @return the {@link ImplicitGrantConfigurer} for further configuration
*/
public ImplicitGrantConfigurer<B> authorizationRequestBaseUri(String authorizationRequestBaseUri) {
@@ -78,11 +79,11 @@ public final class ImplicitGrantConfigurer<B extends HttpSecurityBuilder<B>> ext
/**
* Sets the repository of client registrations.
*
* @param clientRegistrationRepository the repository of client registrations
* @return the {@link ImplicitGrantConfigurer} for further configuration
*/
public ImplicitGrantConfigurer<B> clientRegistrationRepository(ClientRegistrationRepository clientRegistrationRepository) {
public ImplicitGrantConfigurer<B> clientRegistrationRepository(
ClientRegistrationRepository clientRegistrationRepository) {
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
this.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
return this;
@@ -91,13 +92,14 @@ public final class ImplicitGrantConfigurer<B extends HttpSecurityBuilder<B>> ext
@Override
public void configure(B http) {
OAuth2AuthorizationRequestRedirectFilter authorizationRequestFilter = new OAuth2AuthorizationRequestRedirectFilter(
OAuth2ClientConfigurerUtils.getClientRegistrationRepository(this.getBuilder()), this.getAuthorizationRequestBaseUri());
OAuth2ClientConfigurerUtils.getClientRegistrationRepository(this.getBuilder()),
this.getAuthorizationRequestBaseUri());
http.addFilter(this.postProcess(authorizationRequestFilter));
}
private String getAuthorizationRequestBaseUri() {
return this.authorizationRequestBaseUri != null ?
this.authorizationRequestBaseUri :
OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI;
return (this.authorizationRequestBaseUri != null) ? this.authorizationRequestBaseUri
: OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI;
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers.oauth2.client;
import org.springframework.security.authentication.AuthenticationManager;
@@ -43,13 +44,15 @@ import org.springframework.util.Assert;
* The following configuration options are available:
*
* <ul>
* <li>{@link #authorizationCodeGrant()} - support for the OAuth 2.0 Authorization Code Grant</li>
* <li>{@link #authorizationCodeGrant()} - support for the OAuth 2.0 Authorization Code
* Grant</li>
* </ul>
*
* <p>
* Defaults are provided for all configuration options with the only required configuration
* being {@link #clientRegistrationRepository(ClientRegistrationRepository)}.
* Alternatively, a {@link ClientRegistrationRepository} {@code @Bean} may be registered instead.
* Defaults are provided for all configuration options with the only required
* configuration being
* {@link #clientRegistrationRepository(ClientRegistrationRepository)}. Alternatively, a
* {@link ClientRegistrationRepository} {@code @Bean} may be registered instead.
*
* <h2>Security Filters</h2>
*
@@ -87,18 +90,18 @@ import org.springframework.util.Assert;
* @see OAuth2AuthorizedClientRepository
* @see AbstractHttpConfigurer
*/
public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>> extends
AbstractHttpConfigurer<OAuth2ClientConfigurer<B>, B> {
public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>>
extends AbstractHttpConfigurer<OAuth2ClientConfigurer<B>, B> {
private AuthorizationCodeGrantConfigurer authorizationCodeGrantConfigurer = new AuthorizationCodeGrantConfigurer();
/**
* Sets the repository of client registrations.
*
* @param clientRegistrationRepository the repository of client registrations
* @return the {@link OAuth2ClientConfigurer} for further configuration
*/
public OAuth2ClientConfigurer<B> clientRegistrationRepository(ClientRegistrationRepository clientRegistrationRepository) {
public OAuth2ClientConfigurer<B> clientRegistrationRepository(
ClientRegistrationRepository clientRegistrationRepository) {
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
this.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
return this;
@@ -106,11 +109,11 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>> exte
/**
* Sets the repository for authorized client(s).
*
* @param authorizedClientRepository the authorized client repository
* @return the {@link OAuth2ClientConfigurer} for further configuration
*/
public OAuth2ClientConfigurer<B> authorizedClientRepository(OAuth2AuthorizedClientRepository authorizedClientRepository) {
public OAuth2ClientConfigurer<B> authorizedClientRepository(
OAuth2AuthorizedClientRepository authorizedClientRepository) {
Assert.notNull(authorizedClientRepository, "authorizedClientRepository cannot be null");
this.getBuilder().setSharedObject(OAuth2AuthorizedClientRepository.class, authorizedClientRepository);
return this;
@@ -118,19 +121,19 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>> exte
/**
* Sets the service for authorized client(s).
*
* @param authorizedClientService the authorized client service
* @return the {@link OAuth2ClientConfigurer} for further configuration
*/
public OAuth2ClientConfigurer<B> authorizedClientService(OAuth2AuthorizedClientService authorizedClientService) {
Assert.notNull(authorizedClientService, "authorizedClientService cannot be null");
this.authorizedClientRepository(new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService));
this.authorizedClientRepository(
new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService));
return this;
}
/**
* Returns the {@link AuthorizationCodeGrantConfigurer} for configuring the OAuth 2.0 Authorization Code Grant.
*
* Returns the {@link AuthorizationCodeGrantConfigurer} for configuring the OAuth 2.0
* Authorization Code Grant.
* @return the {@link AuthorizationCodeGrantConfigurer}
*/
public AuthorizationCodeGrantConfigurer authorizationCodeGrant() {
@@ -139,22 +142,35 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>> exte
/**
* Configures the OAuth 2.0 Authorization Code Grant.
*
* @param authorizationCodeGrantCustomizer the {@link Customizer} to provide more options for
* the {@link AuthorizationCodeGrantConfigurer}
* @param authorizationCodeGrantCustomizer the {@link Customizer} to provide more
* options for the {@link AuthorizationCodeGrantConfigurer}
* @return the {@link OAuth2ClientConfigurer} for further customizations
*/
public OAuth2ClientConfigurer<B> authorizationCodeGrant(Customizer<AuthorizationCodeGrantConfigurer> authorizationCodeGrantCustomizer) {
public OAuth2ClientConfigurer<B> authorizationCodeGrant(
Customizer<AuthorizationCodeGrantConfigurer> authorizationCodeGrantCustomizer) {
authorizationCodeGrantCustomizer.customize(this.authorizationCodeGrantConfigurer);
return this;
}
@Override
public void init(B builder) {
this.authorizationCodeGrantConfigurer.init(builder);
}
@Override
public void configure(B builder) {
this.authorizationCodeGrantConfigurer.configure(builder);
}
/**
* Configuration options for the OAuth 2.0 Authorization Code Grant.
*/
public class AuthorizationCodeGrantConfigurer {
public final class AuthorizationCodeGrantConfigurer {
private OAuth2AuthorizationRequestResolver authorizationRequestResolver;
private AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
private AuthorizationCodeGrantConfigurer() {
@@ -162,11 +178,12 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>> exte
/**
* Sets the resolver used for resolving {@link OAuth2AuthorizationRequest}'s.
*
* @param authorizationRequestResolver the resolver used for resolving {@link OAuth2AuthorizationRequest}'s
* @param authorizationRequestResolver the resolver used for resolving
* {@link OAuth2AuthorizationRequest}'s
* @return the {@link AuthorizationCodeGrantConfigurer} for further configuration
*/
public AuthorizationCodeGrantConfigurer authorizationRequestResolver(OAuth2AuthorizationRequestResolver authorizationRequestResolver) {
public AuthorizationCodeGrantConfigurer authorizationRequestResolver(
OAuth2AuthorizationRequestResolver authorizationRequestResolver) {
Assert.notNull(authorizationRequestResolver, "authorizationRequestResolver cannot be null");
this.authorizationRequestResolver = authorizationRequestResolver;
return this;
@@ -174,27 +191,26 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>> exte
/**
* Sets the repository used for storing {@link OAuth2AuthorizationRequest}'s.
*
* @param authorizationRequestRepository the repository used for storing {@link OAuth2AuthorizationRequest}'s
* @param authorizationRequestRepository the repository used for storing
* {@link OAuth2AuthorizationRequest}'s
* @return the {@link AuthorizationCodeGrantConfigurer} for further configuration
*/
public AuthorizationCodeGrantConfigurer authorizationRequestRepository(
AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository) {
Assert.notNull(authorizationRequestRepository, "authorizationRequestRepository cannot be null");
this.authorizationRequestRepository = authorizationRequestRepository;
return this;
}
/**
* Sets the client used for requesting the access token credential from the Token Endpoint.
*
* @param accessTokenResponseClient the client used for requesting the access token credential from the Token Endpoint
* Sets the client used for requesting the access token credential from the Token
* Endpoint.
* @param accessTokenResponseClient the client used for requesting the access
* token credential from the Token Endpoint
* @return the {@link AuthorizationCodeGrantConfigurer} for further configuration
*/
public AuthorizationCodeGrantConfigurer accessTokenResponseClient(
OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient) {
Assert.notNull(accessTokenResponseClient, "accessTokenResponseClient cannot be null");
this.accessTokenResponseClient = accessTokenResponseClient;
return this;
@@ -202,7 +218,6 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>> exte
/**
* Returns the {@link OAuth2ClientConfigurer} for further configuration.
*
* @return the {@link OAuth2ClientConfigurer}
*/
public OAuth2ClientConfigurer<B> and() {
@@ -210,25 +225,27 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>> exte
}
private void init(B builder) {
OAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider =
new OAuth2AuthorizationCodeAuthenticationProvider(getAccessTokenResponseClient());
OAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider(
getAccessTokenResponseClient());
builder.authenticationProvider(postProcess(authorizationCodeAuthenticationProvider));
}
private void configure(B builder) {
OAuth2AuthorizationRequestRedirectFilter authorizationRequestRedirectFilter = createAuthorizationRequestRedirectFilter(builder);
OAuth2AuthorizationRequestRedirectFilter authorizationRequestRedirectFilter = createAuthorizationRequestRedirectFilter(
builder);
builder.addFilter(postProcess(authorizationRequestRedirectFilter));
OAuth2AuthorizationCodeGrantFilter authorizationCodeGrantFilter = createAuthorizationCodeGrantFilter(builder);
OAuth2AuthorizationCodeGrantFilter authorizationCodeGrantFilter = createAuthorizationCodeGrantFilter(
builder);
builder.addFilter(postProcess(authorizationCodeGrantFilter));
}
private OAuth2AuthorizationRequestRedirectFilter createAuthorizationRequestRedirectFilter(B builder) {
OAuth2AuthorizationRequestResolver resolver = getAuthorizationRequestResolver();
OAuth2AuthorizationRequestRedirectFilter authorizationRequestRedirectFilter =
new OAuth2AuthorizationRequestRedirectFilter(resolver);
OAuth2AuthorizationRequestRedirectFilter authorizationRequestRedirectFilter = new OAuth2AuthorizationRequestRedirectFilter(
resolver);
if (this.authorizationRequestRepository != null) {
authorizationRequestRedirectFilter.setAuthorizationRequestRepository(this.authorizationRequestRepository);
authorizationRequestRedirectFilter
.setAuthorizationRequestRepository(this.authorizationRequestRepository);
}
RequestCache requestCache = builder.getSharedObject(RequestCache.class);
if (requestCache != null) {
@@ -251,9 +268,7 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>> exte
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
OAuth2AuthorizationCodeGrantFilter authorizationCodeGrantFilter = new OAuth2AuthorizationCodeGrantFilter(
OAuth2ClientConfigurerUtils.getClientRegistrationRepository(builder),
OAuth2ClientConfigurerUtils.getAuthorizedClientRepository(builder),
authenticationManager);
OAuth2ClientConfigurerUtils.getAuthorizedClientRepository(builder), authenticationManager);
if (this.authorizationRequestRepository != null) {
authorizationCodeGrantFilter.setAuthorizationRequestRepository(this.authorizationRequestRepository);
}
@@ -270,15 +285,7 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>> exte
}
return new DefaultAuthorizationCodeTokenResponseClient();
}
}
@Override
public void init(B builder) {
this.authorizationCodeGrantConfigurer.init(builder);
}
@Override
public void configure(B builder) {
this.authorizationCodeGrantConfigurer.configure(builder);
}
}

View File

@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers.oauth2.client;
import java.util.Map;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.ApplicationContext;
@@ -27,8 +30,6 @@ import org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAut
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.util.StringUtils;
import java.util.Map;
/**
* Utility methods for the OAuth 2.0 Client {@link AbstractHttpConfigurer}'s.
*
@@ -41,7 +42,8 @@ final class OAuth2ClientConfigurerUtils {
}
static <B extends HttpSecurityBuilder<B>> ClientRegistrationRepository getClientRegistrationRepository(B builder) {
ClientRegistrationRepository clientRegistrationRepository = builder.getSharedObject(ClientRegistrationRepository.class);
ClientRegistrationRepository clientRegistrationRepository = builder
.getSharedObject(ClientRegistrationRepository.class);
if (clientRegistrationRepository == null) {
clientRegistrationRepository = getClientRegistrationRepositoryBean(builder);
builder.setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
@@ -49,12 +51,15 @@ final class OAuth2ClientConfigurerUtils {
return clientRegistrationRepository;
}
private static <B extends HttpSecurityBuilder<B>> ClientRegistrationRepository getClientRegistrationRepositoryBean(B builder) {
private static <B extends HttpSecurityBuilder<B>> ClientRegistrationRepository getClientRegistrationRepositoryBean(
B builder) {
return builder.getSharedObject(ApplicationContext.class).getBean(ClientRegistrationRepository.class);
}
static <B extends HttpSecurityBuilder<B>> OAuth2AuthorizedClientRepository getAuthorizedClientRepository(B builder) {
OAuth2AuthorizedClientRepository authorizedClientRepository = builder.getSharedObject(OAuth2AuthorizedClientRepository.class);
static <B extends HttpSecurityBuilder<B>> OAuth2AuthorizedClientRepository getAuthorizedClientRepository(
B builder) {
OAuth2AuthorizedClientRepository authorizedClientRepository = builder
.getSharedObject(OAuth2AuthorizedClientRepository.class);
if (authorizedClientRepository == null) {
authorizedClientRepository = getAuthorizedClientRepositoryBean(builder);
if (authorizedClientRepository == null) {
@@ -66,34 +71,45 @@ final class OAuth2ClientConfigurerUtils {
return authorizedClientRepository;
}
private static <B extends HttpSecurityBuilder<B>> OAuth2AuthorizedClientRepository getAuthorizedClientRepositoryBean(B builder) {
Map<String, OAuth2AuthorizedClientRepository> authorizedClientRepositoryMap = BeanFactoryUtils.beansOfTypeIncludingAncestors(
builder.getSharedObject(ApplicationContext.class), OAuth2AuthorizedClientRepository.class);
private static <B extends HttpSecurityBuilder<B>> OAuth2AuthorizedClientRepository getAuthorizedClientRepositoryBean(
B builder) {
Map<String, OAuth2AuthorizedClientRepository> authorizedClientRepositoryMap = BeanFactoryUtils
.beansOfTypeIncludingAncestors(builder.getSharedObject(ApplicationContext.class),
OAuth2AuthorizedClientRepository.class);
if (authorizedClientRepositoryMap.size() > 1) {
throw new NoUniqueBeanDefinitionException(OAuth2AuthorizedClientRepository.class, authorizedClientRepositoryMap.size(),
"Expected single matching bean of type '" + OAuth2AuthorizedClientRepository.class.getName() + "' but found " +
authorizedClientRepositoryMap.size() + ": " + StringUtils.collectionToCommaDelimitedString(authorizedClientRepositoryMap.keySet()));
throw new NoUniqueBeanDefinitionException(OAuth2AuthorizedClientRepository.class,
authorizedClientRepositoryMap.size(),
"Expected single matching bean of type '" + OAuth2AuthorizedClientRepository.class.getName()
+ "' but found " + authorizedClientRepositoryMap.size() + ": "
+ StringUtils.collectionToCommaDelimitedString(authorizedClientRepositoryMap.keySet()));
}
return (!authorizedClientRepositoryMap.isEmpty() ? authorizedClientRepositoryMap.values().iterator().next() : null);
return (!authorizedClientRepositoryMap.isEmpty() ? authorizedClientRepositoryMap.values().iterator().next()
: null);
}
private static <B extends HttpSecurityBuilder<B>> OAuth2AuthorizedClientService getAuthorizedClientService(B builder) {
private static <B extends HttpSecurityBuilder<B>> OAuth2AuthorizedClientService getAuthorizedClientService(
B builder) {
OAuth2AuthorizedClientService authorizedClientService = getAuthorizedClientServiceBean(builder);
if (authorizedClientService == null) {
authorizedClientService = new InMemoryOAuth2AuthorizedClientService(getClientRegistrationRepository(builder));
authorizedClientService = new InMemoryOAuth2AuthorizedClientService(
getClientRegistrationRepository(builder));
}
return authorizedClientService;
}
private static <B extends HttpSecurityBuilder<B>> OAuth2AuthorizedClientService getAuthorizedClientServiceBean(B builder) {
Map<String, OAuth2AuthorizedClientService> authorizedClientServiceMap = BeanFactoryUtils.beansOfTypeIncludingAncestors(
builder.getSharedObject(ApplicationContext.class), OAuth2AuthorizedClientService.class);
private static <B extends HttpSecurityBuilder<B>> OAuth2AuthorizedClientService getAuthorizedClientServiceBean(
B builder) {
Map<String, OAuth2AuthorizedClientService> authorizedClientServiceMap = BeanFactoryUtils
.beansOfTypeIncludingAncestors(builder.getSharedObject(ApplicationContext.class),
OAuth2AuthorizedClientService.class);
if (authorizedClientServiceMap.size() > 1) {
throw new NoUniqueBeanDefinitionException(OAuth2AuthorizedClientService.class, authorizedClientServiceMap.size(),
"Expected single matching bean of type '" + OAuth2AuthorizedClientService.class.getName() + "' but found " +
authorizedClientServiceMap.size() + ": " + StringUtils.collectionToCommaDelimitedString(authorizedClientServiceMap.keySet()));
throw new NoUniqueBeanDefinitionException(OAuth2AuthorizedClientService.class,
authorizedClientServiceMap.size(),
"Expected single matching bean of type '" + OAuth2AuthorizedClientService.class.getName()
+ "' but found " + authorizedClientServiceMap.size() + ": "
+ StringUtils.collectionToCommaDelimitedString(authorizedClientServiceMap.keySet()));
}
return (!authorizedClientServiceMap.isEmpty() ? authorizedClientServiceMap.values().iterator().next() : null);
}
}

Some files were not shown because too many files have changed in this diff Show More