Merge Formatting Changes
Issue gh-8945
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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}"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
}
|
||||
|
||||
@@ -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) -> {
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
}
|
||||
|
||||
@@ -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.*");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<>()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 <global-method-security>
|
||||
* xml support.
|
||||
* Enables Spring Security global method security similar to the
|
||||
* <global-method-security> 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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
* @EnableRSocketSecurity
|
||||
* public class SecurityConfig {
|
||||
* // @formatter:off
|
||||
* @Bean
|
||||
* PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
|
||||
* rsocket
|
||||
* .authorizePayload(authorize ->
|
||||
* .authorizePayload((authorize) ->
|
||||
* authorize
|
||||
* .anyRequest().authenticated()
|
||||
* );
|
||||
* return rsocket.build();
|
||||
* }
|
||||
* // @formatter:on
|
||||
*
|
||||
* // @formatter:off
|
||||
* @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">
|
||||
* @EnableRSocketSecurity
|
||||
* public class SecurityConfig {
|
||||
* // @formatter:off
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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("/static/**");
|
||||
* // 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) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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" };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user