mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-24 21:12:18 +00:00
SEC-1953: Spring Security Java Config support
This is the initial migration of Spring Security Java Config from the external project at https://github.com/SpringSource/spring-security-javaconfig
This commit is contained in:
parent
fba4fec84b
commit
d0c4e6ca72
@ -15,12 +15,22 @@ dependencies {
|
||||
// NB: Don't add other compile time dependencies to the config module as this breaks tooling
|
||||
compile project(':spring-security-core'),
|
||||
project(':spring-security-web'),
|
||||
project(':spring-security-openid'),
|
||||
project(':spring-security-ldap'),
|
||||
"org.aspectj:aspectjweaver:$aspectjVersion",
|
||||
'aopalliance:aopalliance:1.0',
|
||||
"org.springframework:spring-aop:$springVersion",
|
||||
"org.springframework:spring-context:$springVersion",
|
||||
"org.springframework:spring-web:$springVersion",
|
||||
"org.springframework:spring-beans:$springVersion"
|
||||
"org.springframework:spring-beans:$springVersion",
|
||||
"org.springframework:spring-jdbc:$springVersion",
|
||||
"org.springframework:spring-tx:$springVersion",
|
||||
"org.springframework.ldap:spring-ldap-core:$springLdapVersion"
|
||||
compile('org.openid4java:openid4java-nodeps:0.9.6') {
|
||||
exclude group: 'com.google.code.guice', module: 'guice'
|
||||
}
|
||||
compile 'com.google.inject:guice:2.0'
|
||||
compile apacheds_libs
|
||||
|
||||
provided "org.apache.tomcat:tomcat-servlet-api:$servletApiVersion"
|
||||
|
||||
@ -28,6 +38,7 @@ dependencies {
|
||||
|
||||
testCompile project(':spring-security-ldap'),
|
||||
project(':spring-security-openid'),
|
||||
project(':spring-security-cas'),
|
||||
project(':spring-security-core').sourceSets.test.output,
|
||||
'javax.annotation:jsr250-api:1.0',
|
||||
"org.springframework.ldap:spring-ldap-core:$springLdapVersion",
|
||||
|
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.beans.factory.support.AbstractAutowireCapableBeanFactory;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
|
||||
import org.springframework.security.ldap.server.ApacheDSContainer;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class LdapAuthenticationProviderBuilderSecurityBuilderTests extends BaseSpringSpec {
|
||||
def "default configuration"() {
|
||||
when:
|
||||
loadConfig(DefaultLdapConfig)
|
||||
LdapAuthenticationProvider provider = ldapProvider()
|
||||
then:
|
||||
provider.authoritiesPopulator.groupRoleAttribute == "cn"
|
||||
provider.authoritiesPopulator.groupSearchBase == ""
|
||||
provider.authoritiesPopulator.groupSearchFilter == "(uniqueMember={0})"
|
||||
ReflectionTestUtils.getField(provider,"authoritiesMapper").prefix == "ROLE_"
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultLdapConfig extends BaseLdapProviderConfig {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
}
|
||||
}
|
||||
|
||||
def "group roles custom"() {
|
||||
when:
|
||||
loadConfig(GroupRolesConfig)
|
||||
LdapAuthenticationProvider provider = ldapProvider()
|
||||
then:
|
||||
provider.authoritiesPopulator.groupRoleAttribute == "group"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class GroupRolesConfig extends BaseLdapProviderConfig {
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.groupRoleAttribute("group")
|
||||
}
|
||||
}
|
||||
|
||||
def "group search custom"() {
|
||||
when:
|
||||
loadConfig(GroupSearchConfig)
|
||||
LdapAuthenticationProvider provider = ldapProvider()
|
||||
then:
|
||||
provider.authoritiesPopulator.groupSearchFilter == "ou=groupName"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class GroupSearchConfig extends BaseLdapProviderConfig {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.groupSearchFilter("ou=groupName");
|
||||
}
|
||||
}
|
||||
|
||||
def "role prefix custom"() {
|
||||
when:
|
||||
loadConfig(RolePrefixConfig)
|
||||
LdapAuthenticationProvider provider = ldapProvider()
|
||||
then:
|
||||
ReflectionTestUtils.getField(provider,"authoritiesMapper").prefix == "role_"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RolePrefixConfig extends BaseLdapProviderConfig {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.rolePrefix("role_")
|
||||
}
|
||||
}
|
||||
|
||||
def "bind authentication"() {
|
||||
when:
|
||||
loadConfig(BindAuthenticationConfig)
|
||||
AuthenticationManager auth = context.getBean(AuthenticationManager)
|
||||
then:
|
||||
auth
|
||||
auth.authenticate(new UsernamePasswordAuthenticationToken("admin","password")).authorities.collect { it.authority }.sort() == ["ROLE_ADMIN","ROLE_USER"]
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class BindAuthenticationConfig extends BaseLdapServerConfig {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.contextSource(contextSource())
|
||||
.groupSearchBase("ou=groups")
|
||||
.userDnPatterns("uid={0},ou=people");
|
||||
}
|
||||
}
|
||||
|
||||
def ldapProvider() {
|
||||
context.getBean(AuthenticationManager).providers[0]
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static abstract class BaseLdapServerConfig extends BaseLdapProviderConfig {
|
||||
@Bean
|
||||
public ApacheDSContainer ldapServer() throws Exception {
|
||||
ApacheDSContainer apacheDSContainer = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/users.ldif");
|
||||
apacheDSContainer.setPort(33389);
|
||||
return apacheDSContainer;
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static abstract class BaseLdapProviderConfig {
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager() {
|
||||
AuthenticationManagerBuilder registry = new AuthenticationManagerBuilder();
|
||||
registerAuthentication(registry);
|
||||
return registry.build();
|
||||
}
|
||||
|
||||
protected abstract void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception;
|
||||
|
||||
@Bean
|
||||
public BaseLdapPathContextSource contextSource() throws Exception {
|
||||
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
|
||||
"ldap://127.0.0.1:33389/dc=springframework,dc=org")
|
||||
contextSource.userDn = "uid=admin,ou=system"
|
||||
contextSource.password = "secret"
|
||||
contextSource.afterPropertiesSet();
|
||||
return contextSource;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication.ldap
|
||||
|
||||
import static org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.*
|
||||
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.LdapAuthenticationProviderConfig;
|
||||
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
|
||||
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;
|
||||
import org.springframework.security.ldap.userdetails.PersonContextMapper;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class NamespaceLdapAuthenticationProviderTests extends BaseSpringSpec {
|
||||
def "ldap-authentication-provider"() {
|
||||
when:
|
||||
loadConfig(LdapAuthenticationProviderConfig)
|
||||
then:
|
||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password")).authorities*.authority.sort() == ['ROLE_USER']
|
||||
}
|
||||
|
||||
def "ldap-authentication-provider custom"() {
|
||||
when:
|
||||
loadConfig(CustomLdapAuthenticationProviderConfig)
|
||||
LdapAuthenticationProvider provider = findAuthenticationProvider(LdapAuthenticationProvider)
|
||||
then:
|
||||
provider.authoritiesPopulator.groupRoleAttribute == "cn"
|
||||
provider.authoritiesPopulator.groupSearchBase == "ou=groups"
|
||||
provider.authoritiesPopulator.groupSearchFilter == "(member={0})"
|
||||
ReflectionTestUtils.getField(provider,"authoritiesMapper").prefix == "PREFIX_"
|
||||
provider.userDetailsContextMapper instanceof PersonContextMapper
|
||||
provider.authenticator.getUserDns("user") == ["uid=user,ou=people"]
|
||||
provider.authenticator.userSearch.searchBase == "ou=users"
|
||||
provider.authenticator.userSearch.searchFilter == "(uid={0})"
|
||||
}
|
||||
|
||||
def "ldap-authentication-provider password compare"() {
|
||||
when:
|
||||
loadConfig(PasswordCompareLdapConfig)
|
||||
LdapAuthenticationProvider provider = findAuthenticationProvider(LdapAuthenticationProvider)
|
||||
then:
|
||||
provider.authenticator instanceof PasswordComparisonAuthenticator
|
||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password")).authorities*.authority.sort() == ['ROLE_USER']
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.encoding.PlaintextPasswordEncoder;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.ldap.userdetails.PersonContextMapper;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceLdapAuthenticationProviderTestsConfigs {
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class LdapAuthenticationProviderConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.groupSearchBase("ou=groups")
|
||||
.userDnPatterns("uid={0},ou=people"); // ldap-server@user-dn-pattern
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class CustomLdapAuthenticationProviderConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.groupRoleAttribute("cn") // ldap-authentication-provider@group-role-attribute
|
||||
.groupSearchBase("ou=groups") // ldap-authentication-provider@group-search-base
|
||||
.groupSearchFilter("(member={0})") // ldap-authentication-provider@group-search-filter
|
||||
.rolePrefix("PREFIX_") // ldap-authentication-provider@group-search-filter
|
||||
.userDetailsContextMapper(new PersonContextMapper()) // ldap-authentication-provider@user-context-mapper-ref / ldap-authentication-provider@user-details-class
|
||||
.userDnPatterns("uid={0},ou=people") // ldap-authentication-provider@user-dn-pattern
|
||||
.userSearchBase("ou=users") // ldap-authentication-provider@user-dn-pattern
|
||||
.userSearchFilter("(uid={0})") // ldap-authentication-provider@user-search-filter
|
||||
// .contextSource(contextSource) // ldap-authentication-provider@server-ref
|
||||
.contextSource()
|
||||
.ldif("classpath:user.ldif") // ldap-server@ldif
|
||||
.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
|
||||
// .url("ldap://localhost:33389/dc-springframework,dc=org") this overrides root and port and is used for external
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class PasswordCompareLdapConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.ldapAuthentication()
|
||||
.groupSearchBase("ou=groups")
|
||||
.userSearchFilter("(uid={0})")
|
||||
.passwordCompare()
|
||||
.passwordEncoder(new PlaintextPasswordEncoder()) // ldap-authentication-provider/password-compare/password-encoder@ref
|
||||
.passwordAttribute("userPassword"); // ldap-authentication-provider/password-compare@password-attribute
|
||||
}
|
||||
}
|
||||
}
|
42
config/src/integration-test/resources/users.ldif
Normal file
42
config/src/integration-test/resources/users.ldif
Normal file
@ -0,0 +1,42 @@
|
||||
dn: ou=groups,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: organizationalUnit
|
||||
ou: groups
|
||||
|
||||
dn: ou=people,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: organizationalUnit
|
||||
ou: people
|
||||
|
||||
dn: uid=admin,ou=people,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: person
|
||||
objectclass: organizationalPerson
|
||||
objectclass: inetOrgPerson
|
||||
cn: Rod Johnson
|
||||
sn: Johnson
|
||||
uid: admin
|
||||
userPassword: password
|
||||
|
||||
dn: uid=user,ou=people,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: person
|
||||
objectclass: organizationalPerson
|
||||
objectclass: inetOrgPerson
|
||||
cn: Dianne Emu
|
||||
sn: Emu
|
||||
uid: user
|
||||
userPassword: password
|
||||
|
||||
dn: cn=user,ou=groups,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: groupOfNames
|
||||
cn: user
|
||||
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
|
||||
uniqueMember: uid=user,ou=people,dc=springframework,dc=org
|
||||
|
||||
dn: cn=admin,ou=groups,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: groupOfNames
|
||||
cn: admin
|
||||
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
|
@ -0,0 +1,432 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.filter.DelegatingFilterProxy;
|
||||
|
||||
import com.google.inject.internal.ImmutableList.Builder;
|
||||
|
||||
/**
|
||||
* <p>A base {@link SecurityBuilder} that allows {@link SecurityConfigurer} to be
|
||||
* applied to it. This makes modifying the {@link SecurityBuilder} a strategy
|
||||
* that can be customized and broken up into a number of
|
||||
* {@link SecurityConfigurer} objects that have more specific goals than that
|
||||
* of the {@link SecurityBuilder}.</p>
|
||||
*
|
||||
* <p>For example, a {@link SecurityBuilder} may build an
|
||||
* {@link DelegatingFilterProxy}, but a {@link SecurityConfigurer} might
|
||||
* populate the {@link SecurityBuilder} with the 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)
|
||||
*/
|
||||
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>> extends AbstractSecurityBuilder<O> {
|
||||
|
||||
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers =
|
||||
new LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>>();
|
||||
|
||||
private final Map<Class<Object>,Object> sharedObjects = new HashMap<Class<Object>,Object>();
|
||||
|
||||
private final boolean allowConfigurersOfSameType;
|
||||
|
||||
private BuildState buildState = BuildState.UNBUILT;
|
||||
|
||||
private ObjectPostProcessor<Object> objectPostProcessor;
|
||||
|
||||
/**
|
||||
* Creates a new instance without post processing
|
||||
*/
|
||||
protected AbstractConfiguredSecurityBuilder() {
|
||||
this(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR);
|
||||
}
|
||||
|
||||
/***
|
||||
* 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) {
|
||||
this(objectPostProcessor,false);
|
||||
}
|
||||
|
||||
/***
|
||||
* 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, boolean allowConfigurersOfSameType) {
|
||||
Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");
|
||||
this.objectPostProcessor = objectPostProcessor;
|
||||
this.allowConfigurersOfSameType = allowConfigurersOfSameType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a {@link SecurityConfigurerAdapter} to this
|
||||
* {@link SecurityBuilder} and invokes
|
||||
* {@link SecurityConfigurerAdapter#setBuilder(SecurityBuilder)}.
|
||||
*
|
||||
* @param configurer
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer)
|
||||
throws Exception {
|
||||
add(configurer);
|
||||
configurer.addObjectPostProcessor(objectPostProcessor);
|
||||
configurer.setBuilder((B) this);
|
||||
return configurer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws Exception
|
||||
*/
|
||||
public <C extends SecurityConfigurer<O, B>> C apply(C configurer)
|
||||
throws Exception {
|
||||
add(configurer);
|
||||
return configurer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C> void setSharedObject(Class<C> sharedType, C object) {
|
||||
this.sharedObjects.put((Class<Object>) sharedType, object);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C> C getSharedObject(Class<C> sharedType) {
|
||||
return (C) this.sharedObjects.get(sharedType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the shared objects
|
||||
* @return
|
||||
*/
|
||||
public Map<Class<Object>,Object> getSharedObjects() {
|
||||
return Collections.unmodifiableMap(this.sharedObjects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds {@link SecurityConfigurer} ensuring that it is allowed and
|
||||
* invoking {@link SecurityConfigurer#init(SecurityBuilder)} immediately
|
||||
* if necessary.
|
||||
*
|
||||
* @param configurer the {@link SecurityConfigurer} to add
|
||||
* @throws Exception if an error occurs
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception {
|
||||
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");
|
||||
}
|
||||
List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers.get(clazz) : null;
|
||||
if(configs == null) {
|
||||
configs = new ArrayList<SecurityConfigurer<O,B>>(1);
|
||||
}
|
||||
configs.add(configurer);
|
||||
this.configurers.put(clazz, configs);
|
||||
if(buildState.isInitializing()) {
|
||||
configurer.init((B)this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends SecurityConfigurer<O, B>> List<C> getConfigurers(
|
||||
Class<C> clazz) {
|
||||
List<C> configs = (List<C>) this.configurers.get(clazz);
|
||||
if(configs == null) {
|
||||
return new ArrayList<C>();
|
||||
}
|
||||
return new ArrayList<C>(configs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends SecurityConfigurer<O, B>> List<C> removeConfigurers(
|
||||
Class<C> clazz) {
|
||||
List<C> configs = (List<C>) this.configurers.remove(clazz);
|
||||
if(configs == null) {
|
||||
return new ArrayList<C>();
|
||||
}
|
||||
return new ArrayList<C>(configs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends SecurityConfigurer<O, B>> C getConfigurer(
|
||||
Class<C> clazz) {
|
||||
List<SecurityConfigurer<O,B>> configs = this.configurers.get(clazz);
|
||||
if(configs == null) {
|
||||
return null;
|
||||
}
|
||||
if(configs.size() != 1) {
|
||||
throw new IllegalStateException("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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <C extends SecurityConfigurer<O,B>> C removeConfigurer(Class<C> clazz) {
|
||||
List<SecurityConfigurer<O,B>> configs = this.configurers.remove(clazz);
|
||||
if(configs == null) {
|
||||
return null;
|
||||
}
|
||||
if(configs.size() != 1) {
|
||||
throw new IllegalStateException("Only one configurer expected for type " + clazz + ", but got " + configs);
|
||||
}
|
||||
return (C) configs.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the {@link ObjectPostProcessor} to use.
|
||||
* @param objectPostProcessor the {@link ObjectPostProcessor} to use. Cannot be null
|
||||
* @return the {@link SecurityBuilder} for further customizations
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public O objectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
Assert.notNull(objectPostProcessor,"objectPostProcessor cannot be null");
|
||||
this.objectPostProcessor = objectPostProcessor;
|
||||
return (O) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
protected <P> P postProcess(P object) {
|
||||
return (P) this.objectPostProcessor.postProcess(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the build using the {@link SecurityConfigurer}'s that have been applied using the following steps:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Invokes {@link #beforeInit()} for any subclass to hook into</li>
|
||||
* <li>Invokes {@link SecurityConfigurer#init(SecurityBuilder)} for any {@link SecurityConfigurer} that was applied to this builder.</li>
|
||||
* <li>Invokes {@link #beforeConfigure()} for any subclass to hook into</li>
|
||||
* <li>Invokes {@link #performBuild()} which actually builds the Object</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Override
|
||||
protected final O doBuild() throws Exception {
|
||||
synchronized(configurers) {
|
||||
buildState = BuildState.INITIALIZING;
|
||||
|
||||
beforeInit();
|
||||
init();
|
||||
|
||||
buildState = BuildState.CONFIGURING;
|
||||
|
||||
beforeConfigure();
|
||||
configure();
|
||||
|
||||
buildState = BuildState.BUILDING;
|
||||
|
||||
O result = performBuild();
|
||||
|
||||
buildState = BuildState.BUILT;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked prior to invoking each
|
||||
* {@link SecurityConfigurer#init(SecurityBuilder)} method. Subclasses may
|
||||
* override this method to hook into the lifecycle without using a
|
||||
* {@link SecurityConfigurer}.
|
||||
*/
|
||||
protected void beforeInit() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked prior to invoking each
|
||||
* {@link SecurityConfigurer#configure(SecurityBuilder)} method.
|
||||
* Subclasses may override this method to hook into the lifecycle without
|
||||
* using a {@link SecurityConfigurer}.
|
||||
*/
|
||||
protected void beforeConfigure() throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method to build the object that is being returned.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract O performBuild() throws Exception;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void init() throws Exception {
|
||||
Collection<SecurityConfigurer<O,B>> configurers = getConfigurers();
|
||||
|
||||
for(SecurityConfigurer<O,B> configurer : configurers ) {
|
||||
configurer.init((B) this);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void configure() throws Exception {
|
||||
Collection<SecurityConfigurer<O,B>> configurers = getConfigurers();
|
||||
|
||||
for(SecurityConfigurer<O,B> configurer : configurers ) {
|
||||
configurer.configure((B) this);
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<SecurityConfigurer<O, B>> getConfigurers() {
|
||||
List<SecurityConfigurer<O,B>> result = new ArrayList<SecurityConfigurer<O,B>>();
|
||||
for(List<SecurityConfigurer<O,B>> configs : this.configurers.values()) {
|
||||
result.addAll(configs);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The build state for the application
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
private static enum BuildState {
|
||||
/**
|
||||
* This is the state before the {@link Builder#build()} is invoked
|
||||
*/
|
||||
UNBUILT(0),
|
||||
|
||||
/**
|
||||
* The state from when {@link Builder#build()} is first invoked until
|
||||
* all the {@link SecurityConfigurer#init(SecurityBuilder)} methods
|
||||
* have been invoked.
|
||||
*/
|
||||
INITIALIZING(1),
|
||||
|
||||
/**
|
||||
* The state from after all
|
||||
* {@link SecurityConfigurer#init(SecurityBuilder)} have been invoked
|
||||
* until after all the
|
||||
* {@link SecurityConfigurer#configure(SecurityBuilder)} methods have
|
||||
* been invoked.
|
||||
*/
|
||||
CONFIGURING(2),
|
||||
|
||||
/**
|
||||
* From the point after all the
|
||||
* {@link SecurityConfigurer#configure(SecurityBuilder)} have
|
||||
* completed to just after
|
||||
* {@link AbstractConfiguredSecurityBuilder#performBuild()}.
|
||||
*/
|
||||
BUILDING(3),
|
||||
|
||||
/**
|
||||
* After the object has been completely built.
|
||||
*/
|
||||
BUILT(4);
|
||||
|
||||
private final int order;
|
||||
|
||||
BuildState(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public boolean isInitializing() {
|
||||
return INITIALIZING.order == order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the state is CONFIGURING or later
|
||||
* @return
|
||||
*/
|
||||
public boolean isConfigured() {
|
||||
return order >= CONFIGURING.order;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* A base {@link SecurityBuilder} that ensures the object being built is only
|
||||
* built one 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(building.compareAndSet(false, true)) {
|
||||
object = doBuild();
|
||||
return object;
|
||||
}
|
||||
throw new IllegalStateException("This object has already been built");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
if(!building.get()) {
|
||||
throw new IllegalStateException("This object has not been built");
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
/**
|
||||
* Allows initialization of Objects. Typically this is used to call the
|
||||
* {@link Aware} methods, {@link InitializingBean#afterPropertiesSet()}, and
|
||||
* ensure that {@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
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* A do nothing implementation of the {@link ObjectPostProcessor}
|
||||
*/
|
||||
ObjectPostProcessor<Object> QUIESCENT_POSTPROCESSOR = new ObjectPostProcessor<Object>() {
|
||||
@Override
|
||||
public <T> T postProcess(T object) {
|
||||
return object;
|
||||
}
|
||||
};
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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
|
||||
*
|
||||
* @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 occured when building the Object
|
||||
*/
|
||||
O build() throws Exception;
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
/**
|
||||
* Allows for configuring a {@link SecurityBuilder}. All
|
||||
* {@link SecurityConfigurer} first have their {@link #init(SecurityBuilder)}
|
||||
* method invoked. After all {@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.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param builder
|
||||
* @throws Exception
|
||||
*/
|
||||
void init(B builder) throws Exception;
|
||||
|
||||
/**
|
||||
* Configure the {@link SecurityBuilder} by setting the necessary properties
|
||||
* on the {@link SecurityBuilder}.
|
||||
*
|
||||
* @param builder
|
||||
* @throws Exception
|
||||
*/
|
||||
void configure(B builder) throws Exception;
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
|
||||
/**
|
||||
* A base class for {@link SecurityConfigurer} that allows subclasses to only
|
||||
* implement the methods they are interested in. It also provides a mechanism
|
||||
* for using the {@link SecurityConfigurer} and when done gaining access to the
|
||||
* {@link SecurityBuilder} that is being configured.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
* @param <O>
|
||||
* The Object being built by B
|
||||
* @param <B>
|
||||
* The Builder that is building O and is configured by {@link SecurityConfigurerAdapter}
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public B and() {
|
||||
return getBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link SecurityBuilder}. Cannot be null.
|
||||
*
|
||||
* @return the {@link SecurityBuilder}
|
||||
* @throw {@link IllegalStateException} if {@link SecurityBuilder} is null
|
||||
*/
|
||||
protected final B getBuilder() {
|
||||
if(securityBuilder == null) {
|
||||
throw new IllegalStateException("securityBuilder cannot be null");
|
||||
}
|
||||
return 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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> T postProcess(T object) {
|
||||
return (T) this.objectPostProcessor.postProcess(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.securityBuilder = builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link ObjectPostProcessor} that delegates work to numerous
|
||||
* {@link ObjectPostProcessor} implementations.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
private static final class CompositeObjectPostProcessor implements ObjectPostProcessor<Object> {
|
||||
private List<ObjectPostProcessor<? extends Object>> postProcessors = new ArrayList<ObjectPostProcessor<?>>();
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public Object postProcess(Object object) {
|
||||
for(ObjectPostProcessor opp : postProcessors) {
|
||||
Class<?> oppClass = opp.getClass();
|
||||
Class<?> oppType = GenericTypeResolver.resolveTypeArgument(oppClass,ObjectPostProcessor.class);
|
||||
if(oppType == null || oppType.isAssignableFrom(object.getClass())) {
|
||||
object = opp.postProcess(object);
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link ObjectPostProcessor} to use
|
||||
* @param objectPostProcessor the {@link ObjectPostProcessor} to add
|
||||
* @return true if the {@link ObjectPostProcessor} was added, else false
|
||||
*/
|
||||
private boolean addObjectPostProcessor(ObjectPostProcessor<?extends Object> objectPostProcessor) {
|
||||
return this.postProcessors.add(objectPostProcessor);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
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}
|
||||
*/
|
||||
public interface ProviderManagerBuilder<B extends ProviderManagerBuilder<B>> extends SecurityBuilder<AuthenticationManager> {
|
||||
|
||||
/**
|
||||
* Add authentication based upon the custom {@link AuthenticationProvider}
|
||||
* that is passed in. Since the {@link AuthenticationProvider}
|
||||
* implementation is unknown, all customizations must be done externally and
|
||||
* the {@link ProviderManagerBuilder} is returned immediately.
|
||||
*
|
||||
* @return a {@link ProviderManagerBuilder} to allow further authentication
|
||||
* to be provided to the {@link ProviderManagerBuilder}
|
||||
* @throws Exception
|
||||
* if an error occurs when adding the {@link AuthenticationProvider}
|
||||
*/
|
||||
B authenticationProvider(AuthenticationProvider authenticationProvider);
|
||||
}
|
@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
|
||||
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.config.annotation.authentication.configurers.ldap.LdapAuthenticationProviderConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.JdbcUserDetailsManagerConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.userdetails.UserDetailsAwareConfigurer;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link SecurityBuilder} used to create an {@link AuthenticationManager}.
|
||||
* Allows for easily building in memory authentication, LDAP authentication,
|
||||
* JDBC based authentication, adding {@link UserDetailsService}, and adding
|
||||
* {@link AuthenticationProvider}'s.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public class AuthenticationManagerBuilder extends AbstractConfiguredSecurityBuilder<AuthenticationManager, AuthenticationManagerBuilder> implements ProviderManagerBuilder<AuthenticationManagerBuilder> {
|
||||
|
||||
private AuthenticationManager parentAuthenticationManager;
|
||||
private List<AuthenticationProvider> authenticationProviders = new ArrayList<AuthenticationProvider>();
|
||||
private UserDetailsService defaultUserDetailsService;
|
||||
private Boolean eraseCredentials;
|
||||
private AuthenticationEventPublisher eventPublisher;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*/
|
||||
public AuthenticationManagerBuilder() {
|
||||
super(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR,true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.parentAuthenticationManager = authenticationManager;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationEventPublisher}
|
||||
*
|
||||
* @param eventPublisher
|
||||
* the {@link AuthenticationEventPublisher} to use
|
||||
* @return the {@link AuthenticationManagerBuilder} for further
|
||||
* customizations
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
|
||||
this.eraseCredentials = eraseCredentials;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add in memory authentication to the {@link AuthenticationManagerBuilder}
|
||||
* and return a {@link InMemoryUserDetailsManagerConfigurer} to
|
||||
* allow customization of the in memory authentication.
|
||||
*
|
||||
* <p>
|
||||
* This method also ensure that a {@link UserDetailsService} is available
|
||||
* for the {@link #getDefaultUserDetailsService()} method. Note that
|
||||
* additional {@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
|
||||
*/
|
||||
public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()
|
||||
throws Exception {
|
||||
return apply(new InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add JDBC authentication to the {@link AuthenticationManagerBuilder} and
|
||||
* return a {@link JdbcUserDetailsManagerConfigurer} to allow customization of the
|
||||
* JDBC authentication.
|
||||
*
|
||||
* <p>
|
||||
* This method also ensure that a {@link UserDetailsService} is available
|
||||
* for the {@link #getDefaultUserDetailsService()} method. Note that
|
||||
* additional {@link UserDetailsService}'s may override this
|
||||
* {@link UserDetailsService} as the default.
|
||||
* </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 {
|
||||
return apply(new JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add authentication based upon the custom {@link UserDetailsService} that
|
||||
* is passed in. It then returns a {@link DaoAuthenticationConfigurer} to
|
||||
* allow customization of the authentication.
|
||||
*
|
||||
* <p>
|
||||
* This method also ensure that the {@link UserDetailsService} is available
|
||||
* for the {@link #getDefaultUserDetailsService()} method. Note that
|
||||
* additional {@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}
|
||||
* based authentication
|
||||
*/
|
||||
public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder,T> userDetailsService(
|
||||
T userDetailsService) throws Exception {
|
||||
this.defaultUserDetailsService = userDetailsService;
|
||||
return apply(new DaoAuthenticationConfigurer<AuthenticationManagerBuilder,T>(userDetailsService));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add LDAP authentication to the {@link AuthenticationManagerBuilder} and
|
||||
* return a {@link LdapAuthenticationProviderConfigurer} to allow
|
||||
* customization of the LDAP authentication.
|
||||
*
|
||||
* <p>
|
||||
* This method <b>does NOT</b> ensure that a {@link UserDetailsService} is
|
||||
* available for the {@link #getDefaultUserDetailsService()} method.
|
||||
* </p>
|
||||
*
|
||||
* @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 {
|
||||
return apply(new LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add authentication based upon the custom {@link AuthenticationProvider}
|
||||
* that is passed in. Since the {@link AuthenticationProvider}
|
||||
* implementation is unknown, all customizations must be done externally and
|
||||
* the {@link AuthenticationManagerBuilder} is returned immediately.
|
||||
*
|
||||
* <p>
|
||||
* This method <b>does NOT</b> ensure that the {@link UserDetailsService} is
|
||||
* available for the {@link #getDefaultUserDetailsService()} method.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link AuthenticationManagerBuilder} to allow further authentication
|
||||
* to be provided to the {@link AuthenticationManagerBuilder}
|
||||
* @throws Exception
|
||||
* if an error occurs when adding the {@link AuthenticationProvider}
|
||||
*/
|
||||
public AuthenticationManagerBuilder authenticationProvider(
|
||||
AuthenticationProvider authenticationProvider) {
|
||||
this.authenticationProviders.add(authenticationProvider);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProviderManager performBuild() throws Exception {
|
||||
ProviderManager providerManager = new ProviderManager(authenticationProviders, parentAuthenticationManager);
|
||||
if(eraseCredentials != null) {
|
||||
providerManager.setEraseCredentialsAfterAuthentication(eraseCredentials);
|
||||
}
|
||||
if(eventPublisher != null) {
|
||||
providerManager.setAuthenticationEventPublisher(eventPublisher);
|
||||
}
|
||||
providerManager = postProcess(providerManager);
|
||||
return providerManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*/
|
||||
public UserDetailsService getDefaultUserDetailsService() {
|
||||
return this.defaultUserDetailsService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws Exception if an error occurs
|
||||
*/
|
||||
private <C extends UserDetailsAwareConfigurer<AuthenticationManagerBuilder,? extends UserDetailsService>> C apply(C configurer) throws Exception {
|
||||
this.defaultUserDetailsService = configurer.getUserDetailsService();
|
||||
return (C) super.apply(configurer);
|
||||
}
|
||||
}
|
@ -0,0 +1,469 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication.configurers.ldap;
|
||||
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.encoding.PasswordEncoder;
|
||||
import org.springframework.security.authentication.encoding.PlaintextPasswordEncoder;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
|
||||
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
|
||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||
import org.springframework.security.ldap.authentication.AbstractLdapAuthenticator;
|
||||
import org.springframework.security.ldap.authentication.BindAuthenticator;
|
||||
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
|
||||
import org.springframework.security.ldap.authentication.LdapAuthenticator;
|
||||
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator;
|
||||
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
|
||||
import org.springframework.security.ldap.search.LdapUserSearch;
|
||||
import org.springframework.security.ldap.server.ApacheDSContainer;
|
||||
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
|
||||
import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;
|
||||
import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
|
||||
import org.springframework.security.ldap.userdetails.PersonContextMapper;
|
||||
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
|
||||
|
||||
/**
|
||||
* Configures LDAP {@link AuthenticationProvider} in the {@link ProviderManagerBuilder}.
|
||||
*
|
||||
* @param <B> the {@link ProviderManagerBuilder} type that this is configuring.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuilder<B>> extends SecurityConfigurerAdapter<AuthenticationManager,B> {
|
||||
private String groupRoleAttribute = "cn";
|
||||
private String groupSearchBase = "";
|
||||
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 LdapAuthenticationProvider build() throws Exception {
|
||||
BaseLdapPathContextSource contextSource = getContextSource();
|
||||
LdapAuthenticator ldapAuthenticator = createLdapAuthenticator(contextSource);
|
||||
|
||||
DefaultLdapAuthoritiesPopulator authoritiesPopulator = new DefaultLdapAuthoritiesPopulator(
|
||||
contextSource, groupSearchBase);
|
||||
authoritiesPopulator.setGroupRoleAttribute(groupRoleAttribute);
|
||||
authoritiesPopulator.setGroupSearchFilter(groupSearchFilter);
|
||||
|
||||
LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(
|
||||
ldapAuthenticator, authoritiesPopulator);
|
||||
SimpleAuthorityMapper simpleAuthorityMapper = new SimpleAuthorityMapper();
|
||||
simpleAuthorityMapper.setPrefix(rolePrefix);
|
||||
simpleAuthorityMapper.afterPropertiesSet();
|
||||
ldapAuthenticationProvider.setAuthoritiesMapper(simpleAuthorityMapper);
|
||||
if(userDetailsContextMapper != null) {
|
||||
ldapAuthenticationProvider.setUserDetailsContextMapper(userDetailsContextMapper);
|
||||
}
|
||||
return ldapAuthenticationProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
LdapUserSearch userSearch = createUserSearch();
|
||||
if(userSearch != null) {
|
||||
ldapAuthenticator.setUserSearch(userSearch);
|
||||
}
|
||||
if(userDnPatterns != null && userDnPatterns.length > 0) {
|
||||
ldapAuthenticator.setUserDnPatterns(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);
|
||||
ldapAuthenticator.setPasswordAttributeName(passwordAttribute);
|
||||
ldapAuthenticator.setPasswordEncoder(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) {
|
||||
return new BindAuthenticator(contextSource);
|
||||
}
|
||||
|
||||
private LdapUserSearch createUserSearch() {
|
||||
if(userSearchFilter == null) {
|
||||
return null;
|
||||
}
|
||||
return new FilterBasedLdapUserSearch(userSearchBase, userSearchFilter, 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) {
|
||||
this.contextSource = contextSource;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the {@link PasswordEncoder} to be used when authenticating with
|
||||
* password comparison.
|
||||
*
|
||||
* @param passwordEncoder the {@link PasswordEncoder} to use
|
||||
* @return the {@link LdapAuthenticationProviderConfigurer} for further customization
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> passwordEncoder(PasswordEncoder passwordEncoder) {
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If your users are at a fixed location in the directory (i.e. you can work
|
||||
* out the DN directly from the username without doing a directory search),
|
||||
* you can use this attribute to map directly to the DN. It maps directly to
|
||||
* the userDnPatterns 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) {
|
||||
this.userDnPatterns = userDnPatterns;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @see PersonContextMapper
|
||||
* @see InetOrgPersonContextMapper
|
||||
* @see LdapUserDetailsMapper
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> userDetailsContextMapper(UserDetailsContextMapper userDetailsContextMapper) {
|
||||
this.userDetailsContextMapper = userDetailsContextMapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the attribute name which contains the role name. Default is "cn".
|
||||
* @param groupRoleAttribute the attribute name that maps a group to a role.
|
||||
* @return
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> groupRoleAttribute(String groupRoleAttribute) {
|
||||
this.groupRoleAttribute = groupRoleAttribute;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The search base for group membership searches. Defaults to "".
|
||||
* @param groupSearchBase
|
||||
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> groupSearchBase(String groupSearchBase) {
|
||||
this.groupSearchBase = groupSearchBase;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.groupSearchFilter = groupSearchFilter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> rolePrefix(String rolePrefix) {
|
||||
this.rolePrefix = rolePrefix;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> userSearchBase(String userSearchBase) {
|
||||
this.userSearchBase = userSearchBase;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.userSearchFilter = userSearchFilter;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(B builder) throws Exception {
|
||||
LdapAuthenticationProvider provider = postProcess(build());
|
||||
builder.authenticationProvider(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up Password based comparison
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public final class PasswordCompareConfigurer {
|
||||
|
||||
/**
|
||||
* Allows specifying the {@link PasswordEncoder} to use. The default is {@link PlaintextPasswordEncoder}.
|
||||
* @param passwordEncoder the {@link PasswordEncoder} to use
|
||||
* @return the {@link PasswordEncoder} to use
|
||||
*/
|
||||
public PasswordCompareConfigurer passwordEncoder(PasswordEncoder passwordEncoder) {
|
||||
LdapAuthenticationProviderConfigurer.this.passwordEncoder = passwordEncoder;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public PasswordCompareConfigurer passwordAttribute(String passwordAttribute) {
|
||||
LdapAuthenticationProviderConfigurer.this.passwordAttribute = passwordAttribute;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
return LdapAuthenticationProviderConfigurer.this;
|
||||
}
|
||||
|
||||
private PasswordCompareConfigurer() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows building a {@link BaseLdapPathContextSource} and optionally
|
||||
* creating an embedded LDAP instance.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class ContextSourceBuilder {
|
||||
private String ldif = "classpath*:*.ldif";
|
||||
private String managerPassword;
|
||||
private String managerDn;
|
||||
private int port = 33389;
|
||||
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
|
||||
*/
|
||||
public ContextSourceBuilder ldif(String ldif) {
|
||||
this.ldif = ldif;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public ContextSourceBuilder managerDn(String managerDn) {
|
||||
this.managerDn = managerDn;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The password for the manager DN. This is required if the manager-dn is specified.
|
||||
* @param managerPassword password for the manager DN
|
||||
* @return the {@link ContextSourceBuilder} for further customization
|
||||
*/
|
||||
public ContextSourceBuilder managerPassword(String managerPassword) {
|
||||
this.managerPassword = managerPassword;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The port to connect to LDAP to (the default is 33389).
|
||||
* @param port the port to connect to
|
||||
* @return the {@link ContextSourceBuilder} for further customization
|
||||
*/
|
||||
public ContextSourceBuilder port(int port) {
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public ContextSourceBuilder root(String root) {
|
||||
this.root = root;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public ContextSourceBuilder url(String url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link LdapAuthenticationProviderConfigurer} for further
|
||||
* customizations
|
||||
*
|
||||
* @return the {@link LdapAuthenticationProviderConfigurer} for further
|
||||
* customizations
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> and() {
|
||||
return LdapAuthenticationProviderConfigurer.this;
|
||||
}
|
||||
|
||||
private DefaultSpringSecurityContextSource build() throws Exception {
|
||||
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(getProviderUrl());
|
||||
if(managerDn != null) {
|
||||
contextSource.setUserDn(managerDn);
|
||||
if(managerPassword == null) {
|
||||
throw new IllegalStateException("managerPassword is required if managerDn is supplied");
|
||||
}
|
||||
contextSource.setPassword(managerPassword);
|
||||
}
|
||||
contextSource = postProcess(contextSource);
|
||||
if(url != null) {
|
||||
return contextSource;
|
||||
}
|
||||
ApacheDSContainer apacheDsContainer = new ApacheDSContainer(root, ldif);
|
||||
apacheDsContainer.setPort(port);
|
||||
postProcess(apacheDsContainer);
|
||||
return contextSource;
|
||||
}
|
||||
|
||||
private String getProviderUrl() {
|
||||
if(url == null) {
|
||||
return "ldap://127.0.0.1:" + port + "/" + root;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
private ContextSourceBuilder() {}
|
||||
}
|
||||
|
||||
private BaseLdapPathContextSource getContextSource() throws Exception {
|
||||
if(contextSource == null) {
|
||||
contextSource = contextSourceBuilder.build();
|
||||
}
|
||||
return contextSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public PasswordCompareConfigurer passwordCompare() {
|
||||
return new PasswordCompareConfigurer()
|
||||
.passwordAttribute("password")
|
||||
.passwordEncoder(new PlaintextPasswordEncoder());
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
|
||||
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
|
||||
/**
|
||||
* Configures an {@link org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder} to
|
||||
* have in memory authentication. It also allows easily adding users to the in memory authentication.
|
||||
*
|
||||
* @param <B> the type of the {@link SecurityBuilder} that is being configured
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public class InMemoryUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>> extends
|
||||
UserDetailsManagerConfigurer<B,InMemoryUserDetailsManagerConfigurer<B>> {
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*/
|
||||
public InMemoryUserDetailsManagerConfigurer() {
|
||||
super(new InMemoryUserDetailsManager(new ArrayList<UserDetails>()));
|
||||
}
|
||||
}
|
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import java.util.List;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
|
||||
import org.springframework.jdbc.datasource.init.DatabasePopulator;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
|
||||
import org.springframework.security.core.userdetails.UserCache;
|
||||
import org.springframework.security.provisioning.JdbcUserDetailsManager;
|
||||
|
||||
/**
|
||||
* Configures an {@link org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder} to
|
||||
* have JDBC authentication. It also allows easily adding users to the database used for authentication and setting up
|
||||
* the schema.
|
||||
*
|
||||
* <p>
|
||||
* The only required method is the {@link #dataSource(javax.sql.DataSource)} all other methods have reasonable defaults.
|
||||
* </p>
|
||||
*
|
||||
* @param <B> the type of the {@link ProviderManagerBuilder} that is being configured
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public class JdbcUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>> extends
|
||||
UserDetailsManagerConfigurer<B,JdbcUserDetailsManagerConfigurer<B>> {
|
||||
|
||||
private DataSource dataSource;
|
||||
|
||||
private List<Resource> initScripts = new ArrayList<Resource>();
|
||||
|
||||
public JdbcUserDetailsManagerConfigurer(JdbcUserDetailsManager manager) {
|
||||
super(manager);
|
||||
}
|
||||
|
||||
public JdbcUserDetailsManagerConfigurer() {
|
||||
this(new JdbcUserDetailsManager());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws Exception
|
||||
*/
|
||||
public JdbcUserDetailsManagerConfigurer<B> dataSource(DataSource dataSource) throws Exception {
|
||||
this.dataSource = dataSource;
|
||||
getUserDetailsService().setDataSource(dataSource);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the query to be used for finding a user by their username. For example:
|
||||
*
|
||||
* <code>
|
||||
* select username,password,enabled from users where username = ?
|
||||
* </code>
|
||||
* @param query The query to use for selecting the username, password, and if the user is enabled by username.
|
||||
* Must contain a single parameter for the username.
|
||||
* @return The {@link JdbcUserDetailsManagerRegistry} used for additional customizations
|
||||
* @throws Exception
|
||||
*/
|
||||
public JdbcUserDetailsManagerConfigurer<B> usersByUsernameQuery(String query) throws Exception {
|
||||
getUserDetailsService().setUsersByUsernameQuery(query);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the query to be used for finding a user's authorities by their username. For example:
|
||||
*
|
||||
* <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 JdbcUserDetailsManagerRegistry} used for additional customizations
|
||||
* @throws Exception
|
||||
*/
|
||||
public JdbcUserDetailsManagerConfigurer<B> authoritiesByUsernameQuery(String query) throws Exception {
|
||||
getUserDetailsService().setAuthoritiesByUsernameQuery(query);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* An SQL statement to query user's group authorities given a username. For example:
|
||||
*
|
||||
* <code>
|
||||
* select
|
||||
* g.id, g.group_name, ga.authority
|
||||
* from
|
||||
* groups g, group_members gm, group_authorities ga
|
||||
* 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 JdbcUserDetailsManagerRegistry} used for additional customizations
|
||||
* @throws Exception
|
||||
*/
|
||||
public JdbcUserDetailsManagerConfigurer<B> groupAuthoritiesByUsername(String query) throws Exception {
|
||||
JdbcUserDetailsManager userDetailsService = getUserDetailsService();
|
||||
userDetailsService.setEnableGroups(true);
|
||||
userDetailsService.setGroupAuthoritiesByUsernameQuery(query);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-empty string prefix that will be added to role strings loaded from persistent storage (default is "").
|
||||
*
|
||||
* @param rolePrefix
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public JdbcUserDetailsManagerConfigurer<B> rolePrefix(String rolePrefix) throws Exception {
|
||||
getUserDetailsService().setRolePrefix(rolePrefix);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines the {@link UserCache} to use
|
||||
*
|
||||
* @param userCache the {@link UserCache} to use
|
||||
* @return the {@link JdbcUserDetailsManagerConfigurer} for further customizations
|
||||
* @throws Exception
|
||||
*/
|
||||
public JdbcUserDetailsManagerConfigurer<B> userCache(UserCache userCache) throws Exception {
|
||||
getUserDetailsService().setUserCache(userCache);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initUserDetailsService() throws Exception {
|
||||
if(!initScripts.isEmpty()) {
|
||||
getDataSourceInit().afterPropertiesSet();
|
||||
}
|
||||
super.initUserDetailsService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcUserDetailsManager getUserDetailsService() {
|
||||
return (JdbcUserDetailsManager) super.getUserDetailsService();
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the default schema that allows users and authorities to be stored.
|
||||
*
|
||||
* @return The {@link JdbcUserDetailsManagerRegistry} used for additional customizations
|
||||
*/
|
||||
public JdbcUserDetailsManagerConfigurer<B> withDefaultSchema() {
|
||||
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[initScripts.size()]));
|
||||
return dbp;
|
||||
}
|
||||
|
||||
private DataSourceInitializer getDataSourceInit() {
|
||||
DataSourceInitializer dsi = new DataSourceInitializer();
|
||||
dsi.setDatabasePopulator(getDatabasePopulator());
|
||||
dsi.setDataSource(dataSource);
|
||||
return dsi;
|
||||
}
|
||||
}
|
@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.userdetails.UserDetailsServiceConfigurer;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.provisioning.UserDetailsManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class for populating an
|
||||
* {@link org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder} with a
|
||||
* {@link 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
|
||||
*/
|
||||
public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C extends UserDetailsManagerConfigurer<B,C>> extends
|
||||
UserDetailsServiceConfigurer<B,C,UserDetailsManager> {
|
||||
|
||||
private final List<UserDetailsBuilder> userBuilders = new ArrayList<UserDetailsBuilder>();
|
||||
|
||||
protected UserDetailsManagerConfigurer(UserDetailsManager userDetailsManager) {
|
||||
super(userDetailsManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the users that have been added.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected void initUserDetailsService() throws Exception {
|
||||
for(UserDetailsBuilder userBuilder : userBuilders) {
|
||||
getUserDetailsService().createUser(userBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final UserDetailsBuilder withUser(String username) {
|
||||
UserDetailsBuilder userBuilder = new UserDetailsBuilder((C)this);
|
||||
userBuilder.username(username);
|
||||
this.userBuilders.add(userBuilder);
|
||||
return userBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the user to be added. At minimum the username, password, and authorities should provided. The remaining
|
||||
* attributes have reasonable defaults.
|
||||
*
|
||||
* @param <T> the type of {@link UserDetailsManagerConfigurer} to return for chaining methods.
|
||||
*/
|
||||
public class UserDetailsBuilder {
|
||||
private String username;
|
||||
private String password;
|
||||
private List<GrantedAuthority> authorities;
|
||||
private boolean accountExpired;
|
||||
private boolean accountLocked;
|
||||
private boolean credentialsExpired;
|
||||
private boolean disabled;
|
||||
private final C builder;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param builder the builder to return
|
||||
*/
|
||||
private UserDetailsBuilder(C builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link UserDetailsManagerRegistry} for method chaining (i.e. to add another user)
|
||||
*
|
||||
* @return the {@link UserDetailsManagerRegistry} for method chaining (i.e. to add another user)
|
||||
*/
|
||||
public C and() {
|
||||
return 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)
|
||||
*/
|
||||
private UserDetailsBuilder username(String username) {
|
||||
Assert.notNull(username, "username cannot be null");
|
||||
this.username = username;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
public UserDetailsBuilder password(String password) {
|
||||
Assert.notNull(password, "password cannot be null");
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the roles. This method is a shortcut for calling {@link #authorities(String...)}, but automatically
|
||||
* prefixes each entry with "ROLE_". This means the following:
|
||||
*
|
||||
* <code>
|
||||
* builder.roles("USER","ADMIN");
|
||||
* </code>
|
||||
*
|
||||
* is equivalent to
|
||||
*
|
||||
* <code>
|
||||
* builder.authorities("ROLE_USER","ROLE_ADMIN");
|
||||
* </code>
|
||||
*
|
||||
* <p>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 additional attributes for this
|
||||
* user)
|
||||
*/
|
||||
public UserDetailsBuilder roles(String... roles) {
|
||||
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(roles.length);
|
||||
for(String role : roles) {
|
||||
Assert.isTrue(!role.startsWith("ROLE_"), role + " cannot start with ROLE_ (it is automatically added)");
|
||||
authorities.add(new SimpleGrantedAuthority("ROLE_"+role));
|
||||
}
|
||||
return authorities(authorities);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 additional attributes for this
|
||||
* user)
|
||||
* @see #roles(String...)
|
||||
*/
|
||||
public UserDetailsBuilder authorities(GrantedAuthority...authorities) {
|
||||
return authorities(Arrays.asList(authorities));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 additional attributes for this
|
||||
* user)
|
||||
* @see #roles(String...)
|
||||
*/
|
||||
public UserDetailsBuilder authorities(List<? extends GrantedAuthority> authorities) {
|
||||
this.authorities = new ArrayList<GrantedAuthority>(authorities);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 additional attributes for this
|
||||
* user)
|
||||
* @see #roles(String...)
|
||||
*/
|
||||
public UserDetailsBuilder authorities(String... authorities) {
|
||||
return authorities(AuthorityUtils.createAuthorityList(authorities));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
public UserDetailsBuilder accountExpired(boolean accountExpired) {
|
||||
this.accountExpired = accountExpired;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
public UserDetailsBuilder accountLocked(boolean accountLocked) {
|
||||
this.accountLocked = accountLocked;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
public UserDetailsBuilder credentialsExpired(boolean credentialsExpired) {
|
||||
this.credentialsExpired = credentialsExpired;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
public UserDetailsBuilder disabled(boolean disabled) {
|
||||
this.disabled = disabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
private UserDetails build() {
|
||||
return new User(username, password, !disabled, !accountExpired,
|
||||
!credentialsExpired, !accountLocked, authorities);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
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) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
provider.setUserDetailsService(userDetailsService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows specifying the {@link PasswordEncoder} to use with the {@link DaoAuthenticationProvider}. The default is
|
||||
* is to use plain text.
|
||||
*
|
||||
* @param passwordEncoder The {@link PasswordEncoder} to use.
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public C passwordEncoder(PasswordEncoder passwordEncoder) {
|
||||
provider.setPasswordEncoder(passwordEncoder);
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows specifying the
|
||||
* {@link org.springframework.security.authentication.encoding.PasswordEncoder}
|
||||
* to use with the {@link DaoAuthenticationProvider}. The default is is to
|
||||
* use plain text.
|
||||
*
|
||||
* @param passwordEncoder
|
||||
* The
|
||||
* {@link org.springframework.security.authentication.encoding.PasswordEncoder}
|
||||
* to use.
|
||||
* @return the {@link SecurityConfigurer} for further customizations
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public C passwordEncoder(org.springframework.security.authentication.encoding.PasswordEncoder passwordEncoder) {
|
||||
provider.setPasswordEncoder(passwordEncoder);
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(B builder) throws Exception {
|
||||
provider = postProcess(provider);
|
||||
builder.authenticationProvider(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link UserDetailsService} that is used with the {@link DaoAuthenticationProvider}
|
||||
*
|
||||
* @return the {@link UserDetailsService} that is used with the {@link DaoAuthenticationProvider}
|
||||
*/
|
||||
public U getUserDetailsService() {
|
||||
return userDetailsService;
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.authentication.ProviderManagerBuilder;
|
||||
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
|
||||
*
|
||||
*/
|
||||
public class DaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, U extends UserDetailsService> extends AbstractDaoAuthenticationConfigurer<B,DaoAuthenticationConfigurer<B,U>, U>{
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param userDetailsService
|
||||
*/
|
||||
public DaoAuthenticationConfigurer(U userDetailsService) {
|
||||
super(userDetailsService);
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
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}
|
||||
*/
|
||||
public abstract class UserDetailsAwareConfigurer<B extends ProviderManagerBuilder<B>, U extends UserDetailsService> extends SecurityConfigurerAdapter<AuthenticationManager,B> {
|
||||
|
||||
/**
|
||||
* Gets the {@link UserDetailsService} or null if it is not available
|
||||
* @return the {@link UserDetailsService} or null if it is not available
|
||||
*/
|
||||
public abstract U getUserDetailsService();
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.SecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
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 SecurityBuilder}
|
||||
* @param <C> the {@link SecurityConfigurer} (or this)
|
||||
* @param <U> the type of UserDetailsService being used to allow for returning the concrete UserDetailsService.
|
||||
*/
|
||||
public class UserDetailsServiceConfigurer<B extends ProviderManagerBuilder<B>,
|
||||
C extends UserDetailsServiceConfigurer<B, C, U>,
|
||||
U extends UserDetailsService>
|
||||
extends AbstractDaoAuthenticationConfigurer<B, C, U> {
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param userDetailsService the {@link UserDetailsService} that should be used
|
||||
*/
|
||||
public UserDetailsServiceConfigurer(U userDetailsService) {
|
||||
super(userDetailsService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(B builder) throws Exception {
|
||||
initUserDetailsService();
|
||||
|
||||
super.configure(builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows subclasses to initialize the {@link UserDetailsService}. For example, it might add users, initialize
|
||||
* schema, etc.
|
||||
*/
|
||||
protected void initUserDetailsService() throws Exception {}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.Aware;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Allows registering Objects to participate with an
|
||||
* {@link AutowireCapableBeanFactory}'s post processing of {@link Aware}
|
||||
* methods, {@link InitializingBean#afterPropertiesSet()}, and
|
||||
* {@link DisposableBean#destroy()}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
final class AutowireBeanFactoryObjectPostProcessor implements ObjectPostProcessor<Object>, DisposableBean {
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
private final AutowireCapableBeanFactory autowireBeanFactory;
|
||||
private final List<DisposableBean> disposableBeans = new ArrayList<DisposableBean>();
|
||||
|
||||
public 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)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T postProcess(T object) {
|
||||
T result = (T) autowireBeanFactory.initializeBean(object, null);
|
||||
if(result instanceof DisposableBean) {
|
||||
disposableBeans.add((DisposableBean) result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.DisposableBean#destroy()
|
||||
*/
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
for(DisposableBean disposable : disposableBeans) {
|
||||
try {
|
||||
disposable.destroy();
|
||||
} catch(Exception error) {
|
||||
logger.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
|
||||
/**
|
||||
* Spring {@link Configuration} that exports the default
|
||||
* {@link ObjectPostProcessor}. This class is not intended to be imported
|
||||
* manually rather it is imported automatically when using
|
||||
* {@link EnableWebSecurity} or {@link EnableGlobalMethodSecurity}.
|
||||
*
|
||||
* @see EnableWebSecurity
|
||||
* @see EnableGlobalMethodSecurity
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
@Configuration
|
||||
public class ObjectPostProcessorConfiguration {
|
||||
|
||||
@Bean
|
||||
public ObjectPostProcessor<Object> objectPostProcessor(AutowireCapableBeanFactory beanFactory) {
|
||||
return new AutowireBeanFactoryObjectPostProcessor(beanFactory);
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration;
|
||||
|
||||
/**
|
||||
* <p>Enables Spring Security global method security similar to the
|
||||
* <global-method-security> xml support.</p>
|
||||
*
|
||||
* <p>
|
||||
* More advanced configurations may wish to extend
|
||||
* {@link GlobalMethodSecurityConfiguration} and override the protected methods
|
||||
* to provide custom implementations. Note that
|
||||
* {@link EnableGlobalMethodSecurity} still must be included on the class
|
||||
* extending {@link GlobalMethodSecurityConfiguration} to determine the
|
||||
* settings.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||
@Target(value={java.lang.annotation.ElementType.TYPE})
|
||||
@Documented
|
||||
@Import({GlobalMethodSecuritySelector.class,ObjectPostProcessorConfiguration.class})
|
||||
public @interface EnableGlobalMethodSecurity {
|
||||
|
||||
/**
|
||||
* Determines if Spring Security's pre post annotations should be enabled. Default is false.
|
||||
* @return true if pre post annotations should be enabled false otherwise.
|
||||
*/
|
||||
boolean prePostEnabled() default false;
|
||||
|
||||
/**
|
||||
* Determines if Spring Security's {@link Secured} annotations should be enabled.
|
||||
* @return true if {@link Secured} annotations should be enabled false otherwise. Default is false.
|
||||
*/
|
||||
boolean securedEnabled() default false;
|
||||
|
||||
/**
|
||||
* Determines if JSR-250 annotations should be enabled. Default is false.
|
||||
* @return true if JSR-250 should be enabled false otherwise.
|
||||
*/
|
||||
boolean jsr250Enabled() default false;
|
||||
|
||||
/**
|
||||
* Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as
|
||||
* opposed to standard Java interface-based proxies ({@code false}). 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
|
||||
* the Security annotations. 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.
|
||||
*
|
||||
* @return true if CGILIB proxies should be created instead of interface based proxies, else false
|
||||
*/
|
||||
boolean proxyTargetClass() default false;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.Map;
|
||||
|
||||
import org.springframework.aop.config.AopConfigUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
|
||||
/**
|
||||
* Registers an
|
||||
* {@link org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
|
||||
* AnnotationAwareAspectJAutoProxyCreator} against the current
|
||||
* {@link BeanDefinitionRegistry} as appropriate based on a given @
|
||||
* {@link EnableGlobalMethodSecurity} annotation.
|
||||
*
|
||||
* <p>
|
||||
* Note: This class is necessary because AspectJAutoProxyRegistrar only supports
|
||||
* EnableAspectJAutoProxy.
|
||||
* </p>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@Override
|
||||
public void registerBeanDefinitions(
|
||||
AnnotationMetadata importingClassMetadata,
|
||||
BeanDefinitionRegistry registry) {
|
||||
|
||||
AopConfigUtils
|
||||
.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
|
||||
|
||||
Map<String, Object> annotationAttributes = importingClassMetadata
|
||||
.getAnnotationAttributes(EnableGlobalMethodSecurity.class
|
||||
.getName());
|
||||
AnnotationAttributes enableAJAutoProxy = AnnotationAttributes
|
||||
.fromMap(annotationAttributes);
|
||||
|
||||
if (enableAJAutoProxy.getBoolean("proxyTargetClass")) {
|
||||
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,385 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.springframework.aop.framework.ProxyFactoryBean;
|
||||
import org.springframework.aop.target.LazyInitTargetSource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
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;
|
||||
import org.springframework.security.access.AccessDecisionManager;
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.access.AfterInvocationProvider;
|
||||
import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;
|
||||
import org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource;
|
||||
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.AfterInvocationManager;
|
||||
import org.springframework.security.access.intercept.AfterInvocationProviderManager;
|
||||
import org.springframework.security.access.intercept.RunAsManager;
|
||||
import org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;
|
||||
import org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor;
|
||||
import org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource;
|
||||
import org.springframework.security.access.method.MethodSecurityMetadataSource;
|
||||
import org.springframework.security.access.prepost.PostInvocationAdviceProvider;
|
||||
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdvice;
|
||||
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter;
|
||||
import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource;
|
||||
import org.springframework.security.access.vote.AffirmativeBased;
|
||||
import org.springframework.security.access.vote.AuthenticatedVoter;
|
||||
import org.springframework.security.access.vote.RoleVoter;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base {@link Configuration} for enabling global method security. Classes may
|
||||
* extend this class to customize the defaults, but must be sure to specify the
|
||||
* {@link EnableGlobalMethodSecurity} annotation on the subclass.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
* @see EnableGlobalMethodSecurity
|
||||
*/
|
||||
@Configuration
|
||||
public class GlobalMethodSecurityConfiguration implements ImportAware {
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
@Autowired(required=false)
|
||||
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());
|
||||
}
|
||||
};
|
||||
private AuthenticationManager authenticationManager;
|
||||
private AuthenticationManagerBuilder auth = new AuthenticationManagerBuilder();
|
||||
private boolean disableAuthenticationRegistry;
|
||||
private AnnotationAttributes enableMethodSecurity;
|
||||
private MethodSecurityExpressionHandler expressionHandler;
|
||||
|
||||
/**
|
||||
* Creates the default MethodInterceptor which is a MethodSecurityInterceptor using the following methods to
|
||||
* construct it.
|
||||
* <ul>
|
||||
* <li>{@link #accessDecisionManager()}</li>
|
||||
* <li>{@link #afterInvocationManager()}</li>
|
||||
* <li>{@link #authenticationManager()}</li>
|
||||
* <li>{@link #methodSecurityMetadataSource()}</li>
|
||||
* <li>{@link #runAsManager()}</li>
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Subclasses can override this method to provide a different {@link MethodInterceptor}.
|
||||
* </p>
|
||||
*
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Bean
|
||||
public MethodInterceptor methodSecurityInterceptor() throws Exception {
|
||||
MethodSecurityInterceptor methodSecurityInterceptor = new MethodSecurityInterceptor();
|
||||
methodSecurityInterceptor
|
||||
.setAccessDecisionManager(accessDecisionManager());
|
||||
methodSecurityInterceptor
|
||||
.setAfterInvocationManager(afterInvocationManager());
|
||||
methodSecurityInterceptor
|
||||
.setAuthenticationManager(authenticationManager());
|
||||
methodSecurityInterceptor
|
||||
.setSecurityMetadataSource(methodSecurityMetadataSource());
|
||||
RunAsManager runAsManager = runAsManager();
|
||||
if (runAsManager != null) {
|
||||
methodSecurityInterceptor.setRunAsManager(runAsManager);
|
||||
}
|
||||
return methodSecurityInterceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a custom {@link AfterInvocationManager} for the default
|
||||
* implementation of {@link #methodSecurityInterceptor()}. 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
|
||||
*/
|
||||
protected AfterInvocationManager afterInvocationManager() {
|
||||
if(prePostEnabled()) {
|
||||
AfterInvocationProviderManager invocationProviderManager = new AfterInvocationProviderManager();
|
||||
ExpressionBasedPostInvocationAdvice postAdvice = new ExpressionBasedPostInvocationAdvice(getExpressionHandler());
|
||||
PostInvocationAdviceProvider postInvocationAdviceProvider = new PostInvocationAdviceProvider(postAdvice);
|
||||
List<AfterInvocationProvider> afterInvocationProviders = new ArrayList<AfterInvocationProvider>();
|
||||
afterInvocationProviders.add(postInvocationAdviceProvider);
|
||||
invocationProviderManager.setProviders(afterInvocationProviders);
|
||||
return invocationProviderManager;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a custom {@link RunAsManager} for the default implementation of
|
||||
* {@link #methodSecurityInterceptor()}. The default is null.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected RunAsManager runAsManager() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows subclasses to provide a custom {@link AccessDecisionManager}. The default is a {@link AffirmativeBased}
|
||||
* with the following voters:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link PreInvocationAuthorizationAdviceVoter}</li>
|
||||
* <li>{@link RoleVoter} </li>
|
||||
* <li>{@link AuthenticatedVoter} </li>
|
||||
* </ul>
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
protected AccessDecisionManager accessDecisionManager() {
|
||||
List<AccessDecisionVoter> decisionVoters = new ArrayList<AccessDecisionVoter>();
|
||||
ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
|
||||
expressionAdvice.setExpressionHandler(getExpressionHandler());
|
||||
|
||||
decisionVoters.add(new PreInvocationAuthorizationAdviceVoter(
|
||||
expressionAdvice));
|
||||
decisionVoters.add(new RoleVoter());
|
||||
decisionVoters.add(new AuthenticatedVoter());
|
||||
return new AffirmativeBased(decisionVoters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a {@link MethodSecurityExpressionHandler} that is
|
||||
* registered with the {@link ExpressionBasedPreInvocationAdvice}. The default is
|
||||
* {@link DefaultMethodSecurityExpressionHandler}
|
||||
*
|
||||
* <p>Subclasses may override this method to provide a custom {@link MethodSecurityExpressionHandler}</p>
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected MethodSecurityExpressionHandler expressionHandler() {
|
||||
return new DefaultMethodSecurityExpressionHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = expressionHandler();
|
||||
}
|
||||
return 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()}
|
||||
*/
|
||||
protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows providing a custom {@link AuthenticationManager}. The default is
|
||||
* to use any authentication mechanisms registered by {@link #registerAuthentication(AuthenticationManagerBuilder)}. If
|
||||
* {@link #registerAuthentication(AuthenticationManagerBuilder)} was not overriden, then an {@link AuthenticationManager}
|
||||
* is attempted to be autowired by type.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected AuthenticationManager authenticationManager() throws Exception {
|
||||
if(authenticationManager == null) {
|
||||
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());
|
||||
auth.authenticationEventPublisher(eventPublisher);
|
||||
auth.objectPostProcessor(objectPostProcessor);
|
||||
registerAuthentication(auth);
|
||||
if(!disableAuthenticationRegistry) {
|
||||
authenticationManager = auth.build();
|
||||
}
|
||||
if(authenticationManager == null) {
|
||||
authenticationManager = lazyBean(AuthenticationManager.class);
|
||||
}
|
||||
}
|
||||
return authenticationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub classes can override this method to register different types of authentication. If not overridden,
|
||||
* {@link #registerAuthentication(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
|
||||
*/
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
this.disableAuthenticationRegistry = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the default {@link MethodSecurityMetadataSource} that will be
|
||||
* used. It creates a {@link DelegatingMethodSecurityMetadataSource} based
|
||||
* upon {@link #customMethodSecurityMetadataSource()} and the attributes on
|
||||
* {@link EnableGlobalMethodSecurity}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public MethodSecurityMetadataSource methodSecurityMetadataSource() {
|
||||
List<MethodSecurityMetadataSource> sources = new ArrayList<MethodSecurityMetadataSource>();
|
||||
ExpressionBasedAnnotationAttributeFactory attributeFactory = new ExpressionBasedAnnotationAttributeFactory(
|
||||
methodExpressionHandler());
|
||||
MethodSecurityMetadataSource customMethodSecurityMetadataSource = customMethodSecurityMetadataSource();
|
||||
if (customMethodSecurityMetadataSource != null) {
|
||||
sources.add(customMethodSecurityMetadataSource);
|
||||
}
|
||||
if (prePostEnabled()) {
|
||||
sources.add(new PrePostAnnotationSecurityMetadataSource(
|
||||
attributeFactory));
|
||||
}
|
||||
if (securedEnabled()) {
|
||||
sources.add(new SecuredAnnotationSecurityMetadataSource());
|
||||
}
|
||||
if (jsr250Enabled()) {
|
||||
sources.add(new Jsr250MethodSecurityMetadataSource());
|
||||
}
|
||||
return new DelegatingMethodSecurityMetadataSource(sources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link MethodSecurityExpressionHandler} to be used.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public MethodSecurityExpressionHandler methodExpressionHandler() {
|
||||
return new DefaultMethodSecurityExpressionHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link PreInvocationAuthorizationAdvice} to be used. The
|
||||
* default is {@link ExpressionBasedPreInvocationAdvice}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public PreInvocationAuthorizationAdvice preInvocationAuthorizationAdvice() {
|
||||
ExpressionBasedPreInvocationAdvice preInvocationAdvice = new ExpressionBasedPreInvocationAdvice();
|
||||
preInvocationAdvice.setExpressionHandler(methodExpressionHandler());
|
||||
return preInvocationAdvice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the {@link MethodSecurityMetadataSourceAdvisor} to be used.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
@Bean
|
||||
public MethodSecurityMetadataSourceAdvisor metaDataSourceAdvisor() {
|
||||
MethodSecurityMetadataSourceAdvisor methodAdvisor = new MethodSecurityMetadataSourceAdvisor(
|
||||
"methodSecurityInterceptor", methodSecurityMetadataSource(),
|
||||
"methodSecurityMetadataSource");
|
||||
methodAdvisor.setOrder(order());
|
||||
return methodAdvisor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T lazyBean(Class<T> interfaceName) {
|
||||
LazyInitTargetSource lazyTargetSource = new LazyInitTargetSource();
|
||||
String[] beanNamesForType = context.getBeanNamesForType(interfaceName);
|
||||
Assert.isTrue(beanNamesForType.length == 1 , "Expecting to only find a single bean for type " + interfaceName + ", but found " + Arrays.asList(beanNamesForType));
|
||||
lazyTargetSource.setTargetBeanName(beanNamesForType[0]);
|
||||
lazyTargetSource.setBeanFactory(context);
|
||||
ProxyFactoryBean proxyFactory = new ProxyFactoryBean();
|
||||
proxyFactory.setTargetSource(lazyTargetSource);
|
||||
proxyFactory.setInterfaces(new Class[] { interfaceName });
|
||||
return (T) proxyFactory.getObject();
|
||||
}
|
||||
|
||||
private boolean prePostEnabled() {
|
||||
return enableMethodSecurity().getBoolean("prePostEnabled");
|
||||
}
|
||||
|
||||
private boolean securedEnabled() {
|
||||
return enableMethodSecurity().getBoolean("securedEnabled");
|
||||
}
|
||||
|
||||
private boolean jsr250Enabled() {
|
||||
return enableMethodSecurity().getBoolean("jsr250Enabled");
|
||||
}
|
||||
|
||||
private int order() {
|
||||
return (Integer) enableMethodSecurity().get("order");
|
||||
}
|
||||
|
||||
private AnnotationAttributes enableMethodSecurity() {
|
||||
if (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);
|
||||
this.enableMethodSecurity = AnnotationAttributes
|
||||
.fromMap(methodSecurityAttrs);
|
||||
}
|
||||
return this.enableMethodSecurity;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.Map;
|
||||
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.AutoProxyRegistrar;
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Dynamically determines which imports to include using the
|
||||
* {@link EnableGlobalMethodSecurity} annotation.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
final class GlobalMethodSecuritySelector implements ImportSelector {
|
||||
|
||||
@Override
|
||||
public final 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",
|
||||
annoType.getSimpleName(), importingClassMetadata.getClassName()));
|
||||
|
||||
// TODO would be nice if could use BeanClassLoaderAware (does not work)
|
||||
Class<?> importingClass = ClassUtils.resolveClassName(importingClassMetadata.getClassName(), ClassUtils.getDefaultClassLoader());
|
||||
boolean skipMethodSecurityConfiguration = GlobalMethodSecurityConfiguration.class.isAssignableFrom(importingClass);
|
||||
|
||||
AdviceMode mode = attributes.getEnum("mode");
|
||||
String autoProxyClassName = AdviceMode.PROXY == mode ? AutoProxyRegistrar.class.getName()
|
||||
: GlobalMethodSecurityAspectJAutoProxyRegistrar.class.getName();
|
||||
if(skipMethodSecurityConfiguration) {
|
||||
return new String[] { autoProxyClassName };
|
||||
}
|
||||
return new String[] { autoProxyClassName,
|
||||
GlobalMethodSecurityConfiguration.class.getName()};
|
||||
}
|
||||
}
|
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.http.HttpMethod;
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractRequestMatcherMappingConfigurer;
|
||||
import org.springframework.security.web.util.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.AnyRequestMatcher;
|
||||
import org.springframework.security.web.util.RegexRequestMatcher;
|
||||
import org.springframework.security.web.util.RequestMatcher;
|
||||
|
||||
/**
|
||||
* 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 <B> The Builder that is building Object O and is configured by this {@link AbstractRequestMatcherMappingConfigurer}
|
||||
* @param <C> The object that is returned or Chained after creating the RequestMatcher
|
||||
* @param <O> The Object being built by Builder B
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public abstract class AbstractRequestMatcherConfigurer<B extends SecurityBuilder<O>,C,O> extends SecurityConfigurerAdapter<O,B> {
|
||||
private static final RequestMatcher ANY_REQUEST = new AnyRequestMatcher();
|
||||
/**
|
||||
* Maps any request.
|
||||
*
|
||||
* @param method the {@link HttpMethod} to use or {@code null} for any {@link HttpMethod}.
|
||||
* @param antPatterns the ant patterns to create {@link org.springframework.security.web.util.AntPathRequestMatcher}
|
||||
* from
|
||||
*
|
||||
* @return the object that is chained after creating the {@link RequestMatcher}
|
||||
*/
|
||||
public C anyRequest() {
|
||||
return requestMatchers(ANY_REQUEST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a {@link List} of {@link org.springframework.security.web.util.AntPathRequestMatcher} instances.
|
||||
*
|
||||
* @param method the {@link HttpMethod} to use or {@code null} for any {@link HttpMethod}.
|
||||
* @param antPatterns the ant patterns to create {@link org.springframework.security.web.util.AntPathRequestMatcher}
|
||||
* from
|
||||
*
|
||||
* @return the object that is chained after creating the {@link RequestMatcher}
|
||||
*/
|
||||
public C antMatchers(HttpMethod method, String... antPatterns) {
|
||||
return chainRequestMatchers(RequestMatchers.antMatchers(method, antPatterns));
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a {@link List} of {@link org.springframework.security.web.util.AntPathRequestMatcher} instances that do
|
||||
* not care which {@link HttpMethod} is used.
|
||||
*
|
||||
* @param antPatterns the ant patterns to create {@link org.springframework.security.web.util.AntPathRequestMatcher}
|
||||
* from
|
||||
*
|
||||
* @return the object that is chained after creating the {@link RequestMatcher}
|
||||
*/
|
||||
public C antMatchers(String... antPatterns) {
|
||||
return chainRequestMatchers(RequestMatchers.antMatchers(antPatterns));
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a {@link List} of {@link org.springframework.security.web.util.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.RegexRequestMatcher} from
|
||||
*
|
||||
* @return the object that is chained after creating the {@link RequestMatcher}
|
||||
*/
|
||||
public C regexMatchers(HttpMethod method, String... regexPatterns) {
|
||||
return chainRequestMatchers(RequestMatchers.regexMatchers(method,
|
||||
regexPatterns));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link List} of {@link org.springframework.security.web.util.RegexRequestMatcher} instances that do not
|
||||
* specify an {@link HttpMethod}.
|
||||
*
|
||||
* @param regexPatterns the regular expressions to create
|
||||
* {@link org.springframework.security.web.util.RegexRequestMatcher} from
|
||||
*
|
||||
* @return the object that is chained after creating the {@link RequestMatcher}
|
||||
*/
|
||||
public C regexMatchers(String... regexPatterns) {
|
||||
return chainRequestMatchers(RequestMatchers.regexMatchers(regexPatterns));
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates a list of {@link RequestMatcher} instances with the {@link AbstractRequestMatcherMappingConfigurer}
|
||||
*
|
||||
* @param requestMatchers the {@link RequestMatcher} instances
|
||||
*
|
||||
* @return the object that is chained after creating the {@link RequestMatcher}
|
||||
*/
|
||||
public C requestMatchers(RequestMatcher... requestMatchers) {
|
||||
return chainRequestMatchers(Arrays.asList(requestMatchers));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*/
|
||||
protected abstract C chainRequestMatchers(List<RequestMatcher> requestMatchers);
|
||||
|
||||
/**
|
||||
* Utilities for creating {@link RequestMatcher} instances.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
private static final class 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();
|
||||
List<RequestMatcher> matchers = new ArrayList<RequestMatcher>();
|
||||
for(String pattern : antPatterns) {
|
||||
matchers.add(new AntPathRequestMatcher(pattern, method));
|
||||
}
|
||||
return matchers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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();
|
||||
List<RequestMatcher> matchers = new ArrayList<RequestMatcher>();
|
||||
for(String pattern : regexPatterns) {
|
||||
matchers.add(new RegexRequestMatcher(pattern, method));
|
||||
}
|
||||
return matchers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
return regexMatchers(null, regexPatterns);
|
||||
}
|
||||
|
||||
private RequestMatchers() {}
|
||||
}
|
||||
}
|
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.openid.OpenIDAuthenticationFilter;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
||||
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
||||
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
|
||||
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.switchuser.SwitchUserFilter;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.www.DigestAuthenticationFilter;
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
|
||||
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
|
||||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
* @param <H>
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
<C> void setSharedObject(Class<C> sharedType, C object);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
<C> C getSharedObject(Class<C> sharedType);
|
||||
|
||||
/**
|
||||
* Allows adding an additional {@link AuthenticationProvider} to be used
|
||||
*
|
||||
* @param authenticationProvider the {@link AuthenticationProvider} to be added
|
||||
* @return the {@link HttpSecurity} for further customizations
|
||||
*/
|
||||
H authenticationProvider(
|
||||
AuthenticationProvider authenticationProvider);
|
||||
|
||||
/**
|
||||
* Allows adding an additional {@link UserDetailsService} to be used
|
||||
*
|
||||
* @param userDetailsService the {@link UserDetailsService} to be added
|
||||
* @return the {@link HttpSecurity} for further customizations
|
||||
*/
|
||||
H userDetailsService(
|
||||
UserDetailsService userDetailsService) throws Exception;
|
||||
|
||||
/**
|
||||
* Allows adding a {@link Filter} after one of the known {@link Filter}
|
||||
* classes. The 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 afterFilter}
|
||||
* @param afterFilter the Class of the known {@link Filter}.
|
||||
* @return the {@link HttpSecurity} for further customizations
|
||||
*/
|
||||
H addFilterAfter(Filter filter,
|
||||
Class<? extends Filter> afterFilter);
|
||||
|
||||
/**
|
||||
* Allows adding a {@link Filter} before one of the known {@link Filter}
|
||||
* classes. The 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
|
||||
*/
|
||||
H addFilterBefore(Filter filter,
|
||||
Class<? extends Filter> beforeFilter);
|
||||
|
||||
/**
|
||||
* Adds a {@link Filter} that must be an instance of or extend one of the
|
||||
* Filters provided within the Security framework. The method ensures that
|
||||
* the ordering of the Filters is automatically taken care of.
|
||||
*
|
||||
* The ordering of the Filters is:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link ChannelProcessingFilter}</li>
|
||||
* <li>{@link ConcurrentSessionFilter}</li>
|
||||
* <li>{@link SecurityContextPersistenceFilter}</li>
|
||||
* <li>{@link LogoutFilter}</li>
|
||||
* <li>{@link X509AuthenticationFilter}</li>
|
||||
* <li>{@link AbstractPreAuthenticatedProcessingFilter}</li>
|
||||
* <li>{@link org.springframework.security.cas.web.CasAuthenticationFilter}</li>
|
||||
* <li>{@link UsernamePasswordAuthenticationFilter}</li>
|
||||
* <li>{@link ConcurrentSessionFilter}</li>
|
||||
* <li>{@link OpenIDAuthenticationFilter}</li>
|
||||
* <li>{@link DefaultLoginPageViewFilter}</li>
|
||||
* <li>{@link ConcurrentSessionFilter}</li>
|
||||
* <li>{@link DigestAuthenticationFilter}</li>
|
||||
* <li>{@link BasicAuthenticationFilter}</li>
|
||||
* <li>{@link RequestCacheAwareFilter}</li>
|
||||
* <li>{@link SecurityContextHolderAwareRequestFilter}</li>
|
||||
* <li>{@link JaasApiIntegrationFilter}</li>
|
||||
* <li>{@link RememberMeAuthenticationFilter}</li>
|
||||
* <li>{@link AnonymousAuthenticationFilter}</li>
|
||||
* <li>{@link SessionManagementFilter}</li>
|
||||
* <li>{@link ExceptionTranslationFilter}</li>
|
||||
* <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);
|
||||
|
||||
// FIXME shared object or explicit?
|
||||
AuthenticationManager getAuthenticationManager();
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
|
||||
/**
|
||||
* Allows customization to the {@link WebSecurity}. In most instances
|
||||
* users will use {@link EnableWebSecurity} and a create {@link Configuration}
|
||||
* that extends {@link WebSecurityConfigurerAdapter} which will automatically be
|
||||
* applied to the {@link WebSecurity} by the {@link EnableWebSecurity}
|
||||
* annotation.
|
||||
*
|
||||
* @see WebSecurityConfigurerAdapter
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends SecurityConfigurer<Filter, T> {
|
||||
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.util.UrlUtils;
|
||||
|
||||
/**
|
||||
* Spring Security debugging filter.
|
||||
* <p>
|
||||
* Logs information (such as session creation) to help the user understand how requests are being handled
|
||||
* by Spring Security and provide them with other relevant information (such as when sessions are being created).
|
||||
*
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Rob Winch
|
||||
* @since 3.1
|
||||
*/
|
||||
class DebugFilter implements Filter {
|
||||
private static final String ALREADY_FILTERED_ATTR_NAME = DebugFilter.class.getName().concat(".FILTERED");
|
||||
|
||||
private final FilterChainProxy fcp;
|
||||
private final Logger logger = new Logger();
|
||||
|
||||
public DebugFilter(FilterChainProxy fcp) {
|
||||
this.fcp = fcp;
|
||||
}
|
||||
|
||||
public final void doFilter(ServletRequest srvltRequest, ServletResponse srvltResponse, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
|
||||
if (!(srvltRequest instanceof HttpServletRequest) || !(srvltResponse instanceof HttpServletResponse)) {
|
||||
throw new ServletException("DebugFilter just supports HTTP requests");
|
||||
}
|
||||
HttpServletRequest request = (HttpServletRequest) srvltRequest;
|
||||
HttpServletResponse response = (HttpServletResponse) srvltResponse;
|
||||
|
||||
List<Filter> filters = getFilters(request);
|
||||
logger.log("Request received for '" + UrlUtils.buildRequestUrl(request) + "':\n\n" +
|
||||
request + "\n\n" +
|
||||
"servletPath:" + request.getServletPath() + "\n" +
|
||||
"pathInfo:" + request.getPathInfo() + "\n\n" +
|
||||
formatFilters(filters));
|
||||
|
||||
if (request.getAttribute(ALREADY_FILTERED_ATTR_NAME) == null) {
|
||||
invokeWithWrappedRequest(request, response, filterChain);
|
||||
} else {
|
||||
fcp.doFilter(request, response, filterChain);
|
||||
}
|
||||
}
|
||||
|
||||
private void invokeWithWrappedRequest(HttpServletRequest request,
|
||||
HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
|
||||
request.setAttribute(ALREADY_FILTERED_ATTR_NAME, Boolean.TRUE);
|
||||
request = new DebugRequestWrapper(request);
|
||||
try {
|
||||
fcp.doFilter(request, response, filterChain);
|
||||
}
|
||||
finally {
|
||||
request.removeAttribute(ALREADY_FILTERED_ATTR_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
String formatFilters(List<Filter> filters) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Security filter chain: ");
|
||||
if (filters == null) {
|
||||
sb.append("no match");
|
||||
} else if (filters.isEmpty()) {
|
||||
sb.append("[] empty (bypassed by security='none') ");
|
||||
} else {
|
||||
sb.append("[\n");
|
||||
for (Filter f : filters) {
|
||||
sb.append(" ").append(f.getClass().getSimpleName()).append("\n");
|
||||
}
|
||||
sb.append("]");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private List<Filter> getFilters(HttpServletRequest request) {
|
||||
for (SecurityFilterChain chain : fcp.getFilterChains()) {
|
||||
if (chain.matches(request)) {
|
||||
return chain.getFilters();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
}
|
||||
}
|
||||
|
||||
class DebugRequestWrapper extends HttpServletRequestWrapper {
|
||||
private static final Logger logger = new Logger();
|
||||
|
||||
public DebugRequestWrapper(HttpServletRequest request) {
|
||||
super(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpSession getSession() {
|
||||
boolean sessionExists = super.getSession(false) != null;
|
||||
HttpSession session = super.getSession();
|
||||
|
||||
if (!sessionExists) {
|
||||
logger.log("New HTTP session created: " + session.getId(), true);
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpSession getSession(boolean create) {
|
||||
if (!create) {
|
||||
return super.getSession(create);
|
||||
}
|
||||
return getSession();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls output for the Spring Security debug feature.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.1
|
||||
*/
|
||||
final class Logger {
|
||||
final static Log logger = LogFactory.getLog("Spring Security Debugger");
|
||||
|
||||
void log(String message) {
|
||||
log(message, false);
|
||||
}
|
||||
|
||||
void log(String message, boolean dumpStack) {
|
||||
StringBuilder output = new StringBuilder(256);
|
||||
output.append("\n\n************************************************************\n\n");
|
||||
output.append(message).append("\n");
|
||||
|
||||
if (dumpStack) {
|
||||
StringWriter os = new StringWriter();
|
||||
new Exception().printStackTrace(new PrintWriter(os));
|
||||
StringBuffer buffer = os.getBuffer();
|
||||
// Remove the exception in case it scares people.
|
||||
int start = buffer.indexOf("java.lang.Exception");
|
||||
buffer.replace(start, start + 19, "");
|
||||
output.append("\nCall stack: \n").append(os.toString());
|
||||
}
|
||||
|
||||
output.append("\n\n************************************************************\n\n");
|
||||
|
||||
logger.info(output.toString());
|
||||
}
|
||||
}
|
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
||||
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
|
||||
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.switchuser.SwitchUserFilter;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageViewFilter;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.www.DigestAuthenticationFilter;
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
|
||||
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
|
||||
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;
|
||||
|
||||
/**
|
||||
* An internal use only {@link Comparator} that sorts the Security {@link Filter} instances to ensure they are in the
|
||||
* correct order.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
final class FilterComparator implements Comparator<Filter>, Serializable {
|
||||
private static final int STEP = 100;
|
||||
private Map<String,Integer> filterToOrder = new HashMap<String,Integer>();
|
||||
|
||||
FilterComparator() {
|
||||
int order = 100;
|
||||
put(ChannelProcessingFilter.class, order);
|
||||
order += STEP;
|
||||
put(ConcurrentSessionFilter.class, order);
|
||||
order += STEP;
|
||||
put(SecurityContextPersistenceFilter.class, order);
|
||||
order += STEP;
|
||||
put(LogoutFilter.class, order);
|
||||
order += STEP;
|
||||
put(X509AuthenticationFilter.class, order);
|
||||
order += STEP;
|
||||
put(AbstractPreAuthenticatedProcessingFilter.class, order);
|
||||
order += STEP;
|
||||
filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter", order);
|
||||
order += STEP;
|
||||
put(UsernamePasswordAuthenticationFilter.class, order);
|
||||
order += STEP;
|
||||
put(ConcurrentSessionFilter.class, order);
|
||||
order += STEP;
|
||||
filterToOrder.put("org.springframework.security.openid.OpenIDAuthenticationFilter", order);
|
||||
order += STEP;
|
||||
put(DefaultLoginPageViewFilter.class, order);
|
||||
order += STEP;
|
||||
put(ConcurrentSessionFilter.class, order);
|
||||
order += STEP;
|
||||
put(DigestAuthenticationFilter.class, order);
|
||||
order += STEP;
|
||||
put(BasicAuthenticationFilter.class, order);
|
||||
order += STEP;
|
||||
put(RequestCacheAwareFilter.class, order);
|
||||
order += STEP;
|
||||
put(SecurityContextHolderAwareRequestFilter.class, order);
|
||||
order += STEP;
|
||||
put(JaasApiIntegrationFilter.class, order);
|
||||
order += STEP;
|
||||
put(RememberMeAuthenticationFilter.class, order);
|
||||
order += STEP;
|
||||
put(AnonymousAuthenticationFilter.class, order);
|
||||
order += STEP;
|
||||
put(SessionManagementFilter.class, order);
|
||||
order += STEP;
|
||||
put(ExceptionTranslationFilter.class, order);
|
||||
order += STEP;
|
||||
put(FilterSecurityInterceptor.class, order);
|
||||
order += STEP;
|
||||
put(SwitchUserFilter.class, order);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(Filter lhs, Filter rhs) {
|
||||
Integer left = getOrder(lhs.getClass());
|
||||
Integer right = getOrder(rhs.getClass());
|
||||
return left - right;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a particular {@link Filter} is registered to be sorted
|
||||
*
|
||||
* @param filter
|
||||
* @return
|
||||
*/
|
||||
public boolean isRegistered(Class<? extends Filter> filter) {
|
||||
return getOrder(filter) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a {@link Filter} to exist after a particular {@link Filter} that is already registered.
|
||||
* @param filter the {@link Filter} to register
|
||||
* @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) {
|
||||
Integer position = getOrder(afterFilter);
|
||||
if(position == null) {
|
||||
throw new IllegalArgumentException("Cannot register after unregistered Filter "+afterFilter);
|
||||
}
|
||||
|
||||
put(filter, position + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a {@link Filter} to exist before a particular {@link Filter} that is already registered.
|
||||
* @param filter the {@link Filter} to register
|
||||
* @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) {
|
||||
Integer position = getOrder(beforeFilter);
|
||||
if(position == null) {
|
||||
throw new IllegalArgumentException("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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
if(result != null) {
|
||||
return result;
|
||||
}
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,309 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.AbstractRequestMatcherConfigurer;
|
||||
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
import org.springframework.security.web.firewall.DefaultHttpFirewall;
|
||||
import org.springframework.security.web.firewall.HttpFirewall;
|
||||
import org.springframework.security.web.util.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.filter.DelegatingFilterProxy;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The {@link WebSecurity} is created by {@link WebSecurityConfiguration}
|
||||
* to create the {@link FilterChainProxy} known as the Spring Security Filter
|
||||
* Chain (springSecurityFilterChain). The springSecurityFilterChain is the
|
||||
* {@link Filter} that the {@link DelegatingFilterProxy} delegates to.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Customizations to the {@link WebSecurity} can be made by creating a
|
||||
* {@link WebSecurityConfigurer} or more likely by overriding
|
||||
* {@link WebSecurityConfigurerAdapter}.
|
||||
* </p>
|
||||
*
|
||||
* @see EnableWebSecurity
|
||||
* @see WebSecurityConfiguration
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class WebSecurity extends
|
||||
AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements SecurityBuilder<Filter> {
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final List<RequestMatcher> ignoredRequests = new ArrayList<RequestMatcher>();
|
||||
|
||||
private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders =
|
||||
new ArrayList<SecurityBuilder<? extends SecurityFilterChain>>();
|
||||
|
||||
private final IgnoredRequestConfigurer ignoredRequestRegistry =
|
||||
new IgnoredRequestConfigurer();
|
||||
|
||||
private FilterSecurityInterceptor filterSecurityInterceptor;
|
||||
|
||||
private HttpFirewall httpFirewall;
|
||||
|
||||
private boolean debugEnabled;
|
||||
|
||||
private WebInvocationPrivilegeEvaluator privilegeEvaluator;
|
||||
|
||||
private SecurityExpressionHandler<FilterInvocation> expressionHandler = new DefaultWebSecurityExpressionHandler();
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @see WebSecurityConfiguration
|
||||
*/
|
||||
public WebSecurity() {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Allows adding {@link RequestMatcher} instances that should 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:
|
||||
*
|
||||
* <pre>
|
||||
* webSecurityBuilder
|
||||
* .ignoring()
|
||||
* // ignore all URLs that start with /resources/ or /static/
|
||||
* .antMatchers("/resources/**", "/static/**");
|
||||
* </pre>
|
||||
*
|
||||
* Alternatively this will accomplish the same result:
|
||||
*
|
||||
* <pre>
|
||||
* webSecurityBuilder
|
||||
* .ignoring()
|
||||
* // ignore all URLs that start with /resources/ or /static/
|
||||
* .antMatchers("/resources/**")
|
||||
* .antMatchers("/static/**");
|
||||
* </pre>
|
||||
*
|
||||
* Multiple invocations of ignoring() are also additive, so the following is
|
||||
* also equivalent to the previous two examples:
|
||||
*
|
||||
* Alternatively this will accomplish the same result:
|
||||
*
|
||||
* <pre>
|
||||
* webSecurityBuilder
|
||||
* .ignoring()
|
||||
* // ignore all URLs that start with /resources/
|
||||
* .antMatchers("/resources/**");
|
||||
* webSecurityBuilder
|
||||
* .ignoring()
|
||||
* // ignore all URLs that start with /static/
|
||||
* .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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows customizing the {@link HttpFirewall}. The default is
|
||||
* {@link DefaultHttpFirewall}.
|
||||
*
|
||||
* @param httpFirewall the custom {@link HttpFirewall}
|
||||
* @return the {@link WebSecurity} for further customizations
|
||||
*/
|
||||
public WebSecurity httpFirewall(HttpFirewall httpFirewall) {
|
||||
this.httpFirewall = httpFirewall;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
*/
|
||||
public WebSecurity debug(boolean debugEnabled) {
|
||||
this.debugEnabled = debugEnabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Adds builders to create {@link SecurityFilterChain} instances.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 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
|
||||
*/
|
||||
public WebSecurity addSecurityFilterChainBuilder(SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) {
|
||||
this.securityFilterChainBuilders.add(securityFilterChainBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link WebInvocationPrivilegeEvaluator} to be used. If this is
|
||||
* null, then a {@link DefaultWebInvocationPrivilegeEvaluator} will be
|
||||
* created when {@link #setSecurityInterceptor(FilterSecurityInterceptor)}
|
||||
* is non null.
|
||||
*
|
||||
* @param privilegeEvaluator
|
||||
* the {@link WebInvocationPrivilegeEvaluator} to use
|
||||
* @return the {@link WebSecurity} for further customizations
|
||||
*/
|
||||
public WebSecurity privilegeEvaluator(WebInvocationPrivilegeEvaluator privilegeEvaluator) {
|
||||
this.privilegeEvaluator = privilegeEvaluator;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link SecurityExpressionHandler} to be used. If this is null,
|
||||
* 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) {
|
||||
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
|
||||
this.expressionHandler = expressionHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link SecurityExpressionHandler} to be used.
|
||||
* @return
|
||||
*/
|
||||
public SecurityExpressionHandler<FilterInvocation> getExpressionHandler() {
|
||||
return expressionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link WebInvocationPrivilegeEvaluator} to be used.
|
||||
* @return
|
||||
*/
|
||||
public WebInvocationPrivilegeEvaluator getPrivilegeEvaluator() {
|
||||
if(privilegeEvaluator != null) {
|
||||
return privilegeEvaluator;
|
||||
}
|
||||
return filterSecurityInterceptor == null ? null : new DefaultWebInvocationPrivilegeEvaluator(filterSecurityInterceptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link FilterSecurityInterceptor}. This is typically invoked by {@link WebSecurityConfigurerAdapter}.
|
||||
* @param securityInterceptor the {@link FilterSecurityInterceptor} to use
|
||||
*/
|
||||
public void setSecurityInterceptor(FilterSecurityInterceptor securityInterceptor) {
|
||||
this.filterSecurityInterceptor = securityInterceptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Filter performBuild() throws Exception {
|
||||
Assert.state(!securityFilterChainBuilders.isEmpty(), "At least one SecurityFilterBuilder needs to be specified. Invoke FilterChainProxyBuilder.securityFilterChains");
|
||||
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
|
||||
List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>(chainSize);
|
||||
for(RequestMatcher ignoredRequest : ignoredRequests) {
|
||||
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
|
||||
}
|
||||
for(SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
|
||||
securityFilterChains.add(securityFilterChainBuilder.build());
|
||||
}
|
||||
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
|
||||
if(httpFirewall != null) {
|
||||
filterChainProxy.setFirewall(httpFirewall);
|
||||
}
|
||||
filterChainProxy.afterPropertiesSet();
|
||||
|
||||
Filter result = filterChainProxy;
|
||||
if(debugEnabled) {
|
||||
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);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows registering {@link RequestMatcher} instances that should be
|
||||
* ignored by Spring Security.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class IgnoredRequestConfigurer extends AbstractRequestMatcherConfigurer<WebSecurity,IgnoredRequestConfigurer,Filter> {
|
||||
|
||||
@Override
|
||||
protected IgnoredRequestConfigurer chainRequestMatchers(List<RequestMatcher> requestMatchers) {
|
||||
ignoredRequests.addAll(requestMatchers);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link WebSecurity} to be returned for chaining.
|
||||
*/
|
||||
@Override
|
||||
public WebSecurity and() {
|
||||
return WebSecurity.this;
|
||||
}
|
||||
|
||||
private IgnoredRequestConfigurer(){}
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration;
|
||||
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
||||
|
||||
/**
|
||||
* Add this annotation to an {@code @Configuration} class to have the Spring Security
|
||||
* configuration defined in any {@link WebSecurityConfigurer} or more likely by extending the
|
||||
* {@link WebSecurityConfigurerAdapter} base class and overriding individual methods:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableWebSecurity
|
||||
* public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
*
|
||||
* @Override
|
||||
* public void configure(WebSecurity web) throws Exception {
|
||||
* web
|
||||
* .ignoring()
|
||||
* // Spring Security should completely ignore URLs starting with /resources/
|
||||
* .antMatchers("/resources/**");
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* protected void configure(HttpSecurity http) throws Exception {
|
||||
* http
|
||||
* .authorizeUrls()
|
||||
* .antMatchers("/public/**").permitAll()
|
||||
* .anyRequest().hasRole("USER")
|
||||
* .and()
|
||||
* // Possibly more configuration ...
|
||||
* .formLogin() // enable form based log in
|
||||
* // set permitAll for all URLs associated with Form Login
|
||||
* .permitAll();
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* protected void registerAuthentication(AuthenticationManagerBuilder auth) {
|
||||
* registry
|
||||
* // enable in memory based authentication with a user named "user" and "admin"
|
||||
* .inMemoryAuthentication()
|
||||
* .withUser("user").password("password").roles("USER").and()
|
||||
* .withUser("admin").password("password").roles("USER", "ADMIN");
|
||||
* }
|
||||
*
|
||||
* // Possibly more overridden methods ...
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @see WebSecurityConfigurer
|
||||
* @see WebSecurityConfigurerAdapter
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||
@Target(value={java.lang.annotation.ElementType.TYPE})
|
||||
@Documented
|
||||
@Import({WebSecurityConfiguration.class,ObjectPostProcessorConfiguration.class})
|
||||
public @interface EnableWebSecurity {
|
||||
|
||||
/**
|
||||
* Controls debugging support for Spring Security. Default is false.
|
||||
* @return if true, enables debug support with Spring Security
|
||||
*/
|
||||
boolean debug() default false;
|
||||
}
|
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Uses a {@link WebSecurity} to create the {@link FilterChainProxy} that
|
||||
* performs the web based security for Spring Security. It then exports the
|
||||
* necessary beans. Customizations can be made to {@link WebSecurity} by
|
||||
* extending {@link WebSecurityConfigurerAdapter} and exposing it as a
|
||||
* {@link Configuration} or implementing {@link WebSecurityConfigurer} and
|
||||
* exposing it as a {@link Configuration}. This configuration is imported when
|
||||
* using {@link EnableWebSecurity}.
|
||||
*
|
||||
* @see EnableWebSecurity
|
||||
* @see WebSecurity
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
@Configuration
|
||||
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
|
||||
private final WebSecurity webSecurity = new WebSecurity();
|
||||
|
||||
private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
|
||||
|
||||
private ClassLoader beanClassLoader;
|
||||
|
||||
@Bean
|
||||
@DependsOn("springSecurityFilterChain")
|
||||
public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {
|
||||
return webSecurity.getExpressionHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the Spring Security Filter Chain
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Bean(name="springSecurityFilterChain")
|
||||
public Filter springSecurityFilterChain() throws Exception {
|
||||
boolean hasConfigurers = webSecurityConfigurers != null && !webSecurityConfigurers.isEmpty();
|
||||
if(!hasConfigurers) {
|
||||
throw new IllegalStateException("At least one non-null instance of "+ WebSecurityConfigurer.class.getSimpleName()+" must be exposed as a @Bean when using @EnableWebSecurity. Hint try extending "+ WebSecurityConfigurerAdapter.class.getSimpleName());
|
||||
}
|
||||
return webSecurity.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link WebInvocationPrivilegeEvaluator} that is necessary for the JSP tag support.
|
||||
* @return the {@link WebInvocationPrivilegeEvaluator}
|
||||
* @throws Exception
|
||||
*/
|
||||
@Bean
|
||||
@DependsOn("springSecurityFilterChain")
|
||||
public WebInvocationPrivilegeEvaluator privilegeEvaluator() throws Exception {
|
||||
return webSecurity.getPrivilegeEvaluator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>} instances used to create the web configuration.
|
||||
*
|
||||
* @param webSecurityConfigurers the {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>} instances used to create the web configuration
|
||||
* @throws Exception
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setFilterChainProxySecurityConfigurer(
|
||||
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception {
|
||||
Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);
|
||||
|
||||
Integer previousOrder = 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, so it cannot be used on " + config + " too.");
|
||||
}
|
||||
previousOrder = order;
|
||||
}
|
||||
for(SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
|
||||
webSecurity.apply(webSecurityConfigurer);
|
||||
}
|
||||
this.webSecurityConfigurers = webSecurityConfigurers;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A custom verision of the Spring provided AnnotationAwareOrderComparator
|
||||
* that uses {@link AnnotationUtils#findAnnotation(Class, Class)} to look on
|
||||
* super class instances for the {@link Order} annotation.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
private static class AnnotationAwareOrderComparator extends OrderComparator {
|
||||
private static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();
|
||||
|
||||
@Override
|
||||
protected int getOrder(Object obj) {
|
||||
return lookupOrder(obj);
|
||||
}
|
||||
|
||||
private static int lookupOrder(Object obj) {
|
||||
if (obj instanceof Ordered) {
|
||||
return ((Ordered) obj).getOrder();
|
||||
}
|
||||
if (obj != null) {
|
||||
Class<?> clazz = (obj instanceof Class ? (Class<?>) obj : obj.getClass());
|
||||
Order order = AnnotationUtils.findAnnotation(clazz,Order.class);
|
||||
if (order != null) {
|
||||
return order.value();
|
||||
}
|
||||
}
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.context.annotation.ImportAware#setImportMetadata(org.springframework.core.type.AnnotationMetadata)
|
||||
*/
|
||||
@Override
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
Map<String, Object> enableWebSecurityAttrMap = importMetadata.getAnnotationAttributes(EnableWebSecurity.class.getName());
|
||||
AnnotationAttributes enableWebSecurityAttrs = AnnotationAttributes.fromMap(enableWebSecurityAttrMap);
|
||||
if(enableWebSecurityAttrs == null) {
|
||||
// search parent classes
|
||||
Class<?> currentClass = ClassUtils.resolveClassName(importMetadata.getClassName(), beanClassLoader);
|
||||
for(Class<?> classToInspect = currentClass ;classToInspect != null; classToInspect = classToInspect.getSuperclass()) {
|
||||
EnableWebSecurity enableWebSecurityAnnotation = AnnotationUtils.findAnnotation(classToInspect, EnableWebSecurity.class);
|
||||
if(enableWebSecurityAnnotation == null) {
|
||||
continue;
|
||||
}
|
||||
enableWebSecurityAttrMap = AnnotationUtils
|
||||
.getAnnotationAttributes(enableWebSecurityAnnotation);
|
||||
enableWebSecurityAttrs = AnnotationAttributes.fromMap(enableWebSecurityAttrMap);
|
||||
}
|
||||
}
|
||||
boolean debugEnabled = enableWebSecurityAttrs.getBoolean("debug");
|
||||
this.webSecurity.debug(debugEnabled);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang.ClassLoader)
|
||||
*/
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
}
|
@ -0,0 +1,326 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configuration;
|
||||
|
||||
|
||||
import javax.servlet.Filter;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.SecurityContextConfigurer;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
|
||||
/**
|
||||
* Provides a convenient base class for creating a {@link WebSecurityConfigurer}
|
||||
* instance. The implementation allows customization by overriding methods.
|
||||
*
|
||||
* @see EnableWebSecurity
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public abstract class WebSecurityConfigurerAdapter implements SecurityConfigurer<Filter,WebSecurity> {
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@Autowired(required=false)
|
||||
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");
|
||||
}
|
||||
};
|
||||
|
||||
private final AuthenticationManagerBuilder authenticationBuilder = new AuthenticationManagerBuilder();
|
||||
private final AuthenticationManagerBuilder parentAuthenticationBuilder = new AuthenticationManagerBuilder() {
|
||||
@Override
|
||||
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
|
||||
authenticationBuilder.eraseCredentials(eraseCredentials);
|
||||
return super.eraseCredentials(eraseCredentials);
|
||||
}
|
||||
|
||||
};
|
||||
private boolean disableAuthenticationRegistration;
|
||||
private boolean authenticationManagerInitialized;
|
||||
private AuthenticationManager authenticationManager;
|
||||
private HttpSecurity http;
|
||||
private boolean disableDefaults;
|
||||
|
||||
/**
|
||||
* Creates an instance with the default configuration enabled.
|
||||
*/
|
||||
protected WebSecurityConfigurerAdapter() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 enabled, else
|
||||
* false
|
||||
*/
|
||||
protected WebSecurityConfigurerAdapter(boolean disableDefaults) {
|
||||
this.disableDefaults = disableDefaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the default implementation of {@link #authenticationManager()} to attempt to obtain an
|
||||
* {@link AuthenticationManager}. If overridden, the {@link AuthenticationManagerBuilder} should be used to specify
|
||||
* the {@link AuthenticationManager}. The resulting {@link AuthenticationManager}
|
||||
* will be exposed as a Bean as will the last populated {@link UserDetailsService} that is created with the
|
||||
* {@link AuthenticationManagerBuilder}. The {@link UserDetailsService} will also automatically be populated on
|
||||
* {@link HttpSecurity#getSharedObject(Class)} for use with other {@link SecurityContextConfigurer}
|
||||
* (i.e. RememberMeConfigurer )
|
||||
*
|
||||
* <p>For example, the following configuration could be used to register
|
||||
* in memory authentication that exposes an in memory {@link UserDetailsService}:</p>
|
||||
*
|
||||
* <pre>
|
||||
* @Override
|
||||
* protected void registerAuthentication(AuthenticationManagerBuilder auth) {
|
||||
* registry
|
||||
* // enable in memory based authentication with a user named "user" and "admin"
|
||||
* .inMemoryAuthentication()
|
||||
* .withUser("user").password("password").roles("USER").and()
|
||||
* .withUser("admin").password("password").roles("USER", "ADMIN");
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param auth the {@link AuthenticationManagerBuilder} to use
|
||||
* @throws Exception
|
||||
*/
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
this.disableAuthenticationRegistration = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link HttpSecurity} or returns the current instance
|
||||
*
|
||||
* @return the {@link HttpSecurity}
|
||||
* @throws Exception
|
||||
*/
|
||||
protected final HttpSecurity getHttp() throws Exception {
|
||||
if(http != null) {
|
||||
return http;
|
||||
}
|
||||
|
||||
authenticationBuilder.objectPostProcessor(objectPostProcessor);
|
||||
parentAuthenticationBuilder.objectPostProcessor(objectPostProcessor);
|
||||
|
||||
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());
|
||||
parentAuthenticationBuilder.authenticationEventPublisher(eventPublisher);
|
||||
|
||||
AuthenticationManager authenticationManager = authenticationManager();
|
||||
authenticationBuilder.parentAuthenticationManager(authenticationManager);
|
||||
http = new HttpSecurity(objectPostProcessor,authenticationBuilder, parentAuthenticationBuilder.getSharedObjects());
|
||||
http.setSharedObject(UserDetailsService.class, userDetailsService());
|
||||
if(!disableDefaults) {
|
||||
http
|
||||
.exceptionHandling().and()
|
||||
.sessionManagement().and()
|
||||
.securityContext().and()
|
||||
.requestCache().and()
|
||||
.anonymous().and()
|
||||
.servletApi().and()
|
||||
.apply(new DefaultLoginPageConfigurer<HttpSecurity>()).and()
|
||||
.logout();
|
||||
}
|
||||
configure(http);
|
||||
return http;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to expose the {@link AuthenticationManager} from
|
||||
* {@link #registerAuthentication(AuthenticationManagerBuilder)} to be exposed as
|
||||
* a Bean. For example:
|
||||
*
|
||||
* <pre>
|
||||
* @Bean(name name="myAuthenticationManager")
|
||||
* @Override
|
||||
* public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
* return super.authenticationManagerBean();
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return the {@link AuthenticationManager}
|
||||
* @throws Exception
|
||||
*/
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return new AuthenticationManagerDelegator(authenticationBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link AuthenticationManager} to use. The default strategy is if
|
||||
* {@link #registerAuthentication(AuthenticationManagerBuilder)} method is
|
||||
* overridden to use the {@link AuthenticationManagerBuilder} that was passed in.
|
||||
* Otherwise, autowire the {@link AuthenticationManager} by type.
|
||||
*
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
protected AuthenticationManager authenticationManager() throws Exception {
|
||||
if(!authenticationManagerInitialized) {
|
||||
registerAuthentication(parentAuthenticationBuilder);
|
||||
if(disableAuthenticationRegistration) {
|
||||
try {
|
||||
authenticationManager = context.getBean(AuthenticationManager.class);
|
||||
} catch(NoSuchBeanDefinitionException e) {
|
||||
logger.debug("The AuthenticationManager was not found. This is ok for now as it may not be required.",e);
|
||||
}
|
||||
} else {
|
||||
authenticationManagerInitialized = true;
|
||||
authenticationManager = parentAuthenticationBuilder.build();
|
||||
}
|
||||
authenticationManagerInitialized = true;
|
||||
}
|
||||
return authenticationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to expose a {@link UserDetailsService} created from
|
||||
* {@link #registerAuthentication(AuthenticationManagerBuilder)} as a bean. In
|
||||
* general only the following override should be done of this method:
|
||||
*
|
||||
* <pre>
|
||||
* @Bean(name = "myUserDetailsService") // any or no name specified is allowed
|
||||
* @Override
|
||||
* public UserDetailsService userDetailsServiceBean() throws Exception {
|
||||
* return super.userDetailsServiceBean();
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* To change the instance returned, developers should change
|
||||
* {@link #userDetailsService()} instead
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @see {@link #userDetailsService()}
|
||||
*/
|
||||
public UserDetailsService userDetailsServiceBean() throws Exception {
|
||||
return userDetailsService();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows modifying and accessing the {@link UserDetailsService} from
|
||||
* {@link #userDetailsServiceBean()()} without interacting with the
|
||||
* {@link ApplicationContext}. Developers should override this method when
|
||||
* changing the instance of {@link #userDetailsServiceBean()}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected UserDetailsService userDetailsService() {
|
||||
return parentAuthenticationBuilder.getDefaultUserDetailsService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(WebSecurity web) throws Exception {
|
||||
HttpSecurity http = getHttp();
|
||||
FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
|
||||
web
|
||||
.addSecurityFilterChainBuilder(http)
|
||||
.setSecurityInterceptor(securityInterceptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to configure {@link WebSecurity}. For
|
||||
* example, if you wish to ignore certain requests.
|
||||
*/
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to configure the {@link HttpSecurity}.
|
||||
* Typically subclasses should not invoke this method by calling super
|
||||
* as it may override their configuration. The default configuration is:
|
||||
*
|
||||
* <pre>
|
||||
* http
|
||||
* .authorizeUrls()
|
||||
* .anyRequest().authenticated().and()
|
||||
* .formLogin().and()
|
||||
* .httpBasic();
|
||||
* </pre>
|
||||
*
|
||||
* @param http
|
||||
* the {@link HttpSecurity} to modify
|
||||
* @throws Exception
|
||||
* if an error occurs
|
||||
*/
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
|
||||
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.formLogin().and()
|
||||
.httpBasic();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delays the use of the {@link AuthenticationManager} build from the
|
||||
* {@link AuthenticationManagerBuilder} to ensure that it has been fully
|
||||
* configured.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
static final class AuthenticationManagerDelegator implements AuthenticationManager {
|
||||
private AuthenticationManagerBuilder delegateBuilder;
|
||||
private AuthenticationManager delegate;
|
||||
private final Object delegateMonitor = new Object();
|
||||
|
||||
AuthenticationManagerDelegator(AuthenticationManagerBuilder authentication) {
|
||||
this.delegateBuilder = authentication;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
if(delegate != null) {
|
||||
return delegate.authenticate(authentication);
|
||||
}
|
||||
|
||||
synchronized(delegateMonitor) {
|
||||
if (delegate == null) {
|
||||
delegate = this.delegateBuilder.getObject();
|
||||
this.delegateBuilder = null;
|
||||
}
|
||||
}
|
||||
|
||||
return delegate.authenticate(authentication);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,319 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configurers.openid.OpenIDLoginConfigurer;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.PortMapper;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.RememberMeServices;
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||
|
||||
/**
|
||||
* Base class for confuring {@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
|
||||
*/
|
||||
public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecurityBuilder<B>,T extends AbstractAuthenticationFilterConfigurer<B,T, F>, F extends AbstractAuthenticationProcessingFilter> extends AbstractHttpConfigurer<B> {
|
||||
|
||||
private final F authFilter;
|
||||
|
||||
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
|
||||
|
||||
private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
|
||||
|
||||
private LoginUrlAuthenticationEntryPoint authenticationEntryPoint;
|
||||
|
||||
private boolean customLoginPage;
|
||||
private String loginPage;
|
||||
private String loginProcessingUrl;
|
||||
|
||||
private AuthenticationFailureHandler failureHandler;
|
||||
|
||||
private boolean permitAll;
|
||||
|
||||
private String failureUrl;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param authenticationFilter the {@link AbstractAuthenticationProcessingFilter} to use
|
||||
* @param defaultLoginProcessingUrl the default URL to use for {@link #loginProcessingUrl(String)}
|
||||
*/
|
||||
protected AbstractAuthenticationFilterConfigurer(F authenticationFilter, String defaultLoginProcessingUrl) {
|
||||
this.authFilter = authenticationFilter;
|
||||
loginUrl("/login");
|
||||
failureUrl("/login?error");
|
||||
loginProcessingUrl(defaultLoginProcessingUrl);
|
||||
this.customLoginPage = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies where users will go after authenticating successfully if they
|
||||
* have not visited a secured page prior to authenticating. This is a
|
||||
* shortcut for calling {@link #defaultSuccessUrl(String)}.
|
||||
*
|
||||
* @param defaultSuccessUrl
|
||||
* the default success url
|
||||
* @return the {@link FormLoginConfigurer} for additional customization
|
||||
*/
|
||||
public final T defaultSuccessUrl(String defaultSuccessUrl) {
|
||||
return defaultSuccessUrl(defaultSuccessUrl, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies where users will go 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
|
||||
* @return the {@link FormLoginConfigurer} for additional customization
|
||||
*/
|
||||
public final T defaultSuccessUrl(String defaultSuccessUrl, boolean alwaysUse) {
|
||||
SavedRequestAwareAuthenticationSuccessHandler handler = new SavedRequestAwareAuthenticationSuccessHandler();
|
||||
handler.setDefaultTargetUrl(defaultSuccessUrl);
|
||||
handler.setAlwaysUseDefaultTargetUrl(alwaysUse);
|
||||
return successHandler(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the URL used to log in. If the request matches the URL and is an HTTP POST, the
|
||||
* {@link UsernamePasswordAuthenticationFilter} will attempt to authenticate
|
||||
* the request. Otherwise, if the request matches the URL the user will be sent to the login form.
|
||||
*
|
||||
* @param loginUrl the URL used to perform authentication
|
||||
* @return the {@link FormLoginConfigurer} for additional customization
|
||||
*/
|
||||
public final T loginUrl(String loginUrl) {
|
||||
loginProcessingUrl(loginUrl);
|
||||
return loginPage(loginUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.setFilterProcessesUrl(loginProcessingUrl);
|
||||
return getSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a custom {@link AuthenticationDetailsSource}. The default is {@link WebAuthenticationDetailsSource}.
|
||||
*
|
||||
* @param authenticationDetailsSource the custom {@link AuthenticationDetailsSource}
|
||||
* @return the {@link FormLoginConfigurer} for additional customization
|
||||
*/
|
||||
public final T authenticationDetailsSource(AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
return getSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the {@link AuthenticationSuccessHandler} to be used. The
|
||||
* default is {@link SavedRequestAwareAuthenticationSuccessHandler} with no
|
||||
* additional properites set.
|
||||
*
|
||||
* @param successHandler
|
||||
* the {@link AuthenticationSuccessHandler}.
|
||||
* @return the {@link FormLoginConfigurer} for additional customization
|
||||
*/
|
||||
public final T successHandler(AuthenticationSuccessHandler successHandler) {
|
||||
this.successHandler = successHandler;
|
||||
return getSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent of invoking permitAll(true)
|
||||
* @return
|
||||
*/
|
||||
public final T permitAll() {
|
||||
return permitAll(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the urls for {@link #failureUrl(String)} and
|
||||
* {@link #loginUrl(String)} 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
|
||||
*/
|
||||
public final T permitAll(boolean permitAll) {
|
||||
this.permitAll = permitAll;
|
||||
return getSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
this.failureUrl = authenticationFailureUrl;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.failureUrl = null;
|
||||
this.failureHandler = authenticationFailureHandler;
|
||||
return getSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(B http) throws Exception {
|
||||
if(permitAll) {
|
||||
PermitAllSupport.permitAll(http, loginPage, loginProcessingUrl, failureUrl);
|
||||
}
|
||||
http.setSharedObject(AuthenticationEntryPoint.class, postProcess(authenticationEntryPoint));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(B http) throws Exception {
|
||||
PortMapper portMapper = http.getSharedObject(PortMapper.class);
|
||||
if(portMapper != null) {
|
||||
authenticationEntryPoint.setPortMapper(portMapper);
|
||||
}
|
||||
|
||||
authFilter.setAuthenticationManager(http.getAuthenticationManager());
|
||||
authFilter.setAuthenticationSuccessHandler(successHandler);
|
||||
authFilter.setAuthenticationFailureHandler(failureHandler);
|
||||
if(authenticationDetailsSource != null) {
|
||||
authFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
}
|
||||
SessionAuthenticationStrategy sessionAuthenticationStrategy = http.getSharedObject(SessionAuthenticationStrategy.class);
|
||||
if(sessionAuthenticationStrategy != null) {
|
||||
authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
|
||||
}
|
||||
RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
|
||||
if(rememberMeServices != null) {
|
||||
authFilter.setRememberMeServices(rememberMeServices);
|
||||
}
|
||||
F filter = postProcess(authFilter);
|
||||
http.addFilter(filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Specifies the URL to send users to if login is required. If used with
|
||||
* {@link WebSecurityConfigurerAdapter} a default login page will be
|
||||
* generated when this attribute is not specified.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a URL is specified or this is not being used in conjuction with
|
||||
* {@link WebSecurityConfigurerAdapter}, users are required to process the
|
||||
* specified URL to generate a login page.
|
||||
* </p>
|
||||
*/
|
||||
protected T loginPage(String loginPage) {
|
||||
this.loginPage = loginPage;
|
||||
this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);
|
||||
this.customLoginPage = true;
|
||||
return getSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return true if a custom login page has been specified, else false
|
||||
*/
|
||||
public final boolean isCustomLoginPage() {
|
||||
return customLoginPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Authentication Filter
|
||||
* @return
|
||||
*/
|
||||
protected final F getAuthenticationFilter() {
|
||||
return authFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the login page
|
||||
* @return the login page
|
||||
*/
|
||||
protected final String getLoginPage() {
|
||||
return loginPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL to send users to if authentication fails
|
||||
* @return
|
||||
*/
|
||||
protected final String getFailureUrl() {
|
||||
return failureUrl;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private T getSelf() {
|
||||
return (T) this;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.SecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
|
||||
/**
|
||||
* Adds a convenient base class for {@link SecurityConfigurer} instances that
|
||||
* operate on {@link HttpSecurity}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
abstract class AbstractHttpConfigurer<B extends HttpSecurityBuilder<B>> extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {
|
||||
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
|
||||
import org.springframework.security.access.AccessDecisionManager;
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.access.vote.AffirmativeBased;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
|
||||
/**
|
||||
* A base class for configuring the {@link FilterSecurityInterceptor}.
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following Filters are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link FilterSecurityInterceptor}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* The following shared objects are populated to allow other {@link SecurityConfigurer}'s to customize:
|
||||
* <ul>
|
||||
* <li>{@link FilterSecurityInterceptor}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link org.springframework.security.config.annotation.web.builders.HttpSecurity#getAuthenticationManager()}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
|
||||
* @param <C> the type of object that is changed
|
||||
* @param <R> the type of object that is changed for the {@link AbstractRequestMatcherMappingConfigurer}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
* @see ExpressionUrlAuthorizationConfigurer
|
||||
* @see UrlAuthorizationConfigurer
|
||||
*/
|
||||
abstract class AbstractInterceptUrlConfigurer<H extends HttpSecurityBuilder<H>,C,R> extends
|
||||
AbstractRequestMatcherMappingConfigurer<H,R,DefaultSecurityFilterChain> implements
|
||||
SecurityConfigurer<DefaultSecurityFilterChain,H> {
|
||||
private Boolean filterSecurityInterceptorOncePerRequest;
|
||||
|
||||
private AccessDecisionManager accessDecisionManager;
|
||||
|
||||
/**
|
||||
* Allows setting the {@link AccessDecisionManager}. If none is provided, a default {@l AccessDecisionManager} is
|
||||
* created.
|
||||
*
|
||||
* @param accessDecisionManager the {@link AccessDecisionManager} to use
|
||||
* @return the {@link AbstractInterceptUrlConfigurer} for further customization
|
||||
*/
|
||||
public C accessDecisionManager(
|
||||
AccessDecisionManager accessDecisionManager) {
|
||||
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 C filterSecurityInterceptorOncePerRequest(
|
||||
boolean filterSecurityInterceptorOncePerRequest) {
|
||||
this.filterSecurityInterceptorOncePerRequest = filterSecurityInterceptorOncePerRequest;
|
||||
return getSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(H http) throws Exception {
|
||||
FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource();
|
||||
if(metadataSource == null) {
|
||||
return;
|
||||
}
|
||||
FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor(metadataSource, http.getAuthenticationManager());
|
||||
if(filterSecurityInterceptorOncePerRequest != null) {
|
||||
securityInterceptor.setObserveOncePerRequest(filterSecurityInterceptorOncePerRequest);
|
||||
}
|
||||
http.addFilter(securityInterceptor);
|
||||
http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses should implement this method to provide a {@link FilterInvocationSecurityMetadataSource} for the
|
||||
* {@link FilterSecurityInterceptor}.
|
||||
*
|
||||
* @return the {@link FilterInvocationSecurityMetadataSource} to set on the {@link FilterSecurityInterceptor}.
|
||||
* Cannot be null.
|
||||
*/
|
||||
abstract FilterInvocationSecurityMetadataSource createMetadataSource();
|
||||
|
||||
/**
|
||||
* Subclasses should implement this method to provide the {@link AccessDecisionVoter} instances used to create the
|
||||
* default {@link AccessDecisionManager}
|
||||
*
|
||||
* @return the {@link AccessDecisionVoter} instances used to create the
|
||||
* default {@link AccessDecisionManager}
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
abstract List<AccessDecisionVoter> getDecisionVoters();
|
||||
|
||||
/**
|
||||
* Creates the default {@code AccessDecisionManager}
|
||||
* @return the default {@code AccessDecisionManager}
|
||||
*/
|
||||
private AccessDecisionManager createDefaultAccessDecisionManager() {
|
||||
return new AffirmativeBased(getDecisionVoters());
|
||||
}
|
||||
|
||||
/**
|
||||
* If currently null, creates a default {@link AccessDecisionManager} using
|
||||
* {@link #createDefaultAccessDecisionManager()}. Otherwise returns the {@link AccessDecisionManager}.
|
||||
*
|
||||
* @return the {@link AccessDecisionManager} to use
|
||||
*/
|
||||
private AccessDecisionManager getAccessDecisionManager() {
|
||||
if (accessDecisionManager == null) {
|
||||
accessDecisionManager = createDefaultAccessDecisionManager();
|
||||
}
|
||||
return accessDecisionManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link FilterSecurityInterceptor}
|
||||
*
|
||||
* @param metadataSource the {@link FilterInvocationSecurityMetadataSource} to use
|
||||
* @param authenticationManager the {@link AuthenticationManager} to use
|
||||
* @return the {@link FilterSecurityInterceptor}
|
||||
* @throws Exception
|
||||
*/
|
||||
private FilterSecurityInterceptor createFilterSecurityInterceptor(FilterInvocationSecurityMetadataSource metadataSource,
|
||||
AuthenticationManager authenticationManager) throws Exception {
|
||||
FilterSecurityInterceptor securityInterceptor = new FilterSecurityInterceptor();
|
||||
securityInterceptor.setSecurityMetadataSource(metadataSource);
|
||||
securityInterceptor.setAccessDecisionManager(getAccessDecisionManager());
|
||||
securityInterceptor.setAuthenticationManager(authenticationManager);
|
||||
securityInterceptor.afterPropertiesSet();
|
||||
return securityInterceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the current object with a single suppression of
|
||||
* the type
|
||||
*
|
||||
* @return a reference to the current object
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private C getSelf() {
|
||||
return (C) this;
|
||||
}
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.AbstractRequestMatcherConfigurer;
|
||||
import org.springframework.security.web.util.RequestMatcher;
|
||||
|
||||
/**
|
||||
* A base class for registering {@link RequestMatcher}'s. For example, it might allow for specifying which
|
||||
* {@link RequestMatcher} require a certain level of authorization.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*
|
||||
* @param <B> The Builder that is building Object O and is configured by this {@link AbstractRequestMatcherMappingConfigurer}
|
||||
* @param <C> The object that is returned or Chained after creating the RequestMatcher
|
||||
* @param <O> The Object being built by Builder B
|
||||
*
|
||||
* @see ChannelSecurityConfigurer
|
||||
* @see UrlAuthorizationConfigurer
|
||||
* @see ExpressionUrlAuthorizationConfigurer
|
||||
*/
|
||||
public abstract class AbstractRequestMatcherMappingConfigurer<B extends SecurityBuilder<O>,C,O> extends AbstractRequestMatcherConfigurer<B,C,O> {
|
||||
private List<UrlMapping> urlMappings = new ArrayList<UrlMapping>();
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.unmappedMatchers = null;
|
||||
this.urlMappings.add(urlMapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*/
|
||||
protected final C chainRequestMatchers(List<RequestMatcher> requestMatchers) {
|
||||
this.unmappedMatchers = requestMatchers;
|
||||
return chainRequestMatchersInternal(requestMatchers);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*/
|
||||
protected abstract C chainRequestMatchersInternal(List<RequestMatcher> requestMatchers);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
final void addMapping(int index, UrlMapping urlMapping) {
|
||||
this.urlMappings.add(index, urlMapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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')");
|
||||
}
|
||||
|
||||
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
|
||||
for (UrlMapping mapping : getUrlMappings()) {
|
||||
RequestMatcher matcher = mapping.getRequestMatcher();
|
||||
Collection<ConfigAttribute> configAttrs = mapping.getConfigAttrs();
|
||||
requestMap.put(matcher,configAttrs);
|
||||
}
|
||||
return requestMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* A mapping of {@link RequestMatcher} to {@link Collection} of {@link ConfigAttribute} instances
|
||||
*/
|
||||
static final class UrlMapping {
|
||||
private RequestMatcher requestMatcher;
|
||||
private Collection<ConfigAttribute> configAttrs;
|
||||
|
||||
UrlMapping(RequestMatcher requestMatcher,
|
||||
Collection<ConfigAttribute> configAttrs) {
|
||||
this.requestMatcher = requestMatcher;
|
||||
this.configAttrs = configAttrs;
|
||||
}
|
||||
|
||||
public RequestMatcher getRequestMatcher() {
|
||||
return requestMatcher;
|
||||
}
|
||||
|
||||
public Collection<ConfigAttribute> getConfigAttrs() {
|
||||
return configAttrs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationProvider;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
|
||||
/**
|
||||
* Configures Anonymous authentication (i.e. populate an {@link Authentication} that represents an anonymous user
|
||||
* instead of having a null value) for an {@link HttpSecurity}. Specifically this will configure an
|
||||
* {@link AnonymousAuthenticationFilter} and an {@link AnonymousAuthenticationProvider}. All properties have reasonable
|
||||
* defaults, so no additional configuration is required other than applying this {@link SecurityConfigurer}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>> extends SecurityConfigurerAdapter<DefaultSecurityFilterChain,H> {
|
||||
private String key;
|
||||
private AuthenticationProvider authenticationProvider;
|
||||
private AnonymousAuthenticationFilter authenticationFilter;
|
||||
private Object principal = "anonymousUser";
|
||||
private List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS");
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @see HttpSecurity#anonymous()
|
||||
*/
|
||||
public AnonymousConfigurer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables anonymous authentication.
|
||||
*
|
||||
* @return the {@link HttpSecurity} since no further customization of anonymous authentication would be
|
||||
* meaningful.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public H disable() {
|
||||
getBuilder().removeConfigurer(getClass());
|
||||
return getBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 authentication
|
||||
*/
|
||||
public AnonymousConfigurer<H> key(String key) {
|
||||
this.key = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public AnonymousConfigurer<H> principal(Object principal) {
|
||||
this.principal = principal;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return the {@link AnonymousConfigurer} for further customization of anonymous authentication
|
||||
*/
|
||||
public AnonymousConfigurer<H> authorities(List<GrantedAuthority> authorities) {
|
||||
this.authorities = authorities;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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")
|
||||
* @return the {@link AnonymousConfigurer} for further customization of anonymous authentication
|
||||
*/
|
||||
public AnonymousConfigurer<H> authorities(String... authorities) {
|
||||
return authorities(AuthorityUtils.createAuthorityList(authorities));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.authenticationProvider = authenticationProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.authenticationFilter = authenticationFilter;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(H http) throws Exception {
|
||||
if(authenticationProvider == null) {
|
||||
authenticationProvider = new AnonymousAuthenticationProvider(getKey());
|
||||
}
|
||||
if(authenticationFilter == null) {
|
||||
authenticationFilter = new AnonymousAuthenticationFilter(getKey(), principal, authorities);
|
||||
}
|
||||
authenticationProvider = postProcess(authenticationProvider);
|
||||
http.authenticationProvider(authenticationProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(H http) throws Exception {
|
||||
authenticationFilter.afterPropertiesSet();
|
||||
http.addFilter(authenticationFilter);
|
||||
}
|
||||
|
||||
private String getKey() {
|
||||
if(key == null) {
|
||||
key = UUID.randomUUID().toString();
|
||||
}
|
||||
return key;
|
||||
}
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.PortMapper;
|
||||
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
|
||||
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
|
||||
import org.springframework.security.web.access.channel.ChannelProcessor;
|
||||
import org.springframework.security.web.access.channel.InsecureChannelProcessor;
|
||||
import org.springframework.security.web.access.channel.RetryWithHttpEntryPoint;
|
||||
import org.springframework.security.web.access.channel.RetryWithHttpsEntryPoint;
|
||||
import org.springframework.security.web.access.channel.SecureChannelProcessor;
|
||||
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
|
||||
import org.springframework.security.web.util.RequestMatcher;
|
||||
|
||||
/**
|
||||
* Adds channel security (i.e. requires HTTPS or HTTP) to an application. In order for
|
||||
* {@link ChannelSecurityConfigurer} to be useful, at least one {@link RequestMatcher} should be mapped to HTTP
|
||||
* or HTTPS.
|
||||
*
|
||||
* <p>
|
||||
* By default an {@link InsecureChannelProcessor} and a {@link SecureChannelProcessor} will be registered.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following Filters are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link ChannelProcessingFilter}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* No shared objects are created.
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link PortMapper} is used to create the default {@link ChannelProcessor} instances</li>
|
||||
* </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
|
||||
AbstractRequestMatcherMappingConfigurer<H,ChannelSecurityConfigurer<H>.RequiresChannelUrl,DefaultSecurityFilterChain> {
|
||||
private ChannelProcessingFilter channelFilter = new ChannelProcessingFilter();
|
||||
private LinkedHashMap<RequestMatcher,Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher,Collection<ConfigAttribute>>();
|
||||
private List<ChannelProcessor> channelProcessors;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @see HttpSecurity#requiresChannel()
|
||||
*/
|
||||
public ChannelSecurityConfigurer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(H http) throws Exception {
|
||||
ChannelDecisionManagerImpl channelDecisionManager = new ChannelDecisionManagerImpl();
|
||||
channelDecisionManager.setChannelProcessors(getChannelProcessors(http));
|
||||
channelDecisionManager = postProcess(channelDecisionManager);
|
||||
|
||||
channelFilter.setChannelDecisionManager(channelDecisionManager);
|
||||
|
||||
DefaultFilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource =
|
||||
new DefaultFilterInvocationSecurityMetadataSource(requestMap);
|
||||
channelFilter.setSecurityMetadataSource(filterInvocationSecurityMetadataSource);
|
||||
|
||||
channelFilter = postProcess(channelFilter);
|
||||
http.addFilter(channelFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ChannelProcessor} instances to use in {@link ChannelDecisionManagerImpl}
|
||||
* @param channelProcessors
|
||||
* @return
|
||||
*/
|
||||
public ChannelSecurityConfigurer<H> channelProcessors(List<ChannelProcessor> channelProcessors) {
|
||||
this.channelProcessors = channelProcessors;
|
||||
return this;
|
||||
}
|
||||
|
||||
private List<ChannelProcessor> getChannelProcessors(H http) {
|
||||
if(channelProcessors != null) {
|
||||
return 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);
|
||||
}
|
||||
|
||||
|
||||
private ChannelSecurityConfigurer<H> addAttribute(String attribute, List<RequestMatcher> matchers) {
|
||||
for(RequestMatcher matcher : matchers) {
|
||||
Collection<ConfigAttribute> attrs = Arrays.<ConfigAttribute>asList(new SecurityConfig(attribute));
|
||||
requestMap.put(matcher, attrs);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RequiresChannelUrl chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
|
||||
return new RequiresChannelUrl(requestMatchers);
|
||||
}
|
||||
|
||||
public final class RequiresChannelUrl {
|
||||
private List<RequestMatcher> requestMatchers;
|
||||
|
||||
private RequiresChannelUrl(List<RequestMatcher> requestMatchers) {
|
||||
this.requestMatchers = requestMatchers;
|
||||
}
|
||||
|
||||
public ChannelSecurityConfigurer<H> requiresSecure() {
|
||||
return requires("REQUIRES_SECURE_CHANNEL");
|
||||
}
|
||||
|
||||
public ChannelSecurityConfigurer<H> requiresInsecure() {
|
||||
return requires("REQUIRES_INSECURE_CHANNEL");
|
||||
}
|
||||
|
||||
public ChannelSecurityConfigurer<H> requires(String attribute) {
|
||||
return addAttribute(attribute, requestMatchers);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageViewFilter;
|
||||
|
||||
/**
|
||||
* Adds a Filter that will generate a login page if one is not specified otherwise when using {@link WebSecurityConfigurerAdapter}.
|
||||
*
|
||||
* <p>
|
||||
* By default an {@link org.springframework.security.web.access.channel.InsecureChannelProcessor} and a {@link org.springframework.security.web.access.channel.SecureChannelProcessor} will be registered.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following Filters are conditionally populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link DefaultLoginPageViewFilter} if the {@link FormLoginConfigurer} did not have a login page specified</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* No shared objects are created.
|
||||
*isLogoutRequest
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link org.springframework.security.web.PortMapper} is used to create the default {@link org.springframework.security.web.access.channel.ChannelProcessor} instances</li>
|
||||
* <li>{@link FormLoginConfigurer} is used to determine if the {@link DefaultLoginPageConfigurer} should be added and how to configure it.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see WebSecurityConfigurerAdapter
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class DefaultLoginPageConfigurer<H extends HttpSecurityBuilder<H>> extends
|
||||
AbstractHttpConfigurer<H> {
|
||||
|
||||
private DefaultLoginPageViewFilter loginPageGeneratingFilter = new DefaultLoginPageViewFilter();
|
||||
|
||||
@Override
|
||||
public void init(H http) throws Exception {
|
||||
http.setSharedObject(DefaultLoginPageViewFilter.class, loginPageGeneratingFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void configure(H http) throws Exception {
|
||||
AuthenticationEntryPoint authenticationEntryPoint = null;
|
||||
ExceptionHandlingConfigurer<?> exceptionConf = http.getConfigurer(ExceptionHandlingConfigurer.class);
|
||||
if(exceptionConf != null) {
|
||||
authenticationEntryPoint = exceptionConf.getAuthenticationEntryPoint();
|
||||
}
|
||||
|
||||
if(loginPageGeneratingFilter.isEnabled() && authenticationEntryPoint == null) {
|
||||
loginPageGeneratingFilter = postProcess(loginPageGeneratingFilter);
|
||||
http.addFilter(loginPageGeneratingFilter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
||||
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
|
||||
/**
|
||||
* Adds exception handling for Spring Security related exceptions to an application. All properties have reasonable
|
||||
* defaults, so no additional configuration is required other than applying this
|
||||
* {@link org.springframework.security.config.annotation.SecurityConfigurer}.
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following Filters are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link ExceptionTranslationFilter}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* No shared objects are created.
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link HttpSecurity#authenticationEntryPoint()} is used to process requests that require
|
||||
* authentication</li>
|
||||
* <li>If no explicit {@link RequestCache}, is provided a {@link RequestCache} shared object is used to replay
|
||||
* the request after authentication is successful</li>
|
||||
* <li>{@link AuthenticationEntryPoint} - see {@link #authenticationEntryPoint(AuthenticationEntryPoint)} </li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> {
|
||||
|
||||
private AuthenticationEntryPoint authenticationEntryPoint;
|
||||
|
||||
private AccessDeniedHandler accessDeniedHandler;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @see HttpSecurity#exceptionHandling()
|
||||
*/
|
||||
public ExceptionHandlingConfigurer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @see {@link #accessDeniedHandler(org.springframework.security.web.access.AccessDeniedHandler)}
|
||||
*/
|
||||
public ExceptionHandlingConfigurer<H> accessDeniedPage(String accessDeniedUrl) {
|
||||
AccessDeniedHandlerImpl accessDeniedHandler = new AccessDeniedHandlerImpl();
|
||||
accessDeniedHandler.setErrorPage(accessDeniedUrl);
|
||||
return accessDeniedHandler(accessDeniedHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.accessDeniedHandler = accessDeniedHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationEntryPoint} to be used. Defaults to the
|
||||
* {@link HttpSecurity#getSharedObject(Class)} value. If that is not
|
||||
* provided defaults to {@link Http403ForbiddenEntryPoint}.
|
||||
*
|
||||
* @param authenticationEntryPoint the {@link AuthenticationEntryPoint} to use
|
||||
* @return the {@link ExceptionHandlingConfigurer} for further customizations
|
||||
*/
|
||||
public ExceptionHandlingConfigurer<H> authenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
|
||||
this.authenticationEntryPoint = authenticationEntryPoint;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets any explicitly configured {@link AuthenticationEntryPoint}
|
||||
* @return
|
||||
*/
|
||||
AuthenticationEntryPoint getAuthenticationEntryPoint() {
|
||||
return this.authenticationEntryPoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(H http) throws Exception {
|
||||
AuthenticationEntryPoint entryPoint = getEntryPoint(http);
|
||||
ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(entryPoint, getRequestCache(http));
|
||||
if(accessDeniedHandler != null) {
|
||||
exceptionTranslationFilter.setAccessDeniedHandler(accessDeniedHandler);
|
||||
}
|
||||
exceptionTranslationFilter = postProcess(exceptionTranslationFilter);
|
||||
http.addFilter(exceptionTranslationFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link AuthenticationEntryPoint} according to the rules specified by {@link #authenticationEntryPoint(AuthenticationEntryPoint)}
|
||||
* @param http the {@link HttpSecurity} used to look up shared {@link AuthenticationEntryPoint}
|
||||
* @return the {@link AuthenticationEntryPoint} to use
|
||||
*/
|
||||
private AuthenticationEntryPoint getEntryPoint(H http) {
|
||||
AuthenticationEntryPoint entryPoint = this.authenticationEntryPoint;
|
||||
if(entryPoint == null) {
|
||||
AuthenticationEntryPoint sharedEntryPoint = http.getSharedObject(AuthenticationEntryPoint.class);
|
||||
if(sharedEntryPoint != null) {
|
||||
entryPoint = sharedEntryPoint;
|
||||
} else {
|
||||
entryPoint = new Http403ForbiddenEntryPoint();
|
||||
}
|
||||
}
|
||||
return entryPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link RequestCache} to use. If one is defined using
|
||||
* {@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
|
||||
*/
|
||||
private RequestCache getRequestCache(H http) {
|
||||
RequestCache result = http.getSharedObject(RequestCache.class);
|
||||
if(result != null) {
|
||||
return result;
|
||||
}
|
||||
return new HttpSessionRequestCache();
|
||||
}
|
||||
}
|
@ -0,0 +1,296 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
||||
import org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource;
|
||||
import org.springframework.security.web.access.expression.WebExpressionVoter;
|
||||
import org.springframework.security.web.util.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
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>
|
||||
*
|
||||
* The following Filters are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link org.springframework.security.web.access.intercept.FilterSecurityInterceptor}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* The following shared objects are populated to allow other {@link org.springframework.security.config.annotation.SecurityConfigurer}'s to customize:
|
||||
* <ul>
|
||||
* <li>{@link org.springframework.security.web.access.intercept.FilterSecurityInterceptor}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link org.springframework.security.config.annotation.web.builders.HttpSecurity#getAuthenticationManager()}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
* @see {@link org.springframework.security.config.annotation.web.builders.HttpSecurity#authorizeUrls()}
|
||||
*/
|
||||
public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractInterceptUrlConfigurer<H,ExpressionUrlAuthorizationConfigurer<H>,ExpressionUrlAuthorizationConfigurer<H>.AuthorizedUrl> {
|
||||
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 SecurityExpressionHandler<FilterInvocation> expressionHandler = new DefaultWebSecurityExpressionHandler();
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @see HttpSecurity#authorizeUrls()
|
||||
*/
|
||||
public ExpressionUrlAuthorizationConfigurer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ExpressionUrlAuthorizationConfigurer<H> expressionHandler(SecurityExpressionHandler<FilterInvocation> expressionHandler) {
|
||||
this.expressionHandler = expressionHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final AuthorizedUrl chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
|
||||
return new AuthorizedUrl(requestMatchers);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
final List<AccessDecisionVoter> getDecisionVoters() {
|
||||
List<AccessDecisionVoter> decisionVoters = new ArrayList<AccessDecisionVoter>();
|
||||
WebExpressionVoter expressionVoter = new WebExpressionVoter();
|
||||
expressionVoter.setExpressionHandler(expressionHandler);
|
||||
decisionVoters.add(expressionVoter);
|
||||
return decisionVoters;
|
||||
}
|
||||
|
||||
@Override
|
||||
final ExpressionBasedFilterInvocationSecurityMetadataSource createMetadataSource() {
|
||||
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = createRequestMap();
|
||||
if(requestMap.isEmpty()) {
|
||||
throw new IllegalStateException("At least one mapping is required (i.e. authorizeUrls().anyRequest.authenticated())");
|
||||
}
|
||||
return new ExpressionBasedFilterInvocationSecurityMetadataSource(requestMap, expressionHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {@link RequestMatcher} instances
|
||||
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization.
|
||||
*/
|
||||
private ExpressionUrlAuthorizationConfigurer<H> interceptUrl(Iterable<? extends RequestMatcher> requestMatchers, Collection<ConfigAttribute> configAttributes) {
|
||||
for(RequestMatcher requestMatcher : requestMatchers) {
|
||||
addMapping(new UrlMapping(requestMatcher, configAttributes));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
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 + "'");
|
||||
}
|
||||
return "hasRole('ROLE_" + role + "')";
|
||||
}
|
||||
|
||||
private static String hasAuthority(String authority) {
|
||||
return "hasAuthority('" + authority + "')";
|
||||
}
|
||||
|
||||
private static String hasAnyAuthority(String... authorities) {
|
||||
String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','");
|
||||
return "hasAnyAuthority('" + anyAuthorities + "')";
|
||||
}
|
||||
|
||||
private static String hasIpAddress(String ipAddressExpression) {
|
||||
return "hasIpAddress('" + ipAddressExpression + "')";
|
||||
}
|
||||
|
||||
public final class AuthorizedUrl {
|
||||
private List<RequestMatcher> requestMatchers;
|
||||
private boolean not;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param requestMatchers the {@link RequestMatcher} instances to map
|
||||
*/
|
||||
private AuthorizedUrl(List<RequestMatcher> requestMatchers) {
|
||||
this.requestMatchers = requestMatchers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Negates the following expression.
|
||||
*
|
||||
* @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 customization
|
||||
*/
|
||||
public AuthorizedUrl not() {
|
||||
this.not = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 customization
|
||||
*/
|
||||
public ExpressionUrlAuthorizationConfigurer<H> hasRole(String role) {
|
||||
return access(ExpressionUrlAuthorizationConfigurer.hasRole(role));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public ExpressionUrlAuthorizationConfigurer<H> hasAuthority(String authority) {
|
||||
return access(ExpressionUrlAuthorizationConfigurer.hasAuthority(authority));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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).
|
||||
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
|
||||
*/
|
||||
public ExpressionUrlAuthorizationConfigurer<H> hasAnyAuthority(String... authorities) {
|
||||
return access(ExpressionUrlAuthorizationConfigurer.hasAnyAuthority(authorities));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that URLs requires a specific IP Address or
|
||||
* <a href="http://forum.springsource.org/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 ExpressionUrlAuthorizationConfigurer<H> hasIpAddress(String ipaddressExpression) {
|
||||
return access(ExpressionUrlAuthorizationConfigurer.hasIpAddress(ipaddressExpression));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that URLs are allowed by anyone.
|
||||
*
|
||||
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
|
||||
*/
|
||||
public ExpressionUrlAuthorizationConfigurer<H> permitAll() {
|
||||
return access(permitAll);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that URLs are allowed by anonymous users.
|
||||
*
|
||||
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
|
||||
*/
|
||||
public ExpressionUrlAuthorizationConfigurer<H> anonymous() {
|
||||
return access(anonymous);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that URLs are allowed by users that have been remembered.
|
||||
*
|
||||
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
|
||||
* @see {@link RememberMeConfigurer}
|
||||
*/
|
||||
public ExpressionUrlAuthorizationConfigurer<H> rememberMe() {
|
||||
return access(rememberMe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that URLs are not allowed by anyone.
|
||||
*
|
||||
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
|
||||
*/
|
||||
public ExpressionUrlAuthorizationConfigurer<H> denyAll() {
|
||||
return access(denyAll);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that URLs are allowed by any authenticated user.
|
||||
*
|
||||
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
|
||||
*/
|
||||
public ExpressionUrlAuthorizationConfigurer<H> authenticated() {
|
||||
return access(authenticated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that URLs are allowed by users who have authenticated and were not "remembered".
|
||||
*
|
||||
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
|
||||
* @see {@link RememberMeConfigurer}
|
||||
*/
|
||||
public ExpressionUrlAuthorizationConfigurer<H> fullyAuthenticated() {
|
||||
return access(fullyAuthenticated);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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')")
|
||||
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
|
||||
*/
|
||||
public ExpressionUrlAuthorizationConfigurer<H> access(String attribute) {
|
||||
if(not) {
|
||||
attribute = "!" + attribute;
|
||||
}
|
||||
interceptUrl(requestMatchers, SecurityConfig.createList(attribute));
|
||||
return ExpressionUrlAuthorizationConfigurer.this;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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 javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.RememberMeServices;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageViewFilter;
|
||||
|
||||
/**
|
||||
* Adds form based authentication. All attributes have reasonable defaults
|
||||
* making all parameters are optional. If no {@link #loginPage(String)} is
|
||||
* specified, a default login page will be generated by the framework.
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following Filters are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>
|
||||
* {@link UsernamePasswordAuthenticationFilter}
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* The following shared objects are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li> {@link AuthenticationEntryPoint} </li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link HttpSecurity#getAuthenticationManager()}</li>
|
||||
* <li>{@link RememberMeServices} - is optionally used. See {@link RememberMeConfigurer}</li>
|
||||
* <li>{@link SessionAuthenticationStrategy} - is optionally used. See {@link SessionManagementConfigurer}</li>
|
||||
* <li>{@link DefaultLoginPageViewFilter} - if present will be populated with information from the configuration</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractAuthenticationFilterConfigurer<H,FormLoginConfigurer<H>,UsernamePasswordAuthenticationFilter> {
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @see HttpSecurity#formLogin()
|
||||
*/
|
||||
public FormLoginConfigurer() {
|
||||
super(createUsernamePasswordAuthenticationFilter(),"/login");
|
||||
usernameParameter("username");
|
||||
passwordParameter("password");
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Specifies the URL to send users to if login is required. If used with
|
||||
* {@link WebSecurityConfigurerAdapter} a default login page will be
|
||||
* generated when this attribute is not specified.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a URL is specified or this is not being used in conjuction with
|
||||
* {@link WebSecurityConfigurerAdapter}, users are required to process the
|
||||
* specified URL to generate a login page. In general, the login page should
|
||||
* create a form that submits a request with the following requirements to
|
||||
* work with {@link UsernamePasswordAuthenticationFilter}:
|
||||
* </p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>It must be an HTTP POST</li>
|
||||
* <li>It must be submitted to {@link #loginProcessingUrl(String)}</li>
|
||||
* <li>It should include the username as an HTTP parameter by the name of
|
||||
* {@link #usernameParameter(String)}</li>
|
||||
* <li>It should include the password as an HTTP parameter by the name of
|
||||
* {@link #passwordParameter(String)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Example login.jsp</h2>
|
||||
*
|
||||
* Login pages can be rendered with any technology you choose so long as the
|
||||
* rules above are followed. Below is an example login.jsp that can be used as
|
||||
* a quick start when using JSP's or as a baseline to translate into another view
|
||||
* technology.
|
||||
*
|
||||
* <pre>
|
||||
* <!-- loginProcessingUrl should correspond to FormLoginConfigurer#loginProcessingUrl. Don't forget to perform a POST -->
|
||||
* <c:url value="/login" var="loginProcessingUrl"/>
|
||||
* <form action="${loginProcessingUrl}" method="post">
|
||||
* <fieldset>
|
||||
* <legend>Please Login</legend>
|
||||
* <!-- use param.error assuming FormLoginConfigurer#failureUrl contains the query parameter error -->
|
||||
* <c:if test="${param.error != null}">
|
||||
* <div>
|
||||
* Failed to login.
|
||||
* <c:if test="${SPRING_SECURITY_LAST_EXCEPTION != null}">
|
||||
* Reason: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" />
|
||||
* </c:if>
|
||||
* </div>
|
||||
* </c:if>
|
||||
* <!-- the configured LogoutConfigurer#logoutSuccessUrl is /login?logout and contains the query param logout -->
|
||||
* <c:if test="${param.logout != null}">
|
||||
* <div>
|
||||
* You have been logged out.
|
||||
* </div>
|
||||
* </c:if>
|
||||
* <p>
|
||||
* <label for="username">Username</label>
|
||||
* <input type="text" id="username" name="username"/>
|
||||
* </p>
|
||||
* <p>
|
||||
* <label for="password">Password</label>
|
||||
* <input type="password" id="password" name="password"/>
|
||||
* </p>
|
||||
* <!-- if using RememberMeConfigurer make sure remember-me matches RememberMeConfigurer#rememberMeParameter -->
|
||||
* <p>
|
||||
* <label for="remember-me">Remember Me?</label>
|
||||
* <input type="checkbox" id="remember-me" name="remember-me"/>
|
||||
* </p>
|
||||
* <div>
|
||||
* <button type="submit" class="btn">Log in</button>
|
||||
* </div>
|
||||
* </fieldset>
|
||||
* </form>
|
||||
* </pre>
|
||||
*
|
||||
* @param loginPage
|
||||
* the login page to redirect to if authentication is required
|
||||
* (i.e. "/login")
|
||||
* @return the {@link FormLoginConfigurer} for additional customization
|
||||
*/
|
||||
public FormLoginConfigurer<H> loginPage(String loginPage) {
|
||||
return super.loginPage(loginPage);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public FormLoginConfigurer<H> usernameParameter(String usernameParameter) {
|
||||
getAuthenticationFilter().setUsernameParameter(usernameParameter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public FormLoginConfigurer<H> passwordParameter(String passwordParameter) {
|
||||
getAuthenticationFilter().setPasswordParameter(passwordParameter);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(H http) throws Exception {
|
||||
super.init(http);
|
||||
initDefaultLoginFilter(http);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
return getAuthenticationFilter().getUsernameParameter();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
return getAuthenticationFilter().getPasswordParameter();
|
||||
}
|
||||
|
||||
/**
|
||||
* If available, initializes the {@link DefaultLoginPageViewFilter} shared object.
|
||||
*
|
||||
* @param http the {@link HttpSecurityBuilder} to use
|
||||
*/
|
||||
private void initDefaultLoginFilter(H http) {
|
||||
DefaultLoginPageViewFilter loginPageGeneratingFilter = http.getSharedObject(DefaultLoginPageViewFilter.class);
|
||||
if(loginPageGeneratingFilter != null && !isCustomLoginPage()) {
|
||||
loginPageGeneratingFilter.setFormLoginEnabled(true);
|
||||
loginPageGeneratingFilter.setUsernameParameter(getUsernameParameter());
|
||||
loginPageGeneratingFilter.setPasswordParameter(getPasswordParameter());
|
||||
loginPageGeneratingFilter.setLoginPageUrl(getLoginPage());
|
||||
loginPageGeneratingFilter.setFailureUrl(getFailureUrl());
|
||||
loginPageGeneratingFilter.setAuthenticationUrl(getLoginProcessingUrl());
|
||||
}
|
||||
}
|
||||
|
||||
private static UsernamePasswordAuthenticationFilter createUsernamePasswordAuthenticationFilter() {
|
||||
return new UsernamePasswordAuthenticationFilter() {
|
||||
@Override
|
||||
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
|
||||
return "POST".equals(request.getMethod()) && super.requiresAuthentication(request, response);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
|
||||
/**
|
||||
* Adds HTTP basic based authentication. All attributes have reasonable defaults
|
||||
* making all parameters are optional.
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following Filters are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>
|
||||
* {@link BasicAuthenticationFilter}
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* No shared objects are populated
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link HttpSecurity#getAuthenticationManager()} </li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends AbstractHttpConfigurer<B> {
|
||||
private static final String DEFAULT_REALM = "Spring Security Application";
|
||||
|
||||
private AuthenticationEntryPoint authenticationEntryPoint;
|
||||
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @throws Exception
|
||||
* @see {@link HttpSecurity#httpBasic()}
|
||||
*/
|
||||
public HttpBasicConfigurer() throws Exception {
|
||||
realmName(DEFAULT_REALM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for {@link #authenticationEntryPoint(AuthenticationEntryPoint)}
|
||||
* specifying a {@link BasicAuthenticationEntryPoint} with the specified
|
||||
* realm name.
|
||||
*
|
||||
* @param realmName
|
||||
* the HTTP Basic realm to use
|
||||
* @return {@link HttpBasicConfigurer} for additional customization
|
||||
* @throws Exception
|
||||
*/
|
||||
public HttpBasicConfigurer<B> realmName(String realmName) throws Exception {
|
||||
BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint();
|
||||
basicAuthEntryPoint.setRealmName(realmName);
|
||||
basicAuthEntryPoint.afterPropertiesSet();
|
||||
return authenticationEntryPoint(basicAuthEntryPoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link AuthenticationEntryPoint} to be po pulated on
|
||||
* {@link BasicAuthenticationFilter} in the event that authentication fails.
|
||||
* The default to use {@link BasicAuthenticationEntryPoint} with the realm
|
||||
* "Spring Security Application".
|
||||
*
|
||||
* @param authenticationEntryPoint the {@link AuthenticationEntryPoint} to use
|
||||
* @return {@link HttpBasicConfigurer} for additional customization
|
||||
*/
|
||||
public HttpBasicConfigurer<B> authenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
|
||||
this.authenticationEntryPoint = authenticationEntryPoint;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public HttpBasicConfigurer<B> authenticationDetailsSource(AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(B http) throws Exception {
|
||||
AuthenticationManager authenticationManager = http.getAuthenticationManager();
|
||||
BasicAuthenticationFilter basicAuthenticationFilter = new BasicAuthenticationFilter(authenticationManager, authenticationEntryPoint);
|
||||
if(authenticationDetailsSource != null) {
|
||||
basicAuthenticationFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
}
|
||||
basicAuthenticationFilter = postProcess(basicAuthenticationFilter);
|
||||
http.addFilter(basicAuthenticationFilter);
|
||||
}
|
||||
}
|
@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.core.authority.mapping.SimpleMappableAttributesRetriever;
|
||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesUserDetailsService;
|
||||
import org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource;
|
||||
import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter;
|
||||
|
||||
/**
|
||||
* Adds support for J2EE pre authentication.
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following Filters are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>
|
||||
* {@link J2eePreAuthenticatedProcessingFilter}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* <ul>
|
||||
* <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)}
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link HttpSecurity#getAuthenticationManager()}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class JeeConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> {
|
||||
private J2eePreAuthenticatedProcessingFilter j2eePreAuthenticatedProcessingFilter;
|
||||
private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService;
|
||||
private Set<String> mappableRoles = new HashSet<String>();
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @see HttpSecurity#jee()
|
||||
*/
|
||||
public JeeConfigurer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies roles to use map from the {@link HttpServletRequest} to the
|
||||
* {@link UserDetails}. If {@link HttpServletRequest#isUserInRole(String)}
|
||||
* returns true, the role is added to the {@link UserDetails}. This method
|
||||
* is the equivalent of invoking {@link #mappableAuthorities(Set)}. Multiple
|
||||
* invocations of {@link #mappableAuthorities(String...)} will override previous
|
||||
* invocations.
|
||||
*
|
||||
* <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
|
||||
* @see SimpleMappableAttributesRetriever
|
||||
* @see #mappableRoles(String...)
|
||||
*/
|
||||
public JeeConfigurer<H> mappableAuthorities(String... mappableRoles) {
|
||||
this.mappableRoles.clear();
|
||||
for(String role : mappableRoles) {
|
||||
this.mappableRoles.add(role);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies roles to use map from the {@link HttpServletRequest} to the
|
||||
* {@link UserDetails} and automatically prefixes it with "ROLE_". If
|
||||
* {@link HttpServletRequest#isUserInRole(String)} returns true, the role is
|
||||
* added to the {@link UserDetails}. This method is the equivalent of
|
||||
* invoking {@link #mappableAuthorities(Set)}. Multiple invocations of
|
||||
* {@link #mappableRoles(String...)} will override previous invocations.
|
||||
*
|
||||
* <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
|
||||
* @see SimpleMappableAttributesRetriever
|
||||
* @see #mappableAuthorities(String...)
|
||||
*/
|
||||
public JeeConfigurer<H> mappableRoles(String... mappableRoles) {
|
||||
this.mappableRoles.clear();
|
||||
for(String role : mappableRoles) {
|
||||
this.mappableRoles.add("ROLE_" + role);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies roles to use map from the {@link HttpServletRequest} to the
|
||||
* {@link UserDetails}. If {@link HttpServletRequest#isUserInRole(String)}
|
||||
* returns true, the role is added to the {@link UserDetails}. This is the
|
||||
* equivalent of {@link #mappableRoles(String...)}. Multiple invocations of
|
||||
* {@link #mappableAuthorities(Set)} will override previous invocations.
|
||||
*
|
||||
* <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
|
||||
*/
|
||||
public JeeConfigurer<H> mappableAuthorities(Set<String> mappableRoles) {
|
||||
this.mappableRoles = mappableRoles;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public JeeConfigurer<H> authenticatedUserDetailsService(
|
||||
AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticatedUserDetailsService) {
|
||||
this.authenticationUserDetailsService = authenticatedUserDetailsService;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows specifying the {@link J2eePreAuthenticatedProcessingFilter} to
|
||||
* use. If {@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
|
||||
*/
|
||||
public JeeConfigurer<H> j2eePreAuthenticatedProcessingFilter(
|
||||
J2eePreAuthenticatedProcessingFilter j2eePreAuthenticatedProcessingFilter) {
|
||||
this.j2eePreAuthenticatedProcessingFilter = j2eePreAuthenticatedProcessingFilter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates a {@link PreAuthenticatedAuthenticationProvider} into
|
||||
* {@link HttpSecurity#authenticationProvider(org.springframework.security.authentication.AuthenticationProvider)}
|
||||
* and a {@link Http403ForbiddenEntryPoint} into
|
||||
* {@link HttpSecurity#authenticationEntryPoint(org.springframework.security.web.AuthenticationEntryPoint)}
|
||||
*
|
||||
* @see org.springframework.security.config.annotation.SecurityConfigurerAdapter#init(org.springframework.security.config.annotation.SecurityBuilder)
|
||||
*/
|
||||
@Override
|
||||
public void init(H http) throws Exception {
|
||||
PreAuthenticatedAuthenticationProvider authenticationProvider = new PreAuthenticatedAuthenticationProvider();
|
||||
authenticationProvider.setPreAuthenticatedUserDetailsService(getUserDetailsService());
|
||||
authenticationProvider = postProcess(authenticationProvider);
|
||||
|
||||
http
|
||||
.authenticationProvider(authenticationProvider)
|
||||
.setSharedObject(AuthenticationEntryPoint.class,new Http403ForbiddenEntryPoint());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(H http) throws Exception {
|
||||
J2eePreAuthenticatedProcessingFilter filter = getFilter(http
|
||||
.getAuthenticationManager());
|
||||
http.addFilter(filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link J2eePreAuthenticatedProcessingFilter} or creates a default instance using the properties provided.
|
||||
* @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
|
||||
.setAuthenticationDetailsSource(createWebAuthenticationDetailsSource());
|
||||
j2eePreAuthenticatedProcessingFilter = postProcess(j2eePreAuthenticatedProcessingFilter);
|
||||
}
|
||||
|
||||
return 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
detailsSource.setMappableRolesRetriever(rolesRetriever);
|
||||
|
||||
detailsSource = postProcess(detailsSource);
|
||||
return detailsSource;
|
||||
}
|
||||
}
|
@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.List;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler;
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
||||
import org.springframework.security.web.authentication.logout.LogoutHandler;
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageViewFilter;
|
||||
|
||||
/**
|
||||
* Adds logout support. Other {@link SecurityConfigurer} instances may invoke
|
||||
* {@link #addLogoutHandler(LogoutHandler)} in the
|
||||
* {@link #init(HttpSecurity)} phase.
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following Filters are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>
|
||||
* {@link LogoutFilter}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* No shared Objects are created
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* No shared objects are used.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
* @see RememberMeConfigurer
|
||||
*/
|
||||
public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> {
|
||||
private List<LogoutHandler> logoutHandlers = new ArrayList<LogoutHandler>();
|
||||
private SecurityContextLogoutHandler contextLogoutHandler = new SecurityContextLogoutHandler();
|
||||
private String logoutSuccessUrl = "/login?logout";
|
||||
private LogoutSuccessHandler logoutSuccessHandler;
|
||||
private String logoutUrl = "/logout";
|
||||
private boolean permitAll;
|
||||
private boolean customLogoutSuccess;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @see HttpSecurity#logout()
|
||||
*/
|
||||
public LogoutConfigurer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link LogoutHandler}. The {@link SecurityContextLogoutHandler} is
|
||||
* added as the last {@link LogoutHandler} by default.
|
||||
*
|
||||
* @param logoutHandler the {@link LogoutHandler} to add
|
||||
* @return the {@link LogoutConfigurer} for further customization
|
||||
*/
|
||||
public LogoutConfigurer<H> addLogoutHandler(LogoutHandler logoutHandler) {
|
||||
this.logoutHandlers.add(logoutHandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures {@link SecurityContextLogoutHandler} to invalidate the {@link HttpSession} at the time of logout.
|
||||
* @param invalidateHttpSession true if the {@link HttpSession} should be invalidated (default), or false otherwise.
|
||||
* @return the {@link LogoutConfigurer} for further customization
|
||||
*/
|
||||
public LogoutConfigurer<H> invalidateHttpSession(boolean invalidateHttpSession) {
|
||||
contextLogoutHandler.setInvalidateHttpSession(invalidateHttpSession);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL that triggers logout to occur. The default is "/logout"
|
||||
* @param logoutUrl the URL that will invoke logout.
|
||||
* @return the {@link LogoutConfigurer} for further customization
|
||||
*/
|
||||
public LogoutConfigurer<H> logoutUrl(String logoutUrl) {
|
||||
this.logoutUrl = logoutUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public LogoutConfigurer<H> logoutSuccessUrl(String logoutSuccessUrl) {
|
||||
this.customLogoutSuccess = true;
|
||||
this.logoutSuccessUrl = logoutSuccessUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A shortcut for {@link #permitAll(boolean)} with <code>true</code> as an argument.
|
||||
* @return the {@link LogoutConfigurer} for further customizations
|
||||
*/
|
||||
public LogoutConfigurer<H> permitAll() {
|
||||
return permitAll(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public LogoutConfigurer<H> deleteCookies(String... cookieNamesToClear) {
|
||||
return addLogoutHandler(new CookieClearingLogoutHandler(cookieNamesToClear));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.logoutSuccessUrl = null;
|
||||
this.customLogoutSuccess = true;
|
||||
this.logoutSuccessHandler = logoutSuccessHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public LogoutConfigurer<H> permitAll(boolean permitAll) {
|
||||
this.permitAll = permitAll;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
if(logoutSuccessHandler != null) {
|
||||
return logoutSuccessHandler;
|
||||
}
|
||||
SimpleUrlLogoutSuccessHandler logoutSuccessHandler = new SimpleUrlLogoutSuccessHandler();
|
||||
logoutSuccessHandler.setDefaultTargetUrl(logoutSuccessUrl);
|
||||
return logoutSuccessHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(H http) throws Exception {
|
||||
if(permitAll) {
|
||||
PermitAllSupport.permitAll(http, this.logoutUrl, this.logoutSuccessUrl);
|
||||
}
|
||||
|
||||
DefaultLoginPageViewFilter loginPageGeneratingFilter = http.getSharedObject(DefaultLoginPageViewFilter.class);
|
||||
if(loginPageGeneratingFilter != null && !isCustomLogoutSuccess()) {
|
||||
loginPageGeneratingFilter.setLogoutSuccessUrl(getLogoutSuccessUrl());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(H http) throws Exception {
|
||||
LogoutFilter logoutFilter = createLogoutFilter();
|
||||
http.addFilter(logoutFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
private boolean isCustomLogoutSuccess() {
|
||||
return customLogoutSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the logoutSuccesUrl or null if a
|
||||
* {@link #logoutSuccessHandler(LogoutSuccessHandler)} was configured.
|
||||
*
|
||||
* @return the logoutSuccessUrl
|
||||
*/
|
||||
private String getLogoutSuccessUrl() {
|
||||
return logoutSuccessUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link LogoutFilter} using the {@link LogoutHandler}
|
||||
* instances, the {@link #logoutSuccessHandler(LogoutSuccessHandler)} and
|
||||
* the {@link #logoutUrl(String)}.
|
||||
*
|
||||
* @return the {@link LogoutFilter} to use.
|
||||
* @throws Exception
|
||||
*/
|
||||
private LogoutFilter createLogoutFilter() throws Exception {
|
||||
logoutHandlers.add(contextLogoutHandler);
|
||||
LogoutHandler[] handlers = logoutHandlers.toArray(new LogoutHandler[logoutHandlers.size()]);
|
||||
LogoutFilter result = new LogoutFilter(getLogoutSuccessHandler(), handlers);
|
||||
result.setFilterProcessesUrl(logoutUrl);
|
||||
result = postProcess(result);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.access.SecurityConfig;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractRequestMatcherMappingConfigurer.UrlMapping;
|
||||
import org.springframework.security.web.util.RequestMatcher;
|
||||
|
||||
|
||||
/**
|
||||
* Configures non-null URL's to grant access to every URL
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
final class PermitAllSupport {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void permitAll(HttpSecurityBuilder<? extends HttpSecurityBuilder<?>> http, String... urls) {
|
||||
ExpressionUrlAuthorizationConfigurer<?> configurer = http.getConfigurer(ExpressionUrlAuthorizationConfigurer.class);
|
||||
|
||||
if(configurer == null) {
|
||||
throw new IllegalStateException("permitAll only works with HttpSecurity.authorizeUrls()");
|
||||
}
|
||||
|
||||
for(String url : urls) {
|
||||
if(url != null) {
|
||||
configurer.addMapping(0, new UrlMapping(new ExactUrlRequestMatcher(url), SecurityConfig.createList(ExpressionUrlAuthorizationConfigurer.permitAll)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final static class ExactUrlRequestMatcher implements RequestMatcher {
|
||||
private String processUrl;
|
||||
|
||||
private ExactUrlRequestMatcher(String processUrl) {
|
||||
this.processUrl = processUrl;
|
||||
}
|
||||
|
||||
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(request.getContextPath() + processUrl);
|
||||
}
|
||||
}
|
||||
|
||||
private PermitAllSupport() {}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.PortMapper;
|
||||
import org.springframework.security.web.PortMapperImpl;
|
||||
|
||||
/**
|
||||
* Allows configuring a shared {@link PortMapper} instance used to determine the
|
||||
* ports when redirecting between HTTP and HTTPS. The {@link PortMapper} can be
|
||||
* obtained from {@link HttpSecurity#getSharedObject(Class)}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class PortMapperConfigurer<H extends HttpSecurityBuilder<H>> extends SecurityConfigurerAdapter<DefaultSecurityFilterChain,H> {
|
||||
private PortMapper portMapper;
|
||||
private Map<String, String> httpsPortMappings = new HashMap<String,String>();
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*/
|
||||
public PortMapperConfigurer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows specifying the {@link PortMapper} instance.
|
||||
* @param portMapper
|
||||
* @return
|
||||
*/
|
||||
public PortMapperConfigurer<H> portMapper(PortMapper portMapper) {
|
||||
this.portMapper = portMapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a port mapping
|
||||
* @param httpPort the HTTP port that maps to a specific HTTPS port.
|
||||
* @return {@link HttpPortMapping} to define the HTTPS port
|
||||
*/
|
||||
public HttpPortMapping http(int httpPort) {
|
||||
return new HttpPortMapping(httpPort);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(H http) throws Exception {
|
||||
http.setSharedObject(PortMapper.class, getPortMapper());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
PortMapperImpl portMapper = new PortMapperImpl();
|
||||
portMapper.setPortMappings(httpsPortMappings);
|
||||
this.portMapper = portMapper;
|
||||
}
|
||||
return portMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows specifying the HTTPS port for a given HTTP port when redirecting
|
||||
* between HTTP and HTTPS.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class HttpPortMapping {
|
||||
private final int httpPort;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param httpPort
|
||||
* @see PortMapperConfigurer#http(int)
|
||||
*/
|
||||
private HttpPortMapping(int httpPort) {
|
||||
this.httpPort = httpPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the given HTTP port to the provided HTTPS port and vice versa.
|
||||
* @param httpsPort the HTTPS port to map to
|
||||
* @return the {@link PortMapperConfigurer} for further customization
|
||||
*/
|
||||
public PortMapperConfigurer<H> mapsTo(int httpsPort) {
|
||||
httpsPortMappings.put(String.valueOf(httpPort), String.valueOf(httpsPort));
|
||||
return PortMapperConfigurer.this;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
|
||||
import org.springframework.security.authentication.RememberMeAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.RememberMeServices;
|
||||
import org.springframework.security.web.authentication.logout.LogoutHandler;
|
||||
import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;
|
||||
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
|
||||
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
|
||||
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageViewFilter;
|
||||
|
||||
/**
|
||||
* Configures Remember Me authentication. This typically involves the user
|
||||
* checking a box when they enter their username and password that states to
|
||||
* "Remember Me".
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following Filters are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>
|
||||
* {@link RememberMeAuthenticationFilter}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* The following shared objects are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>
|
||||
* {@link HttpSecurity#authenticationProvider(org.springframework.security.authentication.AuthenticationProvider)}
|
||||
* is populated with a {@link RememberMeAuthenticationProvider}</li>
|
||||
* <li>{@link RememberMeServices} is populated as a shared object and available on {@link HttpSecurity#getSharedObject(Class)}</li>
|
||||
* <li>{@link LogoutConfigurer#addLogoutHandler(LogoutHandler)} is used to add a logout handler to clean up the remember me authentication.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link HttpSecurity#getAuthenticationManager()}</li>
|
||||
* <li>{@link UserDetailsService} if no {@link #userDetailsService(UserDetailsService)} was specified.</li>
|
||||
* <li> {@link DefaultLoginPageViewFilter} - if present will be populated with information from the configuration</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> {
|
||||
private AuthenticationSuccessHandler authenticationSuccessHandler;
|
||||
private String key;
|
||||
private RememberMeServices rememberMeServices;
|
||||
private LogoutHandler logoutHandler;
|
||||
private String rememberMeParameter = "remember-me";
|
||||
private String rememberMeCookieName = "remember-me";
|
||||
private PersistentTokenRepository tokenRepository;
|
||||
private UserDetailsService userDetailsService;
|
||||
private Integer tokenValiditySeconds;
|
||||
private Boolean useSecureCookie;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*/
|
||||
public RememberMeConfigurer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows specifying how long (in seconds) a token is valid for
|
||||
*
|
||||
* @param tokenValiditySeconds
|
||||
* @return {@link RememberMeConfigurer} for further customization
|
||||
* @see AbstractRememberMeServices#setTokenValiditySeconds(int)
|
||||
*/
|
||||
public RememberMeConfigurer<H> tokenValiditySeconds(int tokenValiditySeconds) {
|
||||
this.tokenValiditySeconds = tokenValiditySeconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*Whether the cookie should be flagged as secure or not. Secure cookies can only be sent over an HTTPS connection
|
||||
* and thus cannot be accidentally submitted over HTTP where they could be intercepted.
|
||||
* <p>
|
||||
* 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
|
||||
* @see AbstractRememberMeServices#setUseSecureCookie(boolean)
|
||||
*/
|
||||
public RememberMeConfigurer<H> useSecureCookie(boolean useSecureCookie) {
|
||||
this.useSecureCookie = useSecureCookie;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the {@link UserDetailsService} used to look up the
|
||||
* {@link UserDetails} when a remember me token is valid. The default is to
|
||||
* use the {@link UserDetailsService} found by invoking
|
||||
* {@link HttpSecurity#getSharedObject(Class)} which is set when using
|
||||
* {@link WebSecurityConfigurerAdapter#registerAuthentication(org.springframework.security.config.annotation.authentication.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) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.tokenRepository = tokenRepository;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the key to identify tokens created for remember me authentication. Default is a secure randomly generated
|
||||
* key.
|
||||
*
|
||||
* @param key the key to identify tokens created for remember me authentication
|
||||
* @return the {@link RememberMeConfigurer} for further customization
|
||||
*/
|
||||
public RememberMeConfigurer<H> key(String key) {
|
||||
this.key = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows control over the destination a remembered user is sent to when they are successfully authenticated.
|
||||
* By default, the filter will just allow the current request to proceed, but if an
|
||||
* {@code AuthenticationSuccessHandler} is set, it will 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
|
||||
* @see RememberMeAuthenticationFilter#setAuthenticationSuccessHandler(AuthenticationSuccessHandler)
|
||||
*/
|
||||
public RememberMeConfigurer<H> authenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {
|
||||
this.authenticationSuccessHandler = authenticationSuccessHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the {@link RememberMeServices} to use.
|
||||
* @param rememberMeServices the {@link RememberMeServices} to use
|
||||
* @return the {@link RememberMeConfigurer} for further customizations
|
||||
* @see RememberMeServices
|
||||
*/
|
||||
public RememberMeConfigurer<H> rememberMeServices(RememberMeServices rememberMeServices) {
|
||||
this.rememberMeServices = rememberMeServices;
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void init(H http) throws Exception {
|
||||
String key = getKey();
|
||||
RememberMeServices rememberMeServices = getRememberMeServices(http, key);
|
||||
http.setSharedObject(RememberMeServices.class, rememberMeServices);
|
||||
LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
|
||||
if(logoutConfigurer != null) {
|
||||
logoutConfigurer.addLogoutHandler(logoutHandler);
|
||||
}
|
||||
|
||||
RememberMeAuthenticationProvider authenticationProvider = new RememberMeAuthenticationProvider(
|
||||
key);
|
||||
authenticationProvider = postProcess(authenticationProvider);
|
||||
http.authenticationProvider(authenticationProvider);
|
||||
|
||||
initDefaultLoginFilter(http);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(H http) throws Exception {
|
||||
RememberMeAuthenticationFilter rememberMeFilter = new RememberMeAuthenticationFilter(
|
||||
http.getAuthenticationManager(), rememberMeServices);
|
||||
if (authenticationSuccessHandler != null) {
|
||||
rememberMeFilter
|
||||
.setAuthenticationSuccessHandler(authenticationSuccessHandler);
|
||||
}
|
||||
rememberMeFilter = postProcess(rememberMeFilter);
|
||||
http.addFilter(rememberMeFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP parameter used to indicate to remember the user at time of login.
|
||||
* @return the HTTP parameter used to indicate to remember the user
|
||||
*/
|
||||
private String getRememberMeParameter() {
|
||||
return rememberMeParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* If available, initializes the {@link DefaultLoginPageViewFilter} shared object.
|
||||
*
|
||||
* @param http the {@link HttpSecurityBuilder} to use
|
||||
*/
|
||||
private void initDefaultLoginFilter(H http) {
|
||||
DefaultLoginPageViewFilter loginPageGeneratingFilter = http.getSharedObject(DefaultLoginPageViewFilter.class);
|
||||
if(loginPageGeneratingFilter != null) {
|
||||
loginPageGeneratingFilter.setRememberMeParameter(getRememberMeParameter());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link RememberMeServices} or creates the {@link RememberMeServices}.
|
||||
* @param http the {@link HttpSecurity} to lookup shared objects
|
||||
* @param key the {@link #key(String)}
|
||||
* @return the {@link RememberMeServices} to use
|
||||
* @throws Exception
|
||||
*/
|
||||
private RememberMeServices getRememberMeServices(H http,
|
||||
String key) throws Exception {
|
||||
if (rememberMeServices != null) {
|
||||
if (rememberMeServices instanceof LogoutHandler
|
||||
&& logoutHandler == null) {
|
||||
this.logoutHandler = (LogoutHandler) rememberMeServices;
|
||||
}
|
||||
return rememberMeServices;
|
||||
}
|
||||
AbstractRememberMeServices tokenRememberMeServices = createRememberMeServices(
|
||||
http, key);
|
||||
tokenRememberMeServices.setParameter(rememberMeParameter);
|
||||
tokenRememberMeServices.setCookieName(rememberMeCookieName);
|
||||
if (tokenValiditySeconds != null) {
|
||||
tokenRememberMeServices
|
||||
.setTokenValiditySeconds(tokenValiditySeconds);
|
||||
}
|
||||
if (useSecureCookie != null) {
|
||||
tokenRememberMeServices.setUseSecureCookie(useSecureCookie);
|
||||
}
|
||||
tokenRememberMeServices.afterPropertiesSet();
|
||||
logoutHandler = tokenRememberMeServices;
|
||||
rememberMeServices = tokenRememberMeServices;
|
||||
return tokenRememberMeServices;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws Exception
|
||||
*/
|
||||
private AbstractRememberMeServices createRememberMeServices(
|
||||
H http, String key) throws Exception {
|
||||
return tokenRepository == null ? createTokenBasedRememberMeServices(
|
||||
http, key) : createPersistentRememberMeServices(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) {
|
||||
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) {
|
||||
UserDetailsService userDetailsService = getUserDetailsService(http);
|
||||
return new PersistentTokenBasedRememberMeServices(key,
|
||||
userDetailsService, 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
|
||||
*/
|
||||
private UserDetailsService getUserDetailsService(H http) {
|
||||
if(userDetailsService == null) {
|
||||
userDetailsService = http.getSharedObject(UserDetailsService.class);
|
||||
}
|
||||
if(userDetailsService == null) {
|
||||
throw new IllegalStateException("userDetailsService cannot be null. Invoke "
|
||||
+ RememberMeConfigurer.class.getSimpleName() + "#userDetailsService(UserDetailsService) or see its javadoc for alternative approaches.");
|
||||
}
|
||||
return userDetailsService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the key to use for validating remember me tokens. Either the value
|
||||
* passed into {@link #key(String)}, or a secure random string if none was
|
||||
* specified.
|
||||
*
|
||||
* @return the remember me key to use
|
||||
*/
|
||||
private String getKey() {
|
||||
if (key == null) {
|
||||
key = UUID.randomUUID().toString();
|
||||
}
|
||||
return key;
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
|
||||
|
||||
/**
|
||||
* Adds request cache for Spring Security. Specifically this ensures that
|
||||
* requests that are saved (i.e. after authentication is required) are later
|
||||
* replayed. All properties have reasonable defaults, so no additional
|
||||
* configuration is required other than applying this
|
||||
* {@link org.springframework.security.config.annotation.SecurityConfigurer}.
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following Filters are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link RequestCacheAwareFilter}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* No shared objects are created.
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>If no explicit {@link RequestCache}, is provided a {@link RequestCache}
|
||||
* shared object is used to replay the request after authentication is
|
||||
* successful</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
* @see RequestCache
|
||||
*/
|
||||
public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> {
|
||||
|
||||
public RequestCacheConfigurer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public RequestCacheConfigurer<H> requestCache(RequestCache requestCache) {
|
||||
getBuilder().setSharedObject(RequestCache.class, requestCache);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(H http) throws Exception {
|
||||
RequestCache requestCache = getRequestCache(http);
|
||||
RequestCacheAwareFilter requestCacheFilter = new RequestCacheAwareFilter(requestCache);
|
||||
requestCacheFilter = postProcess(requestCacheFilter);
|
||||
http.addFilter(requestCacheFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link RequestCache} to use. If one is defined using
|
||||
* {@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
|
||||
*/
|
||||
private RequestCache getRequestCache(H http) {
|
||||
RequestCache result = http.getSharedObject(RequestCache.class);
|
||||
if(result != null) {
|
||||
return result;
|
||||
}
|
||||
return new HttpSessionRequestCache();
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
|
||||
import org.springframework.security.web.context.SecurityContextRepository;
|
||||
|
||||
/**
|
||||
* Allows persisting and restoring of the {@link SecurityContext} found on the
|
||||
* {@link SecurityContextHolder} for each request by configuring the
|
||||
* {@link SecurityContextPersistenceFilter}. All properties have reasonable
|
||||
* defaults, so no additional configuration is required other than applying this
|
||||
* {@link org.springframework.security.config.annotation.SecurityConfigurer}.
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following Filters are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link SecurityContextPersistenceFilter}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* No shared objects are created.
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>If {@link SessionManagementConfigurer}, is provided and set to always,
|
||||
* then the
|
||||
* {@link SecurityContextPersistenceFilter#setForceEagerSessionCreation(boolean)}
|
||||
* will be set to true.</li>
|
||||
* <li>{@link SecurityContextRepository} must be set and is used on
|
||||
* {@link SecurityContextPersistenceFilter}.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> {
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @see HttpSecurity#securityContext()
|
||||
*/
|
||||
public SecurityContextConfigurer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the shared {@link SecurityContextRepository} that is to be used
|
||||
* @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);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void configure(H http) throws Exception {
|
||||
|
||||
SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
|
||||
SecurityContextPersistenceFilter securityContextFilter = new SecurityContextPersistenceFilter(
|
||||
securityContextRepository);
|
||||
SessionManagementConfigurer<?> sessionManagement = http.getConfigurer(SessionManagementConfigurer.class);
|
||||
SessionCreationPolicy sessionCreationPolicy = sessionManagement == null ? null
|
||||
: sessionManagement.getSessionCreationPolicy();
|
||||
if (SessionCreationPolicy.always == sessionCreationPolicy) {
|
||||
securityContextFilter.setForceEagerSessionCreation(true);
|
||||
}
|
||||
securityContextFilter = postProcess(securityContextFilter);
|
||||
http.addFilter(securityContextFilter);
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
||||
|
||||
/**
|
||||
* Implements select methods from the {@link HttpServletRequest} using the {@link SecurityContext} from the {@link SecurityContextHolder}.
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following Filters are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link SecurityContextHolderAwareRequestFilter}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* No shared objects are created.
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* No shared Objects are used.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class ServletApiConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> {
|
||||
private SecurityContextHolderAwareRequestFilter securityContextRequestFilter = new SecurityContextHolderAwareRequestFilter();
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @see HttpSecurity#servletApi()
|
||||
*/
|
||||
public ServletApiConfigurer() {
|
||||
}
|
||||
|
||||
public ServletApiConfigurer<H> rolePrefix(String rolePrefix) {
|
||||
securityContextRequestFilter.setRolePrefix(rolePrefix);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public H disable() {
|
||||
getBuilder().removeConfigurer(getClass());
|
||||
return getBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(H builder)
|
||||
throws Exception {
|
||||
securityContextRequestFilter = postProcess(securityContextRequestFilter);
|
||||
builder.addFilter(securityContextRequestFilter);
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.HttpSession;
|
||||
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
|
||||
/**
|
||||
* Specifies the various session creation policies for Spring Security.
|
||||
*
|
||||
* FIXME this should be removed once {@link org.springframework.security.config.http.SessionCreationPolicy} is made public.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public enum SessionCreationPolicy {
|
||||
/** Always create an {@link HttpSession} */
|
||||
always,
|
||||
/** Spring Security will never create an {@link HttpSession}, but will use the {@link HttpSession} if it already exists */
|
||||
never,
|
||||
/** Spring Security will only create an {@link HttpSession} if required */
|
||||
ifRequired,
|
||||
/** Spring Security will never create an {@link HttpSession} and it will never use it to obtain the {@link SecurityContext} */
|
||||
stateless
|
||||
}
|
@ -0,0 +1,331 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.core.session.SessionRegistry;
|
||||
import org.springframework.security.core.session.SessionRegistryImpl;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy;
|
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.web.context.NullSecurityContextRepository;
|
||||
import org.springframework.security.web.context.SecurityContextRepository;
|
||||
import org.springframework.security.web.savedrequest.NullRequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.security.web.session.ConcurrentSessionFilter;
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Allows configuring session management.
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following Filters are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link SessionManagementFilter}</li>
|
||||
* <li>{@link ConcurrentSessionFilter} if there are restrictions on how many concurrent sessions a user can have</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* The following shared objects are created:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link RequestCache}</li>
|
||||
* <li>{@link SecurityContextRepository}</li>
|
||||
* <li>{@link SessionManagementConfigurer}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link SecurityContextRepository}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
* @see SessionManagementFilter
|
||||
* @see ConcurrentSessionFilter
|
||||
*/
|
||||
public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> {
|
||||
private SessionAuthenticationStrategy sessionAuthenticationStrategy = new SessionFixationProtectionStrategy();
|
||||
private SessionRegistry sessionRegistry = new SessionRegistryImpl();
|
||||
private Integer maximumSessions;
|
||||
private String expiredUrl;
|
||||
private boolean maxSessionsPreventsLogin;
|
||||
private SessionCreationPolicy sessionPolicy = SessionCreationPolicy.ifRequired;
|
||||
private boolean enableSessionUrlRewriting;
|
||||
private String invalidSessionUrl;
|
||||
private String sessionAuthenticationErrorUrl;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @see HttpSecurity#sessionManagement()
|
||||
*/
|
||||
public SessionManagementConfigurer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Setting this attribute will inject the {@link SessionManagementFilter} with a
|
||||
* {@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
|
||||
*/
|
||||
public SessionManagementConfigurer<H> invalidSessionUrl(String invalidSessionUrl) {
|
||||
this.invalidSessionUrl = invalidSessionUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the URL of the error page which should be shown when the
|
||||
* SessionAuthenticationStrategy raises an exception. If not set, an
|
||||
* unauthorized (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) {
|
||||
this.sessionAuthenticationErrorUrl = sessionAuthenticationErrorUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to true, allows HTTP sessions to be rewritten in the URLs when
|
||||
* using {@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) {
|
||||
this.enableSessionUrlRewriting = enableSessionUrlRewriting;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows specifying the {@link SessionCreationPolicy}
|
||||
* @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.
|
||||
*/
|
||||
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 SessionFixationProtectionStrategy}. If
|
||||
* restricting the maximum number of sessions is configured,
|
||||
* {@link ConcurrentSessionControlStrategy} will be used.
|
||||
*
|
||||
* @param sessionAuthenticationStrategy
|
||||
* @return the {@link SessionManagementConfigurer} for further customizations
|
||||
*/
|
||||
public SessionManagementConfigurer<H> sessionAuthenticationStrategy(SessionAuthenticationStrategy sessionAuthenticationStrategy) {
|
||||
this.sessionAuthenticationStrategy = sessionAuthenticationStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 SessionManagementConfigurer} for further customizations
|
||||
*/
|
||||
public ConcurrencyControlConfigurer maximumSessions(int maximumSessions) {
|
||||
this.maximumSessions = maximumSessions;
|
||||
this.sessionAuthenticationStrategy = null;
|
||||
return new ConcurrencyControlConfigurer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuring controlling of multiple sessions.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public final class ConcurrencyControlConfigurer {
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(H builder) throws Exception {
|
||||
SecurityContextRepository securityContextRepository = builder.getSharedObject(SecurityContextRepository.class);
|
||||
boolean stateless = isStateless();
|
||||
|
||||
if(securityContextRepository == null) {
|
||||
if(stateless) {
|
||||
builder.setSharedObject(SecurityContextRepository.class, new NullSecurityContextRepository());
|
||||
} else {
|
||||
HttpSessionSecurityContextRepository httpSecurityRepository = new HttpSessionSecurityContextRepository();
|
||||
httpSecurityRepository.setDisableUrlRewriting(!enableSessionUrlRewriting);
|
||||
httpSecurityRepository.setAllowSessionCreation(isAllowSessionCreation());
|
||||
builder.setSharedObject(SecurityContextRepository.class, httpSecurityRepository);
|
||||
}
|
||||
}
|
||||
|
||||
RequestCache requestCache = builder.getSharedObject(RequestCache.class);
|
||||
if(requestCache == null) {
|
||||
if(stateless) {
|
||||
builder.setSharedObject(RequestCache.class, new NullRequestCache());
|
||||
}
|
||||
}
|
||||
builder.setSharedObject(SessionAuthenticationStrategy.class, getSessionAuthenticationStrategy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(H http) throws Exception {
|
||||
SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
|
||||
SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(securityContextRepository, getSessionAuthenticationStrategy());
|
||||
if(sessionAuthenticationErrorUrl != null) {
|
||||
sessionManagementFilter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler(sessionAuthenticationErrorUrl));
|
||||
}
|
||||
if(invalidSessionUrl != null) {
|
||||
sessionManagementFilter.setInvalidSessionStrategy(new SimpleRedirectInvalidSessionStrategy(invalidSessionUrl));
|
||||
}
|
||||
sessionManagementFilter = postProcess(sessionManagementFilter);
|
||||
|
||||
http.addFilter(sessionManagementFilter);
|
||||
if(isConcurrentSessionControlEnabled()) {
|
||||
ConcurrentSessionFilter concurrentSessionFilter = new ConcurrentSessionFilter(sessionRegistry, expiredUrl);
|
||||
concurrentSessionFilter = postProcess(concurrentSessionFilter);
|
||||
http.addFilter(concurrentSessionFilter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link SessionCreationPolicy}. Can not be null.
|
||||
* @return the {@link SessionCreationPolicy}
|
||||
*/
|
||||
SessionCreationPolicy getSessionCreationPolicy() {
|
||||
return sessionPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the {@link SessionCreationPolicy} allows session creation, else false
|
||||
* @return true if the {@link SessionCreationPolicy} allows session creation
|
||||
*/
|
||||
private boolean isAllowSessionCreation() {
|
||||
return SessionCreationPolicy.always == sessionPolicy || SessionCreationPolicy.ifRequired == sessionPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the {@link SessionCreationPolicy} is stateless
|
||||
* @return
|
||||
*/
|
||||
private boolean isStateless() {
|
||||
return SessionCreationPolicy.stateless == sessionPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
if(sessionAuthenticationStrategy != null) {
|
||||
return sessionAuthenticationStrategy;
|
||||
}
|
||||
if(isConcurrentSessionControlEnabled()) {
|
||||
ConcurrentSessionControlStrategy concurrentSessionControlStrategy = new ConcurrentSessionControlStrategy(sessionRegistry);
|
||||
concurrentSessionControlStrategy.setMaximumSessions(maximumSessions);
|
||||
concurrentSessionControlStrategy.setExceptionIfMaximumExceeded(maxSessionsPreventsLogin);
|
||||
sessionAuthenticationStrategy = concurrentSessionControlStrategy;
|
||||
}
|
||||
return sessionAuthenticationStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the number of concurrent sessions per user should be restricted.
|
||||
* @return
|
||||
*/
|
||||
private boolean isConcurrentSessionControlEnabled() {
|
||||
return maximumSessions != null;
|
||||
}
|
||||
}
|
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.access.AccessDecisionManager;
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.access.vote.AuthenticatedVoter;
|
||||
import org.springframework.security.access.vote.RoleVoter;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
|
||||
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
|
||||
import org.springframework.security.web.util.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Adds URL based authorization using {@link DefaultFilterInvocationSecurityMetadataSource}. 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>
|
||||
*
|
||||
* The following Filters are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link org.springframework.security.web.access.intercept.FilterSecurityInterceptor}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* The following shared objects are populated to allow other {@link org.springframework.security.config.annotation.SecurityConfigurer}'s to customize:
|
||||
* <ul>
|
||||
* <li>{@link org.springframework.security.web.access.intercept.FilterSecurityInterceptor}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link org.springframework.security.config.annotation.web.builders.HttpSecurity#getAuthenticationManager()}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
|
||||
* @param <C> the type of object that is being chained
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
* @see ExpressionUrlAuthorizationConfigurer
|
||||
*/
|
||||
public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>, C> extends AbstractInterceptUrlConfigurer<H,C,UrlAuthorizationConfigurer<H,C>.AuthorizedUrl> {
|
||||
|
||||
/**
|
||||
* Creates the default {@link AccessDecisionVoter} instances used if an
|
||||
* {@link AccessDecisionManager} was not specified using
|
||||
* {@link #accessDecisionManager(AccessDecisionManager)}.
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
final List<AccessDecisionVoter> getDecisionVoters() {
|
||||
List<AccessDecisionVoter> decisionVoters = new ArrayList<AccessDecisionVoter>();
|
||||
decisionVoters.add(new RoleVoter());
|
||||
decisionVoters.add(new AuthenticatedVoter());
|
||||
return decisionVoters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link FilterInvocationSecurityMetadataSource} to use. The
|
||||
* implementation is a {@link DefaultFilterInvocationSecurityMetadataSource}
|
||||
* .
|
||||
*/
|
||||
@Override
|
||||
FilterInvocationSecurityMetadataSource createMetadataSource() {
|
||||
return new DefaultFilterInvocationSecurityMetadataSource(createRequestMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Chains the {@link RequestMatcher} creation to the {@link AuthorizedUrl} class.
|
||||
*/
|
||||
@Override
|
||||
protected AuthorizedUrl chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
|
||||
return new AuthorizedUrl(requestMatchers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a mapping of the {@link RequestMatcher} instances to the {@link ConfigAttribute} instances.
|
||||
* @param requestMatchers the {@link RequestMatcher} instances that should map to the provided {@link ConfigAttribute} instances
|
||||
* @param configAttributes the {@link ConfigAttribute} instances that should be mapped by the {@link RequestMatcher} instances
|
||||
* @return the {@link UrlAuthorizationConfigurer} for further customizations
|
||||
*/
|
||||
private UrlAuthorizationConfigurer<H,C> addMapping(Iterable<? extends RequestMatcher> requestMatchers, Collection<ConfigAttribute> configAttributes) {
|
||||
for(RequestMatcher requestMatcher : requestMatchers) {
|
||||
addMapping(new UrlMapping(requestMatcher, configAttributes));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.");
|
||||
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.
|
||||
* @return the {@link ConfigAttribute} expressed as a String
|
||||
*/
|
||||
private static String[] hasAnyRole(String... roles) {
|
||||
for(int i=0;i<roles.length;i++) {
|
||||
roles[i] = "ROLE_" + roles[i];
|
||||
}
|
||||
return roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a String for specifying that a user requires one of many authorities
|
||||
* @param authorities the authorities that the user should have at least one of (i.e. ROLE_USER, ROLE_ADMIN, etc).
|
||||
* @return the {@link ConfigAttribute} expressed as a String.
|
||||
*/
|
||||
private static String[] hasAnyAuthority(String... authorities) {
|
||||
return authorities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the specified {@link RequestMatcher} instances to {@link ConfigAttribute} instances.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class AuthorizedUrl {
|
||||
private final List<RequestMatcher> requestMatchers;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param requestMatchers the {@link RequestMatcher} instances to map to some {@link ConfigAttribute} instances.
|
||||
* @see UrlAuthorizationConfigurer#chainRequestMatchers(List)
|
||||
*/
|
||||
private AuthorizedUrl(List<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
|
||||
*/
|
||||
public UrlAuthorizationConfigurer<H,C> hasRole(String role) {
|
||||
return access(UrlAuthorizationConfigurer.hasRole(role));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return the {@link UrlAuthorizationConfigurer} for further customization
|
||||
*/
|
||||
public UrlAuthorizationConfigurer<H,C> hasAnyRole(String... roles) {
|
||||
return access(UrlAuthorizationConfigurer.hasAnyRole(roles));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a user requires an authority.
|
||||
*
|
||||
* @param authority
|
||||
* the authority that should be required
|
||||
* @return the {@link UrlAuthorizationConfigurer} for further customization
|
||||
*/
|
||||
public UrlAuthorizationConfigurer<H,C> hasAuthority(String authority) {
|
||||
return access(authority);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that a user requires one of many authorities
|
||||
* @param authorities the authorities that the user should have at least one of (i.e. ROLE_USER, ROLE_ADMIN, etc).
|
||||
* @return the {@link UrlAuthorizationConfigurer} for further customization
|
||||
*/
|
||||
public UrlAuthorizationConfigurer<H,C> hasAnyAuthority(String... authorities) {
|
||||
return access(UrlAuthorizationConfigurer.hasAnyAuthority(authorities));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that an anonymous user is allowed access
|
||||
* @return the {@link UrlAuthorizationConfigurer} for further customization
|
||||
*/
|
||||
public UrlAuthorizationConfigurer<H,C> anonymous() {
|
||||
return hasRole("ROLE_ANONYMOUS");
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that the user must have the specified {@link ConfigAttribute}'s
|
||||
* @param attributes the {@link ConfigAttribute}'s that restrict access to a URL
|
||||
* @return the {@link UrlAuthorizationConfigurer} for further customization
|
||||
*/
|
||||
public UrlAuthorizationConfigurer<H,C> access(String... attributes) {
|
||||
addMapping(requestMatchers, SecurityConfig.createList(attributes));
|
||||
return UrlAuthorizationConfigurer.this;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails;
|
||||
import org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor;
|
||||
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
|
||||
|
||||
/**
|
||||
* Adds X509 based pre authentication to an application. Since validating the
|
||||
* certificate happens when the client connects, the requesting and validation
|
||||
* of the client certificate should be performed by the container. Spring Security
|
||||
* will then use the certificate to look up the {@link Authentication} for the user.
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following Filters are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link X509AuthenticationFilter}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* The following shared objects are created
|
||||
*
|
||||
* <ul>
|
||||
* <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)}
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>A {@link UserDetailsService} shared object is used if no {@link AuthenticationUserDetailsService} is specified</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class X509Configurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<H> {
|
||||
private X509AuthenticationFilter x509AuthenticationFilter;
|
||||
private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService;
|
||||
private String subjectPrincipalRegex;
|
||||
private AuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails> authenticationDetailsSource;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @see HttpSecurity#x509()
|
||||
*/
|
||||
public X509Configurer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.x509AuthenticationFilter = x509AuthenticationFilter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the {@link AuthenticationDetailsSource}
|
||||
*
|
||||
* @param authenticationDetailsSource the {@link AuthenticationDetailsSource} to use
|
||||
* @return the {@link X509Configurer} to use
|
||||
*/
|
||||
public X509Configurer<H> authenticationDetailsSource(AuthenticationDetailsSource<HttpServletRequest, PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails> authenticationDetailsSource) {
|
||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for invoking {@link #authenticationUserDetailsService(AuthenticationUserDetailsService)} with a {@link UserDetailsByNameServiceWrapper}.
|
||||
*
|
||||
* @param userDetailsService the {@link UserDetailsService} to use
|
||||
* @return the {@link X509Configurer} for further customizations
|
||||
*/
|
||||
public X509Configurer<H> userDetailsService(
|
||||
UserDetailsService userDetailsService) {
|
||||
UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService = new UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken>();
|
||||
authenticationUserDetailsService.setUserDetailsService(userDetailsService);
|
||||
return authenticationUserDetailsService(authenticationUserDetailsService);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return the {@link X509Configurer} for further customizations
|
||||
*/
|
||||
public X509Configurer<H> authenticationUserDetailsService(
|
||||
AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService) {
|
||||
this.authenticationUserDetailsService = authenticationUserDetailsService;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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=(.*?)(?:,|$)").
|
||||
* @return the {@link X509Configurer} for further customizations
|
||||
*/
|
||||
public X509Configurer<H> subjectPrincipalRegex(String subjectPrincipalRegex) {
|
||||
this.subjectPrincipalRegex = subjectPrincipalRegex;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(H http) throws Exception {
|
||||
PreAuthenticatedAuthenticationProvider authenticationProvider = new PreAuthenticatedAuthenticationProvider();
|
||||
authenticationProvider.setPreAuthenticatedUserDetailsService(getAuthenticationUserDetailsService(http));
|
||||
|
||||
http
|
||||
.authenticationProvider(authenticationProvider)
|
||||
.setSharedObject(AuthenticationEntryPoint.class,new Http403ForbiddenEntryPoint());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(H http) throws Exception {
|
||||
X509AuthenticationFilter filter = getFilter(http.getAuthenticationManager());
|
||||
http.addFilter(filter);
|
||||
}
|
||||
|
||||
private X509AuthenticationFilter getFilter(
|
||||
AuthenticationManager authenticationManager) {
|
||||
if (x509AuthenticationFilter == null) {
|
||||
x509AuthenticationFilter = new X509AuthenticationFilter();
|
||||
x509AuthenticationFilter.setAuthenticationManager(authenticationManager);
|
||||
if(subjectPrincipalRegex != null) {
|
||||
SubjectDnX509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
|
||||
principalExtractor.setSubjectDnRegex(subjectPrincipalRegex);
|
||||
x509AuthenticationFilter.setPrincipalExtractor(principalExtractor);
|
||||
}
|
||||
if(authenticationDetailsSource != null) {
|
||||
x509AuthenticationFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||
}
|
||||
x509AuthenticationFilter = postProcess(x509AuthenticationFilter);
|
||||
}
|
||||
|
||||
return x509AuthenticationFilter;
|
||||
}
|
||||
|
||||
private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> getAuthenticationUserDetailsService(H http) {
|
||||
if(authenticationUserDetailsService == null) {
|
||||
userDetailsService(http.getSharedObject(UserDetailsService.class));
|
||||
}
|
||||
return authenticationUserDetailsService;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,463 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers.openid;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.openid4java.consumer.ConsumerException;
|
||||
import org.openid4java.consumer.ConsumerManager;
|
||||
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.openid.OpenIDLoginConfigurer;
|
||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.openid.AxFetchListFactory;
|
||||
import org.springframework.security.openid.OpenID4JavaConsumer;
|
||||
import org.springframework.security.openid.OpenIDAttribute;
|
||||
import org.springframework.security.openid.OpenIDAuthenticationFilter;
|
||||
import org.springframework.security.openid.OpenIDAuthenticationProvider;
|
||||
import org.springframework.security.openid.OpenIDAuthenticationToken;
|
||||
import org.springframework.security.openid.OpenIDConsumer;
|
||||
import org.springframework.security.openid.RegexBasedAxFetchListFactory;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.RememberMeServices;
|
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageViewFilter;
|
||||
|
||||
/**
|
||||
* Adds support for OpenID based authentication.
|
||||
*
|
||||
* <h2>Example Configuration</h2>
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* @Configuration
|
||||
* @EnableWebSecurity
|
||||
* public class OpenIDLoginConfig extends WebSecurityConfigurerAdapter {
|
||||
*
|
||||
* @Override
|
||||
* protected void configure(HttpSecurity http) {
|
||||
* http
|
||||
* .authorizeUrls()
|
||||
* .antMatchers("/**").hasRole("USER")
|
||||
* .and()
|
||||
* .openidLogin()
|
||||
* .permitAll();
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* protected void registerAuthentication(
|
||||
* AuthenticationManagerBuilder auth) throws Exception {
|
||||
* auth
|
||||
* .inMemoryAuthentication()
|
||||
* .withUser("https://www.google.com/accounts/o8/id?id=lmkCn9xzPdsxVwG7pjYMuDgNNdASFmobNkcRPaWU")
|
||||
* .password("password")
|
||||
* .roles("USER");
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following Filters are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>
|
||||
* {@link OpenIDAuthenticationFilter}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* <ul>
|
||||
* <li>
|
||||
* {@link AuthenticationEntryPoint}
|
||||
* is populated with a {@link LoginUrlAuthenticationEntryPoint}</li>
|
||||
* <li>A {@link OpenIDAuthenticationProvider} is populated into
|
||||
* {@link HttpSecurity#authenticationProvider(org.springframework.security.authentication.AuthenticationProvider)}
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link HttpSecurity#getAuthenticationManager()}</li>
|
||||
* <li>{@link RememberMeServices} - is optionally used. See
|
||||
* {@link RememberMeConfigurer}</li>
|
||||
* <li>{@link SessionAuthenticationStrategy} - is optionally used. See
|
||||
* {@link SessionManagementConfigurer}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class OpenIDLoginConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractAuthenticationFilterConfigurer<H,OpenIDLoginConfigurer<H>,OpenIDAuthenticationFilter> {
|
||||
private OpenIDConsumer openIDConsumer;
|
||||
private ConsumerManager consumerManager;
|
||||
private AuthenticationUserDetailsService<OpenIDAuthenticationToken> authenticationUserDetailsService;
|
||||
private List<AttributeExchangeConfigurer> attributeExchangeConfigurers = new ArrayList<AttributeExchangeConfigurer>();
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*/
|
||||
public OpenIDLoginConfigurer() {
|
||||
super(new OpenIDAuthenticationFilter(),"/login/openid");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up OpenID attribute exchange for OpenID's matching the specified
|
||||
* pattern.
|
||||
*
|
||||
* @param identifierPattern
|
||||
* the regular expression for matching on OpenID's (i.e.
|
||||
* "https://www.google.com/.*", ".*yahoo.com.*", etc)
|
||||
* @return a {@link AttributeExchangeConfigurer} for further customizations of the attribute exchange
|
||||
*/
|
||||
public AttributeExchangeConfigurer attributeExchange(String identifierPattern) {
|
||||
AttributeExchangeConfigurer attributeExchangeConfigurer = new AttributeExchangeConfigurer(identifierPattern);
|
||||
this.attributeExchangeConfigurers .add(attributeExchangeConfigurer);
|
||||
return attributeExchangeConfigurer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows specifying the {@link OpenIDConsumer} to be used. The default is
|
||||
* using an {@link OpenID4JavaConsumer}.
|
||||
*
|
||||
* @param consumer
|
||||
* the {@link OpenIDConsumer} to be used
|
||||
* @return the {@link OpenIDLoginConfigurer} for further customizations
|
||||
*/
|
||||
public OpenIDLoginConfigurer<H> consumer(OpenIDConsumer consumer) {
|
||||
this.openIDConsumer = consumer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows specifying the {@link ConsumerManager} to be used. If specified,
|
||||
* will be populated into an {@link OpenID4JavaConsumer}.
|
||||
*
|
||||
* <p>
|
||||
* This is a shortcut for specifying the {@link OpenID4JavaConsumer} with a
|
||||
* specific {@link ConsumerManager} on {@link #consumer(OpenIDConsumer)}.
|
||||
* </p>
|
||||
*
|
||||
* @param consumerManager the {@link ConsumerManager} to use. Cannot be null.
|
||||
* @return the {@link OpenIDLoginConfigurer} for further customizations
|
||||
*/
|
||||
public OpenIDLoginConfigurer<H> consumerManager(ConsumerManager consumerManager) {
|
||||
this.consumerManager = consumerManager;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link AuthenticationUserDetailsService} to use. By default a
|
||||
* {@link UserDetailsByNameServiceWrapper} is used with the
|
||||
* {@link UserDetailsService} shared object found with
|
||||
* {@link HttpSecurity#getSharedObject(Class)}.
|
||||
*
|
||||
* @param authenticationUserDetailsService the {@link AuthenticationDetailsSource} to use
|
||||
* @return the {@link OpenIDLoginConfigurer} for further customizations
|
||||
*/
|
||||
public OpenIDLoginConfigurer<H> authenticationUserDetailsService(AuthenticationUserDetailsService<OpenIDAuthenticationToken> authenticationUserDetailsService) {
|
||||
this.authenticationUserDetailsService = authenticationUserDetailsService;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the URL used to authenticate OpenID requests. If the {@link HttpServletRequest}
|
||||
* matches this URL the {@link OpenIDAuthenticationFilter} will attempt to
|
||||
* authenticate the request. The default is "/login/openid".
|
||||
*
|
||||
* @param loginUrl
|
||||
* the URL used to perform authentication
|
||||
* @return the {@link OpenIDLoginConfigurer} for additional customization
|
||||
*/
|
||||
public OpenIDLoginConfigurer<H> loginProcessingUrl(String loginProcessingUrl) {
|
||||
return super.loginProcessingUrl(loginProcessingUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Specifies the URL to send users to if login is required. If used with
|
||||
* {@link WebSecurityConfigurerAdapter} a default login page will be
|
||||
* generated when this attribute is not specified.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If a URL is specified or this is not being used in conjuction with
|
||||
* {@link WebSecurityConfigurerAdapter}, users are required to process the
|
||||
* specified URL to generate a login page.
|
||||
* </p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>It must be an HTTP POST</li>
|
||||
* <li>It must be submitted to {@link #loginProcessingUrl(String)}</li>
|
||||
* <li>It should include the OpenID as an HTTP parameter by the name of
|
||||
* {@link OpenIDAuthenticationFilter#DEFAULT_CLAIMED_IDENTITY_FIELD}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param loginPage the login page to redirect to if authentication is required (i.e. "/login")
|
||||
* @return the {@link FormLoginConfigurer} for additional customization
|
||||
*/
|
||||
public OpenIDLoginConfigurer<H> loginPage(String loginPage) {
|
||||
return super.loginPage(loginPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(H http) throws Exception {
|
||||
super.init(http);
|
||||
|
||||
OpenIDAuthenticationProvider authenticationProvider = new OpenIDAuthenticationProvider();
|
||||
authenticationProvider.setAuthenticationUserDetailsService(getAuthenticationUserDetailsService(http));
|
||||
authenticationProvider = postProcess(authenticationProvider);
|
||||
http.authenticationProvider(authenticationProvider);
|
||||
|
||||
initDefaultLoginFilter(http);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(H http) throws Exception {
|
||||
getAuthenticationFilter().setConsumer(getConsumer());
|
||||
super.configure(http);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link OpenIDConsumer} that was configured or defaults to an {@link OpenID4JavaConsumer}.
|
||||
* @return the {@link OpenIDConsumer} to use
|
||||
* @throws ConsumerException
|
||||
*/
|
||||
private OpenIDConsumer getConsumer() throws ConsumerException {
|
||||
if(openIDConsumer == null) {
|
||||
openIDConsumer = new OpenID4JavaConsumer(getConsumerManager(), attributesToFetchFactory());
|
||||
}
|
||||
return openIDConsumer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link ConsumerManager} that was configured or defaults to using a {@link ConsumerManager} with the default constructor.
|
||||
* @return the {@link ConsumerManager} to use
|
||||
*/
|
||||
private ConsumerManager getConsumerManager() {
|
||||
if(this.consumerManager != null) {
|
||||
return this.consumerManager;
|
||||
}
|
||||
return new ConsumerManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link RegexBasedAxFetchListFactory} using the attributes
|
||||
* populated by {@link AttributeExchangeConfigurer}
|
||||
*
|
||||
* @return the {@link AxFetchListFactory} to use
|
||||
*/
|
||||
private AxFetchListFactory attributesToFetchFactory() {
|
||||
Map<String,List<OpenIDAttribute>> identityToAttrs = new HashMap<String,List<OpenIDAttribute>>();
|
||||
for(AttributeExchangeConfigurer conf : attributeExchangeConfigurers) {
|
||||
identityToAttrs.put(conf.identifier, conf.getAttributes());
|
||||
}
|
||||
return new RegexBasedAxFetchListFactory(identityToAttrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link AuthenticationUserDetailsService} that was configured or
|
||||
* defaults to {@link UserDetailsByNameServiceWrapper} that uses a
|
||||
* {@link UserDetailsService} looked up using
|
||||
* {@link HttpSecurity#getSharedObject(Class)}
|
||||
*
|
||||
* @param http the current {@link HttpSecurity}
|
||||
* @return the {@link AuthenticationUserDetailsService}.
|
||||
*/
|
||||
private AuthenticationUserDetailsService<OpenIDAuthenticationToken> getAuthenticationUserDetailsService(
|
||||
H http) {
|
||||
if(authenticationUserDetailsService != null) {
|
||||
return authenticationUserDetailsService;
|
||||
}
|
||||
return new UserDetailsByNameServiceWrapper<OpenIDAuthenticationToken>(http.getSharedObject(UserDetailsService.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* If available, initializes the {@link DefaultLoginPageViewFilter} shared object.
|
||||
*
|
||||
* @param http the {@link HttpSecurityBuilder} to use
|
||||
*/
|
||||
private void initDefaultLoginFilter(H http) {
|
||||
DefaultLoginPageViewFilter loginPageGeneratingFilter = http.getSharedObject(DefaultLoginPageViewFilter.class);
|
||||
if(loginPageGeneratingFilter != null && !isCustomLoginPage()) {
|
||||
loginPageGeneratingFilter.setOpenIdEnabled(true);
|
||||
loginPageGeneratingFilter.setOpenIDauthenticationUrl(getLoginProcessingUrl());
|
||||
String loginPageUrl = loginPageGeneratingFilter.getLoginPageUrl();
|
||||
if(loginPageUrl == null) {
|
||||
loginPageGeneratingFilter.setLoginPageUrl(getLoginPage());
|
||||
loginPageGeneratingFilter.setFailureUrl(getFailureUrl());
|
||||
}
|
||||
loginPageGeneratingFilter.setOpenIDusernameParameter(OpenIDAuthenticationFilter.DEFAULT_CLAIMED_IDENTITY_FIELD);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A class used to add OpenID attributes to look up
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public final class AttributeExchangeConfigurer {
|
||||
private final String identifier;
|
||||
private List<OpenIDAttribute> attributes = new ArrayList<OpenIDAttribute>();
|
||||
private List<AttributeConfigurer> attributeConfigurers = new ArrayList<AttributeConfigurer>();
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param identifierPattern the pattern that attempts to match on the OpenID
|
||||
* @see OpenIDLoginConfigurer#attributeExchange(String)
|
||||
*/
|
||||
private AttributeExchangeConfigurer(String identifierPattern) {
|
||||
this.identifier = identifierPattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link OpenIDLoginConfigurer} to customize the OpenID configuration further
|
||||
* @return the {@link OpenIDLoginConfigurer}
|
||||
*/
|
||||
public OpenIDLoginConfigurer<H> and() {
|
||||
return OpenIDLoginConfigurer.this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link OpenIDAttribute} to be obtained for the configured OpenID pattern.
|
||||
* @param attribute the {@link OpenIDAttribute} to obtain
|
||||
* @return the {@link AttributeExchangeConfigurer} for further customization of attribute exchange
|
||||
*/
|
||||
public AttributeExchangeConfigurer attribute(OpenIDAttribute attribute) {
|
||||
this.attributes.add(attribute);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link OpenIDAttribute} with the given name
|
||||
* @param name the name of the {@link OpenIDAttribute} to create
|
||||
* @return an {@link AttributeConfigurer} to further configure the {@link OpenIDAttribute} that should be obtained.
|
||||
*/
|
||||
public AttributeConfigurer attribute(String name) {
|
||||
AttributeConfigurer attributeConfigurer = new AttributeConfigurer(name);
|
||||
this.attributeConfigurers.add(attributeConfigurer);
|
||||
return attributeConfigurer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link OpenIDAttribute}'s for the configured OpenID pattern
|
||||
* @return
|
||||
*/
|
||||
private List<OpenIDAttribute> getAttributes() {
|
||||
for(AttributeConfigurer config : attributeConfigurers) {
|
||||
attributes.add(config.build());
|
||||
}
|
||||
attributeConfigurers.clear();
|
||||
return attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures an {@link OpenIDAttribute}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class AttributeConfigurer {
|
||||
private String name;
|
||||
private int count = 1;
|
||||
private boolean required = false;
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param name the name of the attribute
|
||||
* @see AttributeExchangeConfigurer#attribute(String)
|
||||
*/
|
||||
private AttributeConfigurer(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the number of attribute values to request. Default is 1.
|
||||
* @param count the number of attributes to request.
|
||||
* @return the {@link AttributeConfigurer} for further customization
|
||||
*/
|
||||
public AttributeConfigurer count(int count) {
|
||||
this.count = count;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that this attribute is required. The default is
|
||||
* <code>false</code>. Note that as outlined in the OpenID
|
||||
* specification, required attributes are not validated by the
|
||||
* OpenID Provider. Developers should perform any validation in
|
||||
* custom code.
|
||||
*
|
||||
* @param required specifies the attribute is required
|
||||
* @return the {@link AttributeConfigurer} for further customization
|
||||
*/
|
||||
public AttributeConfigurer required(boolean required) {
|
||||
this.required = required;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The OpenID attribute type.
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public AttributeConfigurer type(String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link AttributeExchangeConfigurer} for further
|
||||
* customization of the attributes
|
||||
*
|
||||
* @return the {@link AttributeConfigurer}
|
||||
*/
|
||||
public AttributeExchangeConfigurer and() {
|
||||
return AttributeExchangeConfigurer.this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the {@link OpenIDAttribute}.
|
||||
* @return
|
||||
*/
|
||||
private OpenIDAttribute build() {
|
||||
OpenIDAttribute attribute = new OpenIDAttribute(name, type);
|
||||
attribute.setCount(count);
|
||||
attribute.setRequired(required);
|
||||
return attribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
/**
|
||||
* Exists for mocking purposes to ensure that the Type information is found.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public interface AnyObjectPostProcessor extends ObjectPostProcessor<Object> {
|
||||
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
import javax.servlet.Filter
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
import org.springframework.context.ConfigurableApplicationContext
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.core.context.SecurityContextImpl
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
||||
import org.springframework.security.web.context.HttpRequestResponseHolder
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository
|
||||
|
||||
import spock.lang.AutoCleanup
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
abstract class BaseSpringSpec extends Specification {
|
||||
@AutoCleanup
|
||||
ConfigurableApplicationContext context
|
||||
|
||||
MockHttpServletRequest request
|
||||
MockHttpServletResponse response
|
||||
MockFilterChain chain
|
||||
|
||||
def setup() {
|
||||
request = new MockHttpServletRequest(method:"GET")
|
||||
response = new MockHttpServletResponse()
|
||||
chain = new MockFilterChain()
|
||||
}
|
||||
|
||||
AuthenticationManagerBuilder authenticationBldr = new AuthenticationManagerBuilder().inMemoryAuthentication().and()
|
||||
|
||||
def cleanup() {
|
||||
SecurityContextHolder.clearContext()
|
||||
}
|
||||
|
||||
def loadConfig(Class<?>... configs) {
|
||||
context = new AnnotationConfigApplicationContext(configs)
|
||||
context
|
||||
}
|
||||
|
||||
def findFilter(Class<?> filter, int index = 0) {
|
||||
filterChain(index).filters.find { filter.isAssignableFrom(it.class)}
|
||||
}
|
||||
|
||||
def filterChain(int index=0) {
|
||||
filterChains()[index]
|
||||
}
|
||||
|
||||
def filterChains() {
|
||||
context.getBean(FilterChainProxy).filterChains
|
||||
}
|
||||
|
||||
Filter getSpringSecurityFilterChain() {
|
||||
context.getBean("springSecurityFilterChain",Filter.class)
|
||||
}
|
||||
|
||||
AuthenticationManager authenticationManager() {
|
||||
context.getBean(AuthenticationManager)
|
||||
}
|
||||
|
||||
AuthenticationManager getAuthenticationManager() {
|
||||
try {
|
||||
authenticationManager().delegateBuilder.getObject()
|
||||
} catch(NoSuchBeanDefinitionException e) {}
|
||||
findFilter(FilterSecurityInterceptor).authenticationManager
|
||||
}
|
||||
|
||||
List<AuthenticationProvider> authenticationProviders() {
|
||||
List<AuthenticationProvider> providers = new ArrayList<AuthenticationProvider>()
|
||||
AuthenticationManager authenticationManager = getAuthenticationManager()
|
||||
while(authenticationManager?.providers) {
|
||||
providers.addAll(authenticationManager.providers)
|
||||
authenticationManager = authenticationManager.parent
|
||||
}
|
||||
providers
|
||||
}
|
||||
|
||||
AuthenticationProvider findAuthenticationProvider(Class<?> provider) {
|
||||
authenticationProviders().find { provider.isAssignableFrom(it.class) }
|
||||
}
|
||||
|
||||
def login(String username="user", String role="ROLE_USER") {
|
||||
login(new UsernamePasswordAuthenticationToken(username, null, AuthorityUtils.createAuthorityList(role)))
|
||||
}
|
||||
|
||||
def login(Authentication auth) {
|
||||
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository()
|
||||
HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(request, response)
|
||||
repo.loadContext(requestResponseHolder)
|
||||
repo.saveContext(new SecurityContextImpl(authentication:auth), requestResponseHolder.request, requestResponseHolder.response)
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
|
||||
import spock.lang.AutoCleanup
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
abstract class BaseWebSpecuritySpec extends BaseSpringSpec {
|
||||
FilterChainProxy springSecurityFilterChain
|
||||
MockHttpServletRequest request
|
||||
MockHttpServletResponse response
|
||||
MockFilterChain chain
|
||||
|
||||
def setup() {
|
||||
request = new MockHttpServletRequest()
|
||||
response = new MockHttpServletResponse()
|
||||
chain = new MockFilterChain()
|
||||
}
|
||||
|
||||
|
||||
def loadConfig(Class<?>... configs) {
|
||||
super.loadConfig(configs)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class ConcereteSecurityConfigurerAdapter extends SecurityConfigurerAdapter<Object, SecurityBuilder<Object>> {
|
||||
private List<Object> list = new ArrayList<Object>();
|
||||
|
||||
@Override
|
||||
public void configure(SecurityBuilder<Object> builder) throws Exception {
|
||||
list = postProcess(list);
|
||||
}
|
||||
|
||||
public ConcereteSecurityConfigurerAdapter list(List<Object> l) {
|
||||
this.list = l;
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class ObjectPostProcessorTests {
|
||||
|
||||
@Test
|
||||
public void convertTypes() {
|
||||
assertThat((Object)PerformConversion.perform(new ArrayList<Object>())).isInstanceOf(LinkedList.class);
|
||||
}
|
||||
}
|
||||
|
||||
class ListToLinkedListObjectPostProcessor implements ObjectPostProcessor<List<?>>{
|
||||
|
||||
@Override
|
||||
public <O extends List<?>> O postProcess(O l) {
|
||||
return (O) new LinkedList(l);
|
||||
}
|
||||
}
|
||||
|
||||
class PerformConversion {
|
||||
public static List<?> perform(ArrayList<?> l) {
|
||||
return new ListToLinkedListObjectPostProcessor().postProcess(l);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation
|
||||
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class SecurityConfigurerAdapterTests extends Specification {
|
||||
ConcereteSecurityConfigurerAdapter conf = new ConcereteSecurityConfigurerAdapter()
|
||||
|
||||
def "addPostProcessor closure"() {
|
||||
setup:
|
||||
SecurityBuilder<Object> builder = Mock()
|
||||
conf.addObjectPostProcessor({ List l ->
|
||||
l.add("a")
|
||||
l
|
||||
} as ObjectPostProcessor<List>)
|
||||
when:
|
||||
conf.init(builder)
|
||||
conf.configure(builder)
|
||||
then:
|
||||
conf.list.contains("a")
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class AuthenticationManagerBuilderTests extends BaseSpringSpec {
|
||||
def "add(AuthenticationProvider) does not perform registration"() {
|
||||
setup:
|
||||
ObjectPostProcessor opp = Mock()
|
||||
AuthenticationProvider provider = Mock()
|
||||
AuthenticationManagerBuilder builder = new AuthenticationManagerBuilder().objectPostProcessor(opp)
|
||||
when: "Adding an AuthenticationProvider"
|
||||
builder.authenticationProvider(provider)
|
||||
builder.build()
|
||||
then: "AuthenticationProvider is not passed into LifecycleManager (it should be managed externally)"
|
||||
0 * opp._(_ as AuthenticationProvider)
|
||||
}
|
||||
|
||||
// https://github.com/SpringSource/spring-security-javaconfig/issues/132
|
||||
def "#132 Custom AuthenticationEventPublisher with Web registerAuthentication"() {
|
||||
setup:
|
||||
AuthenticationEventPublisher aep = Mock()
|
||||
when:
|
||||
AuthenticationManager am = new AuthenticationManagerBuilder()
|
||||
.authenticationEventPublisher(aep)
|
||||
.inMemoryAuthentication()
|
||||
.and()
|
||||
.build()
|
||||
then:
|
||||
am.eventPublisher == aep
|
||||
}
|
||||
|
||||
def "authentication-manager support multiple DaoAuthenticationProvider's"() {
|
||||
setup:
|
||||
loadConfig(MultiAuthenticationProvidersConfig)
|
||||
when:
|
||||
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password"))
|
||||
then:
|
||||
auth.name == "user"
|
||||
auth.authorities*.authority == ['ROLE_USER']
|
||||
when:
|
||||
auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("admin","password"))
|
||||
then:
|
||||
auth.name == "admin"
|
||||
auth.authorities*.authority.sort() == ['ROLE_ADMIN','ROLE_USER']
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class MultiAuthenticationProvidersConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.and()
|
||||
.inMemoryAuthentication()
|
||||
.withUser("admin").password("password").roles("USER","ADMIN")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication
|
||||
|
||||
import java.rmi.registry.Registry;
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.config.annotation.authentication.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.authentication.configurers.userdetails.UserDetailsServiceConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Configuration
|
||||
class BaseAuthenticationConfig {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.withUser("admin").password("password").roles("USER", "ADMIN").and()
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager() {
|
||||
AuthenticationManagerBuilder registry = new AuthenticationManagerBuilder();
|
||||
registerAuthentication(registry);
|
||||
return registry.build();
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.Authentication
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class NamespaceAuthenticationManagerTests extends BaseSpringSpec {
|
||||
def "authentication-manager@erase-credentials=true (default)"() {
|
||||
when:
|
||||
loadConfig(EraseCredentialsTrueDefaultConfig)
|
||||
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password"))
|
||||
then:
|
||||
auth.principal.password == null
|
||||
auth.credentials == null
|
||||
when: "authenticate the same user"
|
||||
auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password"))
|
||||
then: "successfully authenticate again"
|
||||
noExceptionThrown()
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class EraseCredentialsTrueDefaultConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER")
|
||||
}
|
||||
|
||||
// Only necessary to have access to verify the AuthenticationManager
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
}
|
||||
|
||||
def "authentication-manager@erase-credentials=false"() {
|
||||
when:
|
||||
loadConfig(EraseCredentialsFalseConfig)
|
||||
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password"))
|
||||
then:
|
||||
auth.credentials == "password"
|
||||
auth.principal.password == "password"
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class EraseCredentialsFalseConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.eraseCredentials(false)
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER")
|
||||
}
|
||||
|
||||
// Only necessary to have access to verify the AuthenticationManager
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class NamespaceAuthenticationProviderTests extends BaseSpringSpec {
|
||||
def "authentication-provider@ref"() {
|
||||
when:
|
||||
loadConfig(AuthenticationProviderRefConfig)
|
||||
then:
|
||||
authenticationProviders()[1] == AuthenticationProviderRefConfig.expected
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class AuthenticationProviderRefConfig extends WebSecurityConfigurerAdapter {
|
||||
static DaoAuthenticationProvider expected = new DaoAuthenticationProvider()
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.authenticationProvider(expected)
|
||||
}
|
||||
|
||||
// Only necessary to have access to verify the AuthenticationManager
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
}
|
||||
|
||||
def "authentication-provider@user-service-ref"() {
|
||||
when:
|
||||
loadConfig(UserServiceRefConfig)
|
||||
then:
|
||||
findAuthenticationProvider(DaoAuthenticationProvider).userDetailsService == UserServiceRefConfig.expected
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class UserServiceRefConfig extends WebSecurityConfigurerAdapter {
|
||||
static InMemoryUserDetailsManager expected = new InMemoryUserDetailsManager([] as Collection)
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.userDetailsService(expected)
|
||||
}
|
||||
|
||||
// Only necessary to have access to verify the AuthenticationManager
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication
|
||||
|
||||
import javax.sql.DataSource
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.NamespaceJdbcUserServiceTests.CustomJdbcUserServiceSampleConfig.CustomUserCache;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.userdetails.UserCache
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.provisioning.JdbcUserDetailsManager
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class NamespaceJdbcUserServiceTests extends BaseSpringSpec {
|
||||
def "jdbc-user-service"() {
|
||||
when:
|
||||
loadConfig(DataSourceConfig,JdbcUserServiceConfig)
|
||||
then:
|
||||
findAuthenticationProvider(DaoAuthenticationProvider).userDetailsService instanceof JdbcUserDetailsManager
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class JdbcUserServiceConfig extends WebSecurityConfigurerAdapter {
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.jdbcAuthentication()
|
||||
.dataSource(dataSource) // jdbc-user-service@data-source-ref
|
||||
}
|
||||
|
||||
// Only necessary to have access to verify the AuthenticationManager
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
}
|
||||
|
||||
def "jdbc-user-service in memory testing sample"() {
|
||||
when:
|
||||
loadConfig(DataSourceConfig,JdbcUserServiceInMemorySampleConfig)
|
||||
then:
|
||||
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user", "password"))
|
||||
auth.authorities.collect {it.authority} == ['ROLE_USER']
|
||||
auth.name == "user"
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class JdbcUserServiceInMemorySampleConfig extends WebSecurityConfigurerAdapter {
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.jdbcAuthentication()
|
||||
.dataSource(dataSource)
|
||||
// imports the default schema (will fail if already exists)
|
||||
.withDefaultSchema()
|
||||
// adds this user automatically (will fail if already exists)
|
||||
.withUser("user")
|
||||
.password("password")
|
||||
.roles("USER")
|
||||
}
|
||||
|
||||
// Only necessary to have access to verify the AuthenticationManager
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DataSourceConfig {
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder()
|
||||
return builder.setType(EmbeddedDatabaseType.HSQL).build();
|
||||
}
|
||||
}
|
||||
|
||||
def "jdbc-user-service custom"() {
|
||||
when:
|
||||
loadConfig(CustomDataSourceConfig,CustomJdbcUserServiceSampleConfig)
|
||||
then:
|
||||
findAuthenticationProvider(DaoAuthenticationProvider).userDetailsService.userCache instanceof CustomUserCache
|
||||
when:
|
||||
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user", "password"))
|
||||
then:
|
||||
auth.authorities.collect {it.authority}.sort() == ['ROLE_DBA','ROLE_USER']
|
||||
auth.name == 'user'
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class CustomJdbcUserServiceSampleConfig extends WebSecurityConfigurerAdapter {
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.jdbcAuthentication()
|
||||
// jdbc-user-service@dataSource
|
||||
.dataSource(dataSource)
|
||||
// jdbc-user-service@cache-ref
|
||||
.userCache(new CustomUserCache())
|
||||
// jdbc-user-service@users-byusername-query
|
||||
.usersByUsernameQuery("select principal,credentials,true from users where principal = ?")
|
||||
// jdbc-user-service@authorities-by-username-query
|
||||
.authoritiesByUsernameQuery("select principal,role from roles where principal = ?")
|
||||
// jdbc-user-service@group-authorities-by-username-query
|
||||
.groupAuthoritiesByUsername(JdbcUserDetailsManager.DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY)
|
||||
// jdbc-user-service@role-prefix
|
||||
.rolePrefix("ROLE_")
|
||||
|
||||
}
|
||||
|
||||
// Only necessary to have access to verify the AuthenticationManager
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
static class CustomUserCache implements UserCache {
|
||||
|
||||
@Override
|
||||
public UserDetails getUserFromCache(String username) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putUserInCache(UserDetails user) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserFromCache(String username) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomDataSourceConfig {
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder()
|
||||
// simulate that the DB already has the schema loaded and users in it
|
||||
.addScript("CustomJdbcUserServiceSampleConfig.sql")
|
||||
return builder.setType(EmbeddedDatabaseType.HSQL).build();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication
|
||||
|
||||
import static org.springframework.security.config.annotation.authentication.PasswordEncoderConfigurerConfigs.*
|
||||
|
||||
import javax.sql.DataSource
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.configurers.ldap.LdapAuthenticationProviderConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.authority.AuthorityUtils
|
||||
import org.springframework.security.core.userdetails.User
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
|
||||
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class NamespacePasswordEncoderTests extends BaseSpringSpec {
|
||||
def "password-encoder@ref with in memory"() {
|
||||
when:
|
||||
loadConfig(PasswordEncoderWithInMemoryConfig)
|
||||
then:
|
||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user", "password"))
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class PasswordEncoderWithInMemoryConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder()
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password(encoder.encode("password")).roles("USER").and()
|
||||
.passwordEncoder(encoder)
|
||||
}
|
||||
}
|
||||
|
||||
def "password-encoder@ref with jdbc"() {
|
||||
when:
|
||||
loadConfig(PasswordEncoderWithJdbcConfig)
|
||||
then:
|
||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user", "password"))
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class PasswordEncoderWithJdbcConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder()
|
||||
auth
|
||||
.jdbcAuthentication()
|
||||
.withDefaultSchema()
|
||||
.dataSource(dataSource())
|
||||
.withUser("user").password(encoder.encode("password")).roles("USER").and()
|
||||
.passwordEncoder(encoder)
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder()
|
||||
return builder.setType(EmbeddedDatabaseType.HSQL).build();
|
||||
}
|
||||
}
|
||||
|
||||
def "password-encoder@ref with userdetailsservice"() {
|
||||
when:
|
||||
loadConfig(PasswordEncoderWithUserDetailsServiceConfig)
|
||||
then:
|
||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user", "password"))
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class PasswordEncoderWithUserDetailsServiceConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder()
|
||||
User user = new User("user",encoder.encode("password"), AuthorityUtils.createAuthorityList("ROLE_USER"))
|
||||
InMemoryUserDetailsManager uds = new InMemoryUserDetailsManager([user])
|
||||
auth
|
||||
.userDetailsService(uds)
|
||||
.passwordEncoder(encoder)
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder()
|
||||
return builder.setType(EmbeddedDatabaseType.HSQL).build();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
/**
|
||||
* Class Containing the {@link Configuration} for
|
||||
* {@link PasswordEncoderConfigurerTests}. Separate to ensure the configuration
|
||||
* compiles in Java (i.e. we are not using hidden methods).
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public class PasswordEncoderConfigurerConfigs {
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class PasswordEncoderConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
BCryptPasswordEncoder encoder = passwordEncoder();
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password(encoder.encode("password")).roles("USER").and()
|
||||
.passwordEncoder(encoder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BCryptPasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class PasswordEncoderNoAuthManagerLoadsConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
BCryptPasswordEncoder encoder = passwordEncoder();
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password(encoder.encode("password")).roles("USER").and()
|
||||
.passwordEncoder(encoder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BCryptPasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.authentication
|
||||
|
||||
import static org.springframework.security.config.annotation.authentication.PasswordEncoderConfigurerConfigs.*
|
||||
|
||||
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class PasswordEncoderConfigurerTests extends BaseSpringSpec {
|
||||
def "password-encoder@ref with No AuthenticationManager Bean"() {
|
||||
when:
|
||||
loadConfig(PasswordEncoderNoAuthManagerLoadsConfig)
|
||||
then:
|
||||
noExceptionThrown()
|
||||
}
|
||||
|
||||
def "password-encoder@ref with AuthenticationManagerBuilder"() {
|
||||
when:
|
||||
loadConfig(PasswordEncoderConfig)
|
||||
AuthenticationManager authMgr = authenticationManager()
|
||||
then:
|
||||
authMgr.authenticate(new UsernamePasswordAuthenticationToken("user", "password"))
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.configuration
|
||||
|
||||
import javax.servlet.ServletConfig
|
||||
import javax.servlet.ServletContext
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware
|
||||
import org.springframework.beans.factory.BeanFactoryAware
|
||||
import org.springframework.beans.factory.BeanNameAware
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.context.ApplicationContextAware
|
||||
import org.springframework.context.ApplicationEventPublisherAware
|
||||
import org.springframework.context.EnvironmentAware
|
||||
import org.springframework.context.MessageSourceAware
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockServletConfig
|
||||
import org.springframework.mock.web.MockServletContext
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor;
|
||||
import org.springframework.web.context.ServletConfigAware
|
||||
import org.springframework.web.context.ServletContextAware
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class AutowireBeanFactoryObjectPostProcessorTests extends BaseSpringSpec {
|
||||
|
||||
def "Verify All Aware methods are invoked"() {
|
||||
setup:
|
||||
ApplicationContextAware contextAware = Mock(ApplicationContextAware)
|
||||
ApplicationEventPublisherAware publisher = Mock(ApplicationEventPublisherAware)
|
||||
BeanClassLoaderAware classloader = Mock(BeanClassLoaderAware)
|
||||
BeanFactoryAware beanFactory = Mock(BeanFactoryAware)
|
||||
EnvironmentAware environment = Mock(EnvironmentAware)
|
||||
MessageSourceAware messageSource = Mock(MessageSourceAware)
|
||||
ServletConfigAware servletConfig = Mock(ServletConfigAware)
|
||||
ServletContextAware servletContext = Mock(ServletContextAware)
|
||||
DisposableBean disposable = Mock(DisposableBean)
|
||||
|
||||
context = new AnnotationConfigWebApplicationContext([servletConfig:new MockServletConfig(),servletContext:new MockServletContext()])
|
||||
context.register(Config)
|
||||
context.refresh()
|
||||
context.start()
|
||||
|
||||
ObjectPostProcessor opp = context.getBean(ObjectPostProcessor)
|
||||
when:
|
||||
opp.postProcess(contextAware)
|
||||
then:
|
||||
1 * contextAware.setApplicationContext(!null)
|
||||
|
||||
when:
|
||||
opp.postProcess(publisher)
|
||||
then:
|
||||
1 * publisher.setApplicationEventPublisher(!null)
|
||||
|
||||
when:
|
||||
opp.postProcess(classloader)
|
||||
then:
|
||||
1 * classloader.setBeanClassLoader(!null)
|
||||
|
||||
when:
|
||||
opp.postProcess(beanFactory)
|
||||
then:
|
||||
1 * beanFactory.setBeanFactory(!null)
|
||||
|
||||
when:
|
||||
opp.postProcess(environment)
|
||||
then:
|
||||
1 * environment.setEnvironment(!null)
|
||||
|
||||
when:
|
||||
opp.postProcess(messageSource)
|
||||
then:
|
||||
1 * messageSource.setMessageSource(!null)
|
||||
|
||||
when:
|
||||
opp.postProcess(servletConfig)
|
||||
then:
|
||||
1 * servletConfig.setServletConfig(!null)
|
||||
|
||||
when:
|
||||
opp.postProcess(servletContext)
|
||||
then:
|
||||
1 * servletContext.setServletContext(!null)
|
||||
|
||||
when:
|
||||
opp.postProcess(disposable)
|
||||
context.close()
|
||||
context = null
|
||||
then:
|
||||
1 * disposable.destroy()
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
@Bean
|
||||
public ObjectPostProcessor objectPostProcessor(AutowireCapableBeanFactory beanFactory) {
|
||||
return new AutowireBeanFactoryObjectPostProcessor(beanFactory);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.method.configuration
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat
|
||||
import static org.junit.Assert.fail
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.springframework.context.ApplicationContext
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.access.AccessDecisionManager
|
||||
import org.springframework.security.access.AccessDeniedException
|
||||
import org.springframework.security.access.ConfigAttribute
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
|
||||
import org.springframework.security.config.annotation.web.WebSecurityConfigurerAdapterTests.InMemoryAuthWithWebSecurityConfigurerAdapter
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.authority.AuthorityUtils
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class GlobalMethodSecurityConfigurationTests extends BaseSpringSpec {
|
||||
def "messages set when using GlobalMethodSecurityConfiguration"() {
|
||||
when:
|
||||
loadConfig(InMemoryAuthWithGlobalMethodSecurityConfig)
|
||||
then:
|
||||
authenticationManager.messages.messageSource instanceof ApplicationContext
|
||||
}
|
||||
|
||||
def "AuthenticationEventPublisher is registered GlobalMethodSecurityConfiguration"() {
|
||||
when:
|
||||
loadConfig(InMemoryAuthWithGlobalMethodSecurityConfig)
|
||||
then:
|
||||
authenticationManager.eventPublisher instanceof DefaultAuthenticationEventPublisher
|
||||
when:
|
||||
Authentication auth = new UsernamePasswordAuthenticationToken("user",null,AuthorityUtils.createAuthorityList("ROLE_USER"))
|
||||
authenticationManager.eventPublisher.publishAuthenticationSuccess(auth)
|
||||
then:
|
||||
InMemoryAuthWithGlobalMethodSecurityConfig.EVENT.authentication == auth
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public static class InMemoryAuthWithGlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration implements ApplicationListener<AuthenticationSuccessEvent> {
|
||||
static AuthenticationSuccessEvent EVENT
|
||||
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(AuthenticationSuccessEvent e) {
|
||||
EVENT = e
|
||||
}
|
||||
}
|
||||
|
||||
AuthenticationManager getAuthenticationManager() {
|
||||
context.getBean(MethodInterceptor).authenticationManager
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import javax.annotation.security.DenyAll
|
||||
|
||||
import org.springframework.security.access.annotation.Secured
|
||||
import org.springframework.security.access.prepost.PostAuthorize;
|
||||
import org.springframework.security.access.prepost.PreAuthorize
|
||||
import org.springframework.security.core.Authentication
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public interface MethodSecurityService {
|
||||
@PreAuthorize("denyAll")
|
||||
public String preAuthorize();
|
||||
|
||||
@Secured("ROLE_ADMIN")
|
||||
public String secured();
|
||||
|
||||
@DenyAll
|
||||
public String jsr250();
|
||||
|
||||
@Secured(["ROLE_USER","RUN_AS_SUPER"])
|
||||
public Authentication runAs();
|
||||
|
||||
@PreAuthorize("permitAll")
|
||||
public String preAuthorizePermitAll();
|
||||
|
||||
@PreAuthorize("hasPermission(#object,'read')")
|
||||
public String hasPermission(String object);
|
||||
|
||||
@PostAuthorize("hasPermission(#object,'read')")
|
||||
public String postHasPermission(String object);
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.security.core.Authentication
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class MethodSecurityServiceImpl implements MethodSecurityService {
|
||||
|
||||
@Override
|
||||
public String preAuthorize() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String secured() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String jsr250() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication runAs() {
|
||||
return SecurityContextHolder.getContext().getAuthentication();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String preAuthorizePermitAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String hasPermission(String object) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String postHasPermission(String object) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.method.configuration
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat
|
||||
import static org.junit.Assert.fail
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.access.AccessDeniedException
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler
|
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.method.configuration.NamespaceGlobalMethodSecurityTests.BaseMethodConfig;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class NamespaceGlobalMethodSecurityExpressionHandlerTests extends BaseSpringSpec {
|
||||
def setup() {
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new TestingAuthenticationToken("user", "password","ROLE_USER"))
|
||||
}
|
||||
|
||||
def "global-method-security/expression-handler @PreAuthorize"() {
|
||||
setup:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomAccessDecisionManagerConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
when:
|
||||
service.hasPermission("granted")
|
||||
then:
|
||||
noExceptionThrown()
|
||||
when:
|
||||
service.hasPermission("denied")
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
def "global-method-security/expression-handler @PostAuthorize"() {
|
||||
setup:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomAccessDecisionManagerConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
when:
|
||||
service.postHasPermission("granted")
|
||||
then:
|
||||
noExceptionThrown()
|
||||
when:
|
||||
service.postHasPermission("denied")
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public static class CustomAccessDecisionManagerConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected MethodSecurityExpressionHandler expressionHandler() {
|
||||
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler()
|
||||
expressionHandler.permissionEvaluator = new PermissionEvaluator() {
|
||||
boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
|
||||
"granted" == targetDomainObject
|
||||
}
|
||||
boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
|
||||
throw new UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
return expressionHandler
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,443 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.method.configuration
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat
|
||||
import static org.junit.Assert.fail
|
||||
|
||||
import java.lang.reflect.Method
|
||||
|
||||
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
|
||||
import org.springframework.beans.factory.BeanCreationException
|
||||
import org.springframework.context.ConfigurableApplicationContext
|
||||
import org.springframework.context.annotation.AdviceMode
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.core.Ordered
|
||||
import org.springframework.security.access.AccessDecisionManager
|
||||
import org.springframework.security.access.AccessDeniedException
|
||||
import org.springframework.security.access.ConfigAttribute
|
||||
import org.springframework.security.access.SecurityConfig
|
||||
import org.springframework.security.access.intercept.AfterInvocationManager
|
||||
import org.springframework.security.access.intercept.RunAsManager
|
||||
import org.springframework.security.access.intercept.RunAsManagerImpl
|
||||
import org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor
|
||||
import org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor
|
||||
import org.springframework.security.access.method.AbstractMethodSecurityMetadataSource
|
||||
import org.springframework.security.access.method.MethodSecurityMetadataSource
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.BaseAuthenticationConfig;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class NamespaceGlobalMethodSecurityTests extends BaseSpringSpec {
|
||||
def setup() {
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new TestingAuthenticationToken("user", "password","ROLE_USER"))
|
||||
}
|
||||
|
||||
// --- access-decision-manager-ref ---
|
||||
|
||||
def "custom AccessDecisionManager can be used"() {
|
||||
setup: "Create an instance with an AccessDecisionManager that always denies access"
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomAccessDecisionManagerConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
when:
|
||||
service.preAuthorize()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
when:
|
||||
service.secured()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
|
||||
public static class CustomAccessDecisionManagerConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected AccessDecisionManager accessDecisionManager() {
|
||||
return new DenyAllAccessDecisionManager()
|
||||
}
|
||||
|
||||
public static class DenyAllAccessDecisionManager implements AccessDecisionManager {
|
||||
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) {
|
||||
throw new AccessDeniedException("Always Denied")
|
||||
}
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return true
|
||||
}
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- authentication-manager-ref ---
|
||||
|
||||
def "custom AuthenticationManager can be used"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(CustomAuthenticationConfig)
|
||||
MethodSecurityInterceptor interceptor = context.getBean(MethodSecurityInterceptor)
|
||||
interceptor.authenticationManager.authenticate(SecurityContextHolder.context.authentication)
|
||||
then:
|
||||
thrown(UnsupportedOperationException)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity
|
||||
public static class CustomAuthenticationConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected AuthenticationManager authenticationManager() {
|
||||
return new AuthenticationManager() {
|
||||
Authentication authenticate(Authentication authentication) {
|
||||
throw new UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- jsr250-annotations ---
|
||||
|
||||
def "enable jsr250"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(Jsr250Config)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then: "@Secured and @PreAuthorize are ignored"
|
||||
service.secured() == null
|
||||
service.preAuthorize() == null
|
||||
|
||||
when: "@DenyAll method invoked"
|
||||
service.jsr250()
|
||||
then: "access is denied"
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(jsr250Enabled = true)
|
||||
@Configuration
|
||||
public static class Jsr250Config extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
// --- metadata-source-ref ---
|
||||
|
||||
def "custom MethodSecurityMetadataSource can be used with higher priority than other sources"() {
|
||||
setup:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomMethodSecurityMetadataSourceConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
when:
|
||||
service.preAuthorize()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
when:
|
||||
service.secured()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
when:
|
||||
service.jsr250()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity
|
||||
public static class CustomMethodSecurityMetadataSourceConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() {
|
||||
return new AbstractMethodSecurityMetadataSource() {
|
||||
public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
|
||||
// require ROLE_NOBODY for any method on MethodSecurityService class
|
||||
return MethodSecurityService.isAssignableFrom(targetClass) ? [new SecurityConfig("ROLE_NOBODY")] : []
|
||||
}
|
||||
public Collection<ConfigAttribute> getAllConfigAttributes() {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- mode ---
|
||||
|
||||
def "aspectj mode works"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(AspectJModeConfig)
|
||||
then:
|
||||
AnnotationAwareAspectJAutoProxyCreator autoProxyCreator = context.getBean(AnnotationAwareAspectJAutoProxyCreator)
|
||||
autoProxyCreator.proxyTargetClass == true
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(mode = AdviceMode.ASPECTJ, proxyTargetClass = true)
|
||||
public static class AspectJModeConfig extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
def "aspectj mode works extending GlobalMethodSecurityConfiguration"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,AspectJModeExtendsGMSCConfig)
|
||||
then:
|
||||
AnnotationAwareAspectJAutoProxyCreator autoProxyCreator = context.getBean(AnnotationAwareAspectJAutoProxyCreator)
|
||||
autoProxyCreator.proxyTargetClass == false
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(mode = AdviceMode.ASPECTJ)
|
||||
public static class AspectJModeExtendsGMSCConfig extends GlobalMethodSecurityConfiguration {
|
||||
}
|
||||
|
||||
// --- order ---
|
||||
|
||||
def order() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(CustomOrderConfig)
|
||||
MethodSecurityMetadataSourceAdvisor advisor = context.getBean(MethodSecurityMetadataSourceAdvisor)
|
||||
then:
|
||||
advisor.order == 135
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(order = 135)
|
||||
public static class CustomOrderConfig extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
def "order is defaulted to Ordered.LOWEST_PRECEDENCE when using @EnableGlobalMethodSecurity"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(DefaultOrderConfig)
|
||||
MethodSecurityMetadataSourceAdvisor advisor = context.getBean(MethodSecurityMetadataSourceAdvisor)
|
||||
then:
|
||||
advisor.order == Ordered.LOWEST_PRECEDENCE
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity
|
||||
public static class DefaultOrderConfig extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
def "order is defaulted to Ordered.LOWEST_PRECEDENCE when extending GlobalMethodSecurityConfiguration"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,DefaultOrderExtendsMethodSecurityConfig)
|
||||
MethodSecurityMetadataSourceAdvisor advisor = context.getBean(MethodSecurityMetadataSourceAdvisor)
|
||||
then:
|
||||
advisor.order == Ordered.LOWEST_PRECEDENCE
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity
|
||||
public static class DefaultOrderExtendsMethodSecurityConfig extends GlobalMethodSecurityConfiguration {
|
||||
}
|
||||
|
||||
// --- pre-post-annotations ---
|
||||
|
||||
def preAuthorize() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(PreAuthorizeConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then:
|
||||
service.secured() == null
|
||||
service.jsr250() == null
|
||||
|
||||
when:
|
||||
service.preAuthorize()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
@Configuration
|
||||
public static class PreAuthorizeConfig extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
def "prePostEnabled extends GlobalMethodSecurityConfiguration"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,PreAuthorizeExtendsGMSCConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then:
|
||||
service.secured() == null
|
||||
service.jsr250() == null
|
||||
|
||||
when:
|
||||
service.preAuthorize()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
@Configuration
|
||||
public static class PreAuthorizeExtendsGMSCConfig extends GlobalMethodSecurityConfiguration {
|
||||
}
|
||||
|
||||
// --- proxy-target-class ---
|
||||
|
||||
def "proxying classes works"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(ProxyTargetClass)
|
||||
MethodSecurityServiceImpl service = context.getBean(MethodSecurityServiceImpl)
|
||||
then:
|
||||
noExceptionThrown()
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(proxyTargetClass = true)
|
||||
@Configuration
|
||||
public static class ProxyTargetClass extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
def "proxying interfaces works"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(PreAuthorizeConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then: "we get an instance of the interface"
|
||||
noExceptionThrown()
|
||||
when: "try to cast to the class"
|
||||
MethodSecurityServiceImpl serviceImpl = service
|
||||
then: "we get a class cast exception"
|
||||
thrown(ClassCastException)
|
||||
}
|
||||
|
||||
// --- run-as-manager-ref ---
|
||||
|
||||
def "custom RunAsManager"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomRunAsManagerConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then:
|
||||
service.runAs().authorities.find { it.authority == "ROLE_RUN_AS_SUPER"}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(securedEnabled = true)
|
||||
public static class CustomRunAsManagerConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected RunAsManager runAsManager() {
|
||||
RunAsManagerImpl runAsManager = new RunAsManagerImpl()
|
||||
runAsManager.setKey("some key")
|
||||
return runAsManager
|
||||
}
|
||||
}
|
||||
|
||||
// --- secured-annotation ---
|
||||
|
||||
def "secured enabled"() {
|
||||
setup:
|
||||
context = new AnnotationConfigApplicationContext(SecuredConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
when:
|
||||
service.secured()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
service.preAuthorize() == null
|
||||
service.jsr250() == null
|
||||
}
|
||||
|
||||
@EnableGlobalMethodSecurity(securedEnabled = true)
|
||||
@Configuration
|
||||
public static class SecuredConfig extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
// --- after-invocation-provider
|
||||
|
||||
def "custom AfterInvocationManager"() {
|
||||
setup:
|
||||
context = new AnnotationConfigApplicationContext(BaseMethodConfig,CustomAfterInvocationManagerConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
when:
|
||||
service.preAuthorizePermitAll()
|
||||
then:
|
||||
AccessDeniedException e = thrown()
|
||||
e.message == "custom AfterInvocationManager"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public static class CustomAfterInvocationManagerConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected AfterInvocationManager afterInvocationManager() {
|
||||
return new AfterInvocationManagerStub()
|
||||
}
|
||||
|
||||
public static class AfterInvocationManagerStub implements AfterInvocationManager {
|
||||
Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> attributes,
|
||||
Object returnedObject) throws AccessDeniedException {
|
||||
throw new AccessDeniedException("custom AfterInvocationManager")
|
||||
}
|
||||
|
||||
boolean supports(ConfigAttribute attribute) {
|
||||
return true
|
||||
}
|
||||
boolean supports(Class<?> clazz) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- misc ---
|
||||
|
||||
def "good error message when no Enable annotation"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(ExtendsNoEnableAnntotationConfig)
|
||||
MethodSecurityInterceptor interceptor = context.getBean(MethodSecurityInterceptor)
|
||||
interceptor.authenticationManager.authenticate(SecurityContextHolder.context.authentication)
|
||||
then:
|
||||
BeanCreationException e = thrown()
|
||||
e.message.contains(EnableGlobalMethodSecurity.class.getName() + " is required")
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class ExtendsNoEnableAnntotationConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Override
|
||||
protected AuthenticationManager authenticationManager() {
|
||||
return new AuthenticationManager() {
|
||||
Authentication authenticate(Authentication authentication) {
|
||||
throw new UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "import subclass of GlobalMethodSecurityConfiguration"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(ImportSubclassGMSCConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then:
|
||||
service.secured() == null
|
||||
service.jsr250() == null
|
||||
|
||||
when:
|
||||
service.preAuthorize()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(PreAuthorizeExtendsGMSCConfig)
|
||||
public static class ImportSubclassGMSCConfig extends BaseMethodConfig {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class BaseMethodConfig extends BaseAuthenticationConfig {
|
||||
@Bean
|
||||
public MethodSecurityService methodSecurityService() {
|
||||
return new MethodSecurityServiceImpl()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.access.AccessDeniedException
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
|
||||
/**
|
||||
* Demonstrate the samples
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class SampleEnableGlobalMethodSecurityTests extends BaseSpringSpec {
|
||||
def setup() {
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new TestingAuthenticationToken("user", "password","ROLE_USER"))
|
||||
}
|
||||
|
||||
def preAuthorize() {
|
||||
when:
|
||||
loadConfig(SampleWebSecurityConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then:
|
||||
service.secured() == null
|
||||
service.jsr250() == null
|
||||
|
||||
when:
|
||||
service.preAuthorize()
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled=true)
|
||||
public static class SampleWebSecurityConfig {
|
||||
@Bean
|
||||
public MethodSecurityService methodSecurityService() {
|
||||
return new MethodSecurityServiceImpl()
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager() throws Exception {
|
||||
return new AuthenticationManagerBuilder()
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.withUser("admin").password("password").roles("USER", "ADMIN").and()
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
def 'custom permission handler'() {
|
||||
when:
|
||||
loadConfig(CustomPermissionEvaluatorWebSecurityConfig)
|
||||
MethodSecurityService service = context.getBean(MethodSecurityService)
|
||||
then:
|
||||
service.hasPermission("allowed") == null
|
||||
|
||||
when:
|
||||
service.hasPermission("denied") == null
|
||||
then:
|
||||
thrown(AccessDeniedException)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled=true)
|
||||
public static class CustomPermissionEvaluatorWebSecurityConfig extends GlobalMethodSecurityConfiguration {
|
||||
@Bean
|
||||
public MethodSecurityService methodSecurityService() {
|
||||
return new MethodSecurityServiceImpl()
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MethodSecurityExpressionHandler expressionHandler() {
|
||||
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
||||
expressionHandler.setPermissionEvaluator(new CustomPermissionEvaluator());
|
||||
return expressionHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.withUser("admin").password("password").roles("USER", "ADMIN");
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomPermissionEvaluator implements PermissionEvaluator {
|
||||
public boolean hasPermission(Authentication authentication,
|
||||
Object targetDomainObject, Object permission) {
|
||||
return !"denied".equals(targetDomainObject);
|
||||
}
|
||||
|
||||
public boolean hasPermission(Authentication authentication,
|
||||
Serializable targetId, String targetType, Object permission) {
|
||||
return !"denied".equals(targetId);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.provisioning
|
||||
|
||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.UserDetailsManagerConfigurer;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||
import org.springframework.security.core.userdetails.UserDetails
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager
|
||||
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class UserDetailsManagerConfigurerTests extends Specification {
|
||||
|
||||
def "all attributes supported"() {
|
||||
setup:
|
||||
InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager([])
|
||||
when:
|
||||
UserDetails userDetails = new UserDetailsManagerConfigurer<UserDetailsManagerConfigurer<InMemoryUserDetailsManager>>(userDetailsManager)
|
||||
.withUser("user")
|
||||
.password("password")
|
||||
.roles("USER")
|
||||
.disabled(true)
|
||||
.accountExpired(true)
|
||||
.accountLocked(true)
|
||||
.credentialsExpired(true)
|
||||
.build()
|
||||
then:
|
||||
userDetails.username == 'user'
|
||||
userDetails.password == 'password'
|
||||
userDetails.authorities.collect { it.authority } == ["ROLE_USER"]
|
||||
!userDetails.accountNonExpired
|
||||
!userDetails.accountNonLocked
|
||||
!userDetails.credentialsNonExpired
|
||||
!userDetails.enabled
|
||||
}
|
||||
|
||||
def "authorities(GrantedAuthorities...) works"() {
|
||||
setup:
|
||||
InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager([])
|
||||
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER")
|
||||
when:
|
||||
UserDetails userDetails = new UserDetailsManagerConfigurer<UserDetailsManagerConfigurer<InMemoryUserDetailsManager>>(userDetailsManager)
|
||||
.withUser("user")
|
||||
.password("password")
|
||||
.authorities(authority)
|
||||
.build()
|
||||
then:
|
||||
userDetails.authorities == [authority] as Set
|
||||
}
|
||||
|
||||
def "authorities(String...) works"() {
|
||||
setup:
|
||||
InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager([])
|
||||
String authority = "ROLE_USER"
|
||||
when:
|
||||
UserDetails userDetails = new UserDetailsManagerConfigurer<UserDetailsManagerConfigurer<InMemoryUserDetailsManager>>(userDetailsManager)
|
||||
.withUser("user")
|
||||
.password("password")
|
||||
.authorities(authority)
|
||||
.build()
|
||||
then:
|
||||
userDetails.authorities.collect { it.authority } == [authority]
|
||||
}
|
||||
|
||||
|
||||
def "authorities(List) works"() {
|
||||
setup:
|
||||
InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager([])
|
||||
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER")
|
||||
when:
|
||||
UserDetails userDetails = new UserDetailsManagerConfigurer<UserDetailsManagerConfigurer<InMemoryUserDetailsManager>>(userDetailsManager)
|
||||
.withUser("user")
|
||||
.password("password")
|
||||
.authorities([authority])
|
||||
.build()
|
||||
then:
|
||||
userDetails.authorities == [authority] as Set
|
||||
}
|
||||
}
|
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web
|
||||
|
||||
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer
|
||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter
|
||||
import org.springframework.test.util.ReflectionTestUtils
|
||||
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class AbstractConfiguredSecurityBuilderTests extends Specification {
|
||||
|
||||
ConcreteAbstractConfiguredBuilder builder = new ConcreteAbstractConfiguredBuilder()
|
||||
|
||||
def "Null ObjectPostProcessor rejected"() {
|
||||
when:
|
||||
new ConcreteAbstractConfiguredBuilder(null)
|
||||
then:
|
||||
thrown(IllegalArgumentException)
|
||||
when:
|
||||
builder.objectPostProcessor(null);
|
||||
then:
|
||||
thrown(IllegalArgumentException)
|
||||
}
|
||||
|
||||
def "apply null is rejected"() {
|
||||
when:
|
||||
builder.apply(null)
|
||||
then:
|
||||
thrown(IllegalArgumentException)
|
||||
}
|
||||
|
||||
def "Duplicate configurer is removed"() {
|
||||
when:
|
||||
builder.apply(new ConcreteConfigurer())
|
||||
builder.apply(new ConcreteConfigurer())
|
||||
then:
|
||||
ReflectionTestUtils.getField(builder,"configurers").size() == 1
|
||||
}
|
||||
|
||||
def "build twice fails"() {
|
||||
setup:
|
||||
builder.build()
|
||||
when:
|
||||
builder.build()
|
||||
then:
|
||||
thrown(IllegalStateException)
|
||||
}
|
||||
|
||||
def "getObject before build fails"() {
|
||||
when:
|
||||
builder.getObject()
|
||||
then:
|
||||
thrown(IllegalStateException)
|
||||
}
|
||||
|
||||
def "Configurer.init can apply another configurer"() {
|
||||
setup:
|
||||
DelegateConfigurer.CONF = Mock(SecurityConfigurerAdapter)
|
||||
when:
|
||||
builder.apply(new DelegateConfigurer())
|
||||
builder.build()
|
||||
then:
|
||||
1 * DelegateConfigurer.CONF.init(builder)
|
||||
1 * DelegateConfigurer.CONF.configure(builder)
|
||||
}
|
||||
|
||||
def "getConfigurer with multi fails"() {
|
||||
setup:
|
||||
ConcreteAbstractConfiguredBuilder builder = new ConcreteAbstractConfiguredBuilder(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true)
|
||||
builder.apply(new DelegateConfigurer())
|
||||
builder.apply(new DelegateConfigurer())
|
||||
when:
|
||||
builder.getConfigurer(DelegateConfigurer)
|
||||
then: "Fail due to trying to obtain a single DelegateConfigurer and multiple are provided"
|
||||
thrown(IllegalStateException)
|
||||
}
|
||||
|
||||
def "removeConfigurer with multi fails"() {
|
||||
setup:
|
||||
ConcreteAbstractConfiguredBuilder builder = new ConcreteAbstractConfiguredBuilder(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true)
|
||||
builder.apply(new DelegateConfigurer())
|
||||
builder.apply(new DelegateConfigurer())
|
||||
when:
|
||||
builder.removeConfigurer(DelegateConfigurer)
|
||||
then: "Fail due to trying to remove and obtain a single DelegateConfigurer and multiple are provided"
|
||||
thrown(IllegalStateException)
|
||||
}
|
||||
|
||||
def "removeConfigurers with multi"() {
|
||||
setup:
|
||||
DelegateConfigurer c1 = new DelegateConfigurer()
|
||||
DelegateConfigurer c2 = new DelegateConfigurer()
|
||||
ConcreteAbstractConfiguredBuilder builder = new ConcreteAbstractConfiguredBuilder(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true)
|
||||
builder.apply(c1)
|
||||
builder.apply(c2)
|
||||
when:
|
||||
def result = builder.removeConfigurers(DelegateConfigurer)
|
||||
then:
|
||||
result.size() == 2
|
||||
result.contains(c1)
|
||||
result.contains(c2)
|
||||
builder.getConfigurers(DelegateConfigurer).empty
|
||||
}
|
||||
|
||||
def "getConfigurers with multi"() {
|
||||
setup:
|
||||
DelegateConfigurer c1 = new DelegateConfigurer()
|
||||
DelegateConfigurer c2 = new DelegateConfigurer()
|
||||
ConcreteAbstractConfiguredBuilder builder = new ConcreteAbstractConfiguredBuilder(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR, true)
|
||||
builder.apply(c1)
|
||||
builder.apply(c2)
|
||||
when:
|
||||
def result = builder.getConfigurers(DelegateConfigurer)
|
||||
then:
|
||||
result.size() == 2
|
||||
result.contains(c1)
|
||||
result.contains(c2)
|
||||
builder.getConfigurers(DelegateConfigurer).size() == 2
|
||||
}
|
||||
|
||||
private static class DelegateConfigurer extends SecurityConfigurerAdapter<Object, ConcreteAbstractConfiguredBuilder> {
|
||||
private static SecurityConfigurer<Object, ConcreteAbstractConfiguredBuilder> CONF;
|
||||
|
||||
@Override
|
||||
public void init(ConcreteAbstractConfiguredBuilder builder)
|
||||
throws Exception {
|
||||
builder.apply(CONF);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ConcreteConfigurer extends SecurityConfigurerAdapter<Object, ConcreteAbstractConfiguredBuilder> { }
|
||||
|
||||
private static class ConcreteAbstractConfiguredBuilder extends AbstractConfiguredSecurityBuilder<Object, ConcreteAbstractConfiguredBuilder> {
|
||||
|
||||
public ConcreteAbstractConfiguredBuilder() {
|
||||
}
|
||||
|
||||
public ConcreteAbstractConfiguredBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
super(objectPostProcessor);
|
||||
}
|
||||
|
||||
public ConcreteAbstractConfiguredBuilder(ObjectPostProcessor<Object> objectPostProcessor, boolean allowMulti) {
|
||||
super(objectPostProcessor,allowMulti);
|
||||
}
|
||||
|
||||
public Object performBuild() throws Exception {
|
||||
return "success";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web;
|
||||
|
||||
import static org.springframework.security.config.annotation.web.AbstractRequestMatcherConfigurer.RequestMatchers.*
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.web.util.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.RegexRequestMatcher;
|
||||
|
||||
import spock.lang.Specification;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class RequestMatchersTests extends Specification {
|
||||
|
||||
def "regexMatchers(GET,'/a.*') uses RegexRequestMatcher"() {
|
||||
when:
|
||||
def matchers = regexMatchers(HttpMethod.GET, "/a.*")
|
||||
then: 'matcher is a RegexRequestMatcher'
|
||||
matchers.collect {it.class } == [RegexRequestMatcher]
|
||||
}
|
||||
|
||||
def "regexMatchers('/a.*') uses RegexRequestMatcher"() {
|
||||
when:
|
||||
def matchers = regexMatchers("/a.*")
|
||||
then: 'matcher is a RegexRequestMatcher'
|
||||
matchers.collect {it.class } == [RegexRequestMatcher]
|
||||
}
|
||||
|
||||
def "antMatchers(GET,'/a.*') uses AntPathRequestMatcher"() {
|
||||
when:
|
||||
def matchers = antMatchers(HttpMethod.GET, "/a.*")
|
||||
then: 'matcher is a RegexRequestMatcher'
|
||||
matchers.collect {it.class } == [AntPathRequestMatcher]
|
||||
}
|
||||
|
||||
def "antMatchers('/a.*') uses AntPathRequestMatcher"() {
|
||||
when:
|
||||
def matchers = antMatchers("/a.*")
|
||||
then: 'matcher is a AntPathRequestMatcher'
|
||||
matchers.collect {it.class } == [AntPathRequestMatcher]
|
||||
}
|
||||
}
|
@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.core.annotation.Order
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.config.annotation.BaseWebSpecuritySpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
|
||||
|
||||
/**
|
||||
* Demonstrate the samples
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class SampleWebSecurityConfigurerAdapterTests extends BaseWebSpecuritySpec {
|
||||
def "README HelloWorld Sample works"() {
|
||||
setup: "Sample Config is loaded"
|
||||
loadConfig(HelloWorldWebSecurityConfigurerAdapter)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getRedirectedUrl() == "http://localhost/login"
|
||||
when: "fail to log in"
|
||||
super.setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to login error page"
|
||||
response.getRedirectedUrl() == "/login?error"
|
||||
when: "login success"
|
||||
super.setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
request.parameters.username = ["user"] as String[]
|
||||
request.parameters.password = ["password"] as String[]
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to default succes page"
|
||||
response.getRedirectedUrl() == "/"
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>
|
||||
* <http use-expressions="true">
|
||||
* <intercept-url pattern="/resources/**" access="permitAll"/>
|
||||
* <intercept-url pattern="/**" access="authenticated"/>
|
||||
* <logout
|
||||
* logout-success-url="/login?logout"
|
||||
* logout-url="/logout"
|
||||
* <form-login
|
||||
* authentication-failure-url="/login?error"
|
||||
* login-page="/login" <!-- Except Spring Security renders the login page -->
|
||||
* login-processing-url="/login" <!-- but only POST -->
|
||||
* password-parameter="password"
|
||||
* username-parameter="username"
|
||||
* />
|
||||
* </http>
|
||||
* <authentication-manager>
|
||||
* <authentication-provider>
|
||||
* <user-service>
|
||||
* <user username="user" password="password" authorities="ROLE_USER"/>
|
||||
* </user-service>
|
||||
* </authentication-provider>
|
||||
* </authentication-manager>
|
||||
* </code>
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public static class HelloWorldWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER");
|
||||
}
|
||||
}
|
||||
|
||||
def "README Sample works"() {
|
||||
setup: "Sample Config is loaded"
|
||||
loadConfig(SampleWebSecurityConfigurerAdapter)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getRedirectedUrl() == "http://localhost/login"
|
||||
when: "fail to log in"
|
||||
super.setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to login error page"
|
||||
response.getRedirectedUrl() == "/login?error"
|
||||
when: "login success"
|
||||
super.setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
request.parameters.username = ["user"] as String[]
|
||||
request.parameters.password = ["password"] as String[]
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to default succes page"
|
||||
response.getRedirectedUrl() == "/"
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>
|
||||
* <http security="none" pattern="/resources/**"/>
|
||||
* <http use-expressions="true">
|
||||
* <intercept-url pattern="/logout" access="permitAll"/>
|
||||
* <intercept-url pattern="/login" access="permitAll"/>
|
||||
* <intercept-url pattern="/signup" access="permitAll"/>
|
||||
* <intercept-url pattern="/about" access="permitAll"/>
|
||||
* <intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>
|
||||
* <logout
|
||||
* logout-success-url="/login?logout"
|
||||
* logout-url="/logout"
|
||||
* <form-login
|
||||
* authentication-failure-url="/login?error"
|
||||
* login-page="/login"
|
||||
* login-processing-url="/login" <!-- but only POST -->
|
||||
* password-parameter="password"
|
||||
* username-parameter="username"
|
||||
* />
|
||||
* </http>
|
||||
* <authentication-manager>
|
||||
* <authentication-provider>
|
||||
* <user-service>
|
||||
* <user username="user" password="password" authorities="ROLE_USER"/>
|
||||
* <user username="admin" password="password" authorities="ROLE_USER,ROLE_ADMIN"/>
|
||||
* </user-service>
|
||||
* </authentication-provider>
|
||||
* </authentication-manager>
|
||||
* </code>
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public static class SampleWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
.ignoring()
|
||||
.antMatchers("/resources/**");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.antMatchers("/signup","/about").permitAll()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
.loginUrl("/login")
|
||||
// set permitAll for all URLs associated with Form Login
|
||||
.permitAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth) {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.withUser("admin").password("password").roles("USER", "ADMIN");
|
||||
}
|
||||
}
|
||||
|
||||
def "README Multi http Sample works"() {
|
||||
setup:
|
||||
loadConfig(SampleMultiHttpSecurityConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getRedirectedUrl() == "http://localhost/login"
|
||||
when: "fail to log in"
|
||||
super.setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to login error page"
|
||||
response.getRedirectedUrl() == "/login?error"
|
||||
when: "login success"
|
||||
super.setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
request.parameters.username = ["user"] as String[]
|
||||
request.parameters.password = ["password"] as String[]
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to default succes page"
|
||||
response.getRedirectedUrl() == "/"
|
||||
|
||||
when: "request protected API URL"
|
||||
super.setup()
|
||||
request.servletPath = "/api/admin/test"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "get 403"
|
||||
response.getStatus() == 403
|
||||
|
||||
when: "request API for admins with user"
|
||||
super.setup()
|
||||
request.servletPath = "/api/admin/test"
|
||||
request.addHeader("Authorization", "Basic " + "user:password".bytes.encodeBase64().toString())
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "get 403"
|
||||
response.getStatus() == 403
|
||||
|
||||
when: "request API for admins with admin"
|
||||
super.setup()
|
||||
request.servletPath = "/api/admin/test"
|
||||
request.addHeader("Authorization", "Basic " + "admin:password".bytes.encodeBase64().toString())
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "get 200"
|
||||
response.getStatus() == 200
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <code>
|
||||
* <http security="none" pattern="/resources/**"/>
|
||||
* <http use-expressions="true" pattern="/api/**">
|
||||
* <intercept-url pattern="/api/admin/**" access="hasRole('ROLE_ADMIN')"/>
|
||||
* <intercept-url pattern="/api/**" access="hasRole('ROLE_USER')"/>
|
||||
* <http-basic />
|
||||
* </http>
|
||||
* <http use-expressions="true">
|
||||
* <intercept-url pattern="/logout" access="permitAll"/>
|
||||
* <intercept-url pattern="/login" access="permitAll"/>
|
||||
* <intercept-url pattern="/signup" access="permitAll"/>
|
||||
* <intercept-url pattern="/about" access="permitAll"/>
|
||||
* <intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>
|
||||
* <logout
|
||||
* logout-success-url="/login?logout"
|
||||
* logout-url="/logout"
|
||||
* <form-login
|
||||
* authentication-failure-url="/login?error"
|
||||
* login-page="/login"
|
||||
* login-processing-url="/login" <!-- but only POST -->
|
||||
* password-parameter="password"
|
||||
* username-parameter="username"
|
||||
* />
|
||||
* </http>
|
||||
* <authentication-manager>
|
||||
* <authentication-provider>
|
||||
* <user-service>
|
||||
* <user username="user" password="password" authorities="ROLE_USER"/>
|
||||
* <user username="admin" password="password" authorities="ROLE_USER,ROLE_ADMIN"/>
|
||||
* </user-service>
|
||||
* </authentication-provider>
|
||||
* </authentication-manager>
|
||||
* </code>
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public static class SampleMultiHttpSecurityConfig {
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager() {
|
||||
return new AuthenticationManagerBuilder()
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.withUser("admin").password("password").roles("USER", "ADMIN").and()
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Order(1)
|
||||
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/api/**")
|
||||
.authorizeUrls()
|
||||
.antMatchers("/api/admin/**").hasRole("ADMIN")
|
||||
.antMatchers("/api/**").hasRole("USER")
|
||||
.and()
|
||||
.httpBasic();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
.ignoring()
|
||||
.antMatchers("/resources/**");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.antMatchers("/signup","/about").permitAll()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
.loginUrl("/login")
|
||||
.permitAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web;
|
||||
|
||||
import static org.springframework.security.config.annotation.web.WebSecurityConfigurerAdapterTestsConfigs.*
|
||||
import static org.junit.Assert.*
|
||||
|
||||
import javax.sql.DataSource
|
||||
|
||||
import org.springframework.context.ApplicationContext
|
||||
import org.springframework.context.ApplicationListener
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.AuthenticationProvider
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.authentication.event.AuthenticationSuccessEvent
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.configurers.ldap.LdapAuthenticationProviderConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.authority.AuthorityUtils
|
||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class WebSecurityConfigurerAdapterTests extends BaseSpringSpec {
|
||||
|
||||
def "MessageSources populated on AuthenticationProviders"() {
|
||||
when:
|
||||
loadConfig(MessageSourcesPopulatedConfig)
|
||||
List<AuthenticationProvider> providers = authenticationProviders()
|
||||
then:
|
||||
providers*.messages*.messageSource == [context,context,context,context]
|
||||
}
|
||||
|
||||
def "messages set when using WebSecurityConfigurerAdapter"() {
|
||||
when:
|
||||
loadConfig(InMemoryAuthWithWebSecurityConfigurerAdapter)
|
||||
then:
|
||||
authenticationManager.messages.messageSource instanceof ApplicationContext
|
||||
}
|
||||
|
||||
def "AuthenticationEventPublisher is registered for Web registerAuthentication"() {
|
||||
when:
|
||||
loadConfig(InMemoryAuthWithWebSecurityConfigurerAdapter)
|
||||
then:
|
||||
authenticationManager.parent.eventPublisher instanceof DefaultAuthenticationEventPublisher
|
||||
when:
|
||||
Authentication token = new UsernamePasswordAuthenticationToken("user","password")
|
||||
authenticationManager.authenticate(token)
|
||||
then: "We only receive the AuthenticationSuccessEvent once"
|
||||
InMemoryAuthWithWebSecurityConfigurerAdapter.EVENTS.size() == 1
|
||||
InMemoryAuthWithWebSecurityConfigurerAdapter.EVENTS[0].authentication.name == token.principal
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class InMemoryAuthWithWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter implements ApplicationListener<AuthenticationSuccessEvent> {
|
||||
static List<AuthenticationSuccessEvent> EVENTS = []
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER")
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(AuthenticationSuccessEvent e) {
|
||||
EVENTS.add(e)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class WebSecurityConfigurerAdapterTestsConfigs {
|
||||
|
||||
// necessary because groovy resolves incorrect method when using generics
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class MessageSourcesPopulatedConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role1/**")
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("1");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BaseLdapPathContextSource contextSource() throws Exception {
|
||||
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource("ldap://127.0.0.1:33389/dc=springframework,dc=org");
|
||||
contextSource.setUserDn("uid=admin,ou=system");
|
||||
contextSource.setPassword("secret");
|
||||
return contextSource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
|
||||
return builder.setType(EmbeddedDatabaseType.HSQL).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication().and()
|
||||
.jdbcAuthentication()
|
||||
.dataSource(dataSource())
|
||||
.and()
|
||||
.ldapAuthentication()
|
||||
.userDnPatterns("uid={0},ou=people")
|
||||
.contextSource(contextSource());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.builders
|
||||
|
||||
import javax.servlet.Filter
|
||||
import javax.servlet.FilterChain
|
||||
import javax.servlet.ServletException
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.cas.web.CasAuthenticationFilter
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
||||
import org.springframework.web.filter.OncePerRequestFilter
|
||||
|
||||
/**
|
||||
* HttpSecurity tests
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class HttpSecurityTests extends BaseSpringSpec {
|
||||
def "addFilter with unregistered Filter"() {
|
||||
when:
|
||||
loadConfig(UnregisteredFilterConfig)
|
||||
then:
|
||||
BeanCreationException success = thrown()
|
||||
success.message.contains "The Filter class ${UnregisteredFilter.name} does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead."
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class UnregisteredFilterConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.addFilter(new UnregisteredFilter())
|
||||
}
|
||||
}
|
||||
|
||||
static class UnregisteredFilter extends OncePerRequestFilter {
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request,
|
||||
HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/SpringSource/spring-security-javaconfig/issues/104
|
||||
def "#104 addFilter CasAuthenticationFilter"() {
|
||||
when:
|
||||
loadConfig(CasAuthenticationFilterConfig)
|
||||
then:
|
||||
findFilter(CasAuthenticationFilter)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CasAuthenticationFilterConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.addFilter(new CasAuthenticationFilter())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def "requestMatchers() javadoc"() {
|
||||
setup: "load configuration like the config on the requestMatchers() javadoc"
|
||||
loadConfig(RequestMatcherRegistryConfigs)
|
||||
when:
|
||||
super.setup()
|
||||
request.servletPath = "/oauth/a"
|
||||
springSecurityFilterChain.doFilter(request, response, chain)
|
||||
then:
|
||||
response.status == 403
|
||||
where:
|
||||
servletPath | status
|
||||
"/oauth/a" | 403
|
||||
"/oauth/b" | 403
|
||||
"/api/a" | 403
|
||||
"/api/b" | 403
|
||||
"/oauth2/b" | 200
|
||||
"/api2/b" | 200
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class RequestMatcherRegistryConfigs extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.requestMatchers()
|
||||
.antMatchers("/api/**")
|
||||
.antMatchers("/oauth/**")
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.antMatchers("/**").hasRole("USER")
|
||||
.and()
|
||||
.httpBasic()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,511 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.builders
|
||||
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.access.AccessDecisionManager
|
||||
import org.springframework.security.access.ConfigAttribute
|
||||
import org.springframework.security.access.vote.AuthenticatedVoter
|
||||
import org.springframework.security.access.vote.RoleVoter
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.BadCredentialsException
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.builders.NamespaceHttpTests.AuthenticationManagerRefConfig.CustomAuthenticationManager
|
||||
import org.springframework.security.config.annotation.web.builders.NamespaceHttpTests.RequestMatcherRefConfig.MyRequestMatcher
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.SessionCreationPolicy
|
||||
import org.springframework.security.config.annotation.web.configurers.UrlAuthorizationConfigurer
|
||||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.AuthenticationException
|
||||
import org.springframework.security.web.FilterInvocation
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter
|
||||
import org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource
|
||||
import org.springframework.security.web.access.expression.WebExpressionVoter
|
||||
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository
|
||||
import org.springframework.security.web.context.NullSecurityContextRepository
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter
|
||||
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache
|
||||
import org.springframework.security.web.savedrequest.NullRequestCache
|
||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
|
||||
import org.springframework.security.web.session.SessionManagementFilter
|
||||
import org.springframework.security.web.util.RegexRequestMatcher
|
||||
import org.springframework.security.web.util.RequestMatcher
|
||||
|
||||
/**
|
||||
* Tests to verify that all the functionality of <http> attributes is present
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class NamespaceHttpTests extends BaseSpringSpec {
|
||||
def "http@access-decision-manager-ref"() {
|
||||
setup:
|
||||
AccessDecisionManagerRefConfig.ACCESS_DECISION_MGR = Mock(AccessDecisionManager)
|
||||
AccessDecisionManagerRefConfig.ACCESS_DECISION_MGR.supports(FilterInvocation) >> true
|
||||
AccessDecisionManagerRefConfig.ACCESS_DECISION_MGR.supports(_ as ConfigAttribute) >> true
|
||||
when:
|
||||
loadConfig(AccessDecisionManagerRefConfig)
|
||||
then:
|
||||
findFilter(FilterSecurityInterceptor).accessDecisionManager == AccessDecisionManagerRefConfig.ACCESS_DECISION_MGR
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AccessDecisionManagerRefConfig extends BaseWebConfig {
|
||||
static AccessDecisionManager ACCESS_DECISION_MGR
|
||||
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().permitAll()
|
||||
.accessDecisionManager(ACCESS_DECISION_MGR)
|
||||
}
|
||||
}
|
||||
|
||||
def "http@access-denied-page"() {
|
||||
when:
|
||||
loadConfig(AccessDeniedPageConfig)
|
||||
then:
|
||||
findFilter(ExceptionTranslationFilter).accessDeniedHandler.errorPage == "/AccessDeniedPageConfig"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AccessDeniedPageConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.exceptionHandling()
|
||||
.accessDeniedPage("/AccessDeniedPageConfig")
|
||||
}
|
||||
}
|
||||
|
||||
def "http@authentication-manager-ref"() {
|
||||
when: "Specify AuthenticationManager"
|
||||
loadConfig(AuthenticationManagerRefConfig)
|
||||
then: "Populates the AuthenticationManager"
|
||||
findFilter(FilterSecurityInterceptor).authenticationManager.parent.class == CustomAuthenticationManager
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AuthenticationManagerRefConfig extends BaseWebConfig {
|
||||
// demo authentication-manager-ref (could be any value)
|
||||
|
||||
@Override
|
||||
protected AuthenticationManager authenticationManager() throws Exception {
|
||||
return new CustomAuthenticationManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER");
|
||||
}
|
||||
|
||||
static class CustomAuthenticationManager implements AuthenticationManager {
|
||||
public Authentication authenticate(Authentication authentication)
|
||||
throws AuthenticationException {
|
||||
throw new BadCredentialsException("This always fails");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: There is no http@auto-config equivalent in Java Config
|
||||
|
||||
def "http@create-session=always"() {
|
||||
when:
|
||||
loadConfig(IfRequiredConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false
|
||||
findFilter(SecurityContextPersistenceFilter).repo.allowSessionCreation == true
|
||||
findFilter(SessionManagementFilter).securityContextRepository.allowSessionCreation == true
|
||||
findFilter(ExceptionTranslationFilter).requestCache.class == HttpSessionRequestCache
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CreateSessionAlwaysConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.always);
|
||||
}
|
||||
}
|
||||
|
||||
def "http@create-session=stateless"() {
|
||||
when:
|
||||
loadConfig(CreateSessionStatelessConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false
|
||||
findFilter(SecurityContextPersistenceFilter).repo.class == NullSecurityContextRepository
|
||||
findFilter(SessionManagementFilter).securityContextRepository.class == NullSecurityContextRepository
|
||||
findFilter(ExceptionTranslationFilter).requestCache.class == NullRequestCache
|
||||
findFilter(RequestCacheAwareFilter).requestCache.class == NullRequestCache
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CreateSessionStatelessConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.stateless);
|
||||
}
|
||||
}
|
||||
|
||||
def "http@create-session=ifRequired"() {
|
||||
when:
|
||||
loadConfig(IfRequiredConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false
|
||||
findFilter(SecurityContextPersistenceFilter).repo.allowSessionCreation == true
|
||||
findFilter(SessionManagementFilter).securityContextRepository.allowSessionCreation == true
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class IfRequiredConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.ifRequired);
|
||||
}
|
||||
}
|
||||
|
||||
def "http@create-session defaults to ifRequired"() {
|
||||
when:
|
||||
loadConfig(IfRequiredConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false
|
||||
findFilter(SecurityContextPersistenceFilter).repo.allowSessionCreation == true
|
||||
findFilter(SessionManagementFilter).securityContextRepository.allowSessionCreation == true
|
||||
}
|
||||
|
||||
def "http@create-session=never"() {
|
||||
when:
|
||||
loadConfig(CreateSessionNeverConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).forceEagerSessionCreation == false
|
||||
findFilter(SecurityContextPersistenceFilter).repo.allowSessionCreation == false
|
||||
findFilter(SessionManagementFilter).securityContextRepository.allowSessionCreation == false
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CreateSessionNeverConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.never);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultCreateSessionConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
}
|
||||
}
|
||||
|
||||
def "http@disable-url-rewriting = true (default for Java Config)"() {
|
||||
when:
|
||||
loadConfig(DefaultUrlRewritingConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).repo.disableUrlRewriting
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultUrlRewritingConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
}
|
||||
}
|
||||
|
||||
// http@disable-url-rewriting is on by default to disable it create a custom HttpSecurityContextRepository and use security-context-repository-ref
|
||||
|
||||
def "http@disable-url-rewriting = false"() {
|
||||
when:
|
||||
loadConfig(EnableUrlRewritingConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).repo.disableUrlRewriting == false
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class EnableUrlRewritingConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
HttpSessionSecurityContextRepository repository = new HttpSessionSecurityContextRepository()
|
||||
repository.disableUrlRewriting = false // explicitly configured (not necessary due to default values)
|
||||
|
||||
http.
|
||||
securityContext()
|
||||
.securityContextRepository(repository)
|
||||
}
|
||||
}
|
||||
|
||||
def "http@entry-point-ref"() {
|
||||
when:
|
||||
loadConfig(EntryPointRefConfig)
|
||||
then:
|
||||
findFilter(ExceptionTranslationFilter).authenticationEntryPoint.loginFormUrl == "/EntryPointRefConfig"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class EntryPointRefConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.exceptionHandling()
|
||||
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/EntryPointRefConfig"))
|
||||
}
|
||||
}
|
||||
|
||||
def "http@jaas-api-provision"() {
|
||||
when:
|
||||
loadConfig(JaasApiProvisionConfig)
|
||||
then:
|
||||
findFilter(JaasApiIntegrationFilter)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class JaasApiProvisionConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.addFilter(new JaasApiIntegrationFilter())
|
||||
}
|
||||
}
|
||||
|
||||
// http@name is not available since it can be done w/ standard bean configuration easily
|
||||
|
||||
def "http@once-per-request=true"() {
|
||||
when:
|
||||
loadConfig(OncePerRequestConfig)
|
||||
then:
|
||||
findFilter(FilterSecurityInterceptor).observeOncePerRequest
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class OncePerRequestConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER");
|
||||
}
|
||||
}
|
||||
|
||||
def "http@once-per-request=false"() {
|
||||
when:
|
||||
loadConfig(OncePerRequestFalseConfig)
|
||||
then:
|
||||
!findFilter(FilterSecurityInterceptor).observeOncePerRequest
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class OncePerRequestFalseConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.
|
||||
authorizeUrls()
|
||||
.filterSecurityInterceptorOncePerRequest(false)
|
||||
.antMatchers("/users**","/sessions/**").hasRole("ADMIN")
|
||||
.antMatchers("/signup").permitAll()
|
||||
.anyRequest().hasRole("USER");
|
||||
}
|
||||
}
|
||||
|
||||
// http@path-type is not available (instead request matcher instances are used)
|
||||
|
||||
// http@pattern is not available (instead see the tests http@request-matcher-ref ant or http@request-matcher-ref regex)
|
||||
|
||||
def "http@realm"() {
|
||||
when:
|
||||
loadConfig(RealmConfig)
|
||||
then:
|
||||
findFilter(BasicAuthenticationFilter).authenticationEntryPoint.realmName == "RealmConfig"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RealmConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.httpBasic().realmName("RealmConfig")
|
||||
}
|
||||
}
|
||||
|
||||
// http@request-matcher is not available (instead request matcher instances are used)
|
||||
|
||||
def "http@request-matcher-ref ant"() {
|
||||
when:
|
||||
loadConfig(RequestMatcherAntConfig)
|
||||
then:
|
||||
filterChain(0).requestMatcher.pattern == "/api/**"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RequestMatcherAntConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/api/**")
|
||||
}
|
||||
}
|
||||
|
||||
def "http@request-matcher-ref regex"() {
|
||||
when:
|
||||
loadConfig(RequestMatcherRegexConfig)
|
||||
then:
|
||||
filterChain(0).requestMatcher.class == RegexRequestMatcher
|
||||
filterChain(0).requestMatcher.pattern.matcher("/regex/a")
|
||||
filterChain(0).requestMatcher.pattern.matcher("/regex/b")
|
||||
!filterChain(0).requestMatcher.pattern.matcher("/regex1/b")
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RequestMatcherRegexConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.regexMatcher("/regex/.*")
|
||||
}
|
||||
}
|
||||
|
||||
def "http@request-matcher-ref"() {
|
||||
when:
|
||||
loadConfig(RequestMatcherRefConfig)
|
||||
then:
|
||||
filterChain(0).requestMatcher.class == MyRequestMatcher
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class RequestMatcherRefConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.requestMatcher(new MyRequestMatcher());
|
||||
}
|
||||
static class MyRequestMatcher implements RequestMatcher {
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "http@security=none"() {
|
||||
when:
|
||||
loadConfig(SecurityNoneConfig)
|
||||
then:
|
||||
filterChain(0).requestMatcher.pattern == "/resources/**"
|
||||
filterChain(0).filters.empty
|
||||
filterChain(1).requestMatcher.pattern == "/public/**"
|
||||
filterChain(1).filters.empty
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SecurityNoneConfig extends BaseWebConfig {
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web)
|
||||
throws Exception {
|
||||
web
|
||||
.ignoring()
|
||||
.antMatchers("/resources/**","/public/**")
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {}
|
||||
|
||||
}
|
||||
|
||||
def "http@security-context-repository-ref"() {
|
||||
when:
|
||||
loadConfig(SecurityContextRepoConfig)
|
||||
then:
|
||||
findFilter(SecurityContextPersistenceFilter).repo.class == NullSecurityContextRepository
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SecurityContextRepoConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.securityContext()
|
||||
.securityContextRepository(new NullSecurityContextRepository()) // security-context-repository-ref
|
||||
}
|
||||
}
|
||||
|
||||
def "http@servlet-api-provision=false"() {
|
||||
when:
|
||||
loadConfig(ServletApiProvisionConfig)
|
||||
then:
|
||||
findFilter(SecurityContextHolderAwareRequestFilter) == null
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ServletApiProvisionConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.servletApi().disable()
|
||||
}
|
||||
}
|
||||
|
||||
def "http@servlet-api-provision defaults to true"() {
|
||||
when:
|
||||
loadConfig(ServletApiProvisionDefaultsConfig)
|
||||
then:
|
||||
findFilter(SecurityContextHolderAwareRequestFilter) != null
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ServletApiProvisionDefaultsConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
}
|
||||
}
|
||||
|
||||
def "http@use-expressions=true"() {
|
||||
when:
|
||||
loadConfig(UseExpressionsConfig)
|
||||
then:
|
||||
findFilter(FilterSecurityInterceptor).securityMetadataSource.class == ExpressionBasedFilterInvocationSecurityMetadataSource
|
||||
findFilter(FilterSecurityInterceptor).accessDecisionManager.decisionVoters.collect { it.class } == [WebExpressionVoter]
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class UseExpressionsConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.antMatchers("/users**","/sessions/**").hasRole("USER")
|
||||
.antMatchers("/signup").permitAll()
|
||||
.anyRequest().hasRole("USER")
|
||||
}
|
||||
}
|
||||
|
||||
def "http@use-expressions=false"() {
|
||||
when:
|
||||
loadConfig(DisableUseExpressionsConfig)
|
||||
then:
|
||||
findFilter(FilterSecurityInterceptor).securityMetadataSource.class == DefaultFilterInvocationSecurityMetadataSource
|
||||
findFilter(FilterSecurityInterceptor).accessDecisionManager.decisionVoters.collect { it.class } == [RoleVoter, AuthenticatedVoter]
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class DisableUseExpressionsConfig extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.apply(new UrlAuthorizationConfigurer())
|
||||
.antMatchers("/users**","/sessions/**").hasRole("USER")
|
||||
.antMatchers("/signup").hasRole("ANONYMOUS")
|
||||
.anyRequest().hasRole("USER")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.Configuration
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public abstract class BaseWebConfig extends WebSecurityConfigurerAdapter {
|
||||
BaseWebConfig(boolean disableDefaults) {
|
||||
super(disableDefaults)
|
||||
}
|
||||
|
||||
BaseWebConfig() {
|
||||
}
|
||||
|
||||
protected void registerAuthentication(
|
||||
AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.withUser("admin").password("password").roles("USER", "ADMIN");
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.web.builders.DebugFilter;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter
|
||||
|
||||
class EnableWebSecurityTests extends BaseSpringSpec {
|
||||
|
||||
def "@Bean(BeanIds.AUTHENTICATION_MANAGER) includes HttpSecurity's AuthenticationManagerBuilder"() {
|
||||
when:
|
||||
loadConfig(SecurityConfig)
|
||||
AuthenticationManager authenticationManager = context.getBean(AuthenticationManager)
|
||||
AnonymousAuthenticationToken anonymousAuthToken = findFilter(AnonymousAuthenticationFilter).createAuthentication(new MockHttpServletRequest())
|
||||
then:
|
||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user", "password"))
|
||||
authenticationManager.authenticate(anonymousAuthToken)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER");
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean()
|
||||
throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.antMatchers("/*").hasRole("USER")
|
||||
.and()
|
||||
.formLogin();
|
||||
}
|
||||
}
|
||||
|
||||
def "@EnableWebSecurity on superclass"() {
|
||||
when:
|
||||
loadConfig(ChildSecurityConfig)
|
||||
then:
|
||||
context.getBean("springSecurityFilterChain", DebugFilter)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ChildSecurityConfig extends DebugSecurityConfig {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity(debug=true)
|
||||
static class DebugSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configuration;
|
||||
|
||||
import static org.junit.Assert.*
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.core.annotation.Order
|
||||
import org.springframework.security.authentication.AuthenticationManager
|
||||
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.expression.WebSecurityExpressionHandler;
|
||||
import org.springframework.security.web.util.AnyRequestMatcher
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class WebSecurityConfigurationTests extends BaseSpringSpec {
|
||||
|
||||
def "WebSecurityConfigurers are sorted"() {
|
||||
when:
|
||||
loadConfig(SortedWebSecurityConfigurerAdaptersConfig);
|
||||
List<SecurityFilterChain> filterChains = context.getBean(FilterChainProxy).filterChains
|
||||
then:
|
||||
filterChains[0].requestMatcher.pattern == "/ignore1"
|
||||
filterChains[0].filters.empty
|
||||
filterChains[1].requestMatcher.pattern == "/ignore2"
|
||||
filterChains[1].filters.empty
|
||||
|
||||
filterChains[2].requestMatcher.pattern == "/role1/**"
|
||||
filterChains[3].requestMatcher.pattern == "/role2/**"
|
||||
filterChains[4].requestMatcher.pattern == "/role3/**"
|
||||
filterChains[5].requestMatcher.class == AnyRequestMatcher
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class SortedWebSecurityConfigurerAdaptersConfig {
|
||||
public AuthenticationManager authenticationManager() throws Exception {
|
||||
return new AuthenticationManagerBuilder()
|
||||
.inMemoryAuthentication()
|
||||
.withUser("marissa").password("koala").roles("USER").and()
|
||||
.withUser("paul").password("emu").roles("USER").and()
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Order(1)
|
||||
public static class WebConfigurer1 extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
.ignoring()
|
||||
.antMatchers("/ignore1","/ignore2");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role1/**")
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("1");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Order(2)
|
||||
public static class WebConfigurer2 extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role2/**")
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("2");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Order(3)
|
||||
public static class WebConfigurer3 extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role3/**")
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("3");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class WebConfigurer4 extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("4");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "WebSecurityConfigurers fails with duplicate order"() {
|
||||
when:
|
||||
loadConfig(DuplicateOrderConfig);
|
||||
then:
|
||||
BeanCreationException e = thrown()
|
||||
e.message.contains "@Order on WebSecurityConfigurers must be unique"
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class DuplicateOrderConfig {
|
||||
public AuthenticationManager authenticationManager() throws Exception {
|
||||
return new AuthenticationManagerBuilder()
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER").and()
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class WebConfigurer1 extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role1/**")
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("1");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class WebConfigurer2 extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.antMatcher("/role2/**")
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("2");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "Override privilegeEvaluator"() {
|
||||
setup:
|
||||
WebInvocationPrivilegeEvaluator privilegeEvaluator = Mock()
|
||||
PrivilegeEvaluatorConfigurerAdapterConfig.PE = privilegeEvaluator
|
||||
when:
|
||||
loadConfig(PrivilegeEvaluatorConfigurerAdapterConfig)
|
||||
then:
|
||||
context.getBean(WebInvocationPrivilegeEvaluator) == privilegeEvaluator
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class PrivilegeEvaluatorConfigurerAdapterConfig extends WebSecurityConfigurerAdapter {
|
||||
static WebInvocationPrivilegeEvaluator PE
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
.privilegeEvaluator(PE)
|
||||
}
|
||||
}
|
||||
|
||||
def "Override webSecurityExpressionHandler"() {
|
||||
setup:
|
||||
WebSecurityExpressionHandler expressionHandler = Mock()
|
||||
WebSecurityExpressionHandlerConfig.EH = expressionHandler
|
||||
when:
|
||||
loadConfig(WebSecurityExpressionHandlerConfig)
|
||||
then:
|
||||
context.getBean(WebSecurityExpressionHandler) == expressionHandler
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class WebSecurityExpressionHandlerConfig extends WebSecurityConfigurerAdapter {
|
||||
@SuppressWarnings("deprecation")
|
||||
static WebSecurityExpressionHandler EH
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
.expressionHandler(EH)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractRequestMatcherMappingConfigurer;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
|
||||
import org.springframework.security.web.util.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.RegexRequestMatcher;
|
||||
import org.springframework.security.web.util.RequestMatcher;
|
||||
|
||||
import spock.lang.Specification;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
class AbstractRequestMatcherMappingConfigurerTests extends Specification {
|
||||
ConcreteAbstractRequestMatcherMappingConfigurer registry = new ConcreteAbstractRequestMatcherMappingConfigurer()
|
||||
|
||||
def "regexMatchers(GET,'/a.*') uses RegexRequestMatcher"() {
|
||||
when:
|
||||
def matchers = registry.regexMatchers(HttpMethod.GET,"/a.*")
|
||||
then: 'matcher is a RegexRequestMatcher'
|
||||
matchers.collect {it.class } == [RegexRequestMatcher]
|
||||
}
|
||||
|
||||
def "regexMatchers('/a.*') uses RegexRequestMatcher"() {
|
||||
when:
|
||||
def matchers = registry.regexMatchers("/a.*")
|
||||
then: 'matcher is a RegexRequestMatcher'
|
||||
matchers.collect {it.class } == [RegexRequestMatcher]
|
||||
}
|
||||
|
||||
def "antMatchers(GET,'/a.*') uses AntPathRequestMatcher"() {
|
||||
when:
|
||||
def matchers = registry.antMatchers(HttpMethod.GET, "/a.*")
|
||||
then: 'matcher is a RegexRequestMatcher'
|
||||
matchers.collect {it.class } == [AntPathRequestMatcher]
|
||||
}
|
||||
|
||||
def "antMatchers('/a.*') uses AntPathRequestMatcher"() {
|
||||
when:
|
||||
def matchers = registry.antMatchers("/a.*")
|
||||
then: 'matcher is a AntPathRequestMatcher'
|
||||
matchers.collect {it.class } == [AntPathRequestMatcher]
|
||||
}
|
||||
|
||||
static class ConcreteAbstractRequestMatcherMappingConfigurer extends AbstractRequestMatcherMappingConfigurer<HttpSecurity,List<RequestMatcher>,DefaultSecurityFilterChain> {
|
||||
List<AccessDecisionVoter> decisionVoters() {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<RequestMatcher> chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
|
||||
return requestMatchers;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl
|
||||
import org.springframework.security.web.access.channel.ChannelProcessingFilter
|
||||
import org.springframework.security.web.access.channel.InsecureChannelProcessor
|
||||
import org.springframework.security.web.access.channel.SecureChannelProcessor
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class ChannelSecurityConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "requiresChannel ObjectPostProcessor"() {
|
||||
setup: "initialize the AUTH_FILTER as a mock"
|
||||
AnyObjectPostProcessor objectPostProcessor = Mock()
|
||||
when:
|
||||
HttpSecurity http = new HttpSecurity(objectPostProcessor, authenticationBldr, [:])
|
||||
http
|
||||
.requiresChannel()
|
||||
.anyRequest().requiresSecure()
|
||||
.and()
|
||||
.build()
|
||||
|
||||
then: "InsecureChannelProcessor is registered with LifecycleManager"
|
||||
1 * objectPostProcessor.postProcess(_ as InsecureChannelProcessor) >> {InsecureChannelProcessor o -> o}
|
||||
and: "SecureChannelProcessor is registered with LifecycleManager"
|
||||
1 * objectPostProcessor.postProcess(_ as SecureChannelProcessor) >> {SecureChannelProcessor o -> o}
|
||||
and: "ChannelDecisionManagerImpl is registered with LifecycleManager"
|
||||
1 * objectPostProcessor.postProcess(_ as ChannelDecisionManagerImpl) >> {ChannelDecisionManagerImpl o -> o}
|
||||
and: "ChannelProcessingFilter is registered with LifecycleManager"
|
||||
1 * objectPostProcessor.postProcess(_ as ChannelProcessingFilter) >> {ChannelProcessingFilter o -> o}
|
||||
}
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.BeanCreationException
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter
|
||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
import org.springframework.security.web.util.AnyRequestMatcher
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class DefaultFiltersTests extends BaseSpringSpec {
|
||||
def missingConfigMessage = "At least one non-null instance of "+ WebSecurityConfigurer.class.getSimpleName()+" must be exposed as a @Bean when using @EnableWebSecurity. Hint try extending "+ WebSecurityConfigurerAdapter.class.getSimpleName()
|
||||
|
||||
def "DefaultSecurityFilterChainBuilder cannot be null"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(FilterChainProxyBuilderMissingConfig)
|
||||
then:
|
||||
BeanCreationException e = thrown()
|
||||
e.message.contains missingConfigMessage
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class FilterChainProxyBuilderMissingConfig { }
|
||||
|
||||
def "FilterChainProxyBuilder no DefaultSecurityFilterChainBuilder specified"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(FilterChainProxyBuilderNoSecurityFilterBuildersConfig)
|
||||
then:
|
||||
BeanCreationException e = thrown()
|
||||
e.message.contains missingConfigMessage
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class FilterChainProxyBuilderNoSecurityFilterBuildersConfig {
|
||||
@Bean
|
||||
public WebSecurity filterChainProxyBuilder() {
|
||||
new WebSecurity()
|
||||
.ignoring()
|
||||
.antMatchers("/resources/**")
|
||||
}
|
||||
}
|
||||
|
||||
def "null WebInvocationPrivilegeEvaluator"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(NullWebInvocationPrivilegeEvaluatorConfig)
|
||||
then:
|
||||
List<DefaultSecurityFilterChain> filterChains = context.getBean(FilterChainProxy).filterChains
|
||||
filterChains.size() == 1
|
||||
filterChains[0].requestMatcher instanceof AnyRequestMatcher
|
||||
filterChains[0].filters.size() == 1
|
||||
filterChains[0].filters.find { it instanceof UsernamePasswordAuthenticationFilter }
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class NullWebInvocationPrivilegeEvaluatorConfig extends BaseWebConfig {
|
||||
NullWebInvocationPrivilegeEvaluatorConfig() {
|
||||
super(true)
|
||||
}
|
||||
|
||||
protected void configure(HttpSecurity http) {
|
||||
http.formLogin()
|
||||
}
|
||||
}
|
||||
|
||||
def "FilterChainProxyBuilder ignoring resources"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(FilterChainProxyBuilderIgnoringConfig)
|
||||
then:
|
||||
List<DefaultSecurityFilterChain> filterChains = context.getBean(FilterChainProxy).filterChains
|
||||
filterChains.size() == 2
|
||||
filterChains[0].requestMatcher.pattern == '/resources/**'
|
||||
filterChains[0].filters.empty
|
||||
filterChains[1].requestMatcher instanceof AnyRequestMatcher
|
||||
filterChains[1].filters.collect { it.class } ==
|
||||
[SecurityContextPersistenceFilter, LogoutFilter, RequestCacheAwareFilter,
|
||||
SecurityContextHolderAwareRequestFilter, AnonymousAuthenticationFilter, SessionManagementFilter,
|
||||
ExceptionTranslationFilter, FilterSecurityInterceptor ]
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class FilterChainProxyBuilderIgnoringConfig extends BaseWebConfig {
|
||||
@Override
|
||||
public void configure(WebSecurity builder) throws Exception {
|
||||
builder
|
||||
.ignoring()
|
||||
.antMatchers("/resources/**");
|
||||
}
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER");
|
||||
}
|
||||
}
|
||||
|
||||
def "DefaultFilters.permitAll()"() {
|
||||
when:
|
||||
context = new AnnotationConfigApplicationContext(DefaultFiltersConfigPermitAll)
|
||||
then:
|
||||
FilterChainProxy filterChain = context.getBean(FilterChainProxy)
|
||||
|
||||
expect:
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
filterChain.doFilter(new MockHttpServletRequest(servletPath : uri, queryString: query), response, new MockFilterChain())
|
||||
response.redirectedUrl == null
|
||||
where:
|
||||
uri | query
|
||||
"/logout" | null
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class DefaultFiltersConfigPermitAll extends BaseWebConfig {
|
||||
protected void configure(HttpSecurity http) {
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,343 @@
|
||||
|
||||
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.HttpSession
|
||||
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.config.annotation.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig;
|
||||
import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageViewFilter;
|
||||
|
||||
/**
|
||||
* Tests to verify that {@link DefaultLoginPageConfigurer} works
|
||||
*
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class DefaultLoginPageConfigurerTests extends BaseSpringSpec {
|
||||
FilterChainProxy springSecurityFilterChain
|
||||
MockHttpServletRequest request
|
||||
MockHttpServletResponse response
|
||||
MockFilterChain chain
|
||||
|
||||
def setup() {
|
||||
request = new MockHttpServletRequest(method:"GET")
|
||||
response = new MockHttpServletResponse()
|
||||
chain = new MockFilterChain()
|
||||
}
|
||||
|
||||
def "http/form-login default login generating page"() {
|
||||
setup:
|
||||
loadConfig(DefaultLoginPageConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
findFilter(DefaultLoginPageViewFilter)
|
||||
response.getRedirectedUrl() == "http://localhost/login"
|
||||
when: "request the login page"
|
||||
setup()
|
||||
request.requestURI = "/login"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
|
||||
<h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
|
||||
<table>
|
||||
<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
|
||||
<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form></body></html>"""
|
||||
when: "fail to log in"
|
||||
setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to login error page"
|
||||
response.getRedirectedUrl() == "/login?error"
|
||||
when: "request the error page"
|
||||
HttpSession session = request.session
|
||||
setup()
|
||||
request.session = session
|
||||
request.requestURI = "/login"
|
||||
request.queryString = "error"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
|
||||
<p><font color='red'>Your login attempt was not successful, try again.<br/><br/>Reason: Bad credentials</font></p><h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
|
||||
<table>
|
||||
<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
|
||||
<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form></body></html>"""
|
||||
when: "login success"
|
||||
setup()
|
||||
request.requestURI = "/login"
|
||||
request.method = "POST"
|
||||
request.parameters.username = ["user"] as String[]
|
||||
request.parameters.password = ["password"] as String[]
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to default succes page"
|
||||
response.getRedirectedUrl() == "/"
|
||||
}
|
||||
|
||||
def "logout success renders"() {
|
||||
setup:
|
||||
loadConfig(DefaultLoginPageConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when: "logout success"
|
||||
request.requestURI = "/login"
|
||||
request.queryString = "logout"
|
||||
request.method = "GET"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "sent to default success page"
|
||||
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
|
||||
<p><font color='green'>You have been logged out</font></p><h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
|
||||
<table>
|
||||
<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
|
||||
<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form></body></html>"""
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultLoginPageConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
}
|
||||
}
|
||||
|
||||
def "custom logout success handler prevents rendering"() {
|
||||
setup:
|
||||
loadConfig(DefaultLoginPageCustomLogoutSuccessHandlerConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when: "logout success"
|
||||
request.requestURI = "/login"
|
||||
request.queryString = "logout"
|
||||
request.method = "GET"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "default success page is NOT rendered (application is in charge of it)"
|
||||
response.getContentAsString() == ""
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultLoginPageCustomLogoutSuccessHandlerConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.logout()
|
||||
.logoutSuccessHandler(new SimpleUrlLogoutSuccessHandler())
|
||||
.and()
|
||||
.formLogin()
|
||||
}
|
||||
}
|
||||
|
||||
def "custom logout success url prevents rendering"() {
|
||||
setup:
|
||||
loadConfig(DefaultLoginPageCustomLogoutConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when: "logout success"
|
||||
request.requestURI = "/login"
|
||||
request.queryString = "logout"
|
||||
request.method = "GET"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "default success page is NOT rendered (application is in charge of it)"
|
||||
response.getContentAsString() == ""
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultLoginPageCustomLogoutConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.logout()
|
||||
.logoutSuccessUrl("/login?logout")
|
||||
.and()
|
||||
.formLogin()
|
||||
}
|
||||
}
|
||||
|
||||
def "http/form-login default login with remember me"() {
|
||||
setup:
|
||||
loadConfig(DefaultLoginPageWithRememberMeConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when: "request the login page"
|
||||
setup()
|
||||
request.requestURI = "/login"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
|
||||
<h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
|
||||
<table>
|
||||
<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
|
||||
<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
|
||||
<tr><td><input type='checkbox' name='remember-me'/></td><td>Remember me on this computer.</td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form></body></html>"""
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultLoginPageWithRememberMeConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
.and()
|
||||
.rememberMe()
|
||||
}
|
||||
}
|
||||
|
||||
def "http/form-login default login with openid"() {
|
||||
setup:
|
||||
loadConfig(DefaultLoginPageWithOpenIDConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when: "request the login page"
|
||||
request.requestURI = "/login"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getContentAsString() == """<html><head><title>Login Page</title></head><h3>Login with OpenID Identity</h3><form name='oidf' action='/login/openid' method='POST'>
|
||||
<table>
|
||||
<tr><td>Identity:</td><td><input type='text' size='30' name='openid_identifier'/></td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form></body></html>"""
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultLoginPageWithOpenIDConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.openidLogin()
|
||||
}
|
||||
}
|
||||
|
||||
def "http/form-login default login with openid, form login, and rememberme"() {
|
||||
setup:
|
||||
loadConfig(DefaultLoginPageWithFormLoginOpenIDRememberMeConfig)
|
||||
springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when: "request the login page"
|
||||
request.requestURI = "/login"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getContentAsString() == """<html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
|
||||
<h3>Login with Username and Password</h3><form name='f' action='/login' method='POST'>
|
||||
<table>
|
||||
<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
|
||||
<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
|
||||
<tr><td><input type='checkbox' name='remember-me'/></td><td>Remember me on this computer.</td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form><h3>Login with OpenID Identity</h3><form name='oidf' action='/login/openid' method='POST'>
|
||||
<table>
|
||||
<tr><td>Identity:</td><td><input type='text' size='30' name='openid_identifier'/></td></tr>
|
||||
<tr><td><input type='checkbox' name='remember-me'></td><td>Remember me on this computer.</td></tr>
|
||||
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
|
||||
</table>
|
||||
</form></body></html>"""
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultLoginPageWithFormLoginOpenIDRememberMeConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.rememberMe()
|
||||
.and()
|
||||
.formLogin()
|
||||
.and()
|
||||
.openidLogin()
|
||||
}
|
||||
}
|
||||
|
||||
def "default login with custom AuthenticationEntryPoint"() {
|
||||
when:
|
||||
loadConfig(DefaultLoginWithCustomAuthenticationEntryPointConfig)
|
||||
then:
|
||||
!findFilter(DefaultLoginPageViewFilter)
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DefaultLoginWithCustomAuthenticationEntryPointConfig extends BaseWebConfig {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.exceptionHandling()
|
||||
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
}
|
||||
}
|
||||
|
||||
def "DefaultLoginPage ObjectPostProcessor"() {
|
||||
setup:
|
||||
AnyObjectPostProcessor objectPostProcessor = Mock()
|
||||
when:
|
||||
HttpSecurity http = new HttpSecurity(objectPostProcessor, authenticationBldr, [:])
|
||||
DefaultLoginPageConfigurer defaultLoginConfig = new DefaultLoginPageConfigurer([builder:http])
|
||||
defaultLoginConfig.addObjectPostProcessor(objectPostProcessor)
|
||||
http
|
||||
// must set builder manually due to groovy not selecting correct method
|
||||
.apply(defaultLoginConfig).and()
|
||||
.formLogin()
|
||||
.and()
|
||||
.build()
|
||||
|
||||
then: "DefaultLoginPageGeneratingFilter is registered with LifecycleManager"
|
||||
1 * objectPostProcessor.postProcess(_ as DefaultLoginPageViewFilter) >> {DefaultLoginPageViewFilter o -> o}
|
||||
1 * objectPostProcessor.postProcess(_ as UsernamePasswordAuthenticationFilter) >> {UsernamePasswordAuthenticationFilter o -> o}
|
||||
1 * objectPostProcessor.postProcess(_ as LoginUrlAuthenticationEntryPoint) >> {LoginUrlAuthenticationEntryPoint o -> o}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class ExceptionHandlingConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "exception ObjectPostProcessor"() {
|
||||
setup: "initialize the AUTH_FILTER as a mock"
|
||||
AnyObjectPostProcessor opp = Mock()
|
||||
when:
|
||||
HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
|
||||
http
|
||||
.exceptionHandling()
|
||||
.and()
|
||||
.build()
|
||||
|
||||
then: "ExceptionTranslationFilter is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as ExceptionTranslationFilter) >> {ExceptionTranslationFilter o -> o}
|
||||
}
|
||||
}
|
@ -0,0 +1,413 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.BeanCreationException;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.access.vote.AffirmativeBased;
|
||||
import org.springframework.security.authentication.RememberMeAuthenticationToken
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.SecurityExpressions.*
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
|
||||
public class ExpressionUrlAuthorizationConfigurerTests extends BaseSpringSpec {
|
||||
|
||||
def "hasAnyAuthority('ROLE_USER')"() {
|
||||
when:
|
||||
def expression = ExpressionUrlAuthorizationConfigurer.hasAnyAuthority("ROLE_USER")
|
||||
then:
|
||||
expression == "hasAnyAuthority('ROLE_USER')"
|
||||
}
|
||||
|
||||
def "hasAnyAuthority('ROLE_USER','ROLE_ADMIN')"() {
|
||||
when:
|
||||
def expression = ExpressionUrlAuthorizationConfigurer.hasAnyAuthority("ROLE_USER","ROLE_ADMIN")
|
||||
then:
|
||||
expression == "hasAnyAuthority('ROLE_USER','ROLE_ADMIN')"
|
||||
}
|
||||
|
||||
def "hasRole('ROLE_USER') is rejected due to starting with ROLE_"() {
|
||||
when:
|
||||
def expression = ExpressionUrlAuthorizationConfigurer.hasRole("ROLE_USER")
|
||||
then:
|
||||
IllegalArgumentException e = thrown()
|
||||
e.message == "role should not start with 'ROLE_' since it is automatically inserted. Got 'ROLE_USER'"
|
||||
}
|
||||
|
||||
def "authorizeUrls() uses AffirmativeBased AccessDecisionManager"() {
|
||||
when: "Load Config with no specific AccessDecisionManager"
|
||||
loadConfig(NoSpecificAccessDecessionManagerConfig)
|
||||
then: "AccessDecessionManager matches the HttpSecurityBuilder's default"
|
||||
findFilter(FilterSecurityInterceptor).accessDecisionManager.class == AffirmativeBased
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class NoSpecificAccessDecessionManagerConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() no requests"() {
|
||||
when: "Load Config with no requests"
|
||||
loadConfig(NoRequestsConfig)
|
||||
then: "A meaningful exception is thrown"
|
||||
BeanCreationException success = thrown()
|
||||
success.message.contains "At least one mapping is required (i.e. authorizeUrls().anyRequest.authenticated())"
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class NoRequestsConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() incomplete mapping"() {
|
||||
when: "Load Config with incomplete mapping"
|
||||
loadConfig(IncompleteMappingConfig)
|
||||
then: "A meaningful exception is thrown"
|
||||
BeanCreationException success = thrown()
|
||||
success.message.contains "An incomplete mapping was found for "
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class IncompleteMappingConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.antMatchers("/a").authenticated()
|
||||
.anyRequest()
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() hasAuthority"() {
|
||||
setup:
|
||||
loadConfig(HasAuthorityConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
when:
|
||||
super.setup()
|
||||
login()
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 200
|
||||
when:
|
||||
super.setup()
|
||||
login("user","ROLE_INVALID")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class HasAuthorityConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasAuthority("ROLE_USER")
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() hasAnyAuthority"() {
|
||||
setup:
|
||||
loadConfig(HasAnyAuthorityConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
when:
|
||||
super.setup()
|
||||
login("user","ROLE_ADMIN")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 200
|
||||
when:
|
||||
super.setup()
|
||||
login("user","ROLE_DBA")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 200
|
||||
when:
|
||||
super.setup()
|
||||
login("user","ROLE_INVALID")
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class HasAnyAuthorityConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasAnyAuthority("ROLE_ADMIN","ROLE_DBA")
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() hasIpAddress"() {
|
||||
setup:
|
||||
loadConfig(HasIpAddressConfig)
|
||||
when:
|
||||
request.remoteAddr = "192.168.1.1"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
when:
|
||||
super.setup()
|
||||
request.remoteAddr = "192.168.1.0"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 200
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class HasIpAddressConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasIpAddress("192.168.1.0")
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() anonymous"() {
|
||||
setup:
|
||||
loadConfig(AnonymousConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 200
|
||||
when:
|
||||
super.setup()
|
||||
login()
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class AnonymousConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().anonymous()
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() rememberMe"() {
|
||||
setup:
|
||||
loadConfig(RememberMeConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
when:
|
||||
super.setup()
|
||||
login(new RememberMeAuthenticationToken("key", "user", AuthorityUtils.createAuthorityList("ROLE_USER")))
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 200
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class RememberMeConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.rememberMe()
|
||||
.and()
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().rememberMe()
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER")
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() denyAll"() {
|
||||
setup:
|
||||
loadConfig(DenyAllConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
when:
|
||||
super.setup()
|
||||
login(new RememberMeAuthenticationToken("key", "user", AuthorityUtils.createAuthorityList("ROLE_USER")))
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class DenyAllConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().denyAll()
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() not denyAll"() {
|
||||
setup:
|
||||
loadConfig(NotDenyAllConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 200
|
||||
when:
|
||||
super.setup()
|
||||
login(new RememberMeAuthenticationToken("key", "user", AuthorityUtils.createAuthorityList("ROLE_USER")))
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 200
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class NotDenyAllConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().not().denyAll()
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() fullyAuthenticated"() {
|
||||
setup:
|
||||
loadConfig(FullyAuthenticatedConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
when:
|
||||
super.setup()
|
||||
login(new RememberMeAuthenticationToken("key", "user", AuthorityUtils.createAuthorityList("ROLE_USER")))
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 403
|
||||
when:
|
||||
super.setup()
|
||||
login()
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.status == 200
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class FullyAuthenticatedConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.rememberMe()
|
||||
.and()
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().fullyAuthenticated()
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER")
|
||||
}
|
||||
}
|
||||
|
||||
def "authorizeUrls() access"() {
|
||||
setup:
|
||||
loadConfig(AccessConfig)
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "Access is granted due to GET"
|
||||
response.status == 200
|
||||
when:
|
||||
super.setup()
|
||||
login()
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "Access is granted due to role"
|
||||
response.status == 200
|
||||
when:
|
||||
super.setup()
|
||||
request.method = "POST"
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then: "Access is denied"
|
||||
response.status == 403
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class AccessConfig extends WebSecurityConfigurerAdapter {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.rememberMe()
|
||||
.and()
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeUrls()
|
||||
.anyRequest().access("hasRole('ROLE_USER') or request.method == 'GET'")
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerAuthentication(AuthenticationManagerBuilder auth)
|
||||
throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* 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.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.authentication.BadCredentialsException
|
||||
import org.springframework.security.config.annotation.AnyObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.web.AuthenticationEntryPoint
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.PortMapper
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter
|
||||
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter
|
||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
|
||||
import org.springframework.security.web.session.SessionManagementFilter
|
||||
import org.springframework.security.web.util.AnyRequestMatcher
|
||||
import org.springframework.test.util.ReflectionTestUtils
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class FormLoginConfigurerTests extends BaseSpringSpec {
|
||||
def "Form Login"() {
|
||||
when: "load formLogin()"
|
||||
context = new AnnotationConfigApplicationContext(FormLoginConfig)
|
||||
|
||||
then: "FilterChains configured correctly"
|
||||
def filterChains = filterChains()
|
||||
filterChains.size() == 2
|
||||
filterChains[0].requestMatcher.pattern == '/resources/**'
|
||||
filterChains[0].filters.empty
|
||||
filterChains[1].requestMatcher instanceof AnyRequestMatcher
|
||||
filterChains[1].filters.collect { it.class.name.contains('$') ? it.class.superclass : it.class } ==
|
||||
[SecurityContextPersistenceFilter, LogoutFilter, UsernamePasswordAuthenticationFilter,
|
||||
RequestCacheAwareFilter, SecurityContextHolderAwareRequestFilter,
|
||||
AnonymousAuthenticationFilter, SessionManagementFilter, ExceptionTranslationFilter, FilterSecurityInterceptor ]
|
||||
|
||||
and: "UsernamePasswordAuthentictionFilter is configured correctly"
|
||||
UsernamePasswordAuthenticationFilter authFilter = findFilter(UsernamePasswordAuthenticationFilter,1)
|
||||
authFilter.usernameParameter == "username"
|
||||
authFilter.passwordParameter == "password"
|
||||
authFilter.failureHandler.defaultFailureUrl == "/login?error"
|
||||
authFilter.successHandler.defaultTargetUrl == "/"
|
||||
authFilter.requiresAuthentication(new MockHttpServletRequest(requestURI : "/login", method: "POST"), new MockHttpServletResponse())
|
||||
!authFilter.requiresAuthentication(new MockHttpServletRequest(requestURI : "/login", method: "GET"), new MockHttpServletResponse())
|
||||
|
||||
and: "SessionFixationProtectionStrategy is configured correctly"
|
||||
SessionFixationProtectionStrategy sessionStrategy = ReflectionTestUtils.getField(authFilter,"sessionStrategy")
|
||||
sessionStrategy.migrateSessionAttributes
|
||||
|
||||
and: "Exception handling is configured correctly"
|
||||
AuthenticationEntryPoint authEntryPoint = filterChains[1].filters.find { it instanceof ExceptionTranslationFilter}.authenticationEntryPoint
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
authEntryPoint.commence(new MockHttpServletRequest(requestURI: "/private/"), response, new BadCredentialsException(""))
|
||||
response.redirectedUrl == "http://localhost/login"
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class FormLoginConfig extends BaseWebConfig {
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
.ignoring()
|
||||
.antMatchers("/resources/**");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
.loginUrl("/login")
|
||||
}
|
||||
}
|
||||
|
||||
def "FormLogin.permitAll()"() {
|
||||
when: "load formLogin() with permitAll"
|
||||
context = new AnnotationConfigApplicationContext(FormLoginConfigPermitAll)
|
||||
|
||||
then: "the formLogin URLs are granted access"
|
||||
FilterChainProxy filterChain = context.getBean(FilterChainProxy)
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
filterChain.doFilter(new MockHttpServletRequest(servletPath : servletPath, requestURI: servletPath, queryString: query, method: method), response, new MockFilterChain())
|
||||
response.redirectedUrl == redirectUrl
|
||||
|
||||
where:
|
||||
servletPath | method | query | redirectUrl
|
||||
"/login" | "GET" | null | null
|
||||
"/login" | "POST" | null | "/login?error"
|
||||
"/login" | "GET" | "error" | null
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class FormLoginConfigPermitAll extends BaseWebConfig {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
.permitAll()
|
||||
}
|
||||
}
|
||||
|
||||
def "FormLogin uses PortMapper"() {
|
||||
when: "load formLogin() with permitAll"
|
||||
FormLoginUsesPortMapperConfig.PORT_MAPPER = Mock(PortMapper)
|
||||
loadConfig(FormLoginUsesPortMapperConfig)
|
||||
then: "the formLogin URLs are granted access"
|
||||
findFilter(ExceptionTranslationFilter).authenticationEntryPoint.portMapper == FormLoginUsesPortMapperConfig.PORT_MAPPER
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class FormLoginUsesPortMapperConfig extends BaseWebConfig {
|
||||
static PortMapper PORT_MAPPER
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
.permitAll()
|
||||
.and()
|
||||
.portMapper()
|
||||
.portMapper(PORT_MAPPER)
|
||||
}
|
||||
}
|
||||
|
||||
def "FormLogin permitAll ignores failureUrl when failureHandler set"() {
|
||||
setup:
|
||||
PermitAllIgnoresFailureHandlerConfig.FAILURE_HANDLER = Mock(AuthenticationFailureHandler)
|
||||
loadConfig(PermitAllIgnoresFailureHandlerConfig)
|
||||
FilterChainProxy springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when: "access default failureUrl and configured explicit FailureHandler"
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(requestURI:"/login",queryString:"error")
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
springSecurityFilterChain.doFilter(request,response,new MockFilterChain())
|
||||
then: "access is not granted to the failure handler (sent to login page)"
|
||||
response.status == 302
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
static class PermitAllIgnoresFailureHandlerConfig extends BaseWebConfig {
|
||||
static AuthenticationFailureHandler FAILURE_HANDLER
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http
|
||||
.authorizeUrls()
|
||||
.anyRequest().hasRole("USER")
|
||||
.and()
|
||||
.formLogin()
|
||||
.failureHandler(FAILURE_HANDLER)
|
||||
.permitAll()
|
||||
}
|
||||
}
|
||||
|
||||
def "formLogin ObjectPostProcessor"() {
|
||||
setup: "initialize the AUTH_FILTER as a mock"
|
||||
AnyObjectPostProcessor opp = Mock()
|
||||
HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
|
||||
when:
|
||||
http
|
||||
.formLogin()
|
||||
.and()
|
||||
.build()
|
||||
|
||||
then: "UsernamePasswordAuthenticationFilter is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as UsernamePasswordAuthenticationFilter) >> {UsernamePasswordAuthenticationFilter o -> o}
|
||||
and: "LoginUrlAuthenticationEntryPoint is registered with LifecycleManager"
|
||||
1 * opp.postProcess(_ as LoginUrlAuthenticationEntryPoint) >> {LoginUrlAuthenticationEntryPoint o -> o}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user