1
0
mirror of synced 2026-09-06 01:09:48 +00:00

Compare commits

...

76 Commits

Author SHA1 Message Date
Spring Buildmaster e412fb7ac0 Release version 4.1.2.RELEASE 2016-08-11 19:14:32 +00:00
Rob Winch 28278eab89 Fix defaultMethodExpressionHandler autowiring
Previously if a Bean for GlobalMethodSecurityConfiguration's
defaultMethodExpressionHandler was found on a Configuration that also
@Autowired a Bean that enabled method security, the Bean that was
@Autowired would not have security enabled.

This fixes the issue by delaying the lookup of Beans populated on
GlobalMethodSecurityConfiguration's defaultMethodExpressionHandler.

Fixes gh-4020
2016-08-10 23:48:49 -05:00
Rob Winch a93fb1e0e7 Fix csrf() when used then not used
Previously if csrf() was used and subsequently not used, the
TestCsrfTokenRepository was still used. This makes it difficult to test
the actual CsrfTokenRepository implementation.

Now the TestCsrfTokenRepository is only used if explicitly enabled.

Fixes gh-4016
2016-08-09 17:28:33 -04:00
Joe Grandja dabcc5416a MvcRequestMatcher servletPath Polish / XML Config
Fixes gh-4014
2016-08-09 15:47:41 -05:00
Rob Winch 8a6d0cd16d MvcRequestMatcher servletPath / JavaConfig
Issue: gh-3987
2016-08-09 15:47:01 -05:00
Rob Winch edb7ef567a Logout is 204 for XMLHttpRequest
Fixes gh-3997
2016-08-02 14:14:44 -07:00
Rob Winch d002681bec Add ObjectPostProcessor support for SmartInitializingSingleton 2016-07-21 10:46:55 -05:00
Rob Winch 6649d46896 DummyRequest supports methods for MvcRequestMatcher
To support MvcRequestMatcher DummyRequest needs to support
getCharacterEncoding() and getAttribute(String)
2016-07-14 16:02:08 -04:00
Rob Winch 1d97ee8dd6 Add HttpSecurity.mvcMatcher
Fixes gh-3970
2016-07-14 11:46:29 -04:00
Rob Winch 7d1344fca8 Fix NPE requestMatchers().mvcMatchers
Fixes gh-3969
2016-07-14 11:45:45 -04:00
Spring Buildmaster cc04392d9a Next development version 2016-07-07 00:57:53 +00:00
Spring Buildmaster 919f000c80 Release version 4.1.1.RELEASE 2016-07-07 00:57:35 +00:00
Johnny Lim 310bb39a0d Fix typo 2016-07-06 16:22:33 -05:00
Rob Winch 764a4d8414 Fix Error Message typo
Fixes gh-3953
2016-07-06 16:19:29 -05:00
Jakob Englisch b17870ee07 LogoutConfigurer: only allow suitable http methods 2016-07-06 16:17:11 -05:00
Rob Winch 8ad91ef6a5 WithSecurityContextTestExecutionListener > SqlScriptsTestExecutionListener
WithSecurityContextTestExecutionListener should order after
SqlScriptsTestExecutionListener so sql can setup the current user's info
in the database.

Fixes gh-3962
2016-07-06 16:09:17 -05:00
Rob Winch 5f6312c5be Update to Spring 4.3.1
Fixes gh-3963
2016-07-06 15:47:44 -05:00
Rob Winch 9d50944cb2 AntPathRequestMatcher implements RequestVariableExtractor
Issue gh-3964
2016-07-06 15:47:34 -05:00
Rob Winch e4c13e3c0e Add MvcRequestMatcher
Fixes gh-3964
2016-07-06 15:47:23 -05:00
Rob Winch 13bc70f693 Add CorsFilter support 2016-07-05 14:28:04 -05:00
Rob Winch c935d857eb Add mvc namespace to XmlApplicationContext 2016-07-01 22:04:55 -05:00
Rob Winch 843ed3e437 Update to Spring 4.3.1.BUILD-SNAPSHOT 2016-07-01 22:04:55 -05:00
Rob Winch 7f3b3a8b59 Polish
Issue gh-180
2016-07-01 13:17:52 -05:00
Jakob Englisch 261c932b8e Upgrade Gradle to 2.14
Issue gh-3946
2016-06-28 13:13:08 -04:00
Rob Winch 1b4e20e97f Fix InsecureApplicationTests package
Fixes gh-3951
2016-06-28 10:17:17 -05:00
Rob Winch bd5f71bb0d Polish
Fix checkstyle for LDAP JavaConfig Authority mapping

Issue gh-2768
2016-06-21 17:08:37 -05:00
Tony Dalbrekt b76e3be822 LDAP Java Config supports GrantedAuthoritiesMapper
Fixes gh-2768
2016-06-21 16:43:13 -05:00
Rob Winch 26ad1cb4a5 Polish RememberMe Validation
Issue gh-3909
2016-06-21 14:57:15 -05:00
Eddú Meléndez 87224f62e4 RememberMe JavaConfig Validation
Add validation when rememberMeServices and rememberMeCookieName are
provided

Fixes gh-3909
2016-06-21 14:57:01 -05:00
Rob Winch 8f880aea0e Polish Pbkdf2PasswordEncoder
Issue gh-3930
2016-06-21 11:47:50 -05:00
vitaliy_kuzmich 5f658b3ffc Remove double salt in Pbkdf2PasswordEncoder
Issue gh-3930
2016-06-21 11:44:23 -05:00
Rob Winch 77a478ba0d Fix ApacheDSEmbeddedLdifTests checkstyle
Issue gh-54
2016-06-21 09:56:34 -05:00
Marcin Zajączkowski a3c4a5fde7 SEC-2387 - add ignored failing test case 2016-06-21 09:53:38 -05:00
Rob Winch bbeb7f94d7 Fix checkstyle
Issue gh-3920
2016-06-20 19:36:51 -05:00
Rob Winch a2a06d19c1 Add formLogin() Accept Test
Issue gh-3920
2016-06-20 16:23:29 -05:00
Micah Silverman 314828859e Added accept method call to buildRequest in SecurityMockMvcRequestBuilders with default of MediaType.APPLICATION_FORM_URLENCODED 2016-06-20 15:46:01 -05:00
Rob Winch 66858e22ad Disable XMLHttpRequest for formLogin entry point
Previously the following:

http http://localhost:8080/user \
  "X-Requested-With:XMLHttpRequest" "Accept:text/plain"

Produced a 302 instead of a 401

Fixes gh-3887
2016-06-20 15:30:00 -05:00
Rob Winch 2a73f3cdf7 Remove abigious import 2016-06-20 15:03:09 -05:00
Rob Winch dd9b59ba31 Document Digest is insecure
Fixes gh-3894
2016-06-20 14:10:36 -05:00
Eddú Meléndez 39ed7d0eca Propagate rolePrefix to LdapAuthoritiesPopulator
Previous to this commit, custom rolePrefix was not propagated to
LdapAuthoritiesPopulator populating  a wrong authority. Now, rolePrefix
is propagated and the authority is as expected.

Fixes gh-3921
2016-06-20 12:44:02 -05:00
Eddú Meléndez a2ead4cf7a Polish
Fixes gh-3892
2016-06-20 12:35:43 -05:00
Ruben Dijkstra 364db6762e Add failing test for #3905 Fix Assert usage 2016-06-20 09:24:04 -05:00
Ruben Dijkstra e8f4ee8a39 Fix Assert usage 2016-06-20 09:23:51 -05:00
Rob Winch d2b909e7c5 Doc InteractiveAuthenticationEvent doesn't extend AuthentcationEvent
Document why InteractiveAuthenticationEvent doesn't extend
AuthentcationEvent. This is to avoid multiple AuthenticationSuccessEvent
from being sent to any listeners.

Fixes gh-3857
2016-06-17 17:16:54 -05:00
Shannon Carey 9fa2c64737 Documentation SecurityConfig->WebSecurityConfig
Rename SecurityConfig to WebSecurityConfig in the documentation.

Fixes gh-153
2016-06-17 16:55:46 -05:00
Filip Hanik 6b436ff409 Avoid duplicate attribute search.
When using search-and-bind strategy, the user attributes are already returned in the first search.
If the user happens to not have privileges to perform a search, the second search may fail.
(user only has bind privileges)
See https://github.com/cloudfoundry/uaa/issues/342
2016-06-17 16:43:06 -05:00
Ruben Dijkstra ca76e8d784 Remove null-check inside afterPropertiesSet() since it's never null 2016-06-17 16:40:39 -05:00
Rob Winch 2d6051625f Update pom.xml 2016-06-17 14:30:11 -05:00
Rob Winch 477573b3bc Fix @EnableGlobalAuthentication & method seucrity on @Configuration class
Fixes gh-3934
2016-06-17 14:05:11 -05:00
Rob Winch fa1c484587 AuthenticationConfiguration.getAuthenticationManager() supports recursion
AuthenticationConfiguration.getAuthenticationManager() now supports
recursion. This is necessary in instances where something using
@EnableGlobalAuthentication requires an object using method level security.

Fixes gh-3935
2016-06-17 14:02:36 -05:00
Rob Winch 9e3d2e2d99 HTTP Basic default logout ignores text/html
This fixes an issue where Chrome sends an accept header of application/xml
which triggers an HTTP 204 to be returned

Fixes gh-3902
2016-06-14 16:27:56 -05:00
Rob Winch e7fd6f6c3f Remove the CLA confirmation from template
We now use the new CLA tooling which automates this
2016-06-13 13:20:22 -05:00
Pedro Vilaça 208f898403 Improve csrf login caveats
Add a suggestion to retrieve a fresh csrf token right before the
form submission in order to avoid problems with invalid csrf tokens
due session timeouts.

Fixes gh-3925
2016-06-13 16:26:16 +01:00
Rob Winch a7369bf71b Update to CLA tooling 2016-06-08 21:56:22 -05:00
Rob Winch cf78793f8f Fixes for Documentation
Fixes for the Documentation
2016-05-31 21:40:21 -05:00
Ryan W. Moore 8aea83011d Docs: Remove broken link
I think the originally intended destination no longer exists in the
documentation.
2016-05-28 21:09:15 -04:00
Ryan W. Moore fd65652bbe Docs: Fix broken link to security database schema 2016-05-28 21:09:15 -04:00
Ryan W. Moore 38e9f6a851 Docs: Fix broken link to csrfInput tag info
ID names are case sensitive.
2016-05-28 21:09:15 -04:00
Ryan W. Moore cdb04c50e8 Docs: Fix broken link to websocket security info 2016-05-28 21:09:15 -04:00
Ryan W. Moore 057ea4fb17 Docs: Make 'Getting Started' a level 1 section heading
This fixes the following build error:

  asciidoctor: ERROR: index.adoc: line 26: invalid part, must have at least one
  section (e.g., chapter, appendix, etc.)
2016-05-28 21:09:01 -04:00
David Kane 503828c994 Add FAQ for JSP taglib & method security
Updated FAQ to clarify how the url attribute of the authorize tag
interacts with method security
2016-05-23 08:39:54 -05:00
Sola d3b3f8e004 Fix WebSecurityConfigurerAdapter Javadoc
The constructor's Javadoc was incorrect. This commit
fixes it.
2016-05-23 08:12:50 -05:00
Kim Saabye Pedersen 9fcfeaf225 BCryptPasswordEncoder validates strength
Fixes gh-3862
2016-05-20 14:54:26 -05:00
Rob Winch 101190ad8b Format WithSecurityContextTestExecutionListener 2016-05-20 10:46:26 -05:00
Rob Winch 336de35874 Polish WithSecurityContextTestExecutionListener
Extract method for reuse

SecurityContext createSecurityContext(AnnotatedElement annotated,
    WithSecurityContext withSecurityContext,
    TestContext context)

Issue gh-3888
2016-05-20 10:46:26 -05:00
Eddú Meléndez a53d022312 Support WithSecurityContextFactory on superclass
Fixes gh-3888
2016-05-20 10:46:14 -05:00
Rob Winch 9f95bfdfc9 Fix documentation
CsrfTokenResolver -> CsrfTokenArgumentResolver

Fixes gh-3890
2016-05-18 15:10:50 -05:00
Pedro Vilaça ea2b5dd412 Fix wrong class name reference in the docs
In the documentation, there was a reference to a class called CsrfTokenResolver
and it should CsrfTokenArgumentResolver

Fixes gh-3890
2016-05-18 20:26:20 +01:00
Rob Winch 7b61a44929 Fix test .standaloneSetup
Previously, Spring Security's test support did not work well with the
standalone setup. This was because the springSecurityFilterChain was not
found by the WebTestUtils.

This commit ensures that the springSecurityFilterChain is added as a
servlet attribute if it is explicitly defined. WebTestUtils can then
find the springSecurityFilterChain in the ServletContext.

Fixes gh-3881
2016-05-16 11:02:40 -04:00
Rob Winch 602bb457b8 Formatting
Issue gh-3881
2016-05-16 11:02:40 -04:00
Rob Winch f363c62afd Document spring-security-test dependency
Fixes gh-3873
2016-05-16 10:56:50 -04:00
Joe Grandja 66980e827c Add Spring Boot Hello World guide
Add Spring Boot Hello World Guide

Fixes gh-3866
2016-05-13 14:05:29 -05:00
Adrien SAUVEZ c261975be0 Set cookie domain for cancel remember-me
Fixes gh-3871
2016-05-13 13:34:43 -05:00
Rob Winch ede521dc8d authorizeUrls -> authorizeRequests
Replace remaining authorizeUrls with authorizeRequests

Fixes gh-3875
2016-05-09 10:34:36 -05:00
Rob Winch d4218c70f1 Update CookieCsrfTokenRepository docs to cookiHttpOnly=false
Currently CookieCsrfTokenRepository does not specify that the httpOnly
flag needs set to false. We should update the reference to include this
setting (and a comment about it) since it states that the settings will
work with AngularJS.

This commit updates the documentation and provides a convenience factory
method to create a CookieCsrfTokenRepository with cookiHttpOnly=false

Fixes gh-3865
2016-05-06 16:28:04 -04:00
Spring Buildmaster 64f53620f8 Next development version 2016-05-05 04:26:13 +00:00
299 changed files with 6053 additions and 1305 deletions
-3
View File
@@ -1,6 +1,3 @@
<!--
Thanks for contributing to Spring Security. Please provide a brief description of your pull-request and reference any related issue numbers (prefix references with #).
-->
<!-- Please also confirm that you have signed the CLA by put an [X] in the box below: -->
- [] I have signed the CLA
+2 -10
View File
@@ -32,17 +32,9 @@ Is there already an issue that addresses your concern? Do a bit of searching in
If you're considering anything more than correcting a typo or fixing a minor bug , please discuss it on the [Spring Security Gitter](https://gitter.im/spring-projects/spring-security) before submitting a pull request. We're happy to provide guidance but please spend an hour or two researching the subject on your own including searching the forums for prior discussions.
# Sign the Contributor License Agreement
If you have not previously done so, please fill out and submit the [SpringSource CLA form](https://support.springsource.com/spring_committer_signup). You'll receive a token when this process is complete. Keep track of this, you may be asked for it later!
* For **Project** select _Spring Security_
* For **Project Lead** enter _Rob Winch_
* Note that emailing/postal mailing a signed copy is not necessary. Submission of the web form is all that is required.
When you've completed the web form, simply add the following in a comment on your pull request:
> I have signed and agree to the terms of the SpringSource Individual Contributor License Agreement.
You do not need to include your token/id. Please add the statement above to all future pull requests as well, simply so the Spring Security team knows immediately that this process is complete.
If you have not previously done so, please fill out and
submit the [Contributor License Agreement](https://cla.pivotal.io/sign/spring).
# Create your branch from master
Create your topic branch to be submitted as a pull request from master. The Spring team will consider your pull request for backporting on a case-by-case basis; you don't need to worry about submitting anything for backporting.
+3 -3
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
<version>4.1.0.RELEASE</version>
<version>4.1.2.RELEASE</version>
<name>spring-security-acl</name>
<description>spring-security-acl</description>
<url>http://spring.io/spring-security</url>
@@ -35,7 +35,7 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.2.5.RELEASE</version>
<version>4.3.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -51,7 +51,7 @@
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.1.0.RELEASE</version>
<version>4.1.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
@@ -16,12 +16,10 @@
package org.springframework.security.acls.afterinvocation;
import java.util.Arrays;
import java.util.List;
import org.springframework.security.access.AfterInvocationProvider;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
import org.springframework.security.acls.model.Acl;
@@ -46,7 +46,7 @@ public interface AccessControlEntry extends Serializable {
Sid getSid();
/**
* Indicates the a Permission is being granted to the relevant Sid. If false,
* Indicates the permission is being granted to the relevant Sid. If false,
* indicates the permission is being revoked/blocked.
*
* @return true if being granted, false otherwise
@@ -17,7 +17,6 @@ package org.springframework.security.acls;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.util.Locale;
@@ -18,17 +18,13 @@ package org.springframework.security.acls.afterinvocation;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.when;
import org.junit.Test;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.acls.model.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.SpringSecurityMessageSource;
import java.util.ArrayList;
import java.util.Arrays;
@@ -16,7 +16,6 @@
package org.springframework.security.acls.afterinvocation;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
import org.junit.Test;
@@ -15,7 +15,6 @@
*/
package org.springframework.security.acls.jdbc;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import static org.assertj.core.api.Assertions.*;
@@ -26,17 +25,12 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -48,12 +42,10 @@ import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.util.FieldUtils;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.ReflectionUtils;
/**
* Tests {@link EhCacheBasedAclCache}
@@ -16,10 +16,8 @@
package org.springframework.security.acls.sid;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.util.Collection;
import java.util.List;
import org.junit.Test;
@@ -31,7 +29,6 @@ import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.SidRetrievalStrategy;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
/**
+3 -3
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-aspects</artifactId>
<version>4.1.0.RELEASE</version>
<version>4.1.2.RELEASE</version>
<name>spring-security-aspects</name>
<description>spring-security-aspects</description>
<url>http://spring.io/spring-security</url>
@@ -35,7 +35,7 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.2.5.RELEASE</version>
<version>4.3.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -45,7 +45,7 @@
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.1.0.RELEASE</version>
<version>4.1.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
@@ -26,7 +26,7 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.annotation.Secured;
+1 -1
View File
@@ -27,7 +27,7 @@ allprojects {
ext.releaseBuild = version.endsWith('RELEASE')
ext.snapshotBuild = version.endsWith('SNAPSHOT')
ext.springVersion = '4.2.5.RELEASE'
ext.springVersion = '4.3.1.RELEASE'
ext.springLdapVersion = '2.0.2.RELEASE'
group = 'org.springframework.security'
+4 -4
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>4.1.0.RELEASE</version>
<version>4.1.2.RELEASE</version>
<name>spring-security-cas</name>
<description>spring-security-cas</description>
<url>http://spring.io/spring-security</url>
@@ -35,7 +35,7 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.2.5.RELEASE</version>
<version>4.3.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -51,13 +51,13 @@
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.1.0.RELEASE</version>
<version>4.1.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.1.0.RELEASE</version>
<version>4.1.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
@@ -17,14 +17,9 @@
package org.springframework.security.cas.web;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
import java.lang.reflect.Method;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.junit.After;
@@ -40,11 +35,8 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
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.NullRememberMeServices;
import org.springframework.util.ReflectionUtils;
/**
* Tests {@link CasAuthenticationFilter}.
+9 -15
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.1.0.RELEASE</version>
<version>4.1.2.RELEASE</version>
<name>spring-security-config</name>
<description>spring-security-config</description>
<url>http://spring.io/spring-security</url>
@@ -35,7 +35,7 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.2.5.RELEASE</version>
<version>4.3.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -51,7 +51,7 @@
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.1.0.RELEASE</version>
<version>4.1.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
@@ -97,28 +97,28 @@
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
<version>4.1.0.RELEASE</version>
<version>4.1.2.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-messaging</artifactId>
<version>4.1.0.RELEASE</version>
<version>4.1.2.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-openid</artifactId>
<version>4.1.0.RELEASE</version>
<version>4.1.2.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.1.0.RELEASE</version>
<version>4.1.2.RELEASE</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
@@ -248,12 +248,6 @@
<version>4.1.0.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>3.6.10.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
@@ -371,13 +365,13 @@
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-aspects</artifactId>
<version>4.1.0.RELEASE</version>
<version>4.1.2.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>4.1.0.RELEASE</version>
<version>4.1.2.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2016 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.
@@ -15,24 +15,17 @@
*/
package org.springframework.security.config.annotation.authentication.ldap
import org.springframework.context.annotation.Configuration
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.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider
import org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator
import org.springframework.security.ldap.userdetails.PersonContextMapper
import org.springframework.test.util.ReflectionTestUtils
import static org.springframework.security.config.annotation.authentication.ldap.NamespaceLdapAuthenticationProviderTestsConfigs.*
import org.springframework.security.core.authority.SimpleGrantedAuthority
/**
*
* @author Rob Winch
* @author Eddú Meléndez
*
*/
class LdapAuthenticationProviderConfigurerTests extends BaseSpringSpec {
@@ -44,17 +37,54 @@ class LdapAuthenticationProviderConfigurerTests extends BaseSpringSpec {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("bob","bobspassword"))
}
def "authentication-manager support multiple ldap context with default role prefix" () {
when:
loadConfig(MultiLdapAuthenticationProvidersConfig)
then:
def authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("bob", "bobspassword"))
authenticate.authorities.contains(new SimpleGrantedAuthority("ROLE_DEVELOPERS"))
}
def "authentication-manager support multiple ldap context with custom role prefix"() {
when:
loadConfig(MultiLdapWithCustomRolePrefixAuthenticationProvidersConfig)
then:
def authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("bob", "bobspassword"))
authenticate.authorities.contains(new SimpleGrantedAuthority("ROL_DEVELOPERS"))
}
@EnableWebSecurity
static class MultiLdapAuthenticationProvidersConfig extends WebSecurityConfigurerAdapter {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.groupSearchBase("ou=groups")
.groupSearchFilter("(member={0})")
.userDnPatterns("uid={0},ou=people")
.and()
.ldapAuthentication()
.groupSearchBase("ou=groups")
.groupSearchFilter("(member={0})")
.userDnPatterns("uid={0},ou=people")
}
}
@EnableWebSecurity
static class MultiLdapWithCustomRolePrefixAuthenticationProvidersConfig extends
WebSecurityConfigurerAdapter {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.groupSearchBase("ou=groups")
.groupSearchFilter("(member={0})")
.userDnPatterns("uid={0},ou=people")
.rolePrefix("ROL_")
.and()
.ldapAuthentication()
.groupSearchBase("ou=groups")
.groupSearchFilter("(member={0})")
.userDnPatterns("uid={0},ou=people")
.rolePrefix("RUOLO_")
}
}
}
@@ -68,6 +68,7 @@ public abstract class Elements {
public static final String DEBUG = "debug";
public static final String HTTP_FIREWALL = "http-firewall";
public static final String HEADERS = "headers";
public static final String CORS = "cors";
public static final String CSRF = "csrf";
public static final String WEBSOCKET_MESSAGE_BROKER = "websocket-message-broker";
@@ -25,6 +25,7 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.util.Assert;
import org.springframework.web.filter.DelegatingFilterProxy;
@@ -57,7 +58,7 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
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 List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<SecurityConfigurer<O, B>>();
private final Map<Class<Object>, Object> sharedObjects = new HashMap<Class<Object>, Object>();
private final Map<Class<? extends Object>, Object> sharedObjects = new HashMap<Class<? extends Object>, Object>();
private final boolean allowConfigurersOfSameType;
@@ -155,7 +156,7 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
*/
@SuppressWarnings("unchecked")
public <C> void setSharedObject(Class<C> sharedType, C object) {
this.sharedObjects.put((Class<Object>) sharedType, object);
this.sharedObjects.put(sharedType, object);
}
/**
@@ -173,7 +174,7 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
* Gets the shared objects
* @return
*/
public Map<Class<Object>, Object> getSharedObjects() {
public Map<Class<? extends Object>, Object> getSharedObjects() {
return Collections.unmodifiableMap(this.sharedObjects);
}
@@ -300,7 +301,7 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
* @return the possibly modified Object to use
*/
protected <P> P postProcess(P object) {
return (P) this.objectPostProcessor.postProcess(object);
return this.objectPostProcessor.postProcess(object);
}
/**
@@ -19,9 +19,11 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.target.LazyInitTargetSource;
import org.springframework.beans.factory.BeanFactoryUtils;
@@ -34,6 +36,8 @@ import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.util.Assert;
/**
@@ -45,6 +49,9 @@ import org.springframework.util.Assert;
*/
@Configuration
public class AuthenticationConfiguration {
private AtomicBoolean buildingAuthenticationManager = new AtomicBoolean();
private ApplicationContext applicationContext;
private AuthenticationManager authenticationManager;
@@ -79,11 +86,15 @@ public class AuthenticationConfiguration {
}
public AuthenticationManager getAuthenticationManager() throws Exception {
if (authenticationManagerInitialized) {
return authenticationManager;
if (this.authenticationManagerInitialized) {
return this.authenticationManager;
}
AuthenticationManagerBuilder authBuilder = authenticationManagerBuilder(
this.objectPostProcessor);
if (this.buildingAuthenticationManager.getAndSet(true)) {
return new AuthenticationManagerDelegator(authBuilder);
}
AuthenticationManagerBuilder authBuilder = authenticationManagerBuilder(objectPostProcessor);
for (GlobalAuthenticationConfigurerAdapter config : globalAuthConfigurers) {
authBuilder.apply(config);
}
@@ -157,4 +168,44 @@ public class AuthenticationConfiguration {
}
}
}
/**
* Prevents infinite recursion in the event that initializing the
* AuthenticationManager.
*
* @author Rob Winch
* @since 4.1.1
*/
static final class AuthenticationManagerDelegator implements AuthenticationManager {
private AuthenticationManagerBuilder delegateBuilder;
private AuthenticationManager delegate;
private final Object delegateMonitor = new Object();
AuthenticationManagerDelegator(AuthenticationManagerBuilder delegateBuilder) {
Assert.notNull(delegateBuilder, "delegateBuilder cannot be null");
this.delegateBuilder = delegateBuilder;
}
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
if (this.delegate != null) {
return this.delegate.authenticate(authentication);
}
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
this.delegate = this.delegateBuilder.getObject();
this.delegateBuilder = null;
}
}
return this.delegate.authenticate(authentication);
}
@Override
public String toString() {
return "AuthenticationManagerDelegator [delegate=" + this.delegate + "]";
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2016 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.
@@ -24,6 +24,7 @@ import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.authentication.AbstractLdapAuthenticator;
@@ -51,6 +52,7 @@ import java.net.ServerSocket;
* @param <B> the {@link ProviderManagerBuilder} type that this is configuring.
*
* @author Rob Winch
* @author Eddú Meléndez
* @since 3.2
*/
public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuilder<B>>
@@ -68,6 +70,7 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
private Object passwordEncoder;
private String passwordAttribute;
private LdapAuthoritiesPopulator ldapAuthoritiesPopulator;
private GrantedAuthoritiesMapper authoritiesMapper;
private LdapAuthenticationProvider build() throws Exception {
BaseLdapPathContextSource contextSource = getContextSource();
@@ -77,10 +80,7 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(
ldapAuthenticator, authoritiesPopulator);
SimpleAuthorityMapper simpleAuthorityMapper = new SimpleAuthorityMapper();
simpleAuthorityMapper.setPrefix(rolePrefix);
simpleAuthorityMapper.afterPropertiesSet();
ldapAuthenticationProvider.setAuthoritiesMapper(simpleAuthorityMapper);
ldapAuthenticationProvider.setAuthoritiesMapper(getAuthoritiesMapper());
if (userDetailsContextMapper != null) {
ldapAuthenticationProvider
.setUserDetailsContextMapper(userDetailsContextMapper);
@@ -128,11 +128,45 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
contextSource, groupSearchBase);
defaultAuthoritiesPopulator.setGroupRoleAttribute(groupRoleAttribute);
defaultAuthoritiesPopulator.setGroupSearchFilter(groupSearchFilter);
defaultAuthoritiesPopulator.setRolePrefix(rolePrefix);
this.ldapAuthoritiesPopulator = defaultAuthoritiesPopulator;
return defaultAuthoritiesPopulator;
}
/**
* Specifies the {@link GrantedAuthoritiesMapper}.
*
* @param grantedAuthoritiesMapper the {@link GrantedAuthoritiesMapper} the default is {@link SimpleAuthorityMapper}
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
*
* @author Tony Dalbrekt
* @since 4.1.1
*/
public LdapAuthenticationProviderConfigurer<B> authoritiesMapper(GrantedAuthoritiesMapper grantedAuthoritiesMapper) {
this.authoritiesMapper = grantedAuthoritiesMapper;
return this;
}
/**
* Gets the {@link GrantedAuthoritiesMapper} and defaults to {@link SimpleAuthorityMapper}.
*
* @return the {@link GrantedAuthoritiesMapper}
* @throws Exception if errors in {@link SimpleAuthorityMapper#afterPropertiesSet()}
*/
protected GrantedAuthoritiesMapper getAuthoritiesMapper() throws Exception {
if(authoritiesMapper != null) {
return authoritiesMapper;
}
SimpleAuthorityMapper simpleAuthorityMapper = new SimpleAuthorityMapper();
simpleAuthorityMapper.setPrefix(rolePrefix);
simpleAuthorityMapper.afterPropertiesSet();
this.authoritiesMapper = simpleAuthorityMapper;
return simpleAuthorityMapper;
}
/**
* Creates the {@link LdapAuthenticator} to use
*
@@ -15,7 +15,6 @@
*/
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;
@@ -24,6 +24,7 @@ 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.SmartInitializingSingleton;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.util.Assert;
@@ -37,10 +38,11 @@ import org.springframework.util.Assert;
* @since 3.2
*/
final class AutowireBeanFactoryObjectPostProcessor
implements ObjectPostProcessor<Object>, DisposableBean {
implements ObjectPostProcessor<Object>, DisposableBean, SmartInitializingSingleton {
private final Log logger = LogFactory.getLog(getClass());
private final AutowireCapableBeanFactory autowireBeanFactory;
private final List<DisposableBean> disposableBeans = new ArrayList<DisposableBean>();
private final List<SmartInitializingSingleton> smartSingletons = new ArrayList<SmartInitializingSingleton>();
public AutowireBeanFactoryObjectPostProcessor(
AutowireCapableBeanFactory autowireBeanFactory) {
@@ -74,9 +76,22 @@ final class AutowireBeanFactoryObjectPostProcessor
if (result instanceof DisposableBean) {
this.disposableBeans.add((DisposableBean) result);
}
if (result instanceof SmartInitializingSingleton) {
this.smartSingletons.add((SmartInitializingSingleton) result);
}
return result;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.SmartInitializingSingleton#afterSingletonsInstantiated()
*/
@Override
public void afterSingletonsInstantiated() {
for(SmartInitializingSingleton singleton : smartSingletons) {
singleton.afterSingletonsInstantiated();
}
}
/*
* (non-Javadoc)
*
@@ -22,6 +22,8 @@ import java.util.Map;
import org.aopalliance.intercept.MethodInterceptor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AdviceMode;
@@ -76,7 +78,8 @@ import org.springframework.util.Assert;
* @see EnableGlobalMethodSecurity
*/
@Configuration
public class GlobalMethodSecurityConfiguration implements ImportAware {
public class GlobalMethodSecurityConfiguration
implements ImportAware, SmartInitializingSingleton {
private static final Log logger = LogFactory
.getLog(GlobalMethodSecurityConfiguration.class);
private ObjectPostProcessor<Object> objectPostProcessor = new ObjectPostProcessor<Object>() {
@@ -94,6 +97,7 @@ public class GlobalMethodSecurityConfiguration implements ImportAware {
private ApplicationContext context;
private MethodSecurityExpressionHandler expressionHandler;
private Jsr250MethodSecurityMetadataSource jsr250MethodSecurityMetadataSource;
private MethodSecurityInterceptor methodSecurityInterceptor;
/**
* Creates the default MethodInterceptor which is a MethodSecurityInterceptor using
@@ -117,18 +121,68 @@ public class GlobalMethodSecurityConfiguration implements ImportAware {
*/
@Bean
public MethodInterceptor methodSecurityInterceptor() throws Exception {
MethodSecurityInterceptor methodSecurityInterceptor = isAspectJ() ? new AspectJMethodSecurityInterceptor()
this.methodSecurityInterceptor = isAspectJ()
? new AspectJMethodSecurityInterceptor()
: 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;
return this.methodSecurityInterceptor;
}
/*
* (non-Javadoc)
*
* @see org.springframework.beans.factory.SmartInitializingSingleton#
* afterSingletonsInstantiated()
*/
@Override
public void afterSingletonsInstantiated() {
try {
initializeMethodSecurityInterceptor();
}
catch (Exception e) {
throw new RuntimeException(e);
}
PermissionEvaluator permissionEvaluator = getSingleBeanOrNull(
PermissionEvaluator.class);
if (permissionEvaluator != null) {
this.defaultMethodExpressionHandler
.setPermissionEvaluator(permissionEvaluator);
}
RoleHierarchy roleHierarchy = getSingleBeanOrNull(RoleHierarchy.class);
if (roleHierarchy != null) {
this.defaultMethodExpressionHandler.setRoleHierarchy(roleHierarchy);
}
AuthenticationTrustResolver trustResolver = getSingleBeanOrNull(
AuthenticationTrustResolver.class);
if (trustResolver != null) {
this.defaultMethodExpressionHandler.setTrustResolver(trustResolver);
}
}
private <T> T getSingleBeanOrNull(Class<T> type) {
String[] beanNamesForType = this.context.getBeanNamesForType(type);
if (beanNamesForType == null || beanNamesForType.length != 1) {
return null;
}
return this.context.getBean(beanNamesForType[0], type);
}
private void initializeMethodSecurityInterceptor() throws Exception {
if(this.methodSecurityInterceptor == null) {
return;
}
this.methodSecurityInterceptor.setAuthenticationManager(authenticationManager());
}
/**
@@ -329,16 +383,6 @@ public class GlobalMethodSecurityConfiguration implements ImportAware {
enableMethodSecurity = AnnotationAttributes.fromMap(annotationAttributes);
}
@Autowired(required = false)
public void setAuthenticationTrustResolver(AuthenticationTrustResolver trustResolver) {
this.defaultMethodExpressionHandler.setTrustResolver(trustResolver);
}
@Autowired(required = false)
public void setRoleHierarchy(RoleHierarchy roleHierarchy){
this.defaultMethodExpressionHandler.setRoleHierarchy(roleHierarchy);
}
@Autowired(required = false)
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
this.objectPostProcessor = objectPostProcessor;
@@ -352,16 +396,6 @@ public class GlobalMethodSecurityConfiguration implements ImportAware {
this.jsr250MethodSecurityMetadataSource = jsr250MethodSecurityMetadataSource;
}
@Autowired(required = false)
public void setPermissionEvaluator(List<PermissionEvaluator> permissionEvaluators) {
if (permissionEvaluators.size() != 1) {
logger.debug("Not autwiring PermissionEvaluator since size != 1. Got "
+ permissionEvaluators);
}
this.defaultMethodExpressionHandler.setPermissionEvaluator(permissionEvaluators
.get(0));
}
@Autowired(required = false)
public void setMethodSecurityExpressionHandler(
List<MethodSecurityExpressionHandler> handlers) {
@@ -15,17 +15,21 @@
*/
package org.springframework.security.config.annotation.web;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.configurers.AbstractConfigAttributeRequestMatcherRegistry;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ClassUtils;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* A base class for registering {@link RequestMatcher}'s. For example, it might allow for
@@ -40,6 +44,21 @@ import org.springframework.util.ObjectUtils;
public abstract class AbstractRequestMatcherRegistry<C> {
private static final RequestMatcher ANY_REQUEST = AnyRequestMatcher.INSTANCE;
private ApplicationContext context;
protected final void setApplicationContext(ApplicationContext context) {
this.context = context;
}
/**
* Gets the {@link ApplicationContext}
*
* @return the {@link ApplicationContext}
*/
protected final ApplicationContext getApplicationContext() {
return this.context;
}
/**
* Maps any request.
*
@@ -93,6 +112,71 @@ public abstract class AbstractRequestMatcherRegistry<C> {
return chainRequestMatchers(RequestMatchers.antMatchers(antPatterns));
}
/**
* <p>
* Maps an {@link MvcRequestMatcher} that does not care which {@link HttpMethod} is
* used. This matcher will use the same rules that Spring MVC uses for matching. For
* example, often times a mapping of the path "/path" will match on "/path", "/path/",
* "/path.html", etc.
* </p>
* <p>
* If the current request will not be processed by Spring MVC, a reasonable default
* using the pattern as a ant pattern will be used.
* </p>
*
* @param mvcPatterns the patterns to match on. The rules for matching are defined by
* Spring MVC
* @return the object that is chained after creating the {@link RequestMatcher}.
*/
public abstract C mvcMatchers(String... mvcPatterns);
/**
* <p>
* Maps an {@link MvcRequestMatcher} that also specifies a specific {@link HttpMethod}
* to match on. This matcher will use the same rules that Spring MVC uses for
* matching. For example, often times a mapping of the path "/path" will match on
* "/path", "/path/", "/path.html", etc.
* </p>
* <p>
* If the current request will not be processed by Spring MVC, a reasonable default
* using the pattern as a ant pattern will be used.
* </p>
*
* @param method the HTTP method to match on
* @param mvcPatterns the patterns to match on. The rules for matching are defined by
* Spring MVC
* @return the object that is chained after creating the {@link RequestMatcher}.
*/
public abstract C mvcMatchers(HttpMethod method, String... mvcPatterns);
/**
* Creates {@link MvcRequestMatcher} instances for the method and patterns passed in
*
* @param method the HTTP method to use or null if any should be used
* @param mvcPatterns the Spring MVC patterns to match on
* @return a List of {@link MvcRequestMatcher} instances
*/
protected final List<MvcRequestMatcher> createMvcMatchers(HttpMethod method,
String... mvcPatterns) {
boolean isServlet30 = ClassUtils.isPresent("javax.servlet.ServletRegistration", getClass().getClassLoader());
ObjectPostProcessor<Object> opp = this.context.getBean(ObjectPostProcessor.class);
HandlerMappingIntrospector introspector = new HandlerMappingIntrospector(
this.context);
List<MvcRequestMatcher> matchers = new ArrayList<MvcRequestMatcher>(
mvcPatterns.length);
for (String mvcPattern : mvcPatterns) {
MvcRequestMatcher matcher = new MvcRequestMatcher(introspector, mvcPattern);
if (isServlet30) {
opp.postProcess(matcher);
}
if (method != null) {
matcher.setMethod(method);
}
matchers.add(matcher);
}
return matchers;
}
/**
* Maps a {@link List} of
* {@link org.springframework.security.web.util.matcher.RegexRequestMatcher}
@@ -222,4 +306,5 @@ public abstract class AbstractRequestMatcherRegistry<C> {
private RequestMatchers() {
}
}
}
@@ -17,7 +17,6 @@ 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;
@@ -44,6 +44,7 @@ import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
import org.springframework.security.web.session.ConcurrentSessionFilter;
import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.web.filter.CorsFilter;
/**
* An internal use only {@link Comparator} that sorts the Security {@link Filter}
@@ -70,6 +71,8 @@ final class FilterComparator implements Comparator<Filter>, Serializable {
order += STEP;
put(HeaderWriterFilter.class, order);
order += STEP;
put(CorsFilter.class, order);
order += STEP;
put(CsrfFilter.class, order);
order += STEP;
put(LogoutFilter.class, order);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2016 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.
@@ -23,6 +23,8 @@ import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
@@ -38,6 +40,7 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer;
import org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer;
import org.springframework.security.config.annotation.web.configurers.CorsConfigurer;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
@@ -61,7 +64,9 @@ import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.PortMapper;
import org.springframework.security.web.PortMapperImpl;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
@@ -69,6 +74,9 @@ import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
/**
* A {@link HttpSecurity} is similar to Spring Security's XML &lt;http&gt; element in the
@@ -109,7 +117,7 @@ public final class HttpSecurity extends
AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
implements SecurityBuilder<DefaultSecurityFilterChain>,
HttpSecurityBuilder<HttpSecurity> {
private final RequestMatcherConfigurer requestMatcherConfigurer = new RequestMatcherConfigurer();
private final RequestMatcherConfigurer requestMatcherConfigurer;
private List<Filter> filters = new ArrayList<Filter>();
private RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;
private FilterComparator comparator = new FilterComparator();
@@ -122,15 +130,24 @@ public final class HttpSecurity extends
* @param sharedObjects the shared Objects to initialize the {@link HttpSecurity} with
* @see WebSecurityConfiguration
*/
@SuppressWarnings("unchecked")
public HttpSecurity(ObjectPostProcessor<Object> objectPostProcessor,
AuthenticationManagerBuilder authenticationBuilder,
Map<Class<Object>, Object> sharedObjects) {
Map<Class<? extends Object>, Object> sharedObjects) {
super(objectPostProcessor);
Assert.notNull(authenticationBuilder, "authenticationBuilder cannot be null");
setSharedObject(AuthenticationManagerBuilder.class, authenticationBuilder);
for (Map.Entry<Class<Object>, Object> entry : sharedObjects.entrySet()) {
setSharedObject(entry.getKey(), entry.getValue());
for (Map.Entry<Class<? extends Object>, Object> entry : sharedObjects
.entrySet()) {
setSharedObject((Class<Object>) entry.getKey(), entry.getValue());
}
ApplicationContext context = (ApplicationContext) sharedObjects
.get(ApplicationContext.class);
this.requestMatcherConfigurer = new RequestMatcherConfigurer(context);
}
private ApplicationContext getContext() {
return getSharedObject(ApplicationContext.class);
}
/**
@@ -317,6 +334,19 @@ public final class HttpSecurity extends
return getOrApply(new HeadersConfigurer<HttpSecurity>());
}
/**
* Adds a {@link CorsFilter} to be used. If a bean by the name of corsFilter is
* provided, that {@link CorsFilter} is used. Else if corsConfigurationSource is
* defined, then that {@link CorsConfiguration} is used. Otherwise, if Spring MVC is
* on the classpath a {@link HandlerMappingIntrospector} is used.
*
* @return the {@link CorsConfigurer} for customizations
* @throws Exception
*/
public CorsConfigurer<HttpSecurity> cors() throws Exception {
return getOrApply(new CorsConfigurer<HttpSecurity>());
}
/**
* Allows configuring of Session Management.
*
@@ -617,7 +647,8 @@ public final class HttpSecurity extends
*/
public ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests()
throws Exception {
return getOrApply(new ExpressionUrlAuthorizationConfigurer<HttpSecurity>())
ApplicationContext context = getContext();
return getOrApply(new ExpressionUrlAuthorizationConfigurer<HttpSecurity>(context))
.getRegistry();
}
@@ -693,7 +724,8 @@ public final class HttpSecurity extends
* @throws Exception
*/
public CsrfConfigurer<HttpSecurity> csrf() throws Exception {
return getOrApply(new CsrfConfigurer<HttpSecurity>());
ApplicationContext context = getContext();
return getOrApply(new CsrfConfigurer<HttpSecurity>(context));
}
/**
@@ -900,7 +932,9 @@ public final class HttpSecurity extends
*/
public ChannelSecurityConfigurer<HttpSecurity>.ChannelRequestMatcherRegistry requiresChannel()
throws Exception {
return getOrApply(new ChannelSecurityConfigurer<HttpSecurity>()).getRegistry();
ApplicationContext context = getContext();
return getOrApply(new ChannelSecurityConfigurer<HttpSecurity>(context))
.getRegistry();
}
/**
@@ -1048,12 +1082,12 @@ public final class HttpSecurity extends
* Allows specifying which {@link HttpServletRequest} instances this
* {@link HttpSecurity} will be invoked on. This method allows for easily invoking the
* {@link HttpSecurity} for multiple different {@link RequestMatcher} instances. If
* only a single {@link RequestMatcher} is necessary consider using
* only a single {@link RequestMatcher} is necessary consider using {@link #mvcMatcher(String)},
* {@link #antMatcher(String)}, {@link #regexMatcher(String)}, or
* {@link #requestMatcher(RequestMatcher)}.
*
* <p>
* Invoking {@link #requestMatchers()} will not override previous invocations of
* Invoking {@link #requestMatchers()} will not override previous invocations of {@link #mvcMatcher(String)}},
* {@link #requestMatchers()}, {@link #antMatcher(String)},
* {@link #regexMatcher(String)}, and {@link #requestMatcher(RequestMatcher)}.
* </p>
@@ -1162,7 +1196,7 @@ public final class HttpSecurity extends
*
* <p>
* Invoking {@link #requestMatcher(RequestMatcher)} will override previous invocations
* of {@link #requestMatchers()}, {@link #antMatcher(String)},
* of {@link #requestMatchers()}, {@link #mvcMatcher(String)}, {@link #antMatcher(String)},
* {@link #regexMatcher(String)}, and {@link #requestMatcher(RequestMatcher)}.
* </p>
*
@@ -1184,7 +1218,7 @@ public final class HttpSecurity extends
* {@link #requestMatchers()} or {@link #requestMatcher(RequestMatcher)}.
*
* <p>
* Invoking {@link #antMatcher(String)} will override previous invocations of
* Invoking {@link #antMatcher(String)} will override previous invocations of {@link #mvcMatcher(String)}},
* {@link #requestMatchers()}, {@link #antMatcher(String)},
* {@link #regexMatcher(String)}, and {@link #requestMatcher(RequestMatcher)}.
* </p>
@@ -1197,13 +1231,33 @@ public final class HttpSecurity extends
return requestMatcher(new AntPathRequestMatcher(antPattern));
}
/**
* Allows configuring the {@link HttpSecurity} to only be invoked when matching the
* provided Spring MVC pattern. If more advanced configuration is necessary, consider using
* {@link #requestMatchers()} or {@link #requestMatcher(RequestMatcher)}.
*
* <p>
* Invoking {@link #mvcMatcher(String)} will override previous invocations of {@link #mvcMatcher(String)}},
* {@link #requestMatchers()}, {@link #antMatcher(String)},
* {@link #regexMatcher(String)}, and {@link #requestMatcher(RequestMatcher)}.
* </p>
*
* @param mvcPattern the Spring MVC Pattern to match on (i.e. "/admin/**")
* @return the {@link HttpSecurity} for further customizations
* @see MvcRequestMatcher
*/
public HttpSecurity mvcMatcher(String mvcPattern) {
HandlerMappingIntrospector introspector = new HandlerMappingIntrospector(getContext());
return requestMatcher(new MvcRequestMatcher(introspector, mvcPattern));
}
/**
* Allows configuring the {@link HttpSecurity} to only be invoked when matching the
* provided regex pattern. If more advanced configuration is necessary, consider using
* {@link #requestMatchers()} or {@link #requestMatcher(RequestMatcher)}.
*
* <p>
* Invoking {@link #regexMatcher(String)} will override previous invocations of
* Invoking {@link #regexMatcher(String)} will override previous invocations of {@link #mvcMatcher(String)}},
* {@link #requestMatchers()}, {@link #antMatcher(String)},
* {@link #regexMatcher(String)}, and {@link #requestMatcher(RequestMatcher)}.
* </p>
@@ -1216,23 +1270,78 @@ public final class HttpSecurity extends
return requestMatcher(new RegexRequestMatcher(pattern, null));
}
/**
* An extension to {@link RequestMatcherConfigurer} that allows optionally configuring
* the servlet path.
*
* @author Rob Winch
*/
public final class MvcMatchersRequestMatcherConfigurer extends RequestMatcherConfigurer {
/**
* Creates a new instance
* @param context the {@link ApplicationContext} to use
* @param matchers the {@link MvcRequestMatcher} instances to set the servlet path
* on if {@link #servletPath(String)} is set.
*/
private MvcMatchersRequestMatcherConfigurer(ApplicationContext context,
List<MvcRequestMatcher> matchers) {
super(context);
this.matchers = new ArrayList<RequestMatcher>(matchers);
}
public RequestMatcherConfigurer servletPath(String servletPath) {
for (RequestMatcher matcher : this.matchers) {
((MvcRequestMatcher) matcher).setServletPath(servletPath);
}
return this;
}
}
/**
* Allows mapping HTTP requests that this {@link HttpSecurity} will be used for
*
* @author Rob Winch
* @since 3.2
*/
public final class RequestMatcherConfigurer extends
AbstractRequestMatcherRegistry<RequestMatcherConfigurer> {
private List<RequestMatcher> matchers = new ArrayList<RequestMatcher>();
public class RequestMatcherConfigurer
extends AbstractRequestMatcherRegistry<RequestMatcherConfigurer> {
protected List<RequestMatcher> matchers = new ArrayList<RequestMatcher>();
/**
* @param context
*/
private RequestMatcherConfigurer(ApplicationContext context) {
setApplicationContext(context);
}
@Override
public MvcMatchersRequestMatcherConfigurer mvcMatchers(HttpMethod method,
String... mvcPatterns) {
List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
setMatchers(mvcMatchers);
return new MvcMatchersRequestMatcherConfigurer(getContext(), mvcMatchers);
}
@Override
public MvcMatchersRequestMatcherConfigurer mvcMatchers(String... patterns) {
return mvcMatchers(null, patterns);
}
@Override
protected RequestMatcherConfigurer chainRequestMatchers(
List<RequestMatcher> requestMatchers) {
matchers.addAll(requestMatchers);
requestMatcher(new OrRequestMatcher(matchers));
setMatchers(requestMatchers);
return this;
}
private void setMatchers(List<? extends RequestMatcher> requestMatchers) {
this.matchers.addAll(requestMatchers);
requestMatcher(new OrRequestMatcher(this.matchers));
}
/**
* Return the {@link HttpSecurity} for further customizations
*
@@ -1242,8 +1351,6 @@ public final class HttpSecurity extends
return HttpSecurity.this;
}
private RequestMatcherConfigurer() {
}
}
/**
@@ -23,9 +23,11 @@ import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
import org.springframework.security.config.annotation.ObjectPostProcessor;
@@ -47,6 +49,7 @@ import org.springframework.security.web.access.intercept.FilterSecurityIntercept
import org.springframework.security.web.debug.DebugFilter;
import org.springframework.security.web.firewall.DefaultHttpFirewall;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.filter.DelegatingFilterProxy;
@@ -80,7 +83,7 @@ public final class WebSecurity extends
private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders = new ArrayList<SecurityBuilder<? extends SecurityFilterChain>>();
private final IgnoredRequestConfigurer ignoredRequestRegistry = new IgnoredRequestConfigurer();
private IgnoredRequestConfigurer ignoredRequestRegistry;
private FilterSecurityInterceptor filterSecurityInterceptor;
@@ -306,6 +309,30 @@ public final class WebSecurity extends
return result;
}
/**
* An {@link IgnoredRequestConfigurer} that allows optionally configuring the
* {@link MvcRequestMatcher#setMethod(HttpMethod)}
*
* @author Rob Winch
*/
public final class MvcMatchersIgnoredRequestConfigurer
extends IgnoredRequestConfigurer {
private final List<MvcRequestMatcher> mvcMatchers;
private MvcMatchersIgnoredRequestConfigurer(ApplicationContext context,
List<MvcRequestMatcher> mvcMatchers) {
super(context);
this.mvcMatchers = mvcMatchers;
}
public IgnoredRequestConfigurer servletPath(String servletPath) {
for (MvcRequestMatcher matcher : this.mvcMatchers) {
matcher.setServletPath(servletPath);
}
return this;
}
}
/**
* Allows registering {@link RequestMatcher} instances that should be ignored by
* Spring Security.
@@ -313,13 +340,31 @@ public final class WebSecurity extends
* @author Rob Winch
* @since 3.2
*/
public final class IgnoredRequestConfigurer extends
AbstractRequestMatcherRegistry<IgnoredRequestConfigurer> {
public class IgnoredRequestConfigurer
extends AbstractRequestMatcherRegistry<IgnoredRequestConfigurer> {
private IgnoredRequestConfigurer(ApplicationContext context) {
setApplicationContext(context);
}
@Override
public MvcMatchersIgnoredRequestConfigurer mvcMatchers(HttpMethod method,
String... mvcPatterns) {
List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
WebSecurity.this.ignoredRequests.addAll(mvcMatchers);
return new MvcMatchersIgnoredRequestConfigurer(getApplicationContext(),
mvcMatchers);
}
@Override
public MvcMatchersIgnoredRequestConfigurer mvcMatchers(String... mvcPatterns) {
return mvcMatchers(null, mvcPatterns);
}
@Override
protected IgnoredRequestConfigurer chainRequestMatchers(
List<RequestMatcher> requestMatchers) {
ignoredRequests.addAll(requestMatchers);
WebSecurity.this.ignoredRequests.addAll(requestMatchers);
return this;
}
@@ -329,13 +374,13 @@ public final class WebSecurity extends
public WebSecurity and() {
return WebSecurity.this;
}
private IgnoredRequestConfigurer() {
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
defaultWebSecurityExpressionHandler.setApplicationContext(applicationContext);
this.defaultWebSecurityExpressionHandler
.setApplicationContext(applicationContext);
this.ignoredRequestRegistry = new IgnoredRequestConfigurer(applicationContext);
}
}
@@ -38,7 +38,6 @@ import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.access.expression.SecurityExpressionHandler;
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.WebSecurity;
import org.springframework.security.context.DelegatingApplicationListener;
@@ -46,7 +45,7 @@ import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
import org.springframework.util.ClassUtils;
/**
* Uses a {@link WebSecurity} to create the {@link FilterChainProxy} that performs the web
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2016 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.
@@ -18,8 +18,10 @@ package org.springframework.security.config.annotation.web.configuration;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
@@ -103,7 +105,7 @@ public abstract class WebSecurityConfigurerAdapter implements
* 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
* @param disableDefaults true if the default configuration should be disabled, else
* false
*/
protected WebSecurityConfigurerAdapter(boolean disableDefaults) {
@@ -174,12 +176,10 @@ public abstract class WebSecurityConfigurerAdapter implements
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
localConfigureAuthenticationBldr.getSharedObjects());
http.setSharedObject(UserDetailsService.class, userDetailsService());
http.setSharedObject(ApplicationContext.class, context);
http.setSharedObject(ContentNegotiationStrategy.class, contentNegotiationStrategy);
http.setSharedObject(AuthenticationTrustResolver.class, trustResolver);
sharedObjects);
if (!disableDefaults) {
// @formatter:off
http
@@ -329,6 +329,14 @@ public abstract class WebSecurityConfigurerAdapter implements
}
// @formatter:on
/**
* Gets the ApplicationContext
* @return the context
*/
protected final ApplicationContext getApplicationContext() {
return this.context;
}
@Autowired
public void setApplicationContext(ApplicationContext context) {
this.context = context;
@@ -367,6 +375,21 @@ public abstract class WebSecurityConfigurerAdapter implements
this.authenticationConfiguration = authenticationConfiguration;
}
/**
* Creates the shared objects
*
* @return the shared Objects
*/
private Map<Class<? extends Object>, Object> createSharedObjects() {
Map<Class<? extends Object>, Object> sharedObjects = new HashMap<Class<? extends Object>, Object>();
sharedObjects.putAll(localConfigureAuthenticationBldr.getSharedObjects());
sharedObjects.put(UserDetailsService.class, userDetailsService());
sharedObjects.put(ApplicationContext.class, context);
sharedObjects.put(ContentNegotiationStrategy.class, contentNegotiationStrategy);
sharedObjects.put(AuthenticationTrustResolver.class, trustResolver);
return sharedObjects;
}
/**
* Delays the use of the {@link UserDetailsService} from the
* {@link AuthenticationManagerBuilder} to ensure that it has been fully configured.
@@ -481,4 +504,5 @@ public abstract class WebSecurityConfigurerAdapter implements
}
}
}
}
}
@@ -15,6 +15,7 @@
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.Arrays;
import java.util.Collections;
import javax.servlet.http.HttpServletRequest;
@@ -37,7 +38,10 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationFa
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
@@ -243,10 +247,17 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
if (contentNegotiationStrategy == null) {
contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
}
MediaTypeRequestMatcher preferredMatcher = new MediaTypeRequestMatcher(
MediaTypeRequestMatcher mediaMatcher = new MediaTypeRequestMatcher(
contentNegotiationStrategy, MediaType.APPLICATION_XHTML_XML,
new MediaType("image", "*"), MediaType.TEXT_HTML, MediaType.TEXT_PLAIN);
preferredMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
mediaMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
RequestMatcher preferredMatcher = new AndRequestMatcher(Arrays.asList(notXRequestedWith, mediaMatcher));
exceptionHandling.defaultAuthenticationEntryPointFor(
postProcess(authenticationEntryPoint), preferredMatcher);
}
@@ -20,6 +20,8 @@ import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.config.annotation.ObjectPostProcessor;
@@ -36,6 +38,7 @@ 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.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
/**
@@ -80,13 +83,14 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> e
private LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
private List<ChannelProcessor> channelProcessors;
private final ChannelRequestMatcherRegistry REGISTRY = new ChannelRequestMatcherRegistry();
private final ChannelRequestMatcherRegistry REGISTRY;
/**
* Creates a new instance
* @see HttpSecurity#requiresChannel()
*/
public ChannelSecurityConfigurer() {
public ChannelSecurityConfigurer(ApplicationContext context) {
this.REGISTRY = new ChannelRequestMatcherRegistry(context);
}
public ChannelRequestMatcherRegistry getRegistry() {
@@ -134,7 +138,7 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> e
}
private ChannelRequestMatcherRegistry addAttribute(String attribute,
List<RequestMatcher> matchers) {
List<? extends RequestMatcher> matchers) {
for (RequestMatcher matcher : matchers) {
Collection<ConfigAttribute> attrs = Arrays
.<ConfigAttribute> asList(new SecurityConfig(attribute));
@@ -143,8 +147,24 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> e
return REGISTRY;
}
public final class ChannelRequestMatcherRegistry extends
AbstractConfigAttributeRequestMatcherRegistry<RequiresChannelUrl> {
public final class ChannelRequestMatcherRegistry
extends AbstractConfigAttributeRequestMatcherRegistry<RequiresChannelUrl> {
private ChannelRequestMatcherRegistry(ApplicationContext context) {
setApplicationContext(context);
}
@Override
public MvcMatchersRequiresChannelUrl mvcMatchers(HttpMethod method,
String... mvcPatterns) {
List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
return new MvcMatchersRequiresChannelUrl(mvcMatchers);
}
@Override
public MvcMatchersRequiresChannelUrl mvcMatchers(String... patterns) {
return mvcMatchers(null, patterns);
}
@Override
protected RequiresChannelUrl chainRequestMatchersInternal(
@@ -185,15 +205,26 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> e
public H and() {
return ChannelSecurityConfigurer.this.and();
}
}
private ChannelRequestMatcherRegistry() {
public final class MvcMatchersRequiresChannelUrl extends RequiresChannelUrl {
private MvcMatchersRequiresChannelUrl(List<MvcRequestMatcher> matchers) {
super(matchers);
}
public RequiresChannelUrl servletPath(String servletPath) {
for (RequestMatcher matcher : this.requestMatchers) {
((MvcRequestMatcher) matcher).setServletPath(servletPath);
}
return this;
}
}
public final class RequiresChannelUrl {
private List<RequestMatcher> requestMatchers;
public class RequiresChannelUrl {
protected List<? extends RequestMatcher> requestMatchers;
private RequiresChannelUrl(List<RequestMatcher> requestMatchers) {
private RequiresChannelUrl(List<? extends RequestMatcher> requestMatchers) {
this.requestMatchers = requestMatchers;
}
@@ -0,0 +1,114 @@
/*
* Copyright 2002-2016 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.ApplicationContext;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.util.ClassUtils;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
/**
* Adds {@link CorsFilter} to the Spring Security filter chain. If a bean by the name of
* corsFilter is provided, that {@link CorsFilter} is used. Else if
* corsConfigurationSource is defined, then that {@link CorsConfiguration} is used.
* Otherwise, if Spring MVC is on the classpath a {@link HandlerMappingIntrospector} is
* used.
*
* @param <H> the builder to return.
* @author Rob Winch
* @since 4.1.1
*/
public class CorsConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<CorsConfigurer<H>, H> {
private static final String HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector";
private static final String CORS_CONFIGURATION_SOURCE_BEAN_NAME = "corsConfigurationSource";
private static final String CORS_FILTER_BEAN_NAME = "corsFilter";
private CorsConfigurationSource configurationSource;
/**
* Creates a new instance
*
* @see HttpSecurity#cors()
*/
public CorsConfigurer() {
}
public CorsConfigurer<H> configurationSource(
CorsConfigurationSource configurationSource) {
this.configurationSource = configurationSource;
return this;
}
@Override
public void configure(H http) throws Exception {
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
CorsFilter corsFilter = getCorsFilter(context);
if (corsFilter == null) {
throw new IllegalStateException(
"Please configure either a " + CORS_FILTER_BEAN_NAME + " bean or a "
+ CORS_CONFIGURATION_SOURCE_BEAN_NAME + "bean.");
}
http.addFilter(corsFilter);
}
private CorsFilter getCorsFilter(ApplicationContext context) {
if (this.configurationSource != null) {
return new CorsFilter(this.configurationSource);
}
boolean containsCorsFilter = context
.containsBeanDefinition(CORS_FILTER_BEAN_NAME);
if (containsCorsFilter) {
return context.getBean(CORS_FILTER_BEAN_NAME, CorsFilter.class);
}
boolean containsCorsSource = context
.containsBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME);
if (containsCorsSource) {
CorsConfigurationSource configurationSource = context.getBean(
CORS_CONFIGURATION_SOURCE_BEAN_NAME, CorsConfigurationSource.class);
return new CorsFilter(configurationSource);
}
boolean mvcPresent = ClassUtils.isPresent(HANDLER_MAPPING_INTROSPECTOR,
context.getClassLoader());
if (mvcPresent) {
return MvcCorsFilter.getMvcCorsFilter(context);
}
return null;
}
static class MvcCorsFilter {
/**
* This needs to be isolated into a separate class as Spring MVC is an optional
* dependency and will potentially cause ClassLoading issues
* @param context
* @return
*/
private static CorsFilter getMvcCorsFilter(ApplicationContext context) {
HandlerMappingIntrospector mappingIntrospector = new HandlerMappingIntrospector(
context);
return new CorsFilter(mappingIntrospector);
}
}
}
@@ -21,6 +21,8 @@ import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
@@ -35,6 +37,7 @@ import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.security.web.csrf.LazyCsrfTokenRepository;
import org.springframework.security.web.csrf.MissingCsrfTokenException;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.session.InvalidSessionAccessDeniedHandler;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
@@ -78,12 +81,14 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
new HttpSessionCsrfTokenRepository());
private RequestMatcher requireCsrfProtectionMatcher = CsrfFilter.DEFAULT_CSRF_MATCHER;
private List<RequestMatcher> ignoredCsrfProtectionMatchers = new ArrayList<RequestMatcher>();
private final ApplicationContext context;
/**
* Creates a new instance
* @see HttpSecurity#csrf()
*/
public CsrfConfigurer() {
public CsrfConfigurer(ApplicationContext context) {
this.context = context;
}
/**
@@ -141,7 +146,8 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
* @since 4.0
*/
public CsrfConfigurer<H> ignoringAntMatchers(String... antPatterns) {
return new IgnoreCsrfProtectionRegistry().antMatchers(antPatterns).and();
return new IgnoreCsrfProtectionRegistry(this.context).antMatchers(antPatterns)
.and();
}
@SuppressWarnings("unchecked")
@@ -265,6 +271,27 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
private class IgnoreCsrfProtectionRegistry
extends AbstractRequestMatcherRegistry<IgnoreCsrfProtectionRegistry> {
/**
* @param context
*/
private IgnoreCsrfProtectionRegistry(ApplicationContext context) {
setApplicationContext(context);
}
@Override
public MvcMatchersIgnoreCsrfProtectionRegistry mvcMatchers(HttpMethod method,
String... mvcPatterns) {
List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
CsrfConfigurer.this.ignoredCsrfProtectionMatchers.addAll(mvcMatchers);
return new MvcMatchersIgnoreCsrfProtectionRegistry(getApplicationContext(),
mvcMatchers);
}
@Override
public MvcMatchersIgnoreCsrfProtectionRegistry mvcMatchers(String... mvcPatterns) {
return mvcMatchers(null, mvcPatterns);
}
public CsrfConfigurer<H> and() {
return CsrfConfigurer.this;
}
@@ -276,4 +303,28 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
}
/**
* An {@link IgnoreCsrfProtectionRegistry} that allows optionally configuring the
* {@link MvcRequestMatcher#setMethod(HttpMethod)}
*
* @author Rob Winch
*/
private final class MvcMatchersIgnoreCsrfProtectionRegistry
extends IgnoreCsrfProtectionRegistry {
private final List<MvcRequestMatcher> mvcMatchers;
private MvcMatchersIgnoreCsrfProtectionRegistry(ApplicationContext context,
List<MvcRequestMatcher> mvcMatchers) {
super(context);
this.mvcMatchers = mvcMatchers;
}
public IgnoreCsrfProtectionRegistry servletPath(String servletPath) {
for (MvcRequestMatcher matcher : this.mvcMatchers) {
matcher.setServletPath(servletPath);
}
return this;
}
}
}
@@ -21,6 +21,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
@@ -34,6 +35,7 @@ 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.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -84,7 +86,7 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
private static final String fullyAuthenticated = "fullyAuthenticated";
private static final String rememberMe = "rememberMe";
private final ExpressionInterceptUrlRegistry REGISTRY = new ExpressionInterceptUrlRegistry();
private final ExpressionInterceptUrlRegistry REGISTRY;
private SecurityExpressionHandler<FilterInvocation> expressionHandler;
@@ -92,7 +94,8 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
* Creates a new instance
* @see HttpSecurity#authorizeRequests()
*/
public ExpressionUrlAuthorizationConfigurer() {
public ExpressionUrlAuthorizationConfigurer(ApplicationContext context) {
this.REGISTRY = new ExpressionInterceptUrlRegistry(context);
}
public ExpressionInterceptUrlRegistry getRegistry() {
@@ -103,6 +106,23 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
extends
ExpressionUrlAuthorizationConfigurer<H>.AbstractInterceptUrlRegistry<ExpressionInterceptUrlRegistry, AuthorizedUrl> {
/**
* @param context
*/
private ExpressionInterceptUrlRegistry(ApplicationContext context) {
setApplicationContext(context);
}
@Override
public MvcMatchersAuthorizedUrl mvcMatchers(HttpMethod method, String... mvcPatterns) {
return new MvcMatchersAuthorizedUrl(createMvcMatchers(method, mvcPatterns));
}
@Override
public MvcMatchersAuthorizedUrl mvcMatchers(String... patterns) {
return mvcMatchers(null, patterns);
}
@Override
protected final AuthorizedUrl chainRequestMatchersInternal(
List<RequestMatcher> requestMatchers) {
@@ -233,8 +253,32 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
return "hasIpAddress('" + ipAddressExpression + "')";
}
public final class AuthorizedUrl {
private List<RequestMatcher> requestMatchers;
/**
* An {@link AuthorizedUrl} that allows optionally configuring the
* {@link MvcRequestMatcher#setMethod(HttpMethod)}
*
* @author Rob Winch
*/
public class MvcMatchersAuthorizedUrl extends AuthorizedUrl {
/**
* Creates a new instance
*
* @param requestMatchers the {@link RequestMatcher} instances to map
*/
private MvcMatchersAuthorizedUrl(List<MvcRequestMatcher> requestMatchers) {
super(requestMatchers);
}
public AuthorizedUrl servletPath(String servletPath) {
for (MvcRequestMatcher matcher : (List<MvcRequestMatcher>) getMatchers()) {
matcher.setServletPath(servletPath);
}
return this;
}
}
public class AuthorizedUrl {
private List<? extends RequestMatcher> requestMatchers;
private boolean not;
/**
@@ -242,10 +286,14 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
*
* @param requestMatchers the {@link RequestMatcher} instances to map
*/
private AuthorizedUrl(List<RequestMatcher> requestMatchers) {
private AuthorizedUrl(List<? extends RequestMatcher> requestMatchers) {
this.requestMatchers = requestMatchers;
}
protected List<? extends RequestMatcher> getMatchers() {
return this.requestMatchers;
}
/**
* Negates the following expression.
*
@@ -15,6 +15,7 @@
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -32,10 +33,12 @@ import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.accept.ContentNegotiationStrategy;
@@ -76,6 +79,10 @@ import org.springframework.web.accept.HeaderContentNegotiationStrategy;
*/
public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
AbstractHttpConfigurer<HttpBasicConfigurer<B>, B> {
private static final RequestHeaderRequestMatcher X_REQUESTED_WITH = new RequestHeaderRequestMatcher("X-Requested-With",
"XMLHttpRequest");
private static final String DEFAULT_REALM = "Realm";
private AuthenticationEntryPoint authenticationEntryPoint;
@@ -91,13 +98,12 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
realmName(DEFAULT_REALM);
LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints = new LinkedHashMap<RequestMatcher, AuthenticationEntryPoint>();
entryPoints.put(new RequestHeaderRequestMatcher("X-Requested-With",
"XMLHttpRequest"), new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
entryPoints.put(X_REQUESTED_WITH, new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
DelegatingAuthenticationEntryPoint defaultEntryPoint = new DelegatingAuthenticationEntryPoint(
entryPoints);
defaultEntryPoint.setDefaultEntryPoint(basicAuthEntryPoint);
authenticationEntryPoint = defaultEntryPoint;
defaultEntryPoint.setDefaultEntryPoint(this.basicAuthEntryPoint);
this.authenticationEntryPoint = defaultEntryPoint;
}
/**
@@ -110,8 +116,8 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
* @throws Exception
*/
public HttpBasicConfigurer<B> realmName(String realmName) throws Exception {
basicAuthEntryPoint.setRealmName(realmName);
basicAuthEntryPoint.afterPropertiesSet();
this.basicAuthEntryPoint.setRealmName(realmName);
this.basicAuthEntryPoint.afterPropertiesSet();
return this;
}
@@ -144,23 +150,32 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
return this;
}
@Override
public void init(B http) throws Exception {
registerDefaults(http);
}
@SuppressWarnings("unchecked")
private void registerDefaults(B http) {
ContentNegotiationStrategy contentNegotiationStrategy = http
.getSharedObject(ContentNegotiationStrategy.class);
if (contentNegotiationStrategy == null) {
contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
}
MediaTypeRequestMatcher preferredMatcher = new MediaTypeRequestMatcher(
MediaTypeRequestMatcher restMatcher = new MediaTypeRequestMatcher(
contentNegotiationStrategy, MediaType.APPLICATION_ATOM_XML,
MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_XML,
MediaType.MULTIPART_FORM_DATA, MediaType.TEXT_XML);
preferredMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
restMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
RequestMatcher notHtmlMatcher = new NegatedRequestMatcher(
new MediaTypeRequestMatcher(contentNegotiationStrategy,
MediaType.TEXT_HTML));
RequestMatcher restNotHtmlMatcher = new AndRequestMatcher(
Arrays.<RequestMatcher>asList(notHtmlMatcher, restMatcher));
RequestMatcher preferredMatcher = new OrRequestMatcher(Arrays.asList(X_REQUESTED_WITH, restNotHtmlMatcher));
registerDefaultEntryPoint(http, preferredMatcher);
registerDefaultLogoutSuccessHandler(http, preferredMatcher);
@@ -173,7 +188,7 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
return;
}
exceptionHandling.defaultAuthenticationEntryPointFor(
postProcess(authenticationEntryPoint), preferredMatcher);
postProcess(this.authenticationEntryPoint), preferredMatcher);
}
private void registerDefaultLogoutSuccessHandler(B http, RequestMatcher preferredMatcher) {
@@ -191,10 +206,10 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
AuthenticationManager authenticationManager = http
.getSharedObject(AuthenticationManager.class);
BasicAuthenticationFilter basicAuthenticationFilter = new BasicAuthenticationFilter(
authenticationManager, authenticationEntryPoint);
if (authenticationDetailsSource != null) {
authenticationManager, this.authenticationEntryPoint);
if (this.authenticationDetailsSource != null) {
basicAuthenticationFilter
.setAuthenticationDetailsSource(authenticationDetailsSource);
.setAuthenticationDetailsSource(this.authenticationDetailsSource);
}
RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
if(rememberMeServices != null) {
@@ -25,7 +25,6 @@ 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.core.Authentication;
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
import org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler;
import org.springframework.security.web.authentication.logout.DelegatingLogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutFilter;
@@ -35,6 +34,7 @@ import org.springframework.security.web.authentication.logout.SecurityContextLog
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
@@ -346,7 +346,12 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>> extends
this.logoutRequestMatcher = new AntPathRequestMatcher(this.logoutUrl, "POST");
}
else {
this.logoutRequestMatcher = new AntPathRequestMatcher(this.logoutUrl);
this.logoutRequestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(this.logoutUrl, "GET"),
new AntPathRequestMatcher(this.logoutUrl, "POST"),
new AntPathRequestMatcher(this.logoutUrl, "PUT"),
new AntPathRequestMatcher(this.logoutUrl, "DELETE")
);
}
return this.logoutRequestMatcher;
}
@@ -18,10 +18,8 @@ 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;
@@ -79,12 +79,16 @@ import org.springframework.security.web.authentication.ui.DefaultLoginPageGenera
*/
public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<RememberMeConfigurer<H>, H> {
/**
* The default name for remember me parameter name and remember me cookie name
*/
private static final String DEFAULT_REMEMBER_ME_NAME = "remember-me";
private AuthenticationSuccessHandler authenticationSuccessHandler;
private String key;
private RememberMeServices rememberMeServices;
private LogoutHandler logoutHandler;
private String rememberMeParameter = "remember-me";
private String rememberMeCookieName = "remember-me";
private String rememberMeParameter = DEFAULT_REMEMBER_ME_NAME;
private String rememberMeCookieName = DEFAULT_REMEMBER_ME_NAME;
private String rememberMeCookieDomain;
private PersistentTokenRepository tokenRepository;
private UserDetailsService userDetailsService;
@@ -241,9 +245,26 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* Whether the cookie should always be created even if the remember-me parameter is
* not set.
* <p>
* By default this will be set to {@code false}.
*
* @param alwaysRemember set to {@code true} to always trigger remember me,
* {@code false} to use the remember-me parameter.
* @return the {@link RememberMeConfigurer} for further customization
* @see AbstractRememberMeServices#setAlwaysRemember(boolean)
*/
public RememberMeConfigurer<H> alwaysRemember(boolean alwaysRemember) {
this.alwaysRemember = alwaysRemember;
return this;
}
@SuppressWarnings("unchecked")
@Override
public void init(H http) throws Exception {
validateInput();
String key = getKey();
RememberMeServices rememberMeServices = getRememberMeServices(http, key);
http.setSharedObject(RememberMeServices.class, rememberMeServices);
@@ -273,6 +294,17 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
http.addFilter(rememberMeFilter);
}
/**
* Validate rememberMeServices and rememberMeCookieName have not been set at
* the same time.
*/
private void validateInput() {
if (this.rememberMeServices != null && this.rememberMeCookieName != DEFAULT_REMEMBER_ME_NAME) {
throw new IllegalArgumentException("Can not set rememberMeCookieName " +
"and custom rememberMeServices.");
}
}
/**
* 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
@@ -19,6 +19,8 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
@@ -27,9 +29,10 @@ import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer.AuthorizedUrl;
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
@@ -87,7 +90,11 @@ import org.springframework.util.Assert;
*/
public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractInterceptUrlConfigurer<UrlAuthorizationConfigurer<H>, H> {
private final StandardInterceptUrlRegistry REGISTRY = new StandardInterceptUrlRegistry();
private final StandardInterceptUrlRegistry REGISTRY;
public UrlAuthorizationConfigurer(ApplicationContext context) {
this.REGISTRY = new StandardInterceptUrlRegistry(context);
}
/**
* The StandardInterceptUrlRegistry is what users will interact with after applying
@@ -115,6 +122,24 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
extends
ExpressionUrlAuthorizationConfigurer<H>.AbstractInterceptUrlRegistry<StandardInterceptUrlRegistry, AuthorizedUrl> {
/**
* @param context
*/
private StandardInterceptUrlRegistry(ApplicationContext context) {
setApplicationContext(context);
}
@Override
public MvcMatchersAuthorizedUrl mvcMatchers(HttpMethod method,
String... mvcPatterns) {
return new MvcMatchersAuthorizedUrl(createMvcMatchers(method, mvcPatterns));
}
@Override
public MvcMatchersAuthorizedUrl mvcMatchers(String... patterns) {
return mvcMatchers(null, patterns);
}
@Override
protected final AuthorizedUrl chainRequestMatchersInternal(
List<RequestMatcher> requestMatchers) {
@@ -226,6 +251,31 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
return authorities;
}
/**
* An {@link AuthorizedUrl} that allows optionally configuring the
* {@link MvcRequestMatcher#setMethod(HttpMethod)}
*
* @author Rob Winch
*/
public final class MvcMatchersAuthorizedUrl extends AuthorizedUrl {
/**
* Creates a new instance
*
* @param requestMatchers the {@link RequestMatcher} instances to map
*/
private MvcMatchersAuthorizedUrl(List<MvcRequestMatcher> requestMatchers) {
super(requestMatchers);
}
@SuppressWarnings("unchecked")
public AuthorizedUrl servletPath(String servletPath) {
for (MvcRequestMatcher matcher : (List<MvcRequestMatcher>) getMatchers()) {
matcher.setServletPath(servletPath);
}
return this;
}
}
/**
* Maps the specified {@link RequestMatcher} instances to {@link ConfigAttribute}
* instances.
@@ -233,15 +283,15 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
* @author Rob Winch
* @since 3.2
*/
public final class AuthorizedUrl {
private final List<RequestMatcher> requestMatchers;
public class AuthorizedUrl {
private final List<? extends RequestMatcher> requestMatchers;
/**
* Creates a new instance
* @param requestMatchers the {@link RequestMatcher} instances to map to some
* {@link ConfigAttribute} instances.
*/
private AuthorizedUrl(List<RequestMatcher> requestMatchers) {
private AuthorizedUrl(List<? extends RequestMatcher> requestMatchers) {
Assert.notEmpty(requestMatchers,
"requestMatchers must contain at least one value");
this.requestMatchers = requestMatchers;
@@ -307,5 +357,9 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
addMapping(requestMatchers, SecurityConfig.createList(attributes));
return UrlAuthorizationConfigurer.this.REGISTRY;
}
protected List<? extends RequestMatcher> getMatchers() {
return this.requestMatchers;
}
}
}
@@ -18,7 +18,6 @@ package org.springframework.security.config.annotation.web.servlet.configuration
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver;
import org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor;
@@ -40,7 +40,7 @@ import java.util.Arrays;
public class AuthenticationManagerFactoryBean implements
FactoryBean<AuthenticationManager>, BeanFactoryAware {
private BeanFactory bf;
public static final String MISSING_BEAN_ERROR_MESSAGE = "Did you forget to add a gobal <authentication-manager> element "
public static final String MISSING_BEAN_ERROR_MESSAGE = "Did you forget to add a global <authentication-manager> element "
+ "to your configuration (with child <authentication-provider> elements)? Alternatively you can use the "
+ "authentication-manager-ref attribute on your <http> and <global-method-security> elements.";
@@ -30,7 +30,6 @@ import org.springframework.security.authentication.encoding.BaseDigestPasswordEn
import org.springframework.security.authentication.encoding.LdapShaPasswordEncoder;
import org.springframework.security.authentication.encoding.Md4PasswordEncoder;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.authentication.encoding.PlaintextPasswordEncoder;
import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
import org.springframework.security.config.Elements;
@@ -17,7 +17,6 @@ package org.springframework.security.config.authentication;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Iterator;
import java.util.List;
import org.springframework.beans.factory.BeanDefinitionStoreException;
@@ -22,8 +22,6 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -0,0 +1,78 @@
/*
* Copyright 2002-2016 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.http;
import org.w3c.dom.Element;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.CorsFilter;
/**
* Parser for the {@code CorsFilter}.
*
* @author Rob Winch
* @since 4.1.1
*/
public class CorsBeanDefinitionParser {
private static final String HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector";
private static final String ATT_SOURCE = "configuration-source-ref";
private static final String ATT_REF = "ref";
public BeanMetadataElement parse(Element element, ParserContext parserContext) {
if(element == null) {
return null;
}
String filterRef = element.getAttribute(ATT_REF);
if(StringUtils.hasText(filterRef)) {
return new RuntimeBeanReference(filterRef);
}
BeanMetadataElement configurationSource = getSource(element, parserContext);
if(configurationSource == null) {
throw new BeanCreationException("Could not create CorsFilter");
}
BeanDefinitionBuilder filterBldr = BeanDefinitionBuilder.rootBeanDefinition(CorsFilter.class);
filterBldr.addConstructorArgValue(configurationSource);
return filterBldr.getBeanDefinition();
}
public BeanMetadataElement getSource(Element element, ParserContext parserContext) {
String configurationSourceRef = element.getAttribute(ATT_SOURCE);
if (StringUtils.hasText(configurationSourceRef)) {
return new RuntimeBeanReference(configurationSourceRef);
}
boolean mvcPresent = ClassUtils.isPresent(HANDLER_MAPPING_INTROSPECTOR,
getClass().getClassLoader());
if(!mvcPresent) {
return null;
}
BeanDefinitionBuilder configurationSourceBldr = BeanDefinitionBuilder.rootBeanDefinition(HANDLER_MAPPING_INTROSPECTOR);
configurationSourceBldr.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
return configurationSourceBldr.getBeanDefinition();
}
}
@@ -19,11 +19,9 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
@@ -47,7 +45,7 @@ public class FilterChainBeanDefinitionParser implements BeanDefinitionParser {
if (StringUtils.hasText(path)) {
Assert.isTrue(!StringUtils.hasText(requestMatcher), "");
builder.addConstructorArgValue(matcherType.createMatcher(path, null));
builder.addConstructorArgValue(matcherType.createMatcher(pc, path, null));
}
else {
Assert.isTrue(StringUtils.hasText(requestMatcher), "");
@@ -15,10 +15,7 @@
*/
package org.springframework.security.config.http;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition;
@@ -26,7 +23,6 @@ import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.config.Elements;
@@ -75,7 +71,7 @@ public class FilterChainMapBeanDefinitionDecorator implements BeanDefinitionDeco
+ "'must not be empty", elt);
}
BeanDefinition matcher = matcherType.createMatcher(path, null);
BeanDefinition matcher = matcherType.createMatcher(parserContext, path, null);
if (filters.equals(HttpSecurityBeanDefinitionParser.OPT_FILTERS_NONE)) {
securityFilterChains.add(createSecurityFilterChain(matcher,
@@ -15,8 +15,6 @@
*/
package org.springframework.security.config.http;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.BeanDefinition;
@@ -37,6 +35,8 @@ import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import java.util.List;
/**
* Allows for convenient creation of a {@link FilterInvocationSecurityMetadataSource} bean
* for use with a FilterSecurityInterceptor.
@@ -48,12 +48,13 @@ public class FilterInvocationSecurityMetadataSourceParser implements BeanDefinit
private static final String ATT_HTTP_METHOD = "method";
private static final String ATT_PATTERN = "pattern";
private static final String ATT_ACCESS = "access";
private static final String ATT_SERVLET_PATH = "servlet-path";
private static final Log logger = LogFactory
.getLog(FilterInvocationSecurityMetadataSourceParser.class);
public BeanDefinition parse(Element element, ParserContext parserContext) {
List<Element> interceptUrls = DomUtils.getChildElementsByTagName(element,
"intercept-url");
Elements.INTERCEPT_URL);
// Check for attributes that aren't allowed in this context
for (Element elt : interceptUrls) {
@@ -71,6 +72,12 @@ public class FilterInvocationSecurityMetadataSourceParser implements BeanDefinit
"The attribute '" + HttpSecurityBeanDefinitionParser.ATT_FILTERS
+ "' isn't allowed here.", elt);
}
if (StringUtils.hasLength(elt.getAttribute(ATT_SERVLET_PATH))) {
parserContext.getReaderContext().error(
"The attribute '" + ATT_SERVLET_PATH
+ "' isn't allowed here.", elt);
}
}
BeanDefinition mds = createSecurityMetadataSource(interceptUrls, false, element,
@@ -165,7 +172,16 @@ public class FilterInvocationSecurityMetadataSourceParser implements BeanDefinit
method = null;
}
BeanDefinition matcher = matcherType.createMatcher(path, method);
String servletPath = urlElt.getAttribute(ATT_SERVLET_PATH);
if (!StringUtils.hasText(servletPath)) {
servletPath = null;
} else if (!MatcherType.mvc.equals(matcherType)) {
parserContext.getReaderContext().error(
ATT_SERVLET_PATH + " is not applicable for request-matcher: '" + matcherType.name() + "'", urlElt);
}
BeanDefinition matcher = matcherType.createMatcher(parserContext, path,
method, servletPath);
BeanDefinitionBuilder attributeBuilder = BeanDefinitionBuilder
.rootBeanDefinition(SecurityConfig.class);
@@ -194,7 +210,8 @@ public class FilterInvocationSecurityMetadataSourceParser implements BeanDefinit
if (addAuthenticatedAll && filterInvocationDefinitionMap.isEmpty()) {
BeanDefinition matcher = matcherType.createMatcher("/**", null);
BeanDefinition matcher = matcherType.createMatcher(parserContext, "/**",
null);
BeanDefinitionBuilder attributeBuilder = BeanDefinitionBuilder
.rootBeanDefinition(SecurityConfig.class);
attributeBuilder.addConstructorArgValue(new String[] { "authenticated" });
@@ -0,0 +1,51 @@
/*
* Copyright 2012-2016 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.http;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
/**
* Used for creating an instance of {@link HandlerMappingIntrospector} and autowiring the
* {@link ApplicationContext}.
*
* @author Rob Winch
* @since 4.1.1
*/
class HandlerMappingIntrospectorFactoryBean implements ApplicationContextAware {
private ApplicationContext context;
HandlerMappingIntrospector createHandlerMappingIntrospector() {
return new HandlerMappingIntrospector(this.context);
}
/*
* (non-Javadoc)
*
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.
* springframework.context.ApplicationContext)
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.context = applicationContext;
}
}
@@ -127,6 +127,7 @@ class HttpConfigurationBuilder {
private BeanReference fsi;
private BeanReference requestCache;
private BeanDefinition addHeadersFilter;
private BeanMetadataElement corsFilter;
private BeanDefinition csrfFilter;
private BeanMetadataElement csrfLogoutHandler;
private BeanMetadataElement csrfAuthStrategy;
@@ -176,6 +177,7 @@ class HttpConfigurationBuilder {
createChannelProcessingFilter();
createFilterSecurityInterceptor(authenticationManager);
createAddHeadersFilter();
createCorsFilter();
}
private SessionCreationPolicy createPolicy(String createSession) {
@@ -611,7 +613,7 @@ class HttpConfigurationBuilder {
String requiredChannel = urlElt.getAttribute(ATT_REQUIRES_CHANNEL);
if (StringUtils.hasText(requiredChannel)) {
BeanDefinition matcher = matcherType.createMatcher(path, method);
BeanDefinition matcher = matcherType.createMatcher(pc, path, method);
RootBeanDefinition channelAttributes = new RootBeanDefinition(
ChannelAttributeFactory.class);
@@ -737,6 +739,11 @@ class HttpConfigurationBuilder {
private void createAddHeadersFilter() {
Element elmt = DomUtils.getChildElementByTagName(httpElt, Elements.HEADERS);
this.addHeadersFilter = new HeadersBeanDefinitionParser().parse(elmt, pc);
}
private void createCorsFilter() {
Element elmt = DomUtils.getChildElementByTagName(this.httpElt, Elements.CORS);
this.corsFilter = new CorsBeanDefinitionParser().parse(elmt, this.pc);
}
@@ -808,6 +815,10 @@ class HttpConfigurationBuilder {
filters.add(new OrderDecorator(requestCacheAwareFilter, REQUEST_CACHE_FILTER));
}
if (this.corsFilter != null) {
filters.add(new OrderDecorator(this.corsFilter, CORS_FILTER));
}
if (addHeadersFilter != null) {
filters.add(new OrderDecorator(addHeadersFilter, HEADERS_FILTER));
}
@@ -198,7 +198,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
}
else if (StringUtils.hasText(filterChainPattern)) {
filterChainMatcher = MatcherType.fromElement(element).createMatcher(
filterChainMatcher = MatcherType.fromElement(element).createMatcher(pc,
filterChainPattern, null);
}
else {
@@ -18,6 +18,8 @@ package org.springframework.security.config.http;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
@@ -33,17 +35,24 @@ import org.w3c.dom.Element;
*/
public enum MatcherType {
ant(AntPathRequestMatcher.class), regex(RegexRequestMatcher.class), ciRegex(
RegexRequestMatcher.class);
RegexRequestMatcher.class), mvc(MvcRequestMatcher.class);
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "org.springframework.web.servlet.handler.HandlerMappingIntrospector";
private static final String HANDLER_MAPPING_INTROSPECTOR_FACTORY_BEAN_NAME = "org.springframework.security.config.http.HandlerMappingIntrospectorFactoryBean";
private static final String ATT_MATCHER_TYPE = "request-matcher";
private final Class<? extends RequestMatcher> type;
final Class<? extends RequestMatcher> type;
MatcherType(Class<? extends RequestMatcher> type) {
this.type = type;
}
public BeanDefinition createMatcher(String path, String method) {
public BeanDefinition createMatcher(ParserContext pc, String path, String method) {
return createMatcher(pc, path, method, null);
}
public BeanDefinition createMatcher(ParserContext pc, String path, String method, String servletPath) {
if (("/**".equals(path) || "**".equals(path)) && method == null) {
return new RootBeanDefinition(AnyRequestMatcher.class);
}
@@ -51,8 +60,29 @@ public enum MatcherType {
BeanDefinitionBuilder matcherBldr = BeanDefinitionBuilder
.rootBeanDefinition(type);
if (this == mvc) {
if (!pc.getRegistry().isBeanNameInUse(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
BeanDefinitionBuilder hmifb = BeanDefinitionBuilder
.rootBeanDefinition(HandlerMappingIntrospectorFactoryBean.class);
pc.getRegistry().registerBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_FACTORY_BEAN_NAME,
hmifb.getBeanDefinition());
RootBeanDefinition hmi = new RootBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME);
hmi.setFactoryBeanName(HANDLER_MAPPING_INTROSPECTOR_FACTORY_BEAN_NAME);
hmi.setFactoryMethodName("createHandlerMappingIntrospector");
pc.getRegistry().registerBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, hmi);
}
matcherBldr.addConstructorArgReference(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME);
}
matcherBldr.addConstructorArgValue(path);
matcherBldr.addConstructorArgValue(method);
if (this == mvc) {
matcherBldr.addPropertyValue("method", method);
matcherBldr.addPropertyValue("servletPath", servletPath);
}
else {
matcherBldr.addConstructorArgValue(method);
}
if (this == ciRegex) {
matcherBldr.addConstructorArgValue(true);
@@ -28,7 +28,7 @@ import org.springframework.security.web.context.request.async.WebAsyncManagerInt
enum SecurityFilters {
FIRST(Integer.MIN_VALUE), CHANNEL_FILTER, SECURITY_CONTEXT_FILTER, CONCURRENT_SESSION_FILTER,
/** {@link WebAsyncManagerIntegrationFilter} */
WEB_ASYNC_MANAGER_FILTER, HEADERS_FILTER, CSRF_FILTER, LOGOUT_FILTER, X509_FILTER, PRE_AUTH_FILTER, CAS_FILTER, FORM_LOGIN_FILTER, OPENID_FILTER, LOGIN_PAGE_FILTER, DIGEST_AUTH_FILTER, BASIC_AUTH_FILTER, REQUEST_CACHE_FILTER, SERVLET_API_SUPPORT_FILTER, JAAS_API_SUPPORT_FILTER, REMEMBER_ME_FILTER, ANONYMOUS_FILTER, SESSION_MANAGEMENT_FILTER, EXCEPTION_TRANSLATION_FILTER, FILTER_SECURITY_INTERCEPTOR, SWITCH_USER_FILTER, LAST(
WEB_ASYNC_MANAGER_FILTER, HEADERS_FILTER, CORS_FILTER, CSRF_FILTER, LOGOUT_FILTER, X509_FILTER, PRE_AUTH_FILTER, CAS_FILTER, FORM_LOGIN_FILTER, OPENID_FILTER, LOGIN_PAGE_FILTER, DIGEST_AUTH_FILTER, BASIC_AUTH_FILTER, REQUEST_CACHE_FILTER, SERVLET_API_SUPPORT_FILTER, JAAS_API_SUPPORT_FILTER, REMEMBER_ME_FILTER, ANONYMOUS_FILTER, SESSION_MANAGEMENT_FILTER, EXCEPTION_TRANSLATION_FILTER, FILTER_SECURITY_INTERCEPTOR, SWITCH_USER_FILTER, LAST(
Integer.MAX_VALUE);
private static final int INTERVAL = 100;
@@ -46,7 +46,6 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;
import org.springframework.security.access.annotation.Jsr250Voter;
@@ -759,10 +759,10 @@ frame-options.attlist &=
## Specify the strategy to use when ALLOW-FROM is chosen.
attribute strategy {"static","whitelist","regexp"}?
frame-options.attlist &=
## Specify the a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.
## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.
ref?
frame-options.attlist &=
## Specify the a value to use for the chosen strategy.
## Specify a value to use for the chosen strategy.
attribute value {xsd:string}?
frame-options.attlist &=
## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.
@@ -2365,7 +2365,7 @@
</xs:attribute>
<xs:attribute name="value" type="xs:string">
<xs:annotation>
<xs:documentation>Specify the a value to use for the chosen strategy.
<xs:documentation>Specify a value to use for the chosen strategy.
</xs:documentation>
</xs:annotation>
</xs:attribute>
@@ -785,10 +785,10 @@ frame-options.attlist &=
## Specify the strategy to use when ALLOW-FROM is chosen.
attribute strategy {"static","whitelist","regexp"}?
frame-options.attlist &=
## Specify the a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.
## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.
ref?
frame-options.attlist &=
## Specify the a value to use for the chosen strategy.
## Specify a value to use for the chosen strategy.
attribute value {xsd:string}?
frame-options.attlist &=
## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.
@@ -2440,7 +2440,7 @@
</xs:attribute>
<xs:attribute name="value" type="xs:string">
<xs:annotation>
<xs:documentation>Specify the a value to use for the chosen strategy.
<xs:documentation>Specify a value to use for the chosen strategy.
</xs:documentation>
</xs:annotation>
</xs:attribute>
@@ -12,8 +12,8 @@ base64 =
## Whether a string should be base64 encoded
attribute base64 {xsd:boolean}
request-matcher =
## Defines the strategy use for matching incoming requests. Currently the options are 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.
attribute request-matcher {"ant" | "regex" | "ciRegex"}
## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.
attribute request-matcher {"mvc" | "ant" | "regex" | "ciRegex"}
port =
## Specifies an IP port number. Used to configure an embedded LDAP server, for example.
attribute port { xsd:positiveInteger }
@@ -303,7 +303,7 @@ http-firewall =
http =
## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the "security" attribute to "none".
element http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & openid-login? & x509? & jee? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf?) }
element http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & openid-login? & x509? & jee? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) }
http.attlist &=
## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.
attribute pattern {xsd:token}?
@@ -381,6 +381,9 @@ intercept-url.attlist &=
intercept-url.attlist &=
## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be "http", "https" or "any", respectively.
attribute requires-channel {xsd:token}?
intercept-url.attlist &=
## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'.
attribute servlet-path {xsd:token}?
logout =
## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.
@@ -771,12 +774,21 @@ hsts-options.attlist &=
## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true.
attribute request-matcher-ref { xsd:token }?
cors =
## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource
element cors { cors-options.attlist }
cors-options.attlist &=
ref?
cors-options.attlist &=
## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use
attribute configuration-source-ref {xsd:token}?
hpkp =
## Adds support for HTTP Public Key Pinning (HPKP).
element hpkp {hpkp.pins,hpkp.attlist}
hpkp.pins =
## The list with pins
element pins {hpkp.pin+}
element pins {hpkp.pin+}
hpkp.pin =
## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute
element pin {
@@ -830,10 +842,10 @@ frame-options.attlist &=
## Specify the strategy to use when ALLOW-FROM is chosen.
attribute strategy {"static","whitelist","regexp"}?
frame-options.attlist &=
## Specify the a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.
## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen.
ref?
frame-options.attlist &=
## Specify the a value to use for the chosen strategy.
## Specify a value to use for the chosen strategy.
attribute value {xsd:string}?
frame-options.attlist &=
## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'.
@@ -895,4 +907,4 @@ position =
## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.
attribute position {named-security-filter}
named-security-filter = "FIRST" | "CHANNEL_FILTER" | "SECURITY_CONTEXT_FILTER" | "CONCURRENT_SESSION_FILTER" | "WEB_ASYNC_MANAGER_FILTER" | "HEADERS_FILTER" | "CSRF_FILTER" | "LOGOUT_FILTER" | "X509_FILTER" | "PRE_AUTH_FILTER" | "CAS_FILTER" | "FORM_LOGIN_FILTER" | "OPENID_FILTER" | "LOGIN_PAGE_FILTER" | "DIGEST_AUTH_FILTER" | "BASIC_AUTH_FILTER" | "REQUEST_CACHE_FILTER" | "SERVLET_API_SUPPORT_FILTER" | "JAAS_API_SUPPORT_FILTER" | "REMEMBER_ME_FILTER" | "ANONYMOUS_FILTER" | "SESSION_MANAGEMENT_FILTER" | "EXCEPTION_TRANSLATION_FILTER" | "FILTER_SECURITY_INTERCEPTOR" | "SWITCH_USER_FILTER" | "LAST"
named-security-filter = "FIRST" | "CHANNEL_FILTER" | "SECURITY_CONTEXT_FILTER" | "CONCURRENT_SESSION_FILTER" | "WEB_ASYNC_MANAGER_FILTER" | "HEADERS_FILTER" | "CORS_FILTER" | "CSRF_FILTER" | "LOGOUT_FILTER" | "X509_FILTER" | "PRE_AUTH_FILTER" | "CAS_FILTER" | "FORM_LOGIN_FILTER" | "OPENID_FILTER" | "LOGIN_PAGE_FILTER" | "DIGEST_AUTH_FILTER" | "BASIC_AUTH_FILTER" | "REQUEST_CACHE_FILTER" | "SERVLET_API_SUPPORT_FILTER" | "JAAS_API_SUPPORT_FILTER" | "REMEMBER_ME_FILTER" | "ANONYMOUS_FILTER" | "SESSION_MANAGEMENT_FILTER" | "EXCEPTION_TRANSLATION_FILTER" | "FILTER_SECURITY_INTERCEPTOR" | "SWITCH_USER_FILTER" | "LAST"
@@ -34,13 +34,14 @@
<xs:attributeGroup name="request-matcher">
<xs:attribute name="request-matcher" use="required">
<xs:annotation>
<xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'ant'
(for ant path patterns), 'regex' for regular expressions and 'ciRegex' for
case-insensitive regular expressions.
<xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'
(for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions
and 'ciRegex' for case-insensitive regular expressions.
</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="mvc"/>
<xs:enumeration value="ant"/>
<xs:enumeration value="regex"/>
<xs:enumeration value="ciRegex"/>
@@ -1111,6 +1112,7 @@
</xs:element>
<xs:element ref="security:headers"/>
<xs:element ref="security:csrf"/>
<xs:element ref="security:cors"/>
</xs:choice>
<xs:attributeGroup ref="security:http.attlist"/>
</xs:complexType>
@@ -1186,13 +1188,14 @@
</xs:attribute>
<xs:attribute name="request-matcher">
<xs:annotation>
<xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'ant'
(for ant path patterns), 'regex' for regular expressions and 'ciRegex' for
case-insensitive regular expressions.
<xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'
(for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions
and 'ciRegex' for case-insensitive regular expressions.
</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="mvc"/>
<xs:enumeration value="ant"/>
<xs:enumeration value="regex"/>
<xs:enumeration value="ciRegex"/>
@@ -1342,6 +1345,16 @@
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="servlet-path" type="xs:token">
<xs:annotation>
<xs:documentation>The path to the servlet. This attribute is only applicable when 'request-matcher' is
'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are
2 or more HttpServlet's registered in the ServletContext that have mappings starting with
'/' and are different; 2) The pattern starts with the same value of a registered
HttpServlet path, excluding the default (root) HttpServlet '/'.
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:attributeGroup name="logout.attlist">
@@ -1556,13 +1569,14 @@
<xs:attributeGroup name="filter-chain-map.attlist">
<xs:attribute name="request-matcher">
<xs:annotation>
<xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'ant'
(for ant path patterns), 'regex' for regular expressions and 'ciRegex' for
case-insensitive regular expressions.
<xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'
(for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions
and 'ciRegex' for case-insensitive regular expressions.
</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="mvc"/>
<xs:enumeration value="ant"/>
<xs:enumeration value="regex"/>
<xs:enumeration value="ciRegex"/>
@@ -1667,13 +1681,14 @@
</xs:attribute>
<xs:attribute name="request-matcher">
<xs:annotation>
<xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'ant'
(for ant path patterns), 'regex' for regular expressions and 'ciRegex' for
case-insensitive regular expressions.
<xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'
(for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions
and 'ciRegex' for case-insensitive regular expressions.
</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="mvc"/>
<xs:enumeration value="ant"/>
<xs:enumeration value="regex"/>
<xs:enumeration value="ciRegex"/>
@@ -2385,6 +2400,31 @@
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="cors">
<xs:annotation>
<xs:documentation>Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is
specified a HandlerMappingIntrospector is used as the CorsConfigurationSource
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attributeGroup ref="security:cors-options.attlist"/>
</xs:complexType>
</xs:element>
<xs:attributeGroup name="cors-options.attlist">
<xs:attribute name="ref" type="xs:token">
<xs:annotation>
<xs:documentation>Defines a reference to a Spring bean Id.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="configuration-source-ref" type="xs:token">
<xs:annotation>
<xs:documentation>Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to
use
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="hpkp">
<xs:annotation>
<xs:documentation>Adds support for HTTP Public Key Pinning (HPKP).
@@ -2555,7 +2595,7 @@
</xs:attribute>
<xs:attribute name="value" type="xs:string">
<xs:annotation>
<xs:documentation>Specify the a value to use for the chosen strategy.
<xs:documentation>Specify a value to use for the chosen strategy.
</xs:documentation>
</xs:annotation>
</xs:attribute>
@@ -2716,6 +2756,7 @@
<xs:enumeration value="CONCURRENT_SESSION_FILTER"/>
<xs:enumeration value="WEB_ASYNC_MANAGER_FILTER"/>
<xs:enumeration value="HEADERS_FILTER"/>
<xs:enumeration value="CORS_FILTER"/>
<xs:enumeration value="CSRF_FILTER"/>
<xs:enumeration value="LOGOUT_FILTER"/>
<xs:enumeration value="X509_FILTER"/>
@@ -1,26 +1,25 @@
package org.springframework.security.config
import groovy.xml.MarkupBuilder
import org.mockito.Mockito;
import org.springframework.context.support.AbstractXmlApplicationContext
import org.mockito.Mockito
import org.springframework.context.ApplicationListener
import org.springframework.context.support.AbstractRefreshableApplicationContext
import org.springframework.mock.web.MockServletContext
import org.springframework.security.CollectingAppListener
import org.springframework.security.config.util.InMemoryXmlApplicationContext
import org.springframework.security.config.util.InMemoryXmlWebApplicationContext
import org.springframework.security.core.context.SecurityContextHolder
import spock.lang.Specification
import static org.springframework.security.config.ConfigTestUtils.AUTH_PROVIDER_XML
import org.springframework.context.ApplicationListener
import org.springframework.context.ApplicationEvent
import org.springframework.security.authentication.event.AbstractAuthenticationEvent
import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent
import org.springframework.security.access.event.AbstractAuthorizationEvent
import org.springframework.security.CollectingAppListener
import javax.servlet.ServletContext
import static org.springframework.security.config.ConfigTestUtils.AUTH_PROVIDER_XML
/**
*
* @author Luke Taylor
*/
abstract class AbstractXmlConfigTests extends Specification {
AbstractXmlApplicationContext appContext;
AbstractRefreshableApplicationContext appContext;
Writer writer;
MarkupBuilder xml;
ApplicationListener appListener;
@@ -81,4 +80,27 @@ abstract class AbstractXmlConfigTests extends Specification {
appContext = new InMemoryXmlApplicationContext(writer.toString() + extraXml);
appContext.addApplicationListener(appListener);
}
def createWebAppContext() {
createWebAppContext(AUTH_PROVIDER_XML);
}
def createWebAppContext(ServletContext servletContext) {
createWebAppContext(AUTH_PROVIDER_XML, servletContext);
}
def createWebAppContext(String extraXml) {
createWebAppContext(extraXml, null);
}
def createWebAppContext(String extraXml, ServletContext servletContext) {
appContext = new InMemoryXmlWebApplicationContext(writer.toString() + extraXml);
appContext.addApplicationListener(appListener);
if (servletContext != null) {
appContext.setServletContext(servletContext);
} else {
appContext.setServletContext(new MockServletContext());
}
appContext.refresh();
}
}
@@ -17,20 +17,22 @@ package org.springframework.security.config.annotation;
import javax.servlet.Filter
import spock.lang.AutoCleanup
import spock.lang.Specification
import org.springframework.beans.factory.NoSuchBeanDefinitionException
import org.springframework.context.ConfigurableApplicationContext
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.mock.web.MockServletContext
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.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor;
import org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration
import org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration
import org.springframework.security.core.Authentication
import org.springframework.security.core.authority.AuthorityUtils
import org.springframework.security.core.context.SecurityContextHolder
@@ -40,11 +42,9 @@ import org.springframework.security.web.access.intercept.FilterSecurityIntercept
import org.springframework.security.web.context.HttpRequestResponseHolder
import org.springframework.security.web.context.HttpSessionSecurityContextRepository
import org.springframework.security.web.csrf.CsrfToken
import org.springframework.security.web.csrf.DefaultCsrfToken;
import org.springframework.security.web.csrf.DefaultCsrfToken
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository
import spock.lang.AutoCleanup
import spock.lang.Specification
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext
/**
*
@@ -88,7 +88,10 @@ abstract class BaseSpringSpec extends Specification {
}
def loadConfig(Class<?>... configs) {
context = new AnnotationConfigApplicationContext(configs)
context = new AnnotationConfigWebApplicationContext()
context.register(configs)
context.setServletContext(new MockServletContext())
context.refresh()
context
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2016 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.
@@ -15,28 +15,12 @@
*/
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;
import static org.springframework.security.config.annotation.authentication.PasswordEncoderConfigurerConfigs.PasswordEncoderConfig
import static org.springframework.security.config.annotation.authentication.PasswordEncoderConfigurerConfigs.PasswordEncoderNoAuthManagerLoadsConfig;
/**
*
@@ -23,6 +23,7 @@ import org.springframework.context.annotation.Import
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.security.access.annotation.Secured
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.AuthenticationProvider
import org.springframework.security.authentication.TestingAuthenticationToken
@@ -485,4 +486,33 @@ class AuthenticationConfigurationTests extends BaseSpringSpec {
UDS
}
}
def 'EnableGlobalMethodSecurity configuration uses PreAuthorize does not cause BeanCurrentlyInCreationException'() {
when:
loadConfig(UsesPreAuthorizeMethodSecurityConfig,AuthenticationManagerBeanConfig)
then:
noExceptionThrown()
}
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
static class UsesPreAuthorizeMethodSecurityConfig {
@PreAuthorize("denyAll")
void run() {}
}
def 'EnableGlobalMethodSecurity uses method security service'() {
when:
loadConfig(ServicesConfig,UsesPreAuthorizeMethodSecurityConfig,AuthenticationManagerBeanConfig)
then:
noExceptionThrown()
}
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
static class UsesServiceMethodSecurityConfig {
@Autowired
Service service
}
}
@@ -18,6 +18,7 @@ package org.springframework.security.config.annotation.configuration
import org.springframework.beans.factory.BeanClassLoaderAware
import org.springframework.beans.factory.BeanFactoryAware
import org.springframework.beans.factory.DisposableBean
import org.springframework.beans.factory.SmartInitializingSingleton
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.config.AutowireCapableBeanFactory
import org.springframework.beans.factory.support.BeanNameGenerator;
@@ -138,4 +139,36 @@ class AutowireBeanFactoryObjectPostProcessorTests extends BaseSpringSpec {
p.postProcess(new Object())
}
}
def "SmartInitializingSingleton"() {
when:
context = new AnnotationConfigWebApplicationContext([servletConfig:new MockServletConfig(),servletContext:new MockServletContext()])
context.register(SmartConfig)
context.refresh()
context.start()
then:
context.getBean(SmartConfig).smart.instantiated
}
@Configuration
static class SmartConfig {
SmartInitializingSingleton smart = new SmartInitializingSingletonStub()
@Bean
public static ObjectPostProcessor objectPostProcessor(AutowireCapableBeanFactory beanFactory) {
return new AutowireBeanFactoryObjectPostProcessor(beanFactory)
}
@Autowired
public void configure(ObjectPostProcessor<Object> p) {
p.postProcess(smart)
}
}
static class SmartInitializingSingletonStub implements SmartInitializingSingleton {
boolean instantiated
void afterSingletonsInstantiated() {
instantiated = true
}
}
}
@@ -25,7 +25,7 @@ public class DisableUseExpressionsConfig extends BaseWebConfig {
protected void configure(HttpSecurity http) throws Exception {
// This config is also on UrlAuthorizationConfigurer javadoc
http
.apply(new UrlAuthorizationConfigurer<HttpSecurity>()).getRegistry()
.apply(new UrlAuthorizationConfigurer<HttpSecurity>(getApplicationContext())).getRegistry()
.antMatchers("/users**","/sessions/**").hasRole("USER")
.antMatchers("/signup").hasRole("ANONYMOUS")
.anyRequest().hasRole("USER");
@@ -15,7 +15,6 @@
*/
package org.springframework.security.config.annotation.web.configuration.sec2377.a;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@@ -15,7 +15,6 @@
*/
package org.springframework.security.config.annotation.web.configuration.sec2377.b;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@@ -67,5 +67,13 @@ class AbstractConfigAttributeRequestMatcherRegistryTests extends Specification {
List<RequestMatcher> chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
return requestMatchers;
}
List<RequestMatcher> mvcMatchers(String... mvcPatterns) {
null
}
List<RequestMatcher> mvcMatchers(HttpMethod method, String... mvcPatterns) {
null
}
}
}
@@ -0,0 +1,205 @@
/*
* Copyright 2002-2016 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.context.annotation.Bean
import org.springframework.http.*
import org.springframework.security.config.annotation.BaseSpringSpec
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.web.bind.annotation.*
import org.springframework.web.cors.CorsConfiguration
import org.springframework.web.cors.CorsConfigurationSource
import org.springframework.web.cors.UrlBasedCorsConfigurationSource
import org.springframework.web.filter.CorsFilter
import org.springframework.web.servlet.config.annotation.EnableWebMvc
/**
*
* @author Rob Winch
*/
class CorsConfigurerTests extends BaseSpringSpec {
def "HandlerMappingIntrospector default"() {
setup:
loadConfig(DefaultCorsConfig)
when:
addCors()
springSecurityFilterChain.doFilter(request,response,chain)
then:
responseHeaders == ['X-Content-Type-Options':'nosniff',
'X-Frame-Options':'DENY',
'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate',
'Expires' : '0',
'Pragma':'no-cache',
'X-XSS-Protection' : '1; mode=block']
}
@EnableWebSecurity
static class DefaultCorsConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.cors()
}
}
def "HandlerMappingIntrospector explicit"() {
setup:
loadConfig(MvcCorsConfig)
when:
addCors()
springSecurityFilterChain.doFilter(request,response,chain)
then: 'Ensure we a CORS response w/ Spring Security headers too'
responseHeaders['Access-Control-Allow-Origin']
responseHeaders['X-Content-Type-Options']
when:
setupWeb()
addCors(true)
springSecurityFilterChain.doFilter(request,response,chain)
then: 'Ensure we a CORS response w/ Spring Security headers too'
responseHeaders['Access-Control-Allow-Origin']
responseHeaders['X-Content-Type-Options']
response.status == HttpServletResponse.SC_OK
}
@EnableWebMvc
@EnableWebSecurity
static class MvcCorsConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.cors()
}
@RestController
@CrossOrigin(methods = [
RequestMethod.GET, RequestMethod.POST
])
static class CorsController {
@RequestMapping("/")
String hello() {
"Hello"
}
}
}
def "CorsConfigurationSource"() {
setup:
loadConfig(ConfigSourceConfig)
when:
addCors()
springSecurityFilterChain.doFilter(request,response,chain)
then: 'Ensure we a CORS response w/ Spring Security headers too'
responseHeaders['Access-Control-Allow-Origin']
responseHeaders['X-Content-Type-Options']
when:
setupWeb()
addCors(true)
springSecurityFilterChain.doFilter(request,response,chain)
then: 'Ensure we a CORS response w/ Spring Security headers too'
responseHeaders['Access-Control-Allow-Origin']
responseHeaders['X-Content-Type-Options']
response.status == HttpServletResponse.SC_OK
}
@EnableWebSecurity
static class ConfigSourceConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.cors()
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource()
source.registerCorsConfiguration("/**", new CorsConfiguration(allowedOrigins : ['*'], allowedMethods : [
RequestMethod.GET.name(),
RequestMethod.POST.name()
]))
source
}
}
def "CorsFilter"() {
setup:
loadConfig(CorsFilterConfig)
when:
addCors()
springSecurityFilterChain.doFilter(request,response,chain)
then: 'Ensure we a CORS response w/ Spring Security headers too'
responseHeaders['Access-Control-Allow-Origin']
responseHeaders['X-Content-Type-Options']
when:
setupWeb()
addCors(true)
springSecurityFilterChain.doFilter(request,response,chain)
then: 'Ensure we a CORS response w/ Spring Security headers too'
responseHeaders['Access-Control-Allow-Origin']
responseHeaders['X-Content-Type-Options']
response.status == HttpServletResponse.SC_OK
}
@EnableWebSecurity
static class CorsFilterConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.cors()
}
@Bean
CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource()
source.registerCorsConfiguration("/**", new CorsConfiguration(allowedOrigins : ['*'], allowedMethods : [
RequestMethod.GET.name(),
RequestMethod.POST.name()
]))
new CorsFilter(source)
}
}
def addCors(boolean isPreflight=false) {
request.addHeader(HttpHeaders.ORIGIN,"https://example.com")
if(!isPreflight) {
return
}
request.method = HttpMethod.OPTIONS.name()
request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())
}
}
@@ -91,7 +91,20 @@ class ExceptionHandlingConfigurerTests extends BaseSpringSpec {
loadConfig(HttpBasicAndFormLoginEntryPointsConfig)
DelegatingAuthenticationEntryPoint delegateEntryPoint = findFilter(ExceptionTranslationFilter).authenticationEntryPoint
then:
delegateEntryPoint.entryPoints.keySet().collect {it.contentNegotiationStrategy.class} == [HeaderContentNegotiationStrategy,HeaderContentNegotiationStrategy]
def entryPoints = delegateEntryPoint.entryPoints.keySet() as List
entryPoints[0].requestMatchers[1].requestMatchers[1].contentNegotiationStrategy.class == HeaderContentNegotiationStrategy
entryPoints[1].requestMatchers[1].contentNegotiationStrategy.class == HeaderContentNegotiationStrategy
}
def "401 for text/plain and X-Requested-With:XMLHttpRequest"() {
setup:
loadConfig(HttpBasicAndFormLoginEntryPointsConfig)
when:
request.addHeader("Accept", MediaType.TEXT_PLAIN_VALUE)
request.addHeader("X-Requested-With", "XMLHttpRequest")
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.status == HttpServletResponse.SC_UNAUTHORIZED
}
@EnableWebSecurity
@@ -123,7 +136,9 @@ class ExceptionHandlingConfigurerTests extends BaseSpringSpec {
loadConfig(OverrideContentNegotiationStrategySharedObjectConfig)
DelegatingAuthenticationEntryPoint delegateEntryPoint = findFilter(ExceptionTranslationFilter).authenticationEntryPoint
then:
delegateEntryPoint.entryPoints.keySet().collect {it.contentNegotiationStrategy} == [OverrideContentNegotiationStrategySharedObjectConfig.CNS,OverrideContentNegotiationStrategySharedObjectConfig.CNS]
def entryPoints = delegateEntryPoint.entryPoints.keySet() as List
entryPoints[0].requestMatchers[1].contentNegotiationStrategy == OverrideContentNegotiationStrategySharedObjectConfig.CNS
entryPoints[1].requestMatchers[1].requestMatchers[1].contentNegotiationStrategy == OverrideContentNegotiationStrategySharedObjectConfig.CNS
}
def "Override ContentNegotiationStrategy with @Bean"() {
@@ -19,7 +19,6 @@ import java.util.Arrays;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.access.expression.SecurityExpressionOperations;
@@ -15,19 +15,15 @@
*/
package org.springframework.security.config.annotation.web.configurers
import org.springframework.beans.factory.BeanCreationException
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpStatus
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.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurerTests.RememberMeNoLogoutHandler;
import org.springframework.security.web.authentication.RememberMeServices
import org.springframework.security.web.authentication.logout.LogoutFilter
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler
import org.springframework.security.web.util.matcher.RequestMatcher
/**
@@ -91,14 +87,25 @@ class LogoutConfigurerTests extends BaseSpringSpec {
}
}
def "SEC-2311: Logout allows other methods if CSRF is disabled"() {
def "Logout allows other methods if CSRF is disabled"() {
when:
loadConfig(CsrfDisabledConfig)
request.method = "GET"
request.method = method
request.servletPath = "/logout"
findFilter(LogoutFilter).doFilter(request,response,chain)
then:
response.redirectedUrl == "/login?logout"
response.status == httpStatus.value()
response.redirectedUrl == url
where:
method | httpStatus | url
"GET" | HttpStatus.FOUND | "/login?logout"
"POST" | HttpStatus.FOUND | "/login?logout"
"PUT" | HttpStatus.FOUND | "/login?logout"
"DELETE" | HttpStatus.FOUND | "/login?logout"
"OPTIONS" | HttpStatus.OK | null
"HEAD" | HttpStatus.OK | null
"TRACE" | HttpStatus.OK | null
}
@EnableWebSecurity
@@ -113,14 +120,25 @@ class LogoutConfigurerTests extends BaseSpringSpec {
}
def "SEC-2311: Logout allows other methods if CSRF is disabled with custom logout URL"() {
def "Logout allows other methods if CSRF is disabled with custom logout URL"() {
when:
loadConfig(CsrfDisabledCustomLogoutUrlConfig)
request.method = "GET"
request.method = method
request.servletPath = "/custom/logout"
findFilter(LogoutFilter).doFilter(request,response,chain)
then:
response.redirectedUrl == "/login?logout"
response.status == httpStatus.value()
response.redirectedUrl == url
where:
method | httpStatus | url
"GET" | HttpStatus.FOUND | "/login?logout"
"POST" | HttpStatus.FOUND | "/login?logout"
"PUT" | HttpStatus.FOUND | "/login?logout"
"DELETE" | HttpStatus.FOUND | "/login?logout"
"OPTIONS" | HttpStatus.OK | null
"HEAD" | HttpStatus.OK | null
"TRACE" | HttpStatus.OK | null
}
@EnableWebSecurity
@@ -197,4 +215,40 @@ class LogoutConfigurerTests extends BaseSpringSpec {
@EnableWebSecurity
static class LogoutHandlerContentNegotiation extends WebSecurityConfigurerAdapter {
}
// gh-3902
def "logout in chrome is 302"() {
setup:
loadConfig(LogoutHandlerContentNegotiationForChrome)
when:
login()
request.method = 'POST'
request.servletPath = '/logout'
request.addHeader('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8')
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.status == 302
}
@EnableWebSecurity
static class LogoutHandlerContentNegotiationForChrome extends WebSecurityConfigurerAdapter {
}
// gh-3997
def "LogoutConfigurer for XMLHttpRequest is 204"() {
setup:
loadConfig(LogoutXMLHttpRequestConfig)
when:
login()
request.method = 'POST'
request.servletPath = '/logout'
request.addHeader('Accept', 'text/html,application/json')
request.addHeader('X-Requested-With', 'XMLHttpRequest')
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.status == 204
}
@EnableWebSecurity
static class LogoutXMLHttpRequestConfig extends WebSecurityConfigurerAdapter {
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2016 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.
@@ -17,24 +17,27 @@ package org.springframework.security.config.annotation.web.configurers
import javax.servlet.http.Cookie
import org.springframework.beans.factory.BeanCreationException
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.mock.web.MockFilterChain
import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.mock.web.MockHttpServletResponse
import org.springframework.mock.web.MockHttpSession
import org.springframework.security.authentication.RememberMeAuthenticationToken
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider
import org.springframework.security.config.annotation.AnyObjectPostProcessor
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.builders.HttpSecurity
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.authority.AuthorityUtils
import org.springframework.security.core.context.SecurityContext
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.User
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.provisioning.InMemoryUserDetailsManager
import org.springframework.security.web.authentication.RememberMeServices
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter
import org.springframework.security.web.context.HttpRequestResponseHolder
import org.springframework.security.web.context.HttpSessionSecurityContextRepository
@@ -43,6 +46,7 @@ import org.springframework.security.web.context.HttpSessionSecurityContextReposi
* Tests for RememberMeConfigurer that flex edge cases. {@link NamespaceRememberMeTests} demonstrate mapping of the XML namespace to Java Config.
*
* @author Rob Winch
* @author Eddú Meléndez
*/
public class RememberMeConfigurerTests extends BaseSpringSpec {
@@ -155,7 +159,7 @@ public class RememberMeConfigurerTests extends BaseSpringSpec {
response.getRedirectedUrl() == "http://localhost/login"
}
def "http/remember-me with cookied domain"() {
def "http/remember-me with cookie domain"() {
setup:
loadConfig(RememberMeCookieDomainConfig)
when:
@@ -172,6 +176,29 @@ public class RememberMeConfigurerTests extends BaseSpringSpec {
rememberMeCookie.domain == "spring.io"
}
def "http/remember-me with cookie name and custom rememberMeServices throws BeanCreationException"() {
setup:
RememberMeCookieDomainCustomRememberMeServicesConfig.REMEMBER_ME = Mock(RememberMeServices)
when:
loadConfig(RememberMeCookieDomainCustomRememberMeServicesConfig)
then: "response contains remember me cookie"
def ex = thrown(BeanCreationException)
ex instanceof BeanCreationException
}
def "http/remember-me with cookie name and custom rememberMeServices throws IllegalArgumentException"() {
setup:
def httpSec = new HttpSecurity(Mock(ObjectPostProcessor), Mock(AuthenticationManagerBuilder), [:])
RememberMeConfigurer<HttpSecurity> config = new RememberMeConfigurer<HttpSecurity>();
config.rememberMeCookieName("COOKIE_NAME")
config.rememberMeServices(Mock(RememberMeServices))
when:
config.init(httpSec)
then:
IllegalArgumentException ex = thrown()
ex.message == 'Can not set rememberMeCookieName and custom rememberMeServices.'
}
@EnableWebSecurity
static class RememberMeConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
@@ -213,6 +240,33 @@ public class RememberMeConfigurerTests extends BaseSpringSpec {
}
}
@EnableWebSecurity
static class RememberMeCookieDomainCustomRememberMeServicesConfig extends
WebSecurityConfigurerAdapter {
static RememberMeServices REMEMBER_ME
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().hasRole("USER")
.and()
.formLogin()
.and()
.rememberMe()
.rememberMeCookieName("SPRING_COOKIE_DOMAIN")
.rememberMeCookieDomain("spring.io")
.rememberMeServices(REMEMBER_ME);
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
Cookie createRememberMeCookie() {
MockHttpServletRequest request = new MockHttpServletRequest()
MockHttpServletResponse response = new MockHttpServletResponse()
@@ -16,7 +16,6 @@
package org.springframework.security.config.doc
import groovy.util.slurpersupport.GPathResult;
import groovy.util.slurpersupport.NodeChild
import org.springframework.security.config.http.SecurityFilters
@@ -89,7 +88,7 @@ class XsdDocumentedTests extends Specification {
def 'the latest schema is being validated'() {
when: 'all the schemas are found'
def schemas = schemaDocument.getParentFile().list().findAll { it.endsWith('.xsd') }
then: 'the count is equal to 8, if not then schemaDocument needs updated'
then: 'the count is equal to 10, if not then schemaDocument needs updated'
schemas.size() == 10
}
@@ -0,0 +1,176 @@
/*
* Copyright 2002-2016 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.http
import javax.servlet.http.HttpServletResponse
import org.springframework.http.*
import org.springframework.mock.web.*
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint
import org.springframework.web.bind.annotation.*
import org.springframework.web.filter.CorsFilter
import org.springframework.web.cors.CorsConfiguration
import org.springframework.web.cors.UrlBasedCorsConfigurationSource
/**
*
* @author Rob Winch
* @author Tim Ysewyn
*/
class HttpCorsConfigTests extends AbstractHttpConfigTests {
MockHttpServletRequest request
MockHttpServletResponse response
MockFilterChain chain
def setup() {
request = new MockHttpServletRequest(method:"GET")
response = new MockHttpServletResponse()
chain = new MockFilterChain()
}
def "HandlerMappingIntrospector default"() {
setup:
xml.http('entry-point-ref' : 'ep') {
'cors'()
'intercept-url'(pattern:'/**', access: 'authenticated')
}
bean('ep', Http403ForbiddenEntryPoint)
createAppContext()
when:
addCors()
springSecurityFilterChain.doFilter(request,response,chain)
then:
responseHeaders == ['X-Content-Type-Options':'nosniff',
'X-Frame-Options':'DENY',
'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate',
'Expires' : '0',
'Pragma':'no-cache',
'X-XSS-Protection' : '1; mode=block']
}
def "HandlerMappingIntrospector explicit"() {
setup:
xml.http('entry-point-ref' : 'ep') {
'cors'()
'intercept-url'(pattern:'/**', access: 'authenticated')
}
bean('ep', Http403ForbiddenEntryPoint)
bean('controller', CorsController)
xml.'mvc:annotation-driven'()
createAppContext()
when:
addCors()
springSecurityFilterChain.doFilter(request,response,chain)
then: 'Ensure we a CORS response w/ Spring Security headers too'
responseHeaders['Access-Control-Allow-Origin']
responseHeaders['X-Content-Type-Options']
when:
setup()
addCors(true)
springSecurityFilterChain.doFilter(request,response,chain)
then: 'Ensure we a CORS response w/ Spring Security headers too'
responseHeaders['Access-Control-Allow-Origin']
responseHeaders['X-Content-Type-Options']
response.status == HttpServletResponse.SC_OK
}
def "CorsConfigurationSource"() {
setup:
xml.http('entry-point-ref' : 'ep') {
'cors'('configuration-source-ref':'ccs')
'intercept-url'(pattern:'/**', access: 'authenticated')
}
bean('ep', Http403ForbiddenEntryPoint)
bean('ccs', MyCorsConfigurationSource)
createAppContext()
when:
addCors()
springSecurityFilterChain.doFilter(request,response,chain)
then: 'Ensure we a CORS response w/ Spring Security headers too'
responseHeaders['Access-Control-Allow-Origin']
responseHeaders['X-Content-Type-Options']
when:
setup()
addCors(true)
springSecurityFilterChain.doFilter(request,response,chain)
then: 'Ensure we a CORS response w/ Spring Security headers too'
responseHeaders['Access-Control-Allow-Origin']
responseHeaders['X-Content-Type-Options']
response.status == HttpServletResponse.SC_OK
}
def "CorsFilter"() {
setup:
xml.http('entry-point-ref' : 'ep') {
'cors'('ref' : 'cf')
'intercept-url'(pattern:'/**', access: 'authenticated')
}
xml.'b:bean'(id: 'cf', 'class': CorsFilter.name) {
'b:constructor-arg'(ref: 'ccs')
}
bean('ep', Http403ForbiddenEntryPoint)
bean('ccs', MyCorsConfigurationSource)
createAppContext()
when:
addCors()
springSecurityFilterChain.doFilter(request,response,chain)
then: 'Ensure we a CORS response w/ Spring Security headers too'
responseHeaders['Access-Control-Allow-Origin']
responseHeaders['X-Content-Type-Options']
when:
setup()
addCors(true)
springSecurityFilterChain.doFilter(request,response,chain)
then: 'Ensure we a CORS response w/ Spring Security headers too'
responseHeaders['Access-Control-Allow-Origin']
responseHeaders['X-Content-Type-Options']
response.status == HttpServletResponse.SC_OK
}
def addCors(boolean isPreflight=false) {
request.addHeader(HttpHeaders.ORIGIN,"https://example.com")
if(!isPreflight) {
return
}
request.method = HttpMethod.OPTIONS.name()
request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())
}
def getResponseHeaders() {
def headers = [:]
response.headerNames.each { name ->
headers.put(name, response.getHeaderValues(name).join(','))
}
return headers
}
@RestController
@CrossOrigin(methods = [
RequestMethod.GET, RequestMethod.POST
])
static class CorsController {
@RequestMapping("/")
String hello() {
"Hello"
}
}
static class MyCorsConfigurationSource extends UrlBasedCorsConfigurationSource {
MyCorsConfigurationSource() {
registerCorsConfiguration('/**', new CorsConfiguration(allowedOrigins : ['*'], allowedMethods : [
RequestMethod.GET.name(),
RequestMethod.POST.name()
]))
}
}
}
@@ -15,17 +15,25 @@
*/
package org.springframework.security.config.http
import javax.servlet.Filter
import javax.servlet.http.HttpServletResponse
import org.mockito.invocation.InvocationOnMock
import org.mockito.stubbing.Answer
import org.springframework.beans.factory.BeanCreationException
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException
import org.springframework.mock.web.MockFilterChain
import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.mock.web.MockHttpServletResponse
import org.springframework.mock.web.MockServletContext
import org.springframework.security.access.SecurityConfig
import org.springframework.security.crypto.codec.Base64
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import javax.servlet.ServletContext
import javax.servlet.ServletRegistration
import javax.servlet.http.HttpServletResponse
import static org.mockito.Mockito.*
/**
*
* @author Rob Winch
@@ -197,14 +205,216 @@ class InterceptUrlConfigTests extends AbstractHttpConfigTests {
response.status == HttpServletResponse.SC_FORBIDDEN
}
def "intercept-url supports mvc matchers"() {
setup:
MockServletContext servletContext = mockServletContext();
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
MockHttpServletResponse response = new MockHttpServletResponse()
MockFilterChain chain = new MockFilterChain()
xml.http('request-matcher':'mvc') {
'http-basic'()
'intercept-url'(pattern: '/path', access: "denyAll")
}
bean('pathController',PathController)
xml.'mvc:annotation-driven'()
createWebAppContext(servletContext)
when:
request.servletPath = "/path"
springSecurityFilterChain.doFilter(request, response, chain)
then:
response.status == HttpServletResponse.SC_UNAUTHORIZED
when:
request = new MockHttpServletRequest(method:'GET')
response = new MockHttpServletResponse()
chain = new MockFilterChain()
request.servletPath = "/path.html"
springSecurityFilterChain.doFilter(request, response, chain)
then:
response.status == HttpServletResponse.SC_UNAUTHORIZED
when:
request = new MockHttpServletRequest(method:'GET')
response = new MockHttpServletResponse()
chain = new MockFilterChain()
request.servletPath = "/path/"
springSecurityFilterChain.doFilter(request, response, chain)
then:
response.status == HttpServletResponse.SC_UNAUTHORIZED
}
def "intercept-url mvc supports path variables"() {
setup:
MockServletContext servletContext = mockServletContext();
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
MockHttpServletResponse response = new MockHttpServletResponse()
MockFilterChain chain = new MockFilterChain()
xml.http('request-matcher':'mvc') {
'http-basic'()
'intercept-url'(pattern: '/user/{un}/**', access: "#un == 'user'")
}
createWebAppContext(servletContext)
when: 'user can access'
request.servletPath = '/user/user/abc'
springSecurityFilterChain.doFilter(request,response,chain)
then: 'The response is OK'
response.status == HttpServletResponse.SC_OK
when: 'cannot access otheruser'
request = new MockHttpServletRequest(method:'GET', servletPath : '/user/otheruser/abc')
login(request, 'user', 'password')
chain.reset()
springSecurityFilterChain.doFilter(request,response,chain)
then: 'The response is OK'
response.status == HttpServletResponse.SC_FORBIDDEN
when: 'user can access case insensitive URL'
request = new MockHttpServletRequest(method:'GET', servletPath : '/USER/user/abc')
login(request, 'user', 'password')
chain.reset()
springSecurityFilterChain.doFilter(request,response,chain)
then: 'The response is OK'
response.status == HttpServletResponse.SC_FORBIDDEN
}
def "intercept-url mvc matchers with servlet path"() {
setup:
MockServletContext servletContext = mockServletContext("/spring");
MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
MockHttpServletResponse response = new MockHttpServletResponse()
MockFilterChain chain = new MockFilterChain()
xml.http('request-matcher':'mvc') {
'http-basic'()
'intercept-url'(pattern: '/path', access: "denyAll", 'servlet-path': "/spring")
}
bean('pathController',PathController)
xml.'mvc:annotation-driven'()
createWebAppContext(servletContext)
when:
request.servletPath = "/spring"
request.requestURI = "/spring/path"
springSecurityFilterChain.doFilter(request, response, chain)
then:
response.status == HttpServletResponse.SC_UNAUTHORIZED
when:
request = new MockHttpServletRequest(method:'GET')
response = new MockHttpServletResponse()
chain = new MockFilterChain()
request.servletPath = "/spring"
request.requestURI = "/spring/path.html"
springSecurityFilterChain.doFilter(request, response, chain)
then:
response.status == HttpServletResponse.SC_UNAUTHORIZED
when:
request = new MockHttpServletRequest(method:'GET')
response = new MockHttpServletResponse()
chain = new MockFilterChain()
request.servletPath = "/spring"
request.requestURI = "/spring/path/"
springSecurityFilterChain.doFilter(request, response, chain)
then:
response.status == HttpServletResponse.SC_UNAUTHORIZED
}
def "intercept-url mvc matchers servlet path required"() {
when:
MockServletContext servletContext = mockServletContext("/spring");
xml.http('request-matcher':'mvc') {
'http-basic'()
'intercept-url'(pattern: '/path', access: "denyAll")
}
createWebAppContext(servletContext)
then:
thrown(BeanCreationException)
}
def "intercept-url mvc matchers servlet path NOT required"() {
when:
MockServletContext servletContext = mockServletContext();
xml.http('request-matcher':'mvc') {
'http-basic'()
'intercept-url'(pattern: '/path', access: "denyAll")
}
createWebAppContext(servletContext)
then:
noExceptionThrown()
}
def "intercept-url ant matcher with servlet path fails"() {
when:
xml.http('request-matcher':'ant') {
'http-basic'()
'intercept-url'(pattern: '/path', access: "denyAll", 'servlet-path': "/spring")
}
createAppContext()
then:
thrown(BeanDefinitionParsingException)
}
def "intercept-url regex matcher with servlet path fails"() {
when:
xml.http('request-matcher':'regex') {
'http-basic'()
'intercept-url'(pattern: '/path', access: "denyAll", 'servlet-path': "/spring")
}
createAppContext()
then:
thrown(BeanDefinitionParsingException)
}
def "intercept-url ciRegex matcher with servlet path fails"() {
when:
xml.http('request-matcher':'ciRegex') {
'http-basic'()
'intercept-url'(pattern: '/path', access: "denyAll", 'servlet-path': "/spring")
}
createAppContext()
then:
thrown(BeanDefinitionParsingException)
}
def "intercept-url default matcher with servlet path fails"() {
when:
xml.http() {
'http-basic'()
'intercept-url'(pattern: '/path', access: "denyAll", 'servlet-path': "/spring")
}
createAppContext()
then:
thrown(BeanDefinitionParsingException)
}
public static class Id {
public boolean isOne(int i) {
return i == 1;
}
}
private ServletContext mockServletContext() {
return mockServletContext("/");
}
private ServletContext mockServletContext(String servletPath) {
MockServletContext servletContext = spy(new MockServletContext());
final ServletRegistration registration = mock(ServletRegistration.class);
when(registration.getMappings()).thenReturn(Collections.singleton(servletPath));
Answer<Map<String, ? extends ServletRegistration>> answer = new Answer<Map<String, ? extends ServletRegistration>>() {
@Override
public Map<String, ? extends ServletRegistration> answer(InvocationOnMock invocation) throws Throwable {
return Collections.<String, ServletRegistration>singletonMap("spring", registration);
}
};
when(servletContext.getServletRegistrations()).thenAnswer(answer);
return servletContext;
}
def login(MockHttpServletRequest request, String username, String password) {
String toEncode = username + ':' + password
request.addHeader('Authorization','Basic ' + new String(Base64.encode(toEncode.getBytes('UTF-8'))))
}
}
@RestController
static class PathController {
@RequestMapping("/path")
public String path() {
return "path";
}
}
}
@@ -17,7 +17,6 @@
package org.springframework.security.config;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
import java.util.List;
@@ -30,7 +29,7 @@ import javax.servlet.http.HttpServletResponse;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
@@ -42,7 +41,6 @@ import org.springframework.security.web.context.SecurityContextPersistenceFilter
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
/**
* Tests {@link FilterChainProxy}.
@@ -17,7 +17,6 @@ package org.springframework.security.config;
import org.springframework.context.ApplicationListener;
import org.springframework.security.core.session.SessionCreationEvent;
import org.springframework.transaction.annotation.Transactional;
/**
* @author Luke Taylor
@@ -0,0 +1,44 @@
/*
* Copyright 2002-2016 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.junit.Before;
import org.junit.Test;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import static org.assertj.core.api.Assertions.assertThat;
public class LdapAuthenticationProviderConfigurerTest {
private LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> configurer;
@Before
public void setUp() {
configurer = new LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder>();
}
// SEC-2557
@Test
public void getAuthoritiesMapper() throws Exception {
assertThat(configurer.getAuthoritiesMapper()).isInstanceOf(SimpleAuthorityMapper.class);
configurer.authoritiesMapper(new NullAuthoritiesMapper());
assertThat(configurer.getAuthoritiesMapper()).isInstanceOf(NullAuthoritiesMapper.class);
}
}
@@ -0,0 +1,89 @@
/*
* Copyright 2012-2016 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.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import static org.mockito.Mockito.*;
/**
* @author Rob Winch
*/
@RunWith(MockitoJUnitRunner.class)
public class AbstractRequestMatcherRegistryTests {
@Mock
HandlerMappingIntrospector introspector;
MvcRequestMatcher matcher;
ServletContext servletContext;
@Before
public void setup() {
servletContext = spy(new MockServletContext());
matcher = new MvcRequestMatcher(introspector, "/foo");
matcher.setServletContext(servletContext);
}
@Test(expected = IllegalStateException.class)
public void servletPathValidatingMvcRequestMatcherAfterPropertiesSetFailsWithSpringServlet() throws Exception {
setMappings("/spring");
matcher.afterPropertiesSet();
}
@Test
public void servletPathValidatingMvcRequestMatcherAfterPropertiesSetWithSpringServlet() throws Exception {
matcher.setServletPath("/spring");
setMappings("/spring");
matcher.afterPropertiesSet();
}
@Test
public void servletPathValidatingMvcRequestMatcherAfterPropertiesSetDefaultServlet() throws Exception {
setMappings("/");
matcher.afterPropertiesSet();
}
private void setMappings(String... mappings) {
final ServletRegistration registration = mock(ServletRegistration.class);
when(registration.getMappings()).thenReturn(Arrays.asList(mappings));
Answer<Map<String, ? extends ServletRegistration>> answer = new Answer<Map<String, ? extends ServletRegistration>>() {
@Override
public Map<String, ? extends ServletRegistration> answer(InvocationOnMock invocation) throws Throwable {
return Collections.<String, ServletRegistration>singletonMap("spring", registration);
}
};
when(servletContext.getServletRegistrations()).thenAnswer(answer);
}
}
@@ -0,0 +1,228 @@
/*
* Copyright 2012-2016 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.HttpServletResponse;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
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.mock.web.MockServletContext;
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.web.FilterChainProxy;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rob Winch
*/
public class WebSecurityTests {
AnnotationConfigWebApplicationContext context;
MockHttpServletRequest request;
MockHttpServletResponse response;
MockFilterChain chain;
@Autowired
FilterChainProxy springSecurityFilterChain;
@Before
public void setup() {
this.request = new MockHttpServletRequest();
this.request.setMethod("GET");
this.response = new MockHttpServletResponse();
this.chain = new MockFilterChain();
}
@After
public void cleanup() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void ignoringMvcMatcher() throws Exception {
loadConfig(MvcMatcherConfig.class);
this.request.setRequestURI("/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
setup();
this.request.setRequestURI("/path.html");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
setup();
this.request.setRequestURI("/path/");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
setup();
this.request.setRequestURI("/other");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
// @formatter:off
web
.ignoring()
.mvcMatchers("/path");
// @formatter:on
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic().and()
.authorizeRequests()
.anyRequest().denyAll();
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication();
// @formatter:on
}
@RestController
static class PathController {
@RequestMapping("/path")
public String path() {
return "path";
}
}
}
@Test
public void ignoringMvcMatcherServletPath() throws Exception {
loadConfig(MvcMatcherServletPathConfig.class);
this.request.setServletPath("/spring");
this.request.setRequestURI("/spring/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
setup();
this.request.setServletPath("/spring");
this.request.setRequestURI("/spring/path.html");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
setup();
this.request.setServletPath("/spring");
this.request.setRequestURI("/spring/path/");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
setup();
this.request.setServletPath("/other");
this.request.setRequestURI("/other/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherServletPathConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
// @formatter:off
web
.ignoring()
.mvcMatchers("/path").servletPath("/spring")
.mvcMatchers("/notused");
// @formatter:on
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic().and()
.authorizeRequests()
.anyRequest().denyAll();
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication();
// @formatter:on
}
@RestController
static class PathController {
@RequestMapping("/path")
public String path() {
return "path";
}
}
}
public void loadConfig(Class<?>... configs) {
this.context = new AnnotationConfigWebApplicationContext();
this.context.register(configs);
this.context.setServletContext(new MockServletContext());
this.context.refresh();
this.context.getAutowireCapableBeanFactory().autowireBean(this);
}
}
@@ -39,10 +39,8 @@ import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@@ -15,12 +15,19 @@
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.Collections;
import java.util.Map;
import javax.servlet.ServletRegistration;
import javax.servlet.http.HttpServletResponse;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -28,6 +35,7 @@ import org.springframework.http.HttpMethod;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -41,9 +49,15 @@ import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
/**
* @author Rob Winch
@@ -55,12 +69,14 @@ public class AuthorizeRequestsTests {
MockHttpServletRequest request;
MockHttpServletResponse response;
MockFilterChain chain;
MockServletContext servletContext;
@Autowired
FilterChainProxy springSecurityFilterChain;
@Before
public void setup() {
this.servletContext = spy(new MockServletContext());
this.request = new MockHttpServletRequest();
this.request.setMethod("GET");
this.response = new MockHttpServletResponse();
@@ -249,11 +265,242 @@ public class AuthorizeRequestsTests {
}
}
@Test
public void mvcMatcher() throws Exception {
loadConfig(MvcMatcherConfig.class);
this.request.setRequestURI("/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setRequestURI("/path.html");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("/path/");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic().and()
.authorizeRequests()
.mvcMatchers("/path").denyAll();
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication();
// @formatter:on
}
@RestController
static class PathController {
@RequestMapping("/path")
public String path() {
return "path";
}
}
}
@Test
public void mvcMatcherServletPath() throws Exception {
loadConfig(MvcMatcherServletPathConfig.class);
this.request.setServletPath("/spring");
this.request.setRequestURI("/spring/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("/spring");
this.request.setRequestURI("/spring/path.html");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("/spring");
this.request.setRequestURI("/spring/path/");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("/foo");
this.request.setRequestURI("/foo/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
setup();
this.request.setServletPath("/");
this.request.setRequestURI("/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherServletPathConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic().and()
.authorizeRequests()
.mvcMatchers("/path").servletPath("/spring").denyAll();
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication();
// @formatter:on
}
@RestController
static class PathController {
@RequestMapping("/path")
public String path() {
return "path";
}
}
}
@Test
public void mvcMatcherPathVariables() throws Exception {
loadConfig(MvcMatcherPathVariablesConfig.class);
this.request.setRequestURI("/user/user");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
this.setup();
this.request.setRequestURI("/user/deny");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherPathVariablesConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic().and()
.authorizeRequests()
.mvcMatchers("/user/{userName}").access("#userName == 'user'");
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication();
// @formatter:on
}
@RestController
static class PathController {
@RequestMapping("/path")
public String path() {
return "path";
}
}
}
@Test(expected = BeanCreationException.class)
public void mvcMatcherServletPathRequired() throws Exception {
final ServletRegistration registration = mock(ServletRegistration.class);
when(registration.getMappings()).thenReturn(Collections.singleton("/spring"));
Answer<Map<String, ? extends ServletRegistration>> answer = new Answer<Map<String, ? extends ServletRegistration>>() {
@Override
public Map<String, ? extends ServletRegistration> answer(InvocationOnMock invocation) throws Throwable {
return Collections.<String, ServletRegistration>singletonMap("spring", registration);
}
};
when(servletContext.getServletRegistrations()).thenAnswer(answer);
loadConfig(MvcMatcherPathServletPathRequiredConfig.class);
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherPathServletPathRequiredConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic().and()
.authorizeRequests()
.mvcMatchers("/user").denyAll();
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication();
// @formatter:on
}
@RestController
static class PathController {
@RequestMapping("/path")
public String path() {
return "path";
}
}
}
public void loadConfig(Class<?>... configs) {
this.context = new AnnotationConfigWebApplicationContext();
this.context.register(configs);
this.context.setServletContext(this.servletContext);
this.context.refresh();
this.context.getAutowireCapableBeanFactory().autowireBean(this);
}
}
}
@@ -23,12 +23,11 @@ import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
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.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor;
import org.springframework.web.servlet.support.RequestDataValueProcessor;
@@ -17,8 +17,6 @@ package org.springframework.security.config.annotation.web.configurers;
import static org.assertj.core.api.Assertions.assertThat;
import javax.servlet.http.HttpServletResponse;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -0,0 +1,271 @@
/*
* Copyright 2002-2016 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.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
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.mock.web.MockServletContext;
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.web.FilterChainProxy;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rob Winch
*
*/
public class HttpSecurityRequestMatchersTests {
AnnotationConfigWebApplicationContext context;
MockHttpServletRequest request;
MockHttpServletResponse response;
MockFilterChain chain;
@Autowired
FilterChainProxy springSecurityFilterChain;
@Before
public void setup() {
this.request = new MockHttpServletRequest();
this.request.setMethod("GET");
this.response = new MockHttpServletResponse();
this.chain = new MockFilterChain();
}
@After
public void cleanup() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void mvcMatcher() throws Exception {
loadConfig(MvcMatcherConfig.class);
this.request.setServletPath("/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("/path.html");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("/path/");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
}
@Test
public void mvcMatcherGetFiltersNoUnsupportedMethodExceptionFromDummyRequest() {
loadConfig(MvcMatcherConfig.class);
assertThat(springSecurityFilterChain.getFilters("/path")).isNotEmpty();
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.mvcMatcher("/path")
.httpBasic().and()
.authorizeRequests()
.anyRequest().denyAll();
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication();
// @formatter:on
}
@RestController
static class PathController {
@RequestMapping("/path")
public String path() {
return "path";
}
}
}
@Test
public void requestMatchersMvcMatcher() throws Exception {
loadConfig(RequestMatchersMvcMatcherConfig.class);
this.request.setServletPath("/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("/path.html");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("/path/");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class RequestMatchersMvcMatcherConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestMatchers()
.mvcMatchers("/path")
.and()
.httpBasic().and()
.authorizeRequests()
.anyRequest().denyAll();
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication();
// @formatter:on
}
@RestController
static class PathController {
@RequestMapping("/path")
public String path() {
return "path";
}
}
}
@Test
public void requestMatchersMvcMatcherServletPath() throws Exception {
loadConfig(RequestMatchersMvcMatcherServeltPathConfig.class);
this.request.setServletPath("/spring");
this.request.setRequestURI("/spring/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("");
this.request.setRequestURI("/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
setup();
this.request.setServletPath("/other");
this.request.setRequestURI("/other/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class RequestMatchersMvcMatcherServeltPathConfig
extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestMatchers()
.mvcMatchers("/path").servletPath("/spring")
.mvcMatchers("/never-match")
.and()
.httpBasic().and()
.authorizeRequests()
.anyRequest().denyAll();
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication();
// @formatter:on
}
@RestController
static class PathController {
@RequestMapping("/path")
public String path() {
return "path";
}
}
}
public void loadConfig(Class<?>... configs) {
this.context = new AnnotationConfigWebApplicationContext();
this.context.register(configs);
this.context.setServletContext(new MockServletContext());
this.context.refresh();
this.context.getAutowireCapableBeanFactory().autowireBean(this);
}
}
@@ -36,7 +36,6 @@ import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.context.ConfigurableApplicationContext;
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;
@@ -0,0 +1,214 @@
/*
* Copyright 2002-2015 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.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
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.mock.web.MockServletContext;
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.web.FilterChainProxy;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rob Winch
*
*/
public class UrlAuthorizationConfigurerTests {
AnnotationConfigWebApplicationContext context;
MockHttpServletRequest request;
MockHttpServletResponse response;
MockFilterChain chain;
@Autowired
FilterChainProxy springSecurityFilterChain;
@Before
public void setup() {
this.request = new MockHttpServletRequest();
this.request.setMethod("GET");
this.response = new MockHttpServletResponse();
this.chain = new MockFilterChain();
}
@After
public void cleanup() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void mvcMatcher() throws Exception {
loadConfig(MvcMatcherConfig.class);
this.request.setRequestURI("/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setRequestURI("/path.html");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("/path/");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic().and()
.apply(new UrlAuthorizationConfigurer(getApplicationContext())).getRegistry()
.mvcMatchers("/path").hasRole("ADMIN");
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication();
// @formatter:on
}
@RestController
static class PathController {
@RequestMapping("/path")
public String path() {
return "path";
}
}
}
@Test
public void mvcMatcherServletPath() throws Exception {
loadConfig(MvcMatcherServletPathConfig.class);
this.request.setServletPath("/spring");
this.request.setRequestURI("/spring/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("/spring");
this.request.setRequestURI("/spring/path.html");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("/spring");
this.request.setRequestURI("/spring/path/");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus())
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("/foo");
this.request.setRequestURI("/foo/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
setup();
this.request.setServletPath("/");
this.request.setRequestURI("/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherServletPathConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic().and()
.apply(new UrlAuthorizationConfigurer(getApplicationContext())).getRegistry()
.mvcMatchers("/path").servletPath("/spring").hasRole("ADMIN");
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// @formatter:off
auth
.inMemoryAuthentication();
// @formatter:on
}
@RestController
static class PathController {
@RequestMapping("/path")
public String path() {
return "path";
}
}
}
public void loadConfig(Class<?>... configs) {
this.context = new AnnotationConfigWebApplicationContext();
this.context.register(configs);
this.context.setServletContext(new MockServletContext());
this.context.refresh();
this.context.getAutowireCapableBeanFactory().autowireBean(this);
}
}
@@ -20,23 +20,16 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.MethodParameter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageDeliveryException;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import static org.springframework.messaging.simp.SimpMessageType.*;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletConfig;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.TestingAuthenticationToken;
@@ -44,25 +37,14 @@ import org.springframework.security.config.annotation.web.messaging.MessageSecur
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.DefaultCsrfToken;
import org.springframework.security.web.csrf.MissingCsrfTokenException;
import org.springframework.stereotype.Controller;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.server.HandshakeFailureException;
import org.springframework.web.socket.server.HandshakeHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
import org.springframework.web.socket.sockjs.transport.handler.SockJsWebSocketHandler;
import org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSession;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
@@ -0,0 +1,119 @@
/*
* Copyright 2012-2016 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.authentication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
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.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Rob Winch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class AuthenticationConfigurationGh3935Tests {
@Autowired
FilterChainProxy springSecurityFilterChain;
@Autowired
UserDetailsService uds;
@Autowired
BootGlobalAuthenticationConfigurationAdapter adapter;
// gh-3935
@Test
public void loads() {
assertThat(this.springSecurityFilterChain).isNotNull();
}
@Test
public void delegateUsesExisitingAuthentication() {
String username = "user";
String password = "password";
User user = new User(username, password,
AuthorityUtils.createAuthorityList("ROLE_USER"));
when(this.uds.loadUserByUsername(username)).thenReturn(user);
AuthenticationManager authenticationManager = this.adapter.authenticationManager;
assertThat(authenticationManager).isNotNull();
Authentication auth = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(username, password));
verify(this.uds).loadUserByUsername(username);
assertThat(auth.getPrincipal()).isEqualTo(user);
}
@EnableWebSecurity
static class WebSecurity extends WebSecurityConfigurerAdapter {
}
static class BootGlobalAuthenticationConfigurationAdapter
extends GlobalAuthenticationConfigurerAdapter {
private final ApplicationContext context;
private AuthenticationManager authenticationManager;
@Autowired
BootGlobalAuthenticationConfigurationAdapter(ApplicationContext context) {
this.context = context;
}
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
AuthenticationConfiguration configuration = this.context
.getBean(AuthenticationConfiguration.class);
this.authenticationManager = configuration.getAuthenticationManager();
}
}
@Configuration
static class AutoConfig {
@Bean
static BootGlobalAuthenticationConfigurationAdapter adapter(
ApplicationContext context) {
return new BootGlobalAuthenticationConfigurationAdapter(context);
}
@Bean
public UserDetailsService userDetailsService() {
return mock(UserDetailsService.class);
}
}
}
@@ -15,11 +15,9 @@
*/
package org.springframework.security.config.http;
import java.util.Collection;
import org.junit.After;
import org.junit.Test;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.context.support.AbstractXmlApplicationContext;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
@@ -33,6 +31,8 @@ import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
import java.util.Collection;
import static org.assertj.core.api.Assertions.assertThat;
/**
@@ -118,6 +118,13 @@ public class FilterSecurityMetadataSourceBeanDefinitionParserTests {
+ ConfigTestUtils.AUTH_PROVIDER_XML);
}
@Test(expected = BeanDefinitionParsingException.class)
public void parsingInterceptUrlServletPathFails() {
setContext("<filter-security-metadata-source id='fids' use-expressions='false'>"
+ " <intercept-url pattern='/secure' access='ROLE_USER' servlet-path='/spring' />"
+ "</filter-security-metadata-source>");
}
private FilterInvocation createFilterInvocation(String path, String method) {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI(null);
@@ -35,15 +35,11 @@ import org.mockito.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.context.ConfigurableApplicationContext;
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.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.util.InMemoryXmlApplicationContext;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextImpl;
@@ -0,0 +1,86 @@
/*
* Copyright 2012-2016 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.method.configuration;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.mockito.Mockito.mock;
/**
* @author Rob Winch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class Gh4020GlobalMethodSecurityConfigurationTests {
@Autowired
DenyAllService denyAll;
// gh-4020
@Test(expected = AuthenticationCredentialsNotFoundException.class)
public void denyAll() {
this.denyAll.denyAll();
}
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
static class SecurityConfig {
@Bean
PermissionEvaluator permissionEvaluator() {
return mock(PermissionEvaluator.class);
}
@Bean
RoleHierarchy RoleHierarchy() {
return mock(RoleHierarchy.class);
}
@Bean
AuthenticationTrustResolver trustResolver() {
return mock(AuthenticationTrustResolver.class);
}
@Autowired
DenyAllService denyAll;
}
@Configuration
static class ServiceConfig {
@Bean
DenyAllService denyAllService() {
return new DenyAllService();
}
}
@PreAuthorize("denyAll")
static class DenyAllService {
void denyAll() {
}
}
}
@@ -25,27 +25,31 @@ import org.springframework.security.util.InMemoryResource;
* @author Luke Taylor
*/
public class InMemoryXmlApplicationContext extends AbstractXmlApplicationContext {
private static final String BEANS_OPENING = "<b:beans xmlns='http://www.springframework.org/schema/security'\n"
static final String BEANS_OPENING = "<b:beans xmlns='http://www.springframework.org/schema/security'\n"
+ " xmlns:context='http://www.springframework.org/schema/context'\n"
+ " xmlns:b='http://www.springframework.org/schema/beans'\n"
+ " xmlns:aop='http://www.springframework.org/schema/aop'\n"
+ " xmlns:mvc='http://www.springframework.org/schema/mvc'\n"
+ " xmlns:websocket='http://www.springframework.org/schema/websocket'\n"
+ " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'\n"
+ " xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\n"
+ "http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd\n"
+ "http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd\n"
+ "http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd\n"
+ "http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\n"
+ "http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-";
private static final String BEANS_CLOSE = "</b:beans>\n";
static final String BEANS_CLOSE = "</b:beans>\n";
static final String SPRING_SECURITY_VERSION = "4.1";
Resource inMemoryXml;
public InMemoryXmlApplicationContext(String xml) {
this(xml, "4.1", null);
this(xml, SPRING_SECURITY_VERSION, null);
}
public InMemoryXmlApplicationContext(String xml, ApplicationContext parent) {
this(xml, "4.1", parent);
this(xml, SPRING_SECURITY_VERSION, parent);
}
public InMemoryXmlApplicationContext(String xml, String secVersion,
@@ -0,0 +1,60 @@
/*
* Copyright 2012-2016 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.util;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.security.util.InMemoryResource;
import org.springframework.web.context.support.AbstractRefreshableWebApplicationContext;
import java.io.IOException;
import static org.springframework.security.config.util.InMemoryXmlApplicationContext.BEANS_CLOSE;
import static org.springframework.security.config.util.InMemoryXmlApplicationContext.BEANS_OPENING;
import static org.springframework.security.config.util.InMemoryXmlApplicationContext.SPRING_SECURITY_VERSION;
/**
* @author Joe Grandja
*/
public class InMemoryXmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
private Resource inMemoryXml;
public InMemoryXmlWebApplicationContext(String xml) {
this(xml, SPRING_SECURITY_VERSION, null);
}
public InMemoryXmlWebApplicationContext(String xml, ApplicationContext parent) {
this(xml, SPRING_SECURITY_VERSION, parent);
}
public InMemoryXmlWebApplicationContext(String xml, String secVersion,
ApplicationContext parent) {
String fullXml = BEANS_OPENING + secVersion + ".xsd'>\n" + xml + BEANS_CLOSE;
inMemoryXml = new InMemoryResource(fullXml);
setAllowBeanDefinitionOverriding(true);
setParent(parent);
}
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions(new Resource[] { inMemoryXml });
}
}
+2 -2
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.1.0.RELEASE</version>
<version>4.1.2.RELEASE</version>
<name>spring-security-core</name>
<description>spring-security-core</description>
<url>http://spring.io/spring-security</url>
@@ -35,7 +35,7 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.2.5.RELEASE</version>
<version>4.3.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -19,7 +19,6 @@ package org.springframework.security.access.intercept;
import java.util.Collection;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
/**

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