Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 644901fed5 | |||
| 09805317e6 | |||
| 4ab933803f | |||
| d2b1cb572f | |||
| d0166004aa | |||
| 9f96fbcda0 | |||
| ccffb48fd1 | |||
| d0fcdebe88 | |||
| f997e22d9d | |||
| ff908c4d7c | |||
| 521f533fc4 | |||
| 532d0bef14 | |||
| fed6df5167 | |||
| 4feeb0f843 | |||
| 8b0689cbb8 | |||
| 28e158d1cb | |||
| 82f87cf2b6 | |||
| dc5aed9b5f | |||
| f19c9c8625 | |||
| 217a29e6ba | |||
| b2d6380633 | |||
| 43c53c3b78 | |||
| cbad2ff5ca | |||
| b75f2582c4 | |||
| 978459bd1d | |||
| 7cc9d2849e | |||
| f57c9ffcbb | |||
| dd7f809564 | |||
| dc5962af16 | |||
| ae4b2e50e2 | |||
| b007219055 | |||
| c43f1f85ef |
@@ -1,2 +0,0 @@
|
||||
require:
|
||||
members: false
|
||||
+8
-31
@@ -4,32 +4,6 @@ registries:
|
||||
type: maven-repository
|
||||
url: https://repo.spring.io/milestone
|
||||
updates:
|
||||
- package-ecosystem: gradle
|
||||
target-branch: 6.4.x
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
time: '03:00'
|
||||
timezone: Etc/UTC
|
||||
labels:
|
||||
- 'type: dependency-upgrade'
|
||||
registries:
|
||||
- spring-milestones
|
||||
ignore:
|
||||
- dependency-name: com.nimbusds:nimbus-jose-jwt
|
||||
- dependency-name: org.python:jython
|
||||
- dependency-name: org.apache.directory.server:*
|
||||
- dependency-name: org.apache.directory.shared:*
|
||||
- dependency-name: org.junit:junit-bom
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
- dependency-name: org.mockito:mockito-bom
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
- dependency-name: '*'
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
- version-update:semver-minor
|
||||
- package-ecosystem: gradle
|
||||
target-branch: 6.3.x
|
||||
directory: /
|
||||
@@ -45,7 +19,6 @@ updates:
|
||||
- dependency-name: com.nimbusds:nimbus-jose-jwt
|
||||
- dependency-name: org.python:jython
|
||||
- dependency-name: org.apache.directory.server:*
|
||||
- dependency-name: org.apache.directory.shared:*
|
||||
- dependency-name: org.junit:junit-bom
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
@@ -71,7 +44,6 @@ updates:
|
||||
- dependency-name: com.nimbusds:nimbus-jose-jwt
|
||||
- dependency-name: org.python:jython
|
||||
- dependency-name: org.apache.directory.server:*
|
||||
- dependency-name: org.apache.directory.shared:*
|
||||
- dependency-name: org.junit:junit-bom
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
@@ -85,7 +57,6 @@ updates:
|
||||
- dependency-name: '*'
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
- version-update:semver-minor
|
||||
|
||||
- package-ecosystem: github-actions
|
||||
target-branch: 6.3.x
|
||||
@@ -95,8 +66,14 @@ updates:
|
||||
labels:
|
||||
- 'type: task'
|
||||
- 'in: build'
|
||||
ignore:
|
||||
- dependency-name: sjohnr/*
|
||||
- package-ecosystem: github-actions
|
||||
target-branch: main
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
labels:
|
||||
- 'type: task'
|
||||
- 'in: build'
|
||||
- package-ecosystem: github-actions
|
||||
target-branch: docs-build
|
||||
directory: /
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: Set up Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
uses: gradle/gradle-build-action@v3
|
||||
- name: Upgrade Wrappers
|
||||
run: ./gradlew clean upgradeGradleWrapperAll --continue -Porg.gradle.java.installations.auto-download=false
|
||||
env:
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
# List of active maintenance branches.
|
||||
branch: [ main, 6.4.x, 6.3.x ]
|
||||
branch: [ main, 6.3.x, 6.2.x, 5.8.x ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
+25
-14
@@ -79,7 +79,7 @@ See https://github.com/spring-projects/spring-security/tree/main#building-from-s
|
||||
|
||||
The wiki pages https://github.com/spring-projects/spring-framework/wiki/Code-Style[Code Style] and https://github.com/spring-projects/spring-framework/wiki/IntelliJ-IDEA-Editor-Settings[IntelliJ IDEA Editor Settings] define the source file coding standards we use along with some IDEA editor settings we customize.
|
||||
|
||||
To format the code as well as check the style, run `./gradlew format && ./gradlew check`.
|
||||
To format the code as well as check the style, run `./gradlew format check`.
|
||||
|
||||
[[submit-a-pull-request]]
|
||||
=== Submit a Pull Request
|
||||
@@ -89,30 +89,41 @@ We are excited for your pull request! :heart:
|
||||
Please do your best to follow these steps.
|
||||
Don't worry if you don't get them all correct the first time, we will help you.
|
||||
|
||||
1. [[sign-cla]] All commits must include a __Signed-off-by__ trailer at the end of each commit message to indicate that the contributor agrees to the Developer Certificate of Origin.
|
||||
For additional details, please refer to the blog post https://spring.io/blog/2025/01/06/hello-dco-goodbye-cla-simplifying-contributions-to-spring[Hello DCO, Goodbye CLA: Simplifying Contributions to Spring].
|
||||
2. [[create-an-issue-list]] Must you https://github.com/spring-projects/spring-security/issues/new/choose[create an issue] first? No, but it is recommended for features and larger bug fixes. It's easier discuss with the team first to determine the right fix or enhancement.
|
||||
[[sign-cla]]
|
||||
1. If you have not previously done so, please sign the https://cla.spring.io/sign/spring[Contributor License Agreement].
|
||||
You will be reminded automatically when you submit the PR.
|
||||
[[create-an-issue]]
|
||||
1. Must you https://github.com/spring-projects/spring-security/issues/new/choose[create an issue] first? No, but it is recommended for features and larger bug fixes. It's easier discuss with the team first to determine the right fix or enhancement.
|
||||
For typos and straightforward bug fixes, starting with a pull request is encouraged.
|
||||
Please include a description for context and motivation.
|
||||
Note that the team may close your pull request if it's not a fit for the project.
|
||||
3. [[choose-a-branch]] Always check out the branch indicated in the milestone and submit pull requests against it (for example, for milestone `5.8.3` use the `5.8.x` branch).
|
||||
[[choose-a-branch]]
|
||||
1. Always check out the branch indicated in the milestone and submit pull requests against it (for example, for milestone `5.8.3` use the `5.8.x` branch).
|
||||
If there is no milestone, choose `main`.
|
||||
Once merged, the fix will be forwarded-ported to applicable branches including `main`.
|
||||
4. [[create-a-local-branch]] Create a local branch
|
||||
[[create-a-local-branch]]
|
||||
1. Create a local branch
|
||||
If this is for an issue, consider a branch name with the issue number, like `gh-22276`.
|
||||
5. [[write-tests]] Add documentation and JUnit Tests for your changes.
|
||||
6. [[update-copyright]] In all files you edited, if the copyright header is of the form 2002-20xx, update the final copyright year to the current year.
|
||||
7. [[add-since]] If on `main`, add `@since` JavaDoc attributes to new public APIs that your PR adds
|
||||
8. [[change-rnc]] If you are updating the XSD, please instead update the RNC file and then run `./gradlew :spring-security-config:rncToXsd`.
|
||||
9. [[format-code]] For each commit, build the code using `./gradlew format && ./gradlew check`.
|
||||
[[write-tests]]
|
||||
1. Add documentation and JUnit Tests for your changes.
|
||||
[[update-copyright]]
|
||||
1. In all files you edited, if the copyright header is of the form 2002-20xx, update the final copyright year to the current year.
|
||||
[[add-since]]
|
||||
1. If on `main`, add `@since` JavaDoc attributes to new public APIs that your PR adds
|
||||
[[change-rnc]]
|
||||
1. If you are updating the XSD, please instead update the RNC file and then run `./gradlew :spring-security-config:rncToXsd`.
|
||||
[[format-code]]
|
||||
1. For each commit, build the code using `./gradlew format check`.
|
||||
This command ensures the code meets most of <<code-style,the style guide>>; a notable exception is import order.
|
||||
10. [[commit-atomically]] Choose the granularity of your commits consciously and squash commits that represent
|
||||
[[commit-atomically]]
|
||||
1. Choose the granularity of your commits consciously and squash commits that represent
|
||||
multiple edits or corrections of the same logical change.
|
||||
See https://git-scm.com/book/en/Git-Tools-Rewriting-History[Rewriting History section of Pro Git] for an overview of streamlining the commit history.
|
||||
11. [[format-commit-messages]] Format commit messages using 55 characters for the subject line, 72 characters per line
|
||||
[[format-commit-messages]]
|
||||
1. Format commit messages using 55 characters for the subject line, 72 characters per line
|
||||
for the description, followed by the issue fixed, for example, `Closes gh-22276`.
|
||||
See the https://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines[Commit Guidelines section of Pro Git] for best practices around commit messages, and use `git log` to see some examples.
|
||||
Favor imperative tense over present tense (use "Fix" instead of "Fixes"); avoid past tense (use "Fix" instead of "Fixed").
|
||||
Present tense is preferred.
|
||||
+
|
||||
[indent=0]
|
||||
----
|
||||
|
||||
@@ -96,11 +96,7 @@ import org.springframework.util.StringUtils;
|
||||
* All comparisons and prefixes are case sensitive.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
|
||||
* annotations may also prove useful, for example
|
||||
* {@code @PreAuthorize("hasPermission(#id, ObjectsReturnType.class, read)")}
|
||||
*/
|
||||
@Deprecated
|
||||
public class AclEntryVoter extends AbstractAclVoter {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AclEntryVoter.class);
|
||||
|
||||
-5
@@ -20,7 +20,6 @@ import java.util.List;
|
||||
|
||||
import org.springframework.security.access.AfterInvocationProvider;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.acls.AclPermissionEvaluator;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
|
||||
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
@@ -40,11 +39,7 @@ import org.springframework.util.ObjectUtils;
|
||||
* services.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
|
||||
* annotations may also prove useful, for example
|
||||
* {@code @PostAuthorize("hasPermission(filterObject, read)")}
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractAclProvider implements AfterInvocationProvider {
|
||||
|
||||
protected final AclService aclService;
|
||||
|
||||
-5
@@ -26,7 +26,6 @@ import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.AuthorizationServiceException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.acls.AclPermissionEvaluator;
|
||||
import org.springframework.security.acls.model.AclService;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@@ -63,11 +62,7 @@ import org.springframework.security.core.Authentication;
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Paulo Neves
|
||||
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
|
||||
* annotations may also prove useful, for example
|
||||
* {@code @PostFilter("hasPermission(filterObject, read)")}
|
||||
*/
|
||||
@Deprecated
|
||||
public class AclEntryAfterInvocationCollectionFilteringProvider extends AbstractAclProvider {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationCollectionFilteringProvider.class);
|
||||
|
||||
-6
@@ -27,7 +27,6 @@ import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.acls.AclPermissionEvaluator;
|
||||
import org.springframework.security.acls.model.AclService;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@@ -60,12 +59,7 @@ import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
* granted and <code>null</code> will be returned.
|
||||
* <p>
|
||||
* All comparisons and prefixes are case sensitive.
|
||||
*
|
||||
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
|
||||
* annotations may also prove useful, for example
|
||||
* {@code @PostAuthorize("hasPermission(filterObject, read)")}
|
||||
*/
|
||||
@Deprecated
|
||||
public class AclEntryAfterInvocationProvider extends AbstractAclProvider implements MessageSourceAware {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationProvider.class);
|
||||
|
||||
@@ -32,9 +32,7 @@ import org.springframework.core.log.LogMessage;
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Paulo Neves
|
||||
* @deprecated please see {@code PostFilter}
|
||||
*/
|
||||
@Deprecated
|
||||
class ArrayFilterer<T> implements Filterer<T> {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(ArrayFilterer.class);
|
||||
|
||||
-2
@@ -31,9 +31,7 @@ import org.springframework.core.log.LogMessage;
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Paulo Neves
|
||||
* @deprecated please see {@code PostFilter}
|
||||
*/
|
||||
@Deprecated
|
||||
class CollectionFilterer<T> implements Filterer<T> {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(CollectionFilterer.class);
|
||||
|
||||
@@ -23,9 +23,7 @@ import java.util.Iterator;
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Paulo Neves
|
||||
* @deprecated please use {@code PreFilter} and {@code @PostFilter} instead
|
||||
*/
|
||||
@Deprecated
|
||||
interface Filterer<T> extends Iterable<T> {
|
||||
|
||||
/**
|
||||
|
||||
@@ -100,8 +100,8 @@ public class JdbcAclService implements AclService {
|
||||
@Override
|
||||
public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
|
||||
Object[] args = { parentIdentity.getIdentifier().toString(), parentIdentity.getType() };
|
||||
List<ObjectIdentity> objects = this.jdbcOperations.query(this.findChildrenSql,
|
||||
(rs, rowNum) -> mapObjectIdentityRow(rs), args);
|
||||
List<ObjectIdentity> objects = this.jdbcOperations.query(this.findChildrenSql, args,
|
||||
(rs, rowNum) -> mapObjectIdentityRow(rs));
|
||||
return (!objects.isEmpty()) ? objects : null;
|
||||
}
|
||||
|
||||
|
||||
@@ -190,7 +190,8 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
* @return the primary key or null if not found
|
||||
*/
|
||||
protected Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate, Class idType) {
|
||||
List<Long> classIds = this.jdbcOperations.queryForList(this.selectClassPrimaryKey, Long.class, type);
|
||||
List<Long> classIds = this.jdbcOperations.queryForList(this.selectClassPrimaryKey, new Object[] { type },
|
||||
Long.class);
|
||||
|
||||
if (!classIds.isEmpty()) {
|
||||
return classIds.get(0);
|
||||
@@ -241,8 +242,8 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
* @return the primary key or null if not found
|
||||
*/
|
||||
protected Long createOrRetrieveSidPrimaryKey(String sidName, boolean sidIsPrincipal, boolean allowCreate) {
|
||||
List<Long> sidIds = this.jdbcOperations.queryForList(this.selectSidPrimaryKey, Long.class, sidIsPrincipal,
|
||||
sidName);
|
||||
List<Long> sidIds = this.jdbcOperations.queryForList(this.selectSidPrimaryKey,
|
||||
new Object[] { sidIsPrincipal, sidName }, Long.class);
|
||||
if (!sidIds.isEmpty()) {
|
||||
return sidIds.get(0);
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ public class JdbcAclServiceTests {
|
||||
List<ObjectIdentity> result = new ArrayList<>();
|
||||
result.add(new ObjectIdentityImpl(Object.class, "5577"));
|
||||
Object[] args = { "1", "org.springframework.security.acls.jdbc.JdbcAclServiceTests$MockLongIdDomainObject" };
|
||||
given(this.jdbcOperations.query(anyString(), any(RowMapper.class), eq(args))).willReturn(result);
|
||||
given(this.jdbcOperations.query(anyString(), eq(args), any(RowMapper.class))).willReturn(result);
|
||||
ObjectIdentity objectIdentity = new ObjectIdentityImpl(MockLongIdDomainObject.class, 1L);
|
||||
List<ObjectIdentity> objectIdentities = this.aclService.findChildren(objectIdentity);
|
||||
assertThat(objectIdentities).hasSize(1);
|
||||
|
||||
@@ -51,7 +51,6 @@ import org.springframework.security.web.context.SecurityContextRepository;
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -216,8 +215,6 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
||||
|
||||
public CasAuthenticationFilter() {
|
||||
super("/login/cas");
|
||||
RequestMatcher processUri = PathPatternRequestMatcher.withDefaults().matcher("/login/cas");
|
||||
setRequiresAuthenticationRequestMatcher(processUri);
|
||||
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
|
||||
setSecurityContextRepository(this.securityContextRepository);
|
||||
}
|
||||
@@ -322,18 +319,6 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
||||
super.setAuthenticationFailureHandler(new CasAuthenticationFailureHandler(failureHandler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@code RequestMatcher} to match proxy receptor requests. Without setting
|
||||
* this matcher, {@link CasAuthenticationFilter} will not capture any proxy receptor
|
||||
* requets.
|
||||
* @param proxyReceptorMatcher the {@link RequestMatcher} to use
|
||||
* @since 6.5
|
||||
*/
|
||||
public final void setProxyReceptorMatcher(RequestMatcher proxyReceptorMatcher) {
|
||||
Assert.notNull(proxyReceptorMatcher, "proxyReceptorMatcher cannot be null");
|
||||
this.proxyReceptorMatcher = proxyReceptorMatcher;
|
||||
}
|
||||
|
||||
public final void setProxyReceptorUrl(final String proxyReceptorUrl) {
|
||||
this.proxyReceptorMatcher = new AntPathRequestMatcher("/**" + proxyReceptorUrl);
|
||||
}
|
||||
|
||||
+4
-22
@@ -43,7 +43,6 @@ import org.springframework.security.core.context.SecurityContextImpl;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.context.SecurityContextRepository;
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -79,7 +78,7 @@ public class CasAuthenticationFilterTests {
|
||||
|
||||
@Test
|
||||
public void testNormalOperation() throws Exception {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/login/cas");
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setServletPath("/login/cas");
|
||||
request.addParameter("ticket", "ST-0-ER94xMJmn6pha35CQRoZ");
|
||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||
@@ -104,7 +103,7 @@ public class CasAuthenticationFilterTests {
|
||||
String url = "/login/cas";
|
||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||
filter.setFilterProcessesUrl(url);
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("POST", url);
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setServletPath(url);
|
||||
assertThat(filter.requiresAuthentication(request, response)).isTrue();
|
||||
@@ -133,11 +132,10 @@ public class CasAuthenticationFilterTests {
|
||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||
filter.setFilterProcessesUrl(url);
|
||||
filter.setServiceProperties(properties);
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("POST", url);
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setServletPath(url);
|
||||
assertThat(filter.requiresAuthentication(request, response)).isTrue();
|
||||
request = new MockHttpServletRequest("POST", "/other");
|
||||
request.setServletPath("/other");
|
||||
assertThat(filter.requiresAuthentication(request, response)).isFalse();
|
||||
request.setParameter(properties.getArtifactParameter(), "value");
|
||||
@@ -172,7 +170,7 @@ public class CasAuthenticationFilterTests {
|
||||
given(manager.authenticate(any(Authentication.class))).willReturn(authentication);
|
||||
ServiceProperties serviceProperties = new ServiceProperties();
|
||||
serviceProperties.setAuthenticateAllArtifacts(true);
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/authenticate");
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setParameter("ticket", "ST-1-123");
|
||||
request.setServletPath("/authenticate");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
@@ -268,20 +266,4 @@ public class CasAuthenticationFilterTests {
|
||||
verify(securityContextRepository).setContext(any(SecurityContext.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requiresAuthenticationWhenProxyRequestMatcherThenMatches() {
|
||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/pgtCallback");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
request.setServletPath("/pgtCallback");
|
||||
assertThat(filter.requiresAuthentication(request, response)).isFalse();
|
||||
filter.setProxyReceptorMatcher(PathPatternRequestMatcher.withDefaults().matcher(request.getServletPath()));
|
||||
assertThat(filter.requiresAuthentication(request, response)).isFalse();
|
||||
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
|
||||
assertThat(filter.requiresAuthentication(request, response)).isTrue();
|
||||
request.setRequestURI("/other");
|
||||
request.setServletPath("/other");
|
||||
assertThat(filter.requiresAuthentication(request, response)).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -96,7 +96,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||
pc.getReaderContext()
|
||||
.fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or "
|
||||
+ "spring-security-3.1.xsd schema or spring-security-3.2.xsd schema or spring-security-4.0.xsd schema "
|
||||
+ "with Spring Security 6.5. Please update your schema declarations to the 6.5 schema.",
|
||||
+ "with Spring Security 6.4. Please update your schema declarations to the 6.4 schema.",
|
||||
element);
|
||||
}
|
||||
String name = pc.getDelegate().getLocalName(element);
|
||||
@@ -221,7 +221,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||
|
||||
private boolean matchesVersionInternal(Element element) {
|
||||
String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
|
||||
return schemaLocation.matches("(?m).*spring-security-6\\.5.*.xsd.*")
|
||||
return schemaLocation.matches("(?m).*spring-security-6\\.4.*.xsd.*")
|
||||
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|
||||
|| !schemaLocation.matches("(?m).*spring-security.*");
|
||||
}
|
||||
|
||||
+3
-1
@@ -67,7 +67,9 @@ public class AuthenticationManagerBuilder
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param objectPostProcessor the {@link ObjectPostProcessor} instance to use.
|
||||
* @param objectPostProcessor the
|
||||
* {@link org.springframework.security.config.annotation.ObjectPostProcessor} instance
|
||||
* to use.
|
||||
*/
|
||||
public AuthenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
super(objectPostProcessor, true);
|
||||
|
||||
+6
-2
@@ -95,10 +95,14 @@ class InitializeUserDetailsBeanManagerConfigurer extends GlobalAuthenticationCon
|
||||
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
|
||||
UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
|
||||
CompromisedPasswordChecker passwordChecker = getBeanOrNull(CompromisedPasswordChecker.class);
|
||||
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService);
|
||||
DaoAuthenticationProvider provider;
|
||||
if (passwordEncoder != null) {
|
||||
provider.setPasswordEncoder(passwordEncoder);
|
||||
provider = new DaoAuthenticationProvider(passwordEncoder);
|
||||
}
|
||||
else {
|
||||
provider = new DaoAuthenticationProvider();
|
||||
}
|
||||
provider.setUserDetailsService(userDetailsService);
|
||||
if (passwordManager != null) {
|
||||
provider.setUserDetailsPasswordService(passwordManager);
|
||||
}
|
||||
|
||||
+2
-1
@@ -25,6 +25,7 @@ import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.config.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.crypto.password.NoOpPasswordEncoder;
|
||||
@@ -132,7 +133,7 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
|
||||
/**
|
||||
* Adds an {@link ObjectPostProcessor} for this class.
|
||||
* @param objectPostProcessor
|
||||
* @return the {@link LdapAuthenticationProviderConfigurer} for further customizations
|
||||
* @return the {@link ChannelSecurityConfigurer} for further customizations
|
||||
*/
|
||||
public LdapAuthenticationProviderConfigurer<B> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
|
||||
addObjectPostProcessor(objectPostProcessor);
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, C extends AbstractDaoAuthenticationConfigurer<B, C, U>, U extends UserDetailsService>
|
||||
extends UserDetailsAwareConfigurer<B, U> {
|
||||
|
||||
private DaoAuthenticationProvider provider;
|
||||
private DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
|
||||
|
||||
private final U userDetailsService;
|
||||
|
||||
@@ -46,7 +46,7 @@ public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderMana
|
||||
*/
|
||||
AbstractDaoAuthenticationConfigurer(U userDetailsService) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
this.provider = new DaoAuthenticationProvider(userDetailsService);
|
||||
this.provider.setUserDetailsService(userDetailsService);
|
||||
if (userDetailsService instanceof UserDetailsPasswordService) {
|
||||
this.provider.setUserDetailsPasswordService((UserDetailsPasswordService) userDetailsService);
|
||||
}
|
||||
|
||||
+8
-12
@@ -17,7 +17,6 @@
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
|
||||
@@ -32,7 +31,6 @@ import org.springframework.security.aot.hint.SecurityHintsRegistrar;
|
||||
import org.springframework.security.authorization.AuthorizationProxyFactory;
|
||||
import org.springframework.security.authorization.method.AuthorizationAdvisor;
|
||||
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
|
||||
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor;
|
||||
import org.springframework.security.authorization.method.AuthorizeReturnObjectMethodInterceptor;
|
||||
import org.springframework.security.config.Customizer;
|
||||
|
||||
@@ -42,23 +40,21 @@ final class AuthorizationProxyConfiguration implements AopInfrastructureBean {
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static AuthorizationAdvisorProxyFactory authorizationProxyFactory(
|
||||
ObjectProvider<AuthorizationAdvisor> authorizationAdvisors, ObjectProvider<TargetVisitor> targetVisitors,
|
||||
ObjectProvider<Customizer<AuthorizationAdvisorProxyFactory>> customizers) {
|
||||
List<AuthorizationAdvisor> advisors = new ArrayList<>();
|
||||
authorizationAdvisors.forEach(advisors::add);
|
||||
List<TargetVisitor> visitors = new ArrayList<>();
|
||||
targetVisitors.orderedStream().forEach(visitors::add);
|
||||
visitors.add(TargetVisitor.defaults());
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory(advisors);
|
||||
factory.setTargetVisitor(TargetVisitor.of(visitors.toArray(TargetVisitor[]::new)));
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory(new ArrayList<>());
|
||||
customizers.forEach((c) -> c.customize(factory));
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static MethodInterceptor authorizeReturnObjectMethodInterceptor() {
|
||||
return new AuthorizeReturnObjectMethodInterceptor();
|
||||
static MethodInterceptor authorizeReturnObjectMethodInterceptor(ObjectProvider<AuthorizationAdvisor> provider,
|
||||
AuthorizationAdvisorProxyFactory authorizationProxyFactory) {
|
||||
provider.forEach(authorizationProxyFactory::addAdvisor);
|
||||
AuthorizeReturnObjectMethodInterceptor interceptor = new AuthorizeReturnObjectMethodInterceptor(
|
||||
authorizationProxyFactory);
|
||||
authorizationProxyFactory.addAdvisor(interceptor);
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.View;
|
||||
|
||||
@Configuration
|
||||
class AuthorizationProxyWebConfiguration {
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
AuthorizationAdvisorProxyFactory.TargetVisitor webTargetVisitor() {
|
||||
return new WebTargetVisitor();
|
||||
}
|
||||
|
||||
static class WebTargetVisitor implements AuthorizationAdvisorProxyFactory.TargetVisitor, Ordered {
|
||||
|
||||
private static final int DEFAULT_ORDER = 100;
|
||||
|
||||
@Override
|
||||
public Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object target) {
|
||||
if (target instanceof ResponseEntity<?> entity) {
|
||||
return new ResponseEntity<>(proxyFactory.proxy(entity.getBody()), entity.getHeaders(),
|
||||
entity.getStatusCode());
|
||||
}
|
||||
if (target instanceof HttpEntity<?> entity) {
|
||||
return new HttpEntity<>(proxyFactory.proxy(entity.getBody()), entity.getHeaders());
|
||||
}
|
||||
if (target instanceof ModelAndView mav) {
|
||||
View view = mav.getView();
|
||||
String viewName = mav.getViewName();
|
||||
Map<String, Object> model = (Map<String, Object>) proxyFactory.proxy(mav.getModel());
|
||||
ModelAndView proxied = (view != null) ? new ModelAndView(view, model)
|
||||
: new ModelAndView(viewName, model);
|
||||
proxied.setStatus(mav.getStatus());
|
||||
return proxied;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return DEFAULT_ORDER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2004-present 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationResult;
|
||||
import org.springframework.security.authorization.ObservationAuthorizationManager;
|
||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
|
||||
final class DeferringObservationAuthorizationManager<T>
|
||||
implements AuthorizationManager<T>, MethodAuthorizationDeniedHandler {
|
||||
|
||||
private final Supplier<AuthorizationManager<T>> delegate;
|
||||
|
||||
private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();
|
||||
|
||||
DeferringObservationAuthorizationManager(ObjectProvider<ObservationRegistry> provider,
|
||||
AuthorizationManager<T> delegate) {
|
||||
this.delegate = SingletonSupplier.of(() -> {
|
||||
ObservationRegistry registry = provider.getIfAvailable(() -> ObservationRegistry.NOOP);
|
||||
if (registry.isNoop()) {
|
||||
return delegate;
|
||||
}
|
||||
return new ObservationAuthorizationManager<>(registry, delegate);
|
||||
});
|
||||
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
|
||||
this.handler = h;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
|
||||
return this.delegate.get().check(authentication, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
||||
return this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
|
||||
AuthorizationResult authorizationResult) {
|
||||
return this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
|
||||
}
|
||||
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2004-present 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationResult;
|
||||
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager;
|
||||
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
|
||||
final class DeferringObservationReactiveAuthorizationManager<T>
|
||||
implements ReactiveAuthorizationManager<T>, MethodAuthorizationDeniedHandler {
|
||||
|
||||
private final Supplier<ReactiveAuthorizationManager<T>> delegate;
|
||||
|
||||
private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();
|
||||
|
||||
DeferringObservationReactiveAuthorizationManager(ObjectProvider<ObservationRegistry> provider,
|
||||
ReactiveAuthorizationManager<T> delegate) {
|
||||
this.delegate = SingletonSupplier.of(() -> {
|
||||
ObservationRegistry registry = provider.getIfAvailable(() -> ObservationRegistry.NOOP);
|
||||
if (registry.isNoop()) {
|
||||
return delegate;
|
||||
}
|
||||
return new ObservationReactiveAuthorizationManager<>(registry, delegate);
|
||||
});
|
||||
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
|
||||
this.handler = h;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T object) {
|
||||
return this.delegate.get().check(authentication, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
||||
return this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
|
||||
AuthorizationResult authorizationResult) {
|
||||
return this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
|
||||
}
|
||||
|
||||
}
|
||||
-6
@@ -41,9 +41,6 @@ final class MethodSecuritySelector implements ImportSelector {
|
||||
private static final boolean isDataPresent = ClassUtils
|
||||
.isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null);
|
||||
|
||||
private static final boolean isWebPresent = ClassUtils
|
||||
.isPresent("org.springframework.web.servlet.DispatcherServlet", null);
|
||||
|
||||
private static final boolean isObservabilityPresent = ClassUtils
|
||||
.isPresent("io.micrometer.observation.ObservationRegistry", null);
|
||||
|
||||
@@ -70,9 +67,6 @@ final class MethodSecuritySelector implements ImportSelector {
|
||||
if (isDataPresent) {
|
||||
imports.add(AuthorizationProxyDataConfiguration.class.getName());
|
||||
}
|
||||
if (isWebPresent) {
|
||||
imports.add(AuthorizationProxyWebConfiguration.class.getName());
|
||||
}
|
||||
if (isObservabilityPresent) {
|
||||
imports.add(MethodObservationConfiguration.class.getName());
|
||||
}
|
||||
|
||||
-4
@@ -33,16 +33,12 @@ public enum PayloadInterceptorOrder implements Ordered {
|
||||
/**
|
||||
* Where basic authentication is placed.
|
||||
* @see RSocketSecurity#basicAuthentication(Customizer)
|
||||
* @deprecated please see {@link PayloadInterceptorOrder#AUTHENTICATION}
|
||||
*/
|
||||
@Deprecated
|
||||
BASIC_AUTHENTICATION,
|
||||
/**
|
||||
* Where JWT based authentication is performed.
|
||||
* @see RSocketSecurity#jwt(Customizer)
|
||||
* @deprecated please see {@link PayloadInterceptorOrder#AUTHENTICATION}
|
||||
*/
|
||||
@Deprecated
|
||||
JWT_AUTHENTICATION,
|
||||
/**
|
||||
* A generic placeholder for other types of authentication.
|
||||
|
||||
+112
-39
@@ -40,7 +40,7 @@ import org.springframework.core.ResolvableType;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.web.ServletRegistrationsSupport.RegistrationMapping;
|
||||
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;
|
||||
@@ -169,7 +169,7 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||
|
||||
/**
|
||||
* Associates a list of {@link RequestMatcher} instances with the
|
||||
* {@link AbstractRequestMatcherRegistry}
|
||||
* {@link AbstractConfigAttributeRequestMatcherRegistry}
|
||||
* @param requestMatchers the {@link RequestMatcher} instances
|
||||
* @return the object that is chained after creating the {@link RequestMatcher}
|
||||
*/
|
||||
@@ -218,14 +218,9 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||
}
|
||||
List<RequestMatcher> matchers = new ArrayList<>();
|
||||
for (String pattern : patterns) {
|
||||
if (RequestMatcherFactory.usesPathPatterns()) {
|
||||
matchers.add(RequestMatcherFactory.matcher(method, pattern));
|
||||
}
|
||||
else {
|
||||
AntPathRequestMatcher ant = new AntPathRequestMatcher(pattern, (method != null) ? method.name() : null);
|
||||
MvcRequestMatcher mvc = createMvcMatchers(method, pattern).get(0);
|
||||
matchers.add(new DeferredRequestMatcher((c) -> resolve(ant, mvc, c), mvc, ant));
|
||||
}
|
||||
AntPathRequestMatcher ant = new AntPathRequestMatcher(pattern, (method != null) ? method.name() : null);
|
||||
MvcRequestMatcher mvc = createMvcMatchers(method, pattern).get(0);
|
||||
matchers.add(new DeferredRequestMatcher((c) -> resolve(ant, mvc, c), mvc, ant));
|
||||
}
|
||||
return requestMatchers(matchers.toArray(new RequestMatcher[0]));
|
||||
}
|
||||
@@ -240,41 +235,111 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||
}
|
||||
|
||||
private RequestMatcher resolve(AntPathRequestMatcher ant, MvcRequestMatcher mvc, ServletContext servletContext) {
|
||||
ServletRegistrationsSupport registrations = new ServletRegistrationsSupport(servletContext);
|
||||
Collection<RegistrationMapping> mappings = registrations.mappings();
|
||||
if (mappings.isEmpty()) {
|
||||
Map<String, ? extends ServletRegistration> registrations = mappableServletRegistrations(servletContext);
|
||||
if (registrations.isEmpty()) {
|
||||
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, new MockMvcRequestMatcher());
|
||||
}
|
||||
Collection<RegistrationMapping> dispatcherServletMappings = registrations.dispatcherServletMappings();
|
||||
if (dispatcherServletMappings.isEmpty()) {
|
||||
if (!hasDispatcherServlet(registrations)) {
|
||||
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, new MockMvcRequestMatcher());
|
||||
}
|
||||
if (dispatcherServletMappings.size() > 1) {
|
||||
String errorMessage = computeErrorMessage(servletContext.getServletRegistrations().values());
|
||||
throw new IllegalArgumentException(errorMessage);
|
||||
}
|
||||
RegistrationMapping dispatcherServlet = dispatcherServletMappings.iterator().next();
|
||||
if (mappings.size() > 1 && !dispatcherServlet.isDefault()) {
|
||||
String errorMessage = computeErrorMessage(servletContext.getServletRegistrations().values());
|
||||
throw new IllegalArgumentException(errorMessage);
|
||||
}
|
||||
if (dispatcherServlet.isDefault()) {
|
||||
if (mappings.size() == 1) {
|
||||
ServletRegistration dispatcherServlet = requireOneRootDispatcherServlet(registrations);
|
||||
if (dispatcherServlet != null) {
|
||||
if (registrations.size() == 1) {
|
||||
return mvc;
|
||||
}
|
||||
return new DispatcherServletDelegatingRequestMatcher(ant, mvc);
|
||||
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, servletContext);
|
||||
}
|
||||
dispatcherServlet = requireOnlyPathMappedDispatcherServlet(registrations);
|
||||
if (dispatcherServlet != null) {
|
||||
String mapping = dispatcherServlet.getMappings().iterator().next();
|
||||
mvc.setServletPath(mapping.substring(0, mapping.length() - 2));
|
||||
return mvc;
|
||||
}
|
||||
String errorMessage = computeErrorMessage(registrations.values());
|
||||
throw new IllegalArgumentException(errorMessage);
|
||||
}
|
||||
|
||||
private Map<String, ? extends ServletRegistration> mappableServletRegistrations(ServletContext servletContext) {
|
||||
Map<String, ServletRegistration> mappable = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, ? extends ServletRegistration> entry : servletContext.getServletRegistrations()
|
||||
.entrySet()) {
|
||||
if (!entry.getValue().getMappings().isEmpty()) {
|
||||
mappable.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return mappable;
|
||||
}
|
||||
|
||||
private boolean hasDispatcherServlet(Map<String, ? extends ServletRegistration> registrations) {
|
||||
if (registrations == null) {
|
||||
return false;
|
||||
}
|
||||
for (ServletRegistration registration : registrations.values()) {
|
||||
if (isDispatcherServlet(registration)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private ServletRegistration requireOneRootDispatcherServlet(
|
||||
Map<String, ? extends ServletRegistration> registrations) {
|
||||
ServletRegistration rootDispatcherServlet = null;
|
||||
for (ServletRegistration registration : registrations.values()) {
|
||||
if (!isDispatcherServlet(registration)) {
|
||||
continue;
|
||||
}
|
||||
if (registration.getMappings().size() > 1) {
|
||||
return null;
|
||||
}
|
||||
if (!"/".equals(registration.getMappings().iterator().next())) {
|
||||
return null;
|
||||
}
|
||||
rootDispatcherServlet = registration;
|
||||
}
|
||||
return rootDispatcherServlet;
|
||||
}
|
||||
|
||||
private ServletRegistration requireOnlyPathMappedDispatcherServlet(
|
||||
Map<String, ? extends ServletRegistration> registrations) {
|
||||
ServletRegistration pathDispatcherServlet = null;
|
||||
for (ServletRegistration registration : registrations.values()) {
|
||||
if (!isDispatcherServlet(registration)) {
|
||||
return null;
|
||||
}
|
||||
if (registration.getMappings().size() > 1) {
|
||||
return null;
|
||||
}
|
||||
String mapping = registration.getMappings().iterator().next();
|
||||
if (!mapping.startsWith("/") || !mapping.endsWith("/*")) {
|
||||
return null;
|
||||
}
|
||||
if (pathDispatcherServlet != null) {
|
||||
return null;
|
||||
}
|
||||
pathDispatcherServlet = registration;
|
||||
}
|
||||
return pathDispatcherServlet;
|
||||
}
|
||||
|
||||
private boolean isDispatcherServlet(ServletRegistration registration) {
|
||||
Class<?> dispatcherServlet = ClassUtils.resolveClassName("org.springframework.web.servlet.DispatcherServlet",
|
||||
null);
|
||||
try {
|
||||
Class<?> clazz = Class.forName(registration.getClassName());
|
||||
return dispatcherServlet.isAssignableFrom(clazz);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
return false;
|
||||
}
|
||||
return mvc;
|
||||
}
|
||||
|
||||
private static String computeErrorMessage(Collection<? extends ServletRegistration> registrations) {
|
||||
String template = """
|
||||
This method cannot decide whether these patterns are Spring MVC patterns or not. \
|
||||
This is because there is more than one mappable servlet in your servlet context: %s.
|
||||
|
||||
To address this, please create one PathPatternRequestMatcher.Builder#servletPath for each servlet that has \
|
||||
authorized endpoints and use them to construct request matchers manually.
|
||||
""";
|
||||
String template = "This method cannot decide whether these patterns are Spring MVC patterns or not. "
|
||||
+ "If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); "
|
||||
+ "otherwise, please use requestMatchers(AntPathRequestMatcher).\n\n"
|
||||
+ "This is because there is more than one mappable servlet in your servlet context: %s.\n\n"
|
||||
+ "For each MvcRequestMatcher, call MvcRequestMatcher#setServletPath to indicate the servlet path.";
|
||||
Map<String, Collection<String>> mappings = new LinkedHashMap<>();
|
||||
for (ServletRegistration registration : registrations) {
|
||||
mappings.put(registration.getClassName(), registration.getMappings());
|
||||
@@ -453,12 +518,18 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||
|
||||
static class DispatcherServletRequestMatcher implements RequestMatcher {
|
||||
|
||||
private final ServletContext servletContext;
|
||||
|
||||
DispatcherServletRequestMatcher(ServletContext servletContext) {
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
String name = request.getHttpServletMapping().getServletName();
|
||||
ServletRegistration registration = request.getServletContext().getServletRegistration(name);
|
||||
ServletRegistration registration = this.servletContext.getServletRegistration(name);
|
||||
Assert.notNull(registration,
|
||||
() -> computeErrorMessage(request.getServletContext().getServletRegistrations().values()));
|
||||
() -> computeErrorMessage(this.servletContext.getServletRegistrations().values()));
|
||||
try {
|
||||
Class<?> clazz = Class.forName(registration.getClassName());
|
||||
return DispatcherServlet.class.isAssignableFrom(clazz);
|
||||
@@ -478,8 +549,10 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||
|
||||
private final RequestMatcher dispatcherServlet;
|
||||
|
||||
DispatcherServletDelegatingRequestMatcher(AntPathRequestMatcher ant, MvcRequestMatcher mvc) {
|
||||
this(ant, mvc, new OrRequestMatcher(new MockMvcRequestMatcher(), new DispatcherServletRequestMatcher()));
|
||||
DispatcherServletDelegatingRequestMatcher(AntPathRequestMatcher ant, MvcRequestMatcher mvc,
|
||||
ServletContext servletContext) {
|
||||
this(ant, mvc, new OrRequestMatcher(new MockMvcRequestMatcher(),
|
||||
new DispatcherServletRequestMatcher(servletContext)));
|
||||
}
|
||||
|
||||
DispatcherServletDelegatingRequestMatcher(AntPathRequestMatcher ant, MvcRequestMatcher mvc,
|
||||
|
||||
-1
@@ -135,7 +135,6 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>>
|
||||
* <li>{@link DisableEncodeUrlFilter}</li>
|
||||
* <li>{@link ForceEagerSessionCreationFilter}</li>
|
||||
* <li>{@link ChannelProcessingFilter}</li>
|
||||
* <li>{@link org.springframework.security.web.transport.HttpsRedirectFilter}</li>
|
||||
* <li>{@link WebAsyncManagerIntegrationFilter}</li>
|
||||
* <li>{@link SecurityContextHolderFilter}</li>
|
||||
* <li>{@link SecurityContextPersistenceFilter}</li>
|
||||
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.web;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
/**
|
||||
* This utility exists only to facilitate applications opting into using path patterns in
|
||||
* the HttpSecurity DSL. It is for internal use only.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public final class RequestMatcherFactory {
|
||||
|
||||
private static PathPatternRequestMatcher.Builder builder;
|
||||
|
||||
public static void setApplicationContext(ApplicationContext context) {
|
||||
builder = context.getBeanProvider(PathPatternRequestMatcher.Builder.class).getIfUnique();
|
||||
}
|
||||
|
||||
public static boolean usesPathPatterns() {
|
||||
return builder != null;
|
||||
}
|
||||
|
||||
public static RequestMatcher matcher(String path) {
|
||||
return matcher(null, path);
|
||||
}
|
||||
|
||||
public static RequestMatcher matcher(HttpMethod method, String path) {
|
||||
if (builder != null) {
|
||||
return builder.matcher(method, path);
|
||||
}
|
||||
return new AntPathRequestMatcher(path, (method != null) ? method.name() : null);
|
||||
}
|
||||
|
||||
private RequestMatcherFactory() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.web;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.ServletRegistration;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
class ServletRegistrationsSupport {
|
||||
|
||||
private final Collection<RegistrationMapping> registrations;
|
||||
|
||||
ServletRegistrationsSupport(ServletContext servletContext) {
|
||||
Map<String, ? extends ServletRegistration> registrations = servletContext.getServletRegistrations();
|
||||
Collection<RegistrationMapping> mappings = new ArrayList<>();
|
||||
for (Map.Entry<String, ? extends ServletRegistration> entry : registrations.entrySet()) {
|
||||
if (!entry.getValue().getMappings().isEmpty()) {
|
||||
for (String mapping : entry.getValue().getMappings()) {
|
||||
mappings.add(new RegistrationMapping(entry.getValue(), mapping));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.registrations = mappings;
|
||||
}
|
||||
|
||||
Collection<RegistrationMapping> dispatcherServletMappings() {
|
||||
Collection<RegistrationMapping> mappings = new ArrayList<>();
|
||||
for (RegistrationMapping registration : this.registrations) {
|
||||
if (registration.isDispatcherServlet()) {
|
||||
mappings.add(registration);
|
||||
}
|
||||
}
|
||||
return mappings;
|
||||
}
|
||||
|
||||
Collection<RegistrationMapping> mappings() {
|
||||
return this.registrations;
|
||||
}
|
||||
|
||||
record RegistrationMapping(ServletRegistration registration, String mapping) {
|
||||
boolean isDispatcherServlet() {
|
||||
Class<?> dispatcherServlet = ClassUtils
|
||||
.resolveClassName("org.springframework.web.servlet.DispatcherServlet", null);
|
||||
try {
|
||||
Class<?> clazz = Class.forName(this.registration.getClassName());
|
||||
return dispatcherServlet.isAssignableFrom(clazz);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
boolean isDefault() {
|
||||
return "/".equals(this.mapping);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-4
@@ -31,7 +31,6 @@ import org.springframework.security.web.authentication.AuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
||||
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenFilter;
|
||||
import org.springframework.security.web.authentication.ott.OneTimeTokenAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
|
||||
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
|
||||
@@ -54,7 +53,6 @@ import org.springframework.security.web.session.ConcurrentSessionFilter;
|
||||
import org.springframework.security.web.session.DisableEncodeUrlFilter;
|
||||
import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
import org.springframework.security.web.transport.HttpsRedirectFilter;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
/**
|
||||
@@ -79,7 +77,6 @@ final class FilterOrderRegistration {
|
||||
put(DisableEncodeUrlFilter.class, order.next());
|
||||
put(ForceEagerSessionCreationFilter.class, order.next());
|
||||
put(ChannelProcessingFilter.class, order.next());
|
||||
put(HttpsRedirectFilter.class, order.next());
|
||||
order.next(); // gh-8105
|
||||
put(WebAsyncManagerIntegrationFilter.class, order.next());
|
||||
put(SecurityContextHolderFilter.class, order.next());
|
||||
@@ -104,7 +101,6 @@ final class FilterOrderRegistration {
|
||||
"org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter",
|
||||
order.next());
|
||||
put(UsernamePasswordAuthenticationFilter.class, order.next());
|
||||
put(OneTimeTokenAuthenticationFilter.class, order.next());
|
||||
order.next(); // gh-8105
|
||||
put(DefaultResourcesFilter.class, order.next());
|
||||
put(DefaultLoginPageGeneratingFilter.class, order.next());
|
||||
|
||||
+20
-70
@@ -45,7 +45,6 @@ import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
|
||||
import org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer;
|
||||
@@ -59,7 +58,6 @@ import org.springframework.security.config.annotation.web.configurers.Expression
|
||||
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.HttpsRedirectConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.JeeConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.PasswordManagementConfigurer;
|
||||
@@ -3137,9 +3135,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
* the {@link ChannelSecurityConfigurer.ChannelRequestMatcherRegistry}
|
||||
* @return the {@link HttpSecurity} for further customizations
|
||||
* @throws Exception
|
||||
* @deprecated Use {@link #redirectToHttps}
|
||||
*/
|
||||
@Deprecated
|
||||
public HttpSecurity requiresChannel(
|
||||
Customizer<ChannelSecurityConfigurer<HttpSecurity>.ChannelRequestMatcherRegistry> requiresChannelCustomizer)
|
||||
throws Exception {
|
||||
@@ -3148,53 +3144,6 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
return HttpSecurity.this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures channel security. In order for this configuration to be useful at least
|
||||
* one mapping to a required channel must be provided.
|
||||
*
|
||||
* <h2>Example Configuration</h2>
|
||||
*
|
||||
* The example below demonstrates how to require HTTPS for every request. Only
|
||||
* requiring HTTPS for some requests is supported, for example if you need to
|
||||
* differentiate between local and production deployments.
|
||||
*
|
||||
* <pre>
|
||||
* @Configuration
|
||||
* @EnableWebSecurity
|
||||
* public class RequireHttpsConfig {
|
||||
*
|
||||
* @Bean
|
||||
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
* http
|
||||
* .authorizeHttpRequests((authorize) -> authorize
|
||||
* anyRequest().authenticated()
|
||||
* )
|
||||
* .formLogin(withDefaults())
|
||||
* .redirectToHttps(withDefaults());
|
||||
* return http.build();
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public UserDetailsService userDetailsService() {
|
||||
* UserDetails user = User.withDefaultPasswordEncoder()
|
||||
* .username("user")
|
||||
* .password("password")
|
||||
* .roles("USER")
|
||||
* .build();
|
||||
* return new InMemoryUserDetailsManager(user);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* @param httpsRedirectConfigurerCustomizer the {@link Customizer} to provide more
|
||||
* options for the {@link HttpsRedirectConfigurer}
|
||||
* @return the {@link HttpSecurity} for further customizations
|
||||
*/
|
||||
public HttpSecurity redirectToHttps(
|
||||
Customizer<HttpsRedirectConfigurer<HttpSecurity>> httpsRedirectConfigurerCustomizer) throws Exception {
|
||||
httpsRedirectConfigurerCustomizer.customize(getOrApply(new HttpsRedirectConfigurer<>()));
|
||||
return HttpSecurity.this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures HTTP Basic authentication.
|
||||
*
|
||||
@@ -3706,8 +3655,8 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
* {@link #securityMatcher(String...)}, {@link #securityMatchers(Customizer)} and
|
||||
* {@link #securityMatchers()}
|
||||
* </p>
|
||||
* @param requestMatcher the {@link RequestMatcher} to use, for example,
|
||||
* {@code PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GET, "/admin/**")}
|
||||
* @param requestMatcher the {@link RequestMatcher} to use (i.e. new
|
||||
* AntPathRequestMatcher("/admin/**","GET") )
|
||||
* @return the {@link HttpSecurity} for further customizations
|
||||
* @see #securityMatcher(String...)
|
||||
*/
|
||||
@@ -3735,17 +3684,11 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
* @see MvcRequestMatcher
|
||||
*/
|
||||
public HttpSecurity securityMatcher(String... patterns) {
|
||||
List<RequestMatcher> matchers = new ArrayList<>();
|
||||
for (String pattern : patterns) {
|
||||
if (RequestMatcherFactory.usesPathPatterns()) {
|
||||
matchers.add(RequestMatcherFactory.matcher(pattern));
|
||||
}
|
||||
else {
|
||||
RequestMatcher matcher = mvcPresent ? createMvcMatcher(pattern) : createAntMatcher(pattern);
|
||||
matchers.add(matcher);
|
||||
}
|
||||
if (mvcPresent) {
|
||||
this.requestMatcher = new OrRequestMatcher(createMvcMatchers(patterns));
|
||||
return this;
|
||||
}
|
||||
this.requestMatcher = new OrRequestMatcher(matchers);
|
||||
this.requestMatcher = new OrRequestMatcher(createAntMatchers(patterns));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -3758,7 +3701,6 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
* http
|
||||
* // ...
|
||||
* .webAuthn((webAuthn) -> webAuthn
|
||||
* .rpName("Spring Security Relying Party")
|
||||
* .rpId("example.com")
|
||||
* .allowedOrigins("https://example.com")
|
||||
* );
|
||||
@@ -3774,11 +3716,15 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
return HttpSecurity.this;
|
||||
}
|
||||
|
||||
private RequestMatcher createAntMatcher(String pattern) {
|
||||
return new AntPathRequestMatcher(pattern);
|
||||
private List<RequestMatcher> createAntMatchers(String... patterns) {
|
||||
List<RequestMatcher> matchers = new ArrayList<>(patterns.length);
|
||||
for (String pattern : patterns) {
|
||||
matchers.add(new AntPathRequestMatcher(pattern));
|
||||
}
|
||||
return matchers;
|
||||
}
|
||||
|
||||
private RequestMatcher createMvcMatcher(String mvcPattern) {
|
||||
private List<RequestMatcher> createMvcMatchers(String... mvcPatterns) {
|
||||
ResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class, Object.class);
|
||||
ObjectProvider<ObjectPostProcessor<Object>> postProcessors = getContext().getBeanProvider(type);
|
||||
ObjectPostProcessor<Object> opp = postProcessors.getObject();
|
||||
@@ -3789,9 +3735,13 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
}
|
||||
HandlerMappingIntrospector introspector = getContext().getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME,
|
||||
HandlerMappingIntrospector.class);
|
||||
MvcRequestMatcher matcher = new MvcRequestMatcher(introspector, mvcPattern);
|
||||
opp.postProcess(matcher);
|
||||
return matcher;
|
||||
List<RequestMatcher> matchers = new ArrayList<>(mvcPatterns.length);
|
||||
for (String mvcPattern : mvcPatterns) {
|
||||
MvcRequestMatcher matcher = new MvcRequestMatcher(introspector, mvcPattern);
|
||||
opp.postProcess(matcher);
|
||||
matchers.add(matcher);
|
||||
}
|
||||
return matchers;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+39
-44
@@ -35,9 +35,7 @@ import org.springframework.core.ResolvableType;
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.SingleResultAuthorizationManager;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
@@ -60,8 +58,6 @@ import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
||||
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
||||
import org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;
|
||||
import org.springframework.security.web.debug.DebugFilter;
|
||||
import org.springframework.security.web.firewall.CompositeRequestRejectedHandler;
|
||||
import org.springframework.security.web.firewall.HttpFirewall;
|
||||
@@ -235,8 +231,8 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||
|
||||
/**
|
||||
* Set the {@link WebInvocationPrivilegeEvaluator} to be used. If this is not
|
||||
* specified, then a {@link AuthorizationManagerWebInvocationPrivilegeEvaluator} will
|
||||
* be created based on the list of {@link SecurityFilterChain}.
|
||||
* specified, then a {@link RequestMatcherDelegatingWebInvocationPrivilegeEvaluator}
|
||||
* will be created based on the list of {@link SecurityFilterChain}.
|
||||
* @param privilegeEvaluator the {@link WebInvocationPrivilegeEvaluator} to use
|
||||
* @return the {@link WebSecurity} for further customizations
|
||||
*/
|
||||
@@ -305,33 +301,36 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||
+ ".addSecurityFilterChainBuilder directly");
|
||||
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
|
||||
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
|
||||
RequestMatcherDelegatingAuthorizationManager.Builder builder = RequestMatcherDelegatingAuthorizationManager
|
||||
.builder();
|
||||
boolean mappings = false;
|
||||
List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList<>();
|
||||
for (RequestMatcher ignoredRequest : this.ignoredRequests) {
|
||||
WebSecurity.this.logger.warn("You are asking Spring Security to ignore " + ignoredRequest
|
||||
+ ". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.");
|
||||
SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest);
|
||||
securityFilterChains.add(securityFilterChain);
|
||||
builder.add(ignoredRequest, SingleResultAuthorizationManager.permitAll());
|
||||
mappings = true;
|
||||
requestMatcherPrivilegeEvaluatorsEntries
|
||||
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
|
||||
}
|
||||
DefaultSecurityFilterChain anyRequestFilterChain = null;
|
||||
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
|
||||
SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
|
||||
if (anyRequestFilterChain != null) {
|
||||
String message = "A filter chain that matches any request [" + anyRequestFilterChain
|
||||
+ "] has already been configured, which means that this filter chain [" + securityFilterChain
|
||||
+ "] will never get invoked. Please use `HttpSecurity#securityMatcher` to ensure that there is only one filter chain configured for 'any request' and that the 'any request' filter chain is published last.";
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
if (securityFilterChain instanceof DefaultSecurityFilterChain defaultSecurityFilterChain) {
|
||||
if (defaultSecurityFilterChain.getRequestMatcher() instanceof AnyRequestMatcher) {
|
||||
anyRequestFilterChain = defaultSecurityFilterChain;
|
||||
}
|
||||
}
|
||||
securityFilterChains.add(securityFilterChain);
|
||||
mappings = addAuthorizationManager(securityFilterChain, builder) || mappings;
|
||||
requestMatcherPrivilegeEvaluatorsEntries
|
||||
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
|
||||
}
|
||||
if (this.privilegeEvaluator == null) {
|
||||
AuthorizationManager<HttpServletRequest> authorizationManager = mappings ? builder.build()
|
||||
: SingleResultAuthorizationManager.permitAll();
|
||||
AuthorizationManagerWebInvocationPrivilegeEvaluator privilegeEvaluator = new AuthorizationManagerWebInvocationPrivilegeEvaluator(
|
||||
authorizationManager);
|
||||
privilegeEvaluator.setServletContext(this.servletContext);
|
||||
if (this.privilegeEvaluatorRequestTransformer != null) {
|
||||
privilegeEvaluator.setRequestTransformer(this.privilegeEvaluatorRequestTransformer);
|
||||
}
|
||||
this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
|
||||
List.of(new RequestMatcherEntry<>(AnyRequestMatcher.INSTANCE, List.of(privilegeEvaluator))));
|
||||
requestMatcherPrivilegeEvaluatorsEntries);
|
||||
}
|
||||
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
|
||||
if (this.httpFirewall != null) {
|
||||
@@ -346,7 +345,6 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||
new HttpStatusRequestRejectedHandler());
|
||||
filterChainProxy.setRequestRejectedHandler(requestRejectedHandler);
|
||||
}
|
||||
filterChainProxy.setFilterChainValidator(new WebSecurityFilterChainValidator());
|
||||
filterChainProxy.setFilterChainDecorator(getFilterChainDecorator());
|
||||
filterChainProxy.afterPropertiesSet();
|
||||
|
||||
@@ -364,33 +362,30 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean addAuthorizationManager(SecurityFilterChain securityFilterChain,
|
||||
RequestMatcherDelegatingAuthorizationManager.Builder builder) {
|
||||
boolean mappings = false;
|
||||
private RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> getRequestMatcherPrivilegeEvaluatorsEntry(
|
||||
SecurityFilterChain securityFilterChain) {
|
||||
List<WebInvocationPrivilegeEvaluator> privilegeEvaluators = new ArrayList<>();
|
||||
for (Filter filter : securityFilterChain.getFilters()) {
|
||||
if (filter instanceof FilterSecurityInterceptor securityInterceptor) {
|
||||
DefaultWebInvocationPrivilegeEvaluator privilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(
|
||||
securityInterceptor);
|
||||
privilegeEvaluator.setServletContext(this.servletContext);
|
||||
AuthorizationManager<RequestAuthorizationContext> authorizationManager = (authentication, context) -> {
|
||||
HttpServletRequest request = context.getRequest();
|
||||
boolean result = privilegeEvaluator.isAllowed(request.getContextPath(), request.getRequestURI(),
|
||||
request.getMethod(), authentication.get());
|
||||
return new AuthorizationDecision(result);
|
||||
};
|
||||
builder.add(securityFilterChain::matches, authorizationManager);
|
||||
mappings = true;
|
||||
if (filter instanceof FilterSecurityInterceptor) {
|
||||
DefaultWebInvocationPrivilegeEvaluator defaultWebInvocationPrivilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(
|
||||
(FilterSecurityInterceptor) filter);
|
||||
defaultWebInvocationPrivilegeEvaluator.setServletContext(this.servletContext);
|
||||
privilegeEvaluators.add(defaultWebInvocationPrivilegeEvaluator);
|
||||
continue;
|
||||
}
|
||||
if (filter instanceof AuthorizationFilter authorization) {
|
||||
AuthorizationManager<HttpServletRequest> authorizationManager = authorization.getAuthorizationManager();
|
||||
builder.add(securityFilterChain::matches,
|
||||
(authentication, context) -> (AuthorizationDecision) authorizationManager
|
||||
.authorize(authentication, context.getRequest()));
|
||||
mappings = true;
|
||||
if (filter instanceof AuthorizationFilter) {
|
||||
AuthorizationManager<HttpServletRequest> authorizationManager = ((AuthorizationFilter) filter)
|
||||
.getAuthorizationManager();
|
||||
AuthorizationManagerWebInvocationPrivilegeEvaluator evaluator = new AuthorizationManagerWebInvocationPrivilegeEvaluator(
|
||||
authorizationManager);
|
||||
evaluator.setServletContext(this.servletContext);
|
||||
if (this.privilegeEvaluatorRequestTransformer != null) {
|
||||
evaluator.setRequestTransformer(this.privilegeEvaluatorRequestTransformer);
|
||||
}
|
||||
privilegeEvaluators.add(evaluator);
|
||||
}
|
||||
}
|
||||
return mappings;
|
||||
return new RequestMatcherEntry<>(securityFilterChain::matches, privilegeEvaluators);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.web.builders;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.UnreachableFilterChainException;
|
||||
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||
|
||||
/**
|
||||
* A filter chain validator for filter chains built by {@link WebSecurity}
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @author Max Batischev
|
||||
* @since 6.5
|
||||
*/
|
||||
final class WebSecurityFilterChainValidator implements FilterChainProxy.FilterChainValidator {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@Override
|
||||
public void validate(FilterChainProxy filterChainProxy) {
|
||||
List<SecurityFilterChain> chains = filterChainProxy.getFilterChains();
|
||||
checkForAnyRequestRequestMatcher(chains);
|
||||
checkForDuplicateMatchers(chains);
|
||||
checkAuthorizationFilters(chains);
|
||||
}
|
||||
|
||||
private void checkForAnyRequestRequestMatcher(List<SecurityFilterChain> chains) {
|
||||
DefaultSecurityFilterChain anyRequestFilterChain = null;
|
||||
for (SecurityFilterChain chain : chains) {
|
||||
if (anyRequestFilterChain != null) {
|
||||
String message = "A filter chain that matches any request [" + anyRequestFilterChain
|
||||
+ "] has already been configured, which means that this filter chain [" + chain
|
||||
+ "] will never get invoked. Please use `HttpSecurity#securityMatcher` to ensure that there is only one filter chain configured for 'any request' and that the 'any request' filter chain is published last.";
|
||||
throw new UnreachableFilterChainException(message, anyRequestFilterChain, chain);
|
||||
}
|
||||
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
|
||||
if (defaultChain.getRequestMatcher() instanceof AnyRequestMatcher) {
|
||||
anyRequestFilterChain = defaultChain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForDuplicateMatchers(List<SecurityFilterChain> chains) {
|
||||
DefaultSecurityFilterChain filterChain = null;
|
||||
for (SecurityFilterChain chain : chains) {
|
||||
if (filterChain != null) {
|
||||
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
|
||||
if (defaultChain.getRequestMatcher().equals(filterChain.getRequestMatcher())) {
|
||||
throw new UnreachableFilterChainException(
|
||||
"The FilterChainProxy contains two filter chains using the" + " matcher "
|
||||
+ defaultChain.getRequestMatcher(),
|
||||
filterChain, defaultChain);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
|
||||
filterChain = defaultChain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAuthorizationFilters(List<SecurityFilterChain> chains) {
|
||||
Filter authorizationFilter = null;
|
||||
Filter filterSecurityInterceptor = null;
|
||||
for (SecurityFilterChain chain : chains) {
|
||||
for (Filter filter : chain.getFilters()) {
|
||||
if (filter instanceof AuthorizationFilter) {
|
||||
authorizationFilter = filter;
|
||||
}
|
||||
if (filter instanceof FilterSecurityInterceptor) {
|
||||
filterSecurityInterceptor = filter;
|
||||
}
|
||||
}
|
||||
if (authorizationFilter != null && filterSecurityInterceptor != null) {
|
||||
this.logger.warn(
|
||||
"It is not recommended to use authorizeRequests in the configuration. Please only use authorizeHttpRequests");
|
||||
}
|
||||
if (filterSecurityInterceptor != null) {
|
||||
this.logger.warn(
|
||||
"Usage of authorizeRequests is deprecated. Please use authorizeHttpRequests in the configuration");
|
||||
}
|
||||
authorizationFilter = null;
|
||||
filterSecurityInterceptor = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-2
@@ -35,7 +35,6 @@ import org.springframework.security.config.annotation.authentication.configurati
|
||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.JdbcUserDetailsManagerConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer;
|
||||
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
|
||||
@@ -105,7 +104,6 @@ class HttpSecurityConfiguration {
|
||||
@Bean(HTTPSECURITY_BEAN_NAME)
|
||||
@Scope("prototype")
|
||||
HttpSecurity httpSecurity() throws Exception {
|
||||
RequestMatcherFactory.setApplicationContext(this.context);
|
||||
LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(this.context);
|
||||
AuthenticationManagerBuilder authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(
|
||||
this.objectPostProcessor, passwordEncoder);
|
||||
|
||||
+1
-14
@@ -34,8 +34,6 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -162,7 +160,7 @@ final class OAuth2ClientConfiguration {
|
||||
* @since 6.2.0
|
||||
*/
|
||||
static final class OAuth2AuthorizedClientManagerRegistrar
|
||||
implements ApplicationEventPublisherAware, BeanDefinitionRegistryPostProcessor, BeanFactoryAware {
|
||||
implements BeanDefinitionRegistryPostProcessor, BeanFactoryAware {
|
||||
|
||||
static final String BEAN_NAME = "authorizedClientManagerRegistrar";
|
||||
|
||||
@@ -181,8 +179,6 @@ final class OAuth2ClientConfiguration {
|
||||
|
||||
private final AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
|
||||
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
private ListableBeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
@@ -306,10 +302,6 @@ final class OAuth2ClientConfiguration {
|
||||
authorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);
|
||||
}
|
||||
|
||||
if (this.applicationEventPublisher != null) {
|
||||
authorizedClientProvider.setApplicationEventPublisher(this.applicationEventPublisher);
|
||||
}
|
||||
|
||||
return authorizedClientProvider;
|
||||
}
|
||||
|
||||
@@ -431,11 +423,6 @@ final class OAuth2ClientConfiguration {
|
||||
return objectProvider.getIfAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+17
-19
@@ -78,8 +78,6 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
|
||||
|
||||
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
|
||||
|
||||
private static final String PATH_PATTERN_REQUEST_TRANSFORMER_BEAN_NAME = "pathPatternRequestTransformer";
|
||||
|
||||
private BeanResolver beanResolver;
|
||||
|
||||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
||||
@@ -121,6 +119,17 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to ensure Spring MVC request matching is cached.
|
||||
*
|
||||
* Creates a {@link BeanDefinitionRegistryPostProcessor} that detects if a bean named
|
||||
* HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME is defined. If so, it moves the
|
||||
* AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME to another bean name
|
||||
* and then adds a {@link CompositeFilter} that contains
|
||||
* {@link HandlerMappingIntrospector#createCacheFilter()} and the original
|
||||
* FilterChainProxy under the original Bean name.
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
static BeanDefinitionRegistryPostProcessor springSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor() {
|
||||
return new BeanDefinitionRegistryPostProcessor() {
|
||||
@@ -135,15 +144,12 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
|
||||
}
|
||||
|
||||
String hmiRequestTransformerBeanName = HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + "RequestTransformer";
|
||||
if (!registry.containsBeanDefinition(PATH_PATTERN_REQUEST_TRANSFORMER_BEAN_NAME)
|
||||
&& !registry.containsBeanDefinition(hmiRequestTransformerBeanName)) {
|
||||
if (!registry.containsBeanDefinition(hmiRequestTransformerBeanName)) {
|
||||
BeanDefinition hmiRequestTransformer = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(HandlerMappingIntrospectorRequestTransformer.class)
|
||||
.addConstructorArgReference(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)
|
||||
.getBeanDefinition();
|
||||
registry.registerBeanDefinition(hmiRequestTransformerBeanName, hmiRequestTransformer);
|
||||
}
|
||||
if (!registry.containsBeanDefinition(hmiRequestTransformerBeanName)) {
|
||||
BeanDefinition hmiRequestTransformer = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(HandlerMappingIntrospectorRequestTransformer.class)
|
||||
.addConstructorArgReference(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)
|
||||
.getBeanDefinition();
|
||||
registry.registerBeanDefinition(hmiRequestTransformerBeanName, hmiRequestTransformer);
|
||||
}
|
||||
|
||||
BeanDefinition filterChainProxy = registry
|
||||
@@ -172,11 +178,7 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
|
||||
/**
|
||||
* {@link FactoryBean} to defer creation of
|
||||
* {@link HandlerMappingIntrospector#createCacheFilter()}
|
||||
*
|
||||
* @deprecated see {@link WebSecurityConfiguration} for
|
||||
* {@link org.springframework.web.util.pattern.PathPattern} replacement
|
||||
*/
|
||||
@Deprecated
|
||||
static class HandlerMappingIntrospectorCacheFilterFactoryBean
|
||||
implements ApplicationContextAware, FactoryBean<Filter> {
|
||||
|
||||
@@ -205,11 +207,7 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
|
||||
* Extends {@link FilterChainProxy} to provide as much passivity as possible but
|
||||
* delegates to {@link CompositeFilter} for
|
||||
* {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}.
|
||||
*
|
||||
* @deprecated see {@link WebSecurityConfiguration} for
|
||||
* {@link org.springframework.web.util.pattern.PathPattern} replacement
|
||||
*/
|
||||
@Deprecated
|
||||
static class CompositeFilterChainProxy extends FilterChainProxy {
|
||||
|
||||
/**
|
||||
|
||||
+18
-188
@@ -16,29 +16,16 @@
|
||||
|
||||
package org.springframework.security.config.annotation.web.configuration;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
@@ -58,18 +45,11 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor;
|
||||
import org.springframework.security.context.DelegatingApplicationListener;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
|
||||
import org.springframework.security.web.debug.DebugFilter;
|
||||
import org.springframework.security.web.firewall.HttpFirewall;
|
||||
import org.springframework.security.web.firewall.RequestRejectedHandler;
|
||||
import org.springframework.web.filter.CompositeFilter;
|
||||
import org.springframework.web.filter.ServletRequestPathFilter;
|
||||
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||
|
||||
/**
|
||||
* Uses a {@link WebSecurity} to create the {@link FilterChainProxy} that performs the web
|
||||
@@ -85,16 +65,23 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||
* @see WebSecurity
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class WebSecurityConfiguration implements ImportAware {
|
||||
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
|
||||
|
||||
private WebSecurity webSecurity;
|
||||
|
||||
private Boolean debugEnabled;
|
||||
|
||||
private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
|
||||
|
||||
private List<SecurityFilterChain> securityFilterChains = Collections.emptyList();
|
||||
|
||||
private List<WebSecurityCustomizer> webSecurityCustomizers = Collections.emptyList();
|
||||
|
||||
private ClassLoader beanClassLoader;
|
||||
|
||||
@Autowired(required = false)
|
||||
private HttpSecurity httpSecurity;
|
||||
|
||||
@Bean
|
||||
public static DelegatingApplicationListener delegatingApplicationListener() {
|
||||
return new DelegatingApplicationListener();
|
||||
@@ -112,15 +99,14 @@ public class WebSecurityConfiguration implements ImportAware {
|
||||
* @throws Exception
|
||||
*/
|
||||
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
|
||||
public Filter springSecurityFilterChain(ObjectProvider<HttpSecurity> provider) throws Exception {
|
||||
public Filter springSecurityFilterChain() throws Exception {
|
||||
boolean hasFilterChain = !this.securityFilterChains.isEmpty();
|
||||
if (!hasFilterChain) {
|
||||
this.webSecurity.addSecurityFilterChainBuilder(() -> {
|
||||
HttpSecurity httpSecurity = provider.getObject();
|
||||
httpSecurity.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated());
|
||||
httpSecurity.formLogin(Customizer.withDefaults());
|
||||
httpSecurity.httpBasic(Customizer.withDefaults());
|
||||
return httpSecurity.build();
|
||||
this.httpSecurity.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated());
|
||||
this.httpSecurity.formLogin(Customizer.withDefaults());
|
||||
this.httpSecurity.httpBasic(Customizer.withDefaults());
|
||||
return this.httpSecurity.build();
|
||||
});
|
||||
}
|
||||
for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
|
||||
@@ -178,6 +164,7 @@ public class WebSecurityConfiguration implements ImportAware {
|
||||
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
|
||||
this.webSecurity.apply(webSecurityConfigurer);
|
||||
}
|
||||
this.webSecurityConfigurers = webSecurityConfigurers;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
@@ -206,49 +193,9 @@ public class WebSecurityConfiguration implements ImportAware {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to ensure Spring MVC request matching is cached.
|
||||
*
|
||||
* Creates a {@link BeanDefinitionRegistryPostProcessor} that detects if a bean named
|
||||
* HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME is defined. If so, it moves the
|
||||
* AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME to another bean name
|
||||
* and then adds a {@link CompositeFilter} that contains
|
||||
* {@link HandlerMappingIntrospector#createCacheFilter()} and the original
|
||||
* FilterChainProxy under the original Bean name.
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
static BeanDefinitionRegistryPostProcessor springSecurityPathPatternParserBeanDefinitionRegistryPostProcessor() {
|
||||
return new BeanDefinitionRegistryPostProcessor() {
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
|
||||
BeanDefinition filterChainProxy = registry
|
||||
.getBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
|
||||
|
||||
if (filterChainProxy.getResolvableType().isInstance(CompositeFilterChainProxy.class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder pppCacheFilterBldr = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(ServletRequestPathFilter.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
|
||||
ManagedList<BeanMetadataElement> filters = new ManagedList<>();
|
||||
filters.add(pppCacheFilterBldr.getBeanDefinition());
|
||||
filters.add(filterChainProxy);
|
||||
BeanDefinitionBuilder compositeSpringSecurityFilterChainBldr = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(CompositeFilterChainProxy.class)
|
||||
.addConstructorArgValue(filters);
|
||||
|
||||
registry.removeBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
|
||||
registry.registerBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME,
|
||||
compositeSpringSecurityFilterChainBldr.getBeanDefinition());
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,121 +231,4 @@ public class WebSecurityConfiguration implements ImportAware {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends {@link FilterChainProxy} to provide as much passivity as possible but
|
||||
* delegates to {@link CompositeFilter} for
|
||||
* {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}.
|
||||
*/
|
||||
static class CompositeFilterChainProxy extends FilterChainProxy {
|
||||
|
||||
/**
|
||||
* Used for {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}
|
||||
*/
|
||||
private final Filter doFilterDelegate;
|
||||
|
||||
private final FilterChainProxy springSecurityFilterChain;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param filters the Filters to delegate to. One of which must be
|
||||
* FilterChainProxy.
|
||||
*/
|
||||
CompositeFilterChainProxy(List<? extends Filter> filters) {
|
||||
this.doFilterDelegate = createDoFilterDelegate(filters);
|
||||
this.springSecurityFilterChain = findFilterChainProxy(filters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
this.springSecurityFilterChain.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
this.doFilterDelegate.doFilter(request, response, chain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Filter> getFilters(String url) {
|
||||
return this.springSecurityFilterChain.getFilters(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SecurityFilterChain> getFilterChains() {
|
||||
return this.springSecurityFilterChain.getFilterChains();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
|
||||
this.springSecurityFilterChain.setSecurityContextHolderStrategy(securityContextHolderStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilterChainValidator(FilterChainValidator filterChainValidator) {
|
||||
this.springSecurityFilterChain.setFilterChainValidator(filterChainValidator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilterChainDecorator(FilterChainDecorator filterChainDecorator) {
|
||||
this.springSecurityFilterChain.setFilterChainDecorator(filterChainDecorator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFirewall(HttpFirewall firewall) {
|
||||
this.springSecurityFilterChain.setFirewall(firewall);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRequestRejectedHandler(RequestRejectedHandler requestRejectedHandler) {
|
||||
this.springSecurityFilterChain.setRequestRejectedHandler(requestRejectedHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used through reflection by Spring Security's Test support to lookup the
|
||||
* FilterChainProxy Filters for a specific HttpServletRequest.
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
private List<? extends Filter> getFilters(HttpServletRequest request) {
|
||||
List<SecurityFilterChain> filterChains = this.springSecurityFilterChain.getFilterChains();
|
||||
for (SecurityFilterChain chain : filterChains) {
|
||||
if (chain.matches(request)) {
|
||||
return chain.getFilters();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the Filter to delegate to for doFilter
|
||||
* @param filters the Filters to delegate to.
|
||||
* @return the Filter for doFilter
|
||||
*/
|
||||
private static Filter createDoFilterDelegate(List<? extends Filter> filters) {
|
||||
CompositeFilter delegate = new CompositeFilter();
|
||||
delegate.setFilters(filters);
|
||||
return delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the FilterChainProxy in a List of Filter
|
||||
* @param filters
|
||||
* @return non-null FilterChainProxy
|
||||
* @throws IllegalStateException if the FilterChainProxy cannot be found
|
||||
*/
|
||||
private static FilterChainProxy findFilterChainProxy(List<? extends Filter> filters) {
|
||||
for (Filter filter : filters) {
|
||||
if (filter instanceof FilterChainProxy fcp) {
|
||||
return fcp;
|
||||
}
|
||||
if (filter instanceof DebugFilter debugFilter) {
|
||||
return debugFilter.getFilterChainProxy();
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Couldn't find FilterChainProxy in " + filters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-14
@@ -21,7 +21,6 @@ import java.util.Collections;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
@@ -29,7 +28,6 @@ import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.PortMapper;
|
||||
import org.springframework.security.web.PortResolver;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
@@ -274,10 +272,6 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
|
||||
if (portMapper != null) {
|
||||
this.authenticationEntryPoint.setPortMapper(portMapper);
|
||||
}
|
||||
PortResolver portResolver = getBeanOrNull(http, PortResolver.class);
|
||||
if (portResolver != null) {
|
||||
this.authenticationEntryPoint.setPortResolver(portResolver);
|
||||
}
|
||||
RequestCache requestCache = http.getSharedObject(RequestCache.class);
|
||||
if (requestCache != null) {
|
||||
this.defaultSuccessHandler.setRequestCache(requestCache);
|
||||
@@ -418,14 +412,6 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
|
||||
this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);
|
||||
}
|
||||
|
||||
private <C> C getBeanOrNull(B http, Class<C> clazz) {
|
||||
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
|
||||
if (context == null) {
|
||||
return null;
|
||||
}
|
||||
return context.getBeanProvider(clazz).getIfUnique();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private T getSelf() {
|
||||
return (T) this;
|
||||
|
||||
-9
@@ -22,9 +22,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
||||
import org.springframework.security.core.annotation.SecurityAnnotationScanner;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -38,14 +36,7 @@ import org.springframework.util.Assert;
|
||||
* @see ChannelSecurityConfigurer
|
||||
* @see UrlAuthorizationConfigurer
|
||||
* @see ExpressionUrlAuthorizationConfigurer
|
||||
* @deprecated In modern Spring Security APIs, each API manages its own configuration
|
||||
* context. As such there is no direct replacement for this interface. In the case of
|
||||
* method security, please see {@link SecurityAnnotationScanner} and
|
||||
* {@link AuthorizationManager}. In the case of channel security, please see
|
||||
* {@code HttpsRedirectFilter}. In the case of web security, please see
|
||||
* {@link AuthorizationManager}.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractConfigAttributeRequestMatcherRegistry<C> extends AbstractRequestMatcherRegistry<C> {
|
||||
|
||||
private List<UrlMapping> urlMappings = new ArrayList<>();
|
||||
|
||||
+7
-3
@@ -31,7 +31,6 @@ import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
* {@link HttpSecurity}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Ding Hao
|
||||
*/
|
||||
public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>
|
||||
extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {
|
||||
@@ -71,8 +70,13 @@ public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T,
|
||||
return this.securityContextHolderStrategy;
|
||||
}
|
||||
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
|
||||
this.securityContextHolderStrategy = context.getBeanProvider(SecurityContextHolderStrategy.class)
|
||||
.getIfUnique(SecurityContextHolder::getContextHolderStrategy);
|
||||
String[] names = context.getBeanNamesForType(SecurityContextHolderStrategy.class);
|
||||
if (names.length == 1) {
|
||||
this.securityContextHolderStrategy = context.getBean(SecurityContextHolderStrategy.class);
|
||||
}
|
||||
else {
|
||||
this.securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();
|
||||
}
|
||||
return this.securityContextHolderStrategy;
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -151,7 +151,6 @@ public abstract class AbstractInterceptUrlConfigurer<C extends AbstractIntercept
|
||||
return securityInterceptor;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public abstract class AbstractInterceptUrlRegistry<R extends AbstractInterceptUrlRegistry<R, T>, T>
|
||||
extends AbstractConfigAttributeRequestMatcherRegistry<T> {
|
||||
|
||||
|
||||
+5
-3
@@ -33,7 +33,6 @@ import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationManagers;
|
||||
import org.springframework.security.authorization.SingleResultAuthorizationManager;
|
||||
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
||||
@@ -58,6 +57,9 @@ import org.springframework.util.function.SingletonSupplier;
|
||||
public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
extends AbstractHttpConfigurer<AuthorizeHttpRequestsConfigurer<H>, H> {
|
||||
|
||||
static final AuthorizationManager<RequestAuthorizationContext> permitAllAuthorizationManager = (a,
|
||||
o) -> new AuthorizationDecision(true);
|
||||
|
||||
private final AuthorizationManagerRequestMatcherRegistry registry;
|
||||
|
||||
private final AuthorizationEventPublisher publisher;
|
||||
@@ -285,7 +287,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||
* customizations
|
||||
*/
|
||||
public AuthorizationManagerRequestMatcherRegistry permitAll() {
|
||||
return access(SingleResultAuthorizationManager.permitAll());
|
||||
return access(permitAllAuthorizationManager);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -294,7 +296,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||
* customizations
|
||||
*/
|
||||
public AuthorizationManagerRequestMatcherRegistry denyAll() {
|
||||
return access(SingleResultAuthorizationManager.denyAll());
|
||||
return access((a, o) -> new AuthorizationDecision(false));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
-10
@@ -78,9 +78,7 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
* @author Rob Winch
|
||||
* @author Onur Kagan Ozcan
|
||||
* @since 3.2
|
||||
* @deprecated please use {@link HttpsRedirectConfigurer} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
extends AbstractHttpConfigurer<ChannelSecurityConfigurer<H>, H> {
|
||||
|
||||
@@ -149,10 +147,6 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
return this.REGISTRY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated no replacement planned
|
||||
*/
|
||||
@Deprecated
|
||||
public final class ChannelRequestMatcherRegistry
|
||||
extends AbstractConfigAttributeRequestMatcherRegistry<RequiresChannelUrl> {
|
||||
|
||||
@@ -221,10 +215,6 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated no replacement planned
|
||||
*/
|
||||
@Deprecated
|
||||
public class RequiresChannelUrl {
|
||||
|
||||
protected List<? extends RequestMatcher> requestMatchers;
|
||||
|
||||
+8
-7
@@ -107,14 +107,15 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHt
|
||||
* @return
|
||||
*/
|
||||
private static CorsFilter getMvcCorsFilter(ApplicationContext context) {
|
||||
if (context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
|
||||
CorsConfigurationSource corsConfigurationSource = context
|
||||
.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, CorsConfigurationSource.class);
|
||||
return new CorsFilter(corsConfigurationSource);
|
||||
if (!context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
|
||||
throw new NoSuchBeanDefinitionException(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, "A Bean named "
|
||||
+ HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + " of type "
|
||||
+ HandlerMappingIntrospector.class.getName()
|
||||
+ " is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.");
|
||||
}
|
||||
throw new NoSuchBeanDefinitionException(CorsConfigurationSource.class,
|
||||
"Failed to find a bean that implements `CorsConfigurationSource`. Please ensure that you are using "
|
||||
+ "`@EnableWebMvc`, are publishing a `WebMvcConfigurer`, or are publishing a `CorsConfigurationSource` bean.");
|
||||
HandlerMappingIntrospector mappingIntrospector = context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME,
|
||||
HandlerMappingIntrospector.class);
|
||||
return new CorsFilter(mappingIntrospector);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+9
@@ -33,6 +33,13 @@ import org.springframework.security.web.csrf.CsrfToken;
|
||||
* Adds a Filter that will generate a login page if one is not specified otherwise when
|
||||
* using {@link EnableWebSecurity}.
|
||||
*
|
||||
* <p>
|
||||
* By default an
|
||||
* {@link org.springframework.security.web.access.channel.InsecureChannelProcessor} and a
|
||||
* {@link org.springframework.security.web.access.channel.SecureChannelProcessor} will be
|
||||
* registered.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following Filters are conditionally populated
|
||||
@@ -51,6 +58,8 @@ import org.springframework.security.web.csrf.CsrfToken;
|
||||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link org.springframework.security.web.PortMapper} is used to create the default
|
||||
* {@link org.springframework.security.web.access.channel.ChannelProcessor} instances</li>
|
||||
* <li>{@link FormLoginConfigurer} is used to determine if the
|
||||
* {@link DefaultLoginPageConfigurer} should be added and how to configure it.</li>
|
||||
* </ul>
|
||||
|
||||
-1
@@ -202,7 +202,6 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
|
||||
return "hasIpAddress('" + ipAddressExpression + "')";
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public final class ExpressionInterceptUrlRegistry extends
|
||||
ExpressionUrlAuthorizationConfigurer<H>.AbstractInterceptUrlRegistry<ExpressionInterceptUrlRegistry, AuthorizedUrl> {
|
||||
|
||||
|
||||
+2
-3
@@ -16,9 +16,7 @@
|
||||
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
@@ -28,6 +26,7 @@ import org.springframework.security.web.authentication.RememberMeServices;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
/**
|
||||
@@ -235,7 +234,7 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
|
||||
|
||||
@Override
|
||||
protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
|
||||
return RequestMatcherFactory.matcher(HttpMethod.POST, loginProcessingUrl);
|
||||
return new AntPathRequestMatcher(loginProcessingUrl, "POST");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.web.PortMapper;
|
||||
import org.springframework.security.web.transport.HttpsRedirectFilter;
|
||||
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
/**
|
||||
* Specifies for what requests the application should redirect to HTTPS. When this
|
||||
* configurer is added, it redirects all HTTP requests by default to HTTPS.
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following Filters are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link HttpsRedirectFilter}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* No shared objects are created.
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link PortMapper} is used to configure {@link HttpsRedirectFilter}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
|
||||
* @author Josh Cummings
|
||||
* @since 6.5
|
||||
*/
|
||||
public final class HttpsRedirectConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
extends AbstractHttpConfigurer<HeadersConfigurer<H>, H> {
|
||||
|
||||
private RequestMatcher requestMatcher;
|
||||
|
||||
public HttpsRedirectConfigurer<H> requestMatchers(RequestMatcher... matchers) {
|
||||
this.requestMatcher = new OrRequestMatcher(matchers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(H http) throws Exception {
|
||||
HttpsRedirectFilter filter = new HttpsRedirectFilter();
|
||||
if (this.requestMatcher != null) {
|
||||
filter.setRequestMatcher(this.requestMatcher);
|
||||
}
|
||||
PortMapper mapper = http.getSharedObject(PortMapper.class);
|
||||
if (mapper != null) {
|
||||
filter.setPortMapper(mapper);
|
||||
}
|
||||
http.addFilter(filter);
|
||||
}
|
||||
|
||||
}
|
||||
+3
-4
@@ -22,10 +22,8 @@ import java.util.List;
|
||||
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler;
|
||||
@@ -39,6 +37,7 @@ import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuc
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.web.context.SecurityContextRepository;
|
||||
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;
|
||||
@@ -145,7 +144,7 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
* (i.e. log out) to protect against
|
||||
* <a href="https://en.wikipedia.org/wiki/Cross-site_request_forgery">CSRF
|
||||
* attacks</a>. If you really want to use an HTTP GET, you can use
|
||||
* <code>logoutRequestMatcher(PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GEt, logoutUrl));</code>
|
||||
* <code>logoutRequestMatcher(new AntPathRequestMatcher(logoutUrl, "GET"));</code>
|
||||
* </p>
|
||||
* @param logoutUrl the URL that will invoke logout.
|
||||
* @return the {@link LogoutConfigurer} for further customization
|
||||
@@ -369,7 +368,7 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
}
|
||||
|
||||
private RequestMatcher createLogoutRequestMatcher(String httpMethod) {
|
||||
return RequestMatcherFactory.matcher(HttpMethod.valueOf(httpMethod), this.logoutUrl);
|
||||
return new AntPathRequestMatcher(this.logoutUrl, httpMethod);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -17,9 +17,9 @@
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
|
||||
import org.springframework.security.web.RequestMatcherRedirectFilter;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -55,7 +55,7 @@ public final class PasswordManagementConfigurer<B extends HttpSecurityBuilder<B>
|
||||
@Override
|
||||
public void configure(B http) throws Exception {
|
||||
RequestMatcherRedirectFilter changePasswordFilter = new RequestMatcherRedirectFilter(
|
||||
RequestMatcherFactory.matcher(WELL_KNOWN_CHANGE_PASSWORD_PATTERN), this.changePasswordPage);
|
||||
new AntPathRequestMatcher(WELL_KNOWN_CHANGE_PASSWORD_PATTERN), this.changePasswordPage);
|
||||
http.addFilterBefore(postProcess(changePasswordFilter), UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -19,7 +19,6 @@ package org.springframework.security.config.annotation.web.configurers;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.authorization.SingleResultAuthorizationManager;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractConfigAttributeRequestMatcherRegistry.UrlMapping;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
@@ -64,7 +63,7 @@ final class PermitAllSupport {
|
||||
SecurityConfig.createList(ExpressionUrlAuthorizationConfigurer.permitAll)));
|
||||
}
|
||||
else {
|
||||
httpConfigurer.addFirst(matcher, SingleResultAuthorizationManager.permitAll());
|
||||
httpConfigurer.addFirst(matcher, AuthorizeHttpRequestsConfigurer.permitAllAuthorizationManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-13
@@ -21,10 +21,8 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||
import org.springframework.security.web.savedrequest.NullRequestCache;
|
||||
@@ -142,13 +140,13 @@ public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private RequestMatcher createDefaultSavedRequestMatcher(H http) {
|
||||
RequestMatcher notFavIcon = new NegatedRequestMatcher(getFaviconRequestMatcher());
|
||||
RequestMatcher notFavIcon = new NegatedRequestMatcher(new AntPathRequestMatcher("/**/favicon.*"));
|
||||
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
|
||||
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
|
||||
boolean isCsrfEnabled = http.getConfigurer(CsrfConfigurer.class) != null;
|
||||
List<RequestMatcher> matchers = new ArrayList<>();
|
||||
if (isCsrfEnabled) {
|
||||
RequestMatcher getRequests = RequestMatcherFactory.matcher(HttpMethod.GET, "/**");
|
||||
RequestMatcher getRequests = new AntPathRequestMatcher("/**", "GET");
|
||||
matchers.add(0, getRequests);
|
||||
}
|
||||
matchers.add(notFavIcon);
|
||||
@@ -169,13 +167,4 @@ public final class RequestCacheConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
return new NegatedRequestMatcher(mediaRequest);
|
||||
}
|
||||
|
||||
private RequestMatcher getFaviconRequestMatcher() {
|
||||
if (RequestMatcherFactory.usesPathPatterns()) {
|
||||
return RequestMatcherFactory.matcher("/favicon.*");
|
||||
}
|
||||
else {
|
||||
return new AntPathRequestMatcher("/**/favicon.*");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-18
@@ -47,7 +47,6 @@ import org.springframework.security.web.authentication.session.NullAuthenticated
|
||||
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
|
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
|
||||
import org.springframework.security.web.authentication.session.SessionLimit;
|
||||
import org.springframework.security.web.context.DelegatingSecurityContextRepository;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.web.context.NullSecurityContextRepository;
|
||||
@@ -124,7 +123,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
|
||||
private SessionRegistry sessionRegistry;
|
||||
|
||||
private SessionLimit sessionLimit;
|
||||
private Integer maximumSessions;
|
||||
|
||||
private String expiredUrl;
|
||||
|
||||
@@ -330,7 +329,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
* @return the {@link SessionManagementConfigurer} for further customizations
|
||||
*/
|
||||
public ConcurrencyControlConfigurer maximumSessions(int maximumSessions) {
|
||||
this.sessionLimit = SessionLimit.of(maximumSessions);
|
||||
this.maximumSessions = maximumSessions;
|
||||
this.propertiesThatRequireImplicitAuthentication.add("maximumSessions = " + maximumSessions);
|
||||
return new ConcurrencyControlConfigurer();
|
||||
}
|
||||
@@ -571,7 +570,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
SessionRegistry sessionRegistry = getSessionRegistry(http);
|
||||
ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlStrategy = new ConcurrentSessionControlAuthenticationStrategy(
|
||||
sessionRegistry);
|
||||
concurrentSessionControlStrategy.setMaximumSessions(this.sessionLimit);
|
||||
concurrentSessionControlStrategy.setMaximumSessions(this.maximumSessions);
|
||||
concurrentSessionControlStrategy.setExceptionIfMaximumExceeded(this.maxSessionsPreventsLogin);
|
||||
concurrentSessionControlStrategy = postProcess(concurrentSessionControlStrategy);
|
||||
RegisterSessionAuthenticationStrategy registerSessionStrategy = new RegisterSessionAuthenticationStrategy(
|
||||
@@ -615,7 +614,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
* @return
|
||||
*/
|
||||
private boolean isConcurrentSessionControlEnabled() {
|
||||
return this.sessionLimit != null;
|
||||
return this.maximumSessions != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -707,19 +706,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
* @return the {@link ConcurrencyControlConfigurer} for further customizations
|
||||
*/
|
||||
public ConcurrencyControlConfigurer maximumSessions(int maximumSessions) {
|
||||
SessionManagementConfigurer.this.sessionLimit = SessionLimit.of(maximumSessions);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the behaviour when a session limit is detected.
|
||||
* @param sessionLimit the {@link SessionLimit} to check the maximum number of
|
||||
* sessions for a user
|
||||
* @return the {@link ConcurrencyControlConfigurer} for further customizations
|
||||
* @since 6.5
|
||||
*/
|
||||
public ConcurrencyControlConfigurer maximumSessions(SessionLimit sessionLimit) {
|
||||
SessionManagementConfigurer.this.sessionLimit = sessionLimit;
|
||||
SessionManagementConfigurer.this.maximumSessions = maximumSessions;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -203,7 +203,6 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
return authorities;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public final class StandardInterceptUrlRegistry extends
|
||||
UrlAuthorizationConfigurer<H>.AbstractInterceptUrlRegistry<StandardInterceptUrlRegistry, AuthorizedUrl> {
|
||||
|
||||
|
||||
+11
-62
@@ -23,7 +23,6 @@ import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
@@ -44,9 +43,7 @@ import org.springframework.security.web.webauthn.management.WebAuthnRelyingParty
|
||||
import org.springframework.security.web.webauthn.management.Webauthn4JRelyingPartyOperations;
|
||||
import org.springframework.security.web.webauthn.registration.DefaultWebAuthnRegistrationPageGeneratingFilter;
|
||||
import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsFilter;
|
||||
import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsRepository;
|
||||
import org.springframework.security.web.webauthn.registration.WebAuthnRegistrationFilter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Configures WebAuthn for Spring Security applications
|
||||
@@ -66,17 +63,12 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
|
||||
private boolean disableDefaultRegistrationPage = false;
|
||||
|
||||
private PublicKeyCredentialCreationOptionsRepository creationOptionsRepository;
|
||||
|
||||
private HttpMessageConverter<Object> converter;
|
||||
|
||||
/**
|
||||
* The Relying Party id.
|
||||
* @param rpId the relying party id
|
||||
* @return the {@link WebAuthnConfigurer} for further customization
|
||||
*/
|
||||
public WebAuthnConfigurer<H> rpId(String rpId) {
|
||||
Assert.hasText(rpId, "rpId be null or empty");
|
||||
this.rpId = rpId;
|
||||
return this;
|
||||
}
|
||||
@@ -87,7 +79,6 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
* @return the {@link WebAuthnConfigurer} for further customization
|
||||
*/
|
||||
public WebAuthnConfigurer<H> rpName(String rpName) {
|
||||
Assert.hasText(rpName, "rpName can't be null or empty");
|
||||
this.rpName = rpName;
|
||||
return this;
|
||||
}
|
||||
@@ -109,7 +100,6 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
* @see #allowedOrigins(String...)
|
||||
*/
|
||||
public WebAuthnConfigurer<H> allowedOrigins(Set<String> allowedOrigins) {
|
||||
Assert.notNull(allowedOrigins, "allowedOrigins can't be null");
|
||||
this.allowedOrigins = allowedOrigins;
|
||||
return this;
|
||||
}
|
||||
@@ -126,59 +116,23 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link HttpMessageConverter} used for WebAuthn to read/write to the HTTP
|
||||
* request/response.
|
||||
* @param converter the {@link HttpMessageConverter}
|
||||
* @return the {@link WebAuthnConfigurer} for further customization
|
||||
*/
|
||||
public WebAuthnConfigurer<H> messageConverter(HttpMessageConverter<Object> converter) {
|
||||
Assert.notNull(converter, "converter can't be null");
|
||||
this.converter = converter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets PublicKeyCredentialCreationOptionsRepository
|
||||
* @param creationOptionsRepository the creationOptionsRepository
|
||||
* @return the {@link WebAuthnConfigurer} for further customization
|
||||
*/
|
||||
public WebAuthnConfigurer<H> creationOptionsRepository(
|
||||
PublicKeyCredentialCreationOptionsRepository creationOptionsRepository) {
|
||||
Assert.notNull(creationOptionsRepository, "creationOptionsRepository can't be null");
|
||||
this.creationOptionsRepository = creationOptionsRepository;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(H http) throws Exception {
|
||||
UserDetailsService userDetailsService = getSharedOrBean(http, UserDetailsService.class)
|
||||
.orElseThrow(() -> new IllegalStateException("Missing UserDetailsService Bean"));
|
||||
UserDetailsService userDetailsService = getSharedOrBean(http, UserDetailsService.class).orElseGet(() -> {
|
||||
throw new IllegalStateException("Missing UserDetailsService Bean");
|
||||
});
|
||||
PublicKeyCredentialUserEntityRepository userEntities = getSharedOrBean(http,
|
||||
PublicKeyCredentialUserEntityRepository.class)
|
||||
.orElse(userEntityRepository());
|
||||
UserCredentialRepository userCredentials = getSharedOrBean(http, UserCredentialRepository.class)
|
||||
.orElse(userCredentialRepository());
|
||||
WebAuthnRelyingPartyOperations rpOperations = webAuthnRelyingPartyOperations(userEntities, userCredentials);
|
||||
PublicKeyCredentialCreationOptionsRepository creationOptionsRepository = creationOptionsRepository();
|
||||
WebAuthnAuthenticationFilter webAuthnAuthnFilter = new WebAuthnAuthenticationFilter();
|
||||
webAuthnAuthnFilter.setAuthenticationManager(
|
||||
new ProviderManager(new WebAuthnAuthenticationProvider(rpOperations, userDetailsService)));
|
||||
WebAuthnRegistrationFilter webAuthnRegistrationFilter = new WebAuthnRegistrationFilter(userCredentials,
|
||||
rpOperations);
|
||||
PublicKeyCredentialCreationOptionsFilter creationOptionsFilter = new PublicKeyCredentialCreationOptionsFilter(
|
||||
rpOperations);
|
||||
if (creationOptionsRepository != null) {
|
||||
webAuthnRegistrationFilter.setCreationOptionsRepository(creationOptionsRepository);
|
||||
creationOptionsFilter.setCreationOptionsRepository(creationOptionsRepository);
|
||||
}
|
||||
if (this.converter != null) {
|
||||
webAuthnRegistrationFilter.setConverter(this.converter);
|
||||
creationOptionsFilter.setConverter(this.converter);
|
||||
}
|
||||
http.addFilterBefore(webAuthnAuthnFilter, BasicAuthenticationFilter.class);
|
||||
http.addFilterAfter(webAuthnRegistrationFilter, AuthorizationFilter.class);
|
||||
http.addFilterBefore(creationOptionsFilter, AuthorizationFilter.class);
|
||||
http.addFilterAfter(new WebAuthnRegistrationFilter(userCredentials, rpOperations), AuthorizationFilter.class);
|
||||
http.addFilterBefore(new PublicKeyCredentialCreationOptionsFilter(rpOperations), AuthorizationFilter.class);
|
||||
http.addFilterBefore(new PublicKeyCredentialRequestOptionsFilter(rpOperations), AuthorizationFilter.class);
|
||||
|
||||
DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http
|
||||
@@ -205,14 +159,6 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
}
|
||||
}
|
||||
|
||||
private PublicKeyCredentialCreationOptionsRepository creationOptionsRepository() {
|
||||
if (this.creationOptionsRepository != null) {
|
||||
return this.creationOptionsRepository;
|
||||
}
|
||||
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
|
||||
return context.getBeanProvider(PublicKeyCredentialCreationOptionsRepository.class).getIfUnique();
|
||||
}
|
||||
|
||||
private <C> Optional<C> getSharedOrBean(H http, Class<C> type) {
|
||||
C shared = http.getSharedObject(type);
|
||||
return Optional.ofNullable(shared).or(() -> getBeanOrNull(type));
|
||||
@@ -243,9 +189,12 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
PublicKeyCredentialUserEntityRepository userEntities, UserCredentialRepository userCredentials) {
|
||||
Optional<WebAuthnRelyingPartyOperations> webauthnOperationsBean = getBeanOrNull(
|
||||
WebAuthnRelyingPartyOperations.class);
|
||||
return webauthnOperationsBean.orElseGet(() -> new Webauthn4JRelyingPartyOperations(userEntities,
|
||||
userCredentials, PublicKeyCredentialRpEntity.builder().id(this.rpId).name(this.rpName).build(),
|
||||
this.allowedOrigins));
|
||||
if (webauthnOperationsBean.isPresent()) {
|
||||
return webauthnOperationsBean.get();
|
||||
}
|
||||
String rpName = (this.rpName != null) ? this.rpName : this.rpId;
|
||||
return new Webauthn4JRelyingPartyOperations(userEntities, userCredentials,
|
||||
PublicKeyCredentialRpEntity.builder().id(this.rpId).name(rpName).build(), this.allowedOrigins);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+19
-56
@@ -38,7 +38,6 @@ import org.springframework.core.ResolvableType;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
@@ -57,7 +56,6 @@ import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationC
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
|
||||
import org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizedClientRefreshedEventListener;
|
||||
import org.springframework.security.oauth2.client.oidc.session.InMemoryOidcSessionRegistry;
|
||||
import org.springframework.security.oauth2.client.oidc.session.OidcSessionInformation;
|
||||
import org.springframework.security.oauth2.client.oidc.session.OidcSessionRegistry;
|
||||
@@ -70,7 +68,6 @@ import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
||||
import org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository;
|
||||
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
|
||||
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||
@@ -84,7 +81,6 @@ import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.PortResolver;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
@@ -95,6 +91,7 @@ import org.springframework.security.web.context.SecurityContextRepository;
|
||||
import org.springframework.security.web.csrf.CsrfToken;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||
@@ -364,8 +361,6 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
public void init(B http) throws Exception {
|
||||
OAuth2LoginAuthenticationFilter authenticationFilter = new OAuth2LoginAuthenticationFilter(
|
||||
this.getClientRegistrationRepository(), this.getAuthorizedClientRepository(), this.loginProcessingUrl);
|
||||
RequestMatcher processUri = RequestMatcherFactory.matcher(this.loginProcessingUrl);
|
||||
authenticationFilter.setRequiresAuthenticationRequestMatcher(processUri);
|
||||
authenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
|
||||
if (this.securityContextRepository != null) {
|
||||
authenticationFilter.setSecurityContextRepository(this.securityContextRepository);
|
||||
@@ -406,24 +401,14 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService = getOidcUserService();
|
||||
OidcAuthorizationCodeAuthenticationProvider oidcAuthorizationCodeAuthenticationProvider = new OidcAuthorizationCodeAuthenticationProvider(
|
||||
accessTokenResponseClient, oidcUserService);
|
||||
OidcAuthorizedClientRefreshedEventListener oidcAuthorizedClientRefreshedEventListener = new OidcAuthorizedClientRefreshedEventListener();
|
||||
oidcAuthorizedClientRefreshedEventListener.setUserService(oidcUserService);
|
||||
oidcAuthorizedClientRefreshedEventListener
|
||||
.setApplicationEventPublisher(http.getSharedObject(ApplicationContext.class));
|
||||
|
||||
JwtDecoderFactory<ClientRegistration> jwtDecoderFactory = this.getJwtDecoderFactoryBean();
|
||||
if (jwtDecoderFactory != null) {
|
||||
oidcAuthorizationCodeAuthenticationProvider.setJwtDecoderFactory(jwtDecoderFactory);
|
||||
oidcAuthorizedClientRefreshedEventListener.setJwtDecoderFactory(jwtDecoderFactory);
|
||||
}
|
||||
if (userAuthoritiesMapper != null) {
|
||||
oidcAuthorizationCodeAuthenticationProvider.setAuthoritiesMapper(userAuthoritiesMapper);
|
||||
oidcAuthorizedClientRefreshedEventListener.setAuthoritiesMapper(userAuthoritiesMapper);
|
||||
}
|
||||
http.authenticationProvider(this.postProcess(oidcAuthorizationCodeAuthenticationProvider));
|
||||
|
||||
registerDelegateApplicationListener(this.postProcess(oidcAuthorizedClientRefreshedEventListener));
|
||||
configureOidcUserRefreshedEventListener(http);
|
||||
}
|
||||
else {
|
||||
http.authenticationProvider(new OidcAuthenticationRequestChecker());
|
||||
@@ -433,8 +418,19 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
|
||||
@Override
|
||||
public void configure(B http) throws Exception {
|
||||
OAuth2AuthorizationRequestRedirectFilter authorizationRequestFilter = new OAuth2AuthorizationRequestRedirectFilter(
|
||||
getAuthorizationRequestResolver());
|
||||
OAuth2AuthorizationRequestRedirectFilter authorizationRequestFilter;
|
||||
if (this.authorizationEndpointConfig.authorizationRequestResolver != null) {
|
||||
authorizationRequestFilter = new OAuth2AuthorizationRequestRedirectFilter(
|
||||
this.authorizationEndpointConfig.authorizationRequestResolver);
|
||||
}
|
||||
else {
|
||||
String authorizationRequestBaseUri = this.authorizationEndpointConfig.authorizationRequestBaseUri;
|
||||
if (authorizationRequestBaseUri == null) {
|
||||
authorizationRequestBaseUri = OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI;
|
||||
}
|
||||
authorizationRequestFilter = new OAuth2AuthorizationRequestRedirectFilter(
|
||||
this.getClientRegistrationRepository(), authorizationRequestBaseUri);
|
||||
}
|
||||
if (this.authorizationEndpointConfig.authorizationRequestRepository != null) {
|
||||
authorizationRequestFilter
|
||||
.setAuthorizationRequestRepository(this.authorizationEndpointConfig.authorizationRequestRepository);
|
||||
@@ -450,8 +446,7 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
http.addFilter(this.postProcess(authorizationRequestFilter));
|
||||
OAuth2LoginAuthenticationFilter authenticationFilter = this.getAuthenticationFilter();
|
||||
if (this.redirectionEndpointConfig.authorizationResponseBaseUri != null) {
|
||||
authenticationFilter.setRequiresAuthenticationRequestMatcher(
|
||||
RequestMatcherFactory.matcher(this.redirectionEndpointConfig.authorizationResponseBaseUri));
|
||||
authenticationFilter.setFilterProcessesUrl(this.redirectionEndpointConfig.authorizationResponseBaseUri);
|
||||
}
|
||||
if (this.authorizationEndpointConfig.authorizationRequestRepository != null) {
|
||||
authenticationFilter
|
||||
@@ -463,24 +458,7 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
|
||||
@Override
|
||||
protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
|
||||
return RequestMatcherFactory.matcher(loginProcessingUrl);
|
||||
}
|
||||
|
||||
private OAuth2AuthorizationRequestResolver getAuthorizationRequestResolver() {
|
||||
if (this.authorizationEndpointConfig.authorizationRequestResolver != null) {
|
||||
return this.authorizationEndpointConfig.authorizationRequestResolver;
|
||||
}
|
||||
ClientRegistrationRepository clientRegistrationRepository = this.getClientRegistrationRepository();
|
||||
ResolvableType resolvableType = ResolvableType.forClass(OAuth2AuthorizationRequestResolver.class);
|
||||
OAuth2AuthorizationRequestResolver bean = getBeanOrNull(resolvableType);
|
||||
if (bean != null) {
|
||||
return bean;
|
||||
}
|
||||
String authorizationRequestBaseUri = this.authorizationEndpointConfig.authorizationRequestBaseUri;
|
||||
if (authorizationRequestBaseUri == null) {
|
||||
authorizationRequestBaseUri = OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI;
|
||||
}
|
||||
return new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository, authorizationRequestBaseUri);
|
||||
return new AntPathRequestMatcher(loginProcessingUrl);
|
||||
}
|
||||
|
||||
private ClientRegistrationRepository getClientRegistrationRepository() {
|
||||
@@ -601,8 +579,8 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
}
|
||||
|
||||
private AuthenticationEntryPoint getLoginEntryPoint(B http, String providerLoginPage) {
|
||||
RequestMatcher loginPageMatcher = RequestMatcherFactory.matcher(this.getLoginPage());
|
||||
RequestMatcher faviconMatcher = RequestMatcherFactory.matcher("/favicon.ico");
|
||||
RequestMatcher loginPageMatcher = new AntPathRequestMatcher(this.getLoginPage());
|
||||
RequestMatcher faviconMatcher = new AntPathRequestMatcher("/favicon.ico");
|
||||
RequestMatcher defaultEntryPointMatcher = this.getAuthenticationEntryPointMatcher(http);
|
||||
RequestMatcher defaultLoginPageMatcher = new AndRequestMatcher(
|
||||
new OrRequestMatcher(loginPageMatcher, faviconMatcher), defaultEntryPointMatcher);
|
||||
@@ -610,13 +588,8 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
|
||||
RequestMatcher formLoginNotEnabled = getFormLoginNotEnabledRequestMatcher(http);
|
||||
LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints = new LinkedHashMap<>();
|
||||
LoginUrlAuthenticationEntryPoint loginUrlEntryPoint = new LoginUrlAuthenticationEntryPoint(providerLoginPage);
|
||||
PortResolver portResolver = getBeanOrNull(ResolvableType.forClass(PortResolver.class));
|
||||
if (portResolver != null) {
|
||||
loginUrlEntryPoint.setPortResolver(portResolver);
|
||||
}
|
||||
entryPoints.put(new AndRequestMatcher(notXRequestedWith, new NegatedRequestMatcher(defaultLoginPageMatcher),
|
||||
formLoginNotEnabled), loginUrlEntryPoint);
|
||||
formLoginNotEnabled), new LoginUrlAuthenticationEntryPoint(providerLoginPage));
|
||||
DelegatingAuthenticationEntryPoint loginEntryPoint = new DelegatingAuthenticationEntryPoint(entryPoints);
|
||||
loginEntryPoint.setDefaultEntryPoint(this.getAuthenticationEntryPoint());
|
||||
return loginEntryPoint;
|
||||
@@ -652,16 +625,6 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
registerDelegateApplicationListener(listener);
|
||||
}
|
||||
|
||||
private void configureOidcUserRefreshedEventListener(B http) {
|
||||
OidcUserRefreshedEventListener oidcUserRefreshedEventListener = new OidcUserRefreshedEventListener();
|
||||
oidcUserRefreshedEventListener.setSecurityContextHolderStrategy(this.getSecurityContextHolderStrategy());
|
||||
SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
|
||||
if (securityContextRepository != null) {
|
||||
oidcUserRefreshedEventListener.setSecurityContextRepository(securityContextRepository);
|
||||
}
|
||||
registerDelegateApplicationListener(oidcUserRefreshedEventListener);
|
||||
}
|
||||
|
||||
private void registerDelegateApplicationListener(ApplicationListener<?> delegate) {
|
||||
DelegatingApplicationListener delegating = getBeanOrNull(
|
||||
ResolvableType.forType(DelegatingApplicationListener.class));
|
||||
|
||||
+1
-1
@@ -127,7 +127,7 @@ public final class OidcBackChannelLogoutHandler implements LogoutHandler {
|
||||
String computeLogoutEndpoint(HttpServletRequest request, OidcBackChannelLogoutAuthentication token) {
|
||||
// @formatter:off
|
||||
UriComponents uriComponents = UriComponentsBuilder
|
||||
.fromUriString(UrlUtils.buildFullRequestUrl(request))
|
||||
.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
|
||||
.replacePath(request.getContextPath())
|
||||
.replaceQuery(null)
|
||||
.fragment(null)
|
||||
|
||||
+2
-4
@@ -20,14 +20,13 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -46,8 +45,7 @@ final class OidcLogoutAuthenticationConverter implements AuthenticationConverter
|
||||
|
||||
private final ClientRegistrationRepository clientRegistrationRepository;
|
||||
|
||||
private RequestMatcher requestMatcher = PathPatternRequestMatcher.withDefaults()
|
||||
.matcher(HttpMethod.POST, DEFAULT_LOGOUT_URI);
|
||||
private RequestMatcher requestMatcher = new AntPathRequestMatcher(DEFAULT_LOGOUT_URI, "POST");
|
||||
|
||||
OidcLogoutAuthenticationConverter(ClientRegistrationRepository clientRegistrationRepository) {
|
||||
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
|
||||
|
||||
-86
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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
|
||||
*
|
||||
* https://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.oauth2.client;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.oauth2.client.oidc.authentication.event.OidcUserRefreshedEvent;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.web.context.SecurityContextRepository;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
/**
|
||||
* An {@link ApplicationListener} that listens for events of type
|
||||
* {@link OidcUserRefreshedEvent} and refreshes the {@link SecurityContext}.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @since 6.5
|
||||
* @see org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizedClientRefreshedEventListener
|
||||
*/
|
||||
final class OidcUserRefreshedEventListener implements ApplicationListener<OidcUserRefreshedEvent> {
|
||||
|
||||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
||||
.getContextHolderStrategy();
|
||||
|
||||
private SecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository();
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(OidcUserRefreshedEvent event) {
|
||||
SecurityContext securityContext = this.securityContextHolderStrategy.createEmptyContext();
|
||||
securityContext.setAuthentication(event.getAuthentication());
|
||||
this.securityContextHolderStrategy.setContext(securityContext);
|
||||
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
if (!(requestAttributes instanceof ServletRequestAttributes servletRequestAttributes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
HttpServletRequest request = servletRequestAttributes.getRequest();
|
||||
HttpServletResponse response = servletRequestAttributes.getResponse();
|
||||
this.securityContextRepository.saveContext(securityContext, request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
|
||||
* the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
|
||||
* @param securityContextHolderStrategy the {@link SecurityContextHolderStrategy} to
|
||||
* use
|
||||
*/
|
||||
void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
|
||||
Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
|
||||
this.securityContextHolderStrategy = securityContextHolderStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} upon
|
||||
* receiving an {@link OidcUserRefreshedEvent}.
|
||||
* @param securityContextRepository the {@link SecurityContextRepository} to use
|
||||
*/
|
||||
void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {
|
||||
Assert.notNull(securityContextRepository, "securityContextRepository cannot be null");
|
||||
this.securityContextRepository = securityContextRepository;
|
||||
}
|
||||
|
||||
}
|
||||
-239
@@ -1,239 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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
|
||||
*
|
||||
* https://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.oauth2.server.resource;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationManagerResolver;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.DPoPAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.DPoPAuthenticationToken;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.AuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
/**
|
||||
* An {@link AbstractHttpConfigurer} for OAuth 2.0 Demonstrating Proof of Possession
|
||||
* (DPoP) support.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 6.5
|
||||
* @see DPoPAuthenticationProvider
|
||||
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc9449">RFC 9449
|
||||
* OAuth 2.0 Demonstrating Proof of Possession (DPoP)</a>
|
||||
*/
|
||||
final class DPoPAuthenticationConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
extends AbstractHttpConfigurer<DPoPAuthenticationConfigurer<B>, B> {
|
||||
|
||||
private RequestMatcher requestMatcher;
|
||||
|
||||
private AuthenticationConverter authenticationConverter;
|
||||
|
||||
private AuthenticationSuccessHandler authenticationSuccessHandler;
|
||||
|
||||
private AuthenticationFailureHandler authenticationFailureHandler;
|
||||
|
||||
@Override
|
||||
public void configure(B http) {
|
||||
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
|
||||
http.authenticationProvider(new DPoPAuthenticationProvider(getTokenAuthenticationManager(http)));
|
||||
AuthenticationFilter authenticationFilter = new AuthenticationFilter(authenticationManager,
|
||||
getAuthenticationConverter());
|
||||
authenticationFilter.setRequestMatcher(getRequestMatcher());
|
||||
authenticationFilter.setSuccessHandler(getAuthenticationSuccessHandler());
|
||||
authenticationFilter.setFailureHandler(getAuthenticationFailureHandler());
|
||||
authenticationFilter.setSecurityContextRepository(new RequestAttributeSecurityContextRepository());
|
||||
authenticationFilter = postProcess(authenticationFilter);
|
||||
http.addFilter(authenticationFilter);
|
||||
}
|
||||
|
||||
private AuthenticationManager getTokenAuthenticationManager(B http) {
|
||||
OAuth2ResourceServerConfigurer<B> resourceServerConfigurer = http
|
||||
.getConfigurer(OAuth2ResourceServerConfigurer.class);
|
||||
final AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver = resourceServerConfigurer
|
||||
.getAuthenticationManagerResolver();
|
||||
if (authenticationManagerResolver == null) {
|
||||
return resourceServerConfigurer.getAuthenticationManager(http);
|
||||
}
|
||||
return (authentication) -> {
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
|
||||
AuthenticationManager authenticationManager = authenticationManagerResolver
|
||||
.resolve(servletRequestAttributes.getRequest());
|
||||
return authenticationManager.authenticate(authentication);
|
||||
};
|
||||
}
|
||||
|
||||
private RequestMatcher getRequestMatcher() {
|
||||
if (this.requestMatcher == null) {
|
||||
this.requestMatcher = new DPoPRequestMatcher();
|
||||
}
|
||||
return this.requestMatcher;
|
||||
}
|
||||
|
||||
private AuthenticationConverter getAuthenticationConverter() {
|
||||
if (this.authenticationConverter == null) {
|
||||
this.authenticationConverter = new DPoPAuthenticationConverter();
|
||||
}
|
||||
return this.authenticationConverter;
|
||||
}
|
||||
|
||||
private AuthenticationSuccessHandler getAuthenticationSuccessHandler() {
|
||||
if (this.authenticationSuccessHandler == null) {
|
||||
this.authenticationSuccessHandler = (request, response, authentication) -> {
|
||||
// No-op - will continue on filter chain
|
||||
};
|
||||
}
|
||||
return this.authenticationSuccessHandler;
|
||||
}
|
||||
|
||||
private AuthenticationFailureHandler getAuthenticationFailureHandler() {
|
||||
if (this.authenticationFailureHandler == null) {
|
||||
this.authenticationFailureHandler = new AuthenticationEntryPointFailureHandler(
|
||||
new DPoPAuthenticationEntryPoint());
|
||||
}
|
||||
return this.authenticationFailureHandler;
|
||||
}
|
||||
|
||||
private static final class DPoPRequestMatcher implements RequestMatcher {
|
||||
|
||||
@Override
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
|
||||
if (!StringUtils.hasText(authorization)) {
|
||||
return false;
|
||||
}
|
||||
return StringUtils.startsWithIgnoreCase(authorization, OAuth2AccessToken.TokenType.DPOP.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class DPoPAuthenticationConverter implements AuthenticationConverter {
|
||||
|
||||
private static final Pattern AUTHORIZATION_PATTERN = Pattern.compile("^DPoP (?<token>[a-zA-Z0-9-._~+/]+=*)$",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
@Override
|
||||
public Authentication convert(HttpServletRequest request) {
|
||||
List<String> authorizationList = Collections.list(request.getHeaders(HttpHeaders.AUTHORIZATION));
|
||||
if (CollectionUtils.isEmpty(authorizationList)) {
|
||||
return null;
|
||||
}
|
||||
if (authorizationList.size() != 1) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST,
|
||||
"Found multiple Authorization headers.", null);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
String authorization = authorizationList.get(0);
|
||||
if (!StringUtils.startsWithIgnoreCase(authorization, OAuth2AccessToken.TokenType.DPOP.getValue())) {
|
||||
return null;
|
||||
}
|
||||
Matcher matcher = AUTHORIZATION_PATTERN.matcher(authorization);
|
||||
if (!matcher.matches()) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, "DPoP access token is malformed.",
|
||||
null);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
String accessToken = matcher.group("token");
|
||||
List<String> dPoPProofList = Collections
|
||||
.list(request.getHeaders(OAuth2AccessToken.TokenType.DPOP.getValue()));
|
||||
if (CollectionUtils.isEmpty(dPoPProofList) || dPoPProofList.size() != 1) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST,
|
||||
"DPoP proof is missing or invalid.", null);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
String dPoPProof = dPoPProofList.get(0);
|
||||
return new DPoPAuthenticationToken(accessToken, dPoPProof, request.getMethod(),
|
||||
request.getRequestURL().toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class DPoPAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response,
|
||||
AuthenticationException authenticationException) {
|
||||
Map<String, String> parameters = new LinkedHashMap<>();
|
||||
if (authenticationException instanceof OAuth2AuthenticationException oauth2AuthenticationException) {
|
||||
OAuth2Error error = oauth2AuthenticationException.getError();
|
||||
parameters.put(OAuth2ParameterNames.ERROR, error.getErrorCode());
|
||||
if (StringUtils.hasText(error.getDescription())) {
|
||||
parameters.put(OAuth2ParameterNames.ERROR_DESCRIPTION, error.getDescription());
|
||||
}
|
||||
if (StringUtils.hasText(error.getUri())) {
|
||||
parameters.put(OAuth2ParameterNames.ERROR_URI, error.getUri());
|
||||
}
|
||||
}
|
||||
parameters.put("algs",
|
||||
JwsAlgorithms.RS256 + " " + JwsAlgorithms.RS384 + " " + JwsAlgorithms.RS512 + " "
|
||||
+ JwsAlgorithms.PS256 + " " + JwsAlgorithms.PS384 + " " + JwsAlgorithms.PS512 + " "
|
||||
+ JwsAlgorithms.ES256 + " " + JwsAlgorithms.ES384 + " " + JwsAlgorithms.ES512);
|
||||
String wwwAuthenticate = toWWWAuthenticateHeader(parameters);
|
||||
response.addHeader(HttpHeaders.WWW_AUTHENTICATE, wwwAuthenticate);
|
||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||
}
|
||||
|
||||
private static String toWWWAuthenticateHeader(Map<String, String> parameters) {
|
||||
StringBuilder wwwAuthenticate = new StringBuilder();
|
||||
wwwAuthenticate.append(OAuth2AccessToken.TokenType.DPOP.getValue());
|
||||
if (!parameters.isEmpty()) {
|
||||
wwwAuthenticate.append(" ");
|
||||
int i = 0;
|
||||
for (Map.Entry<String, String> entry : parameters.entrySet()) {
|
||||
wwwAuthenticate.append(entry.getKey()).append("=\"").append(entry.getValue()).append("\"");
|
||||
if (i++ != parameters.size() - 1) {
|
||||
wwwAuthenticate.append(", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
return wwwAuthenticate.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-17
@@ -64,7 +64,6 @@ 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.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.accept.ContentNegotiationStrategy;
|
||||
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
||||
|
||||
@@ -148,14 +147,6 @@ import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
||||
public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
extends AbstractHttpConfigurer<OAuth2ResourceServerConfigurer<H>, H> {
|
||||
|
||||
private static final boolean dPoPAuthenticationAvailable;
|
||||
|
||||
static {
|
||||
ClassLoader classLoader = OAuth2ResourceServerConfigurer.class.getClassLoader();
|
||||
dPoPAuthenticationAvailable = ClassUtils
|
||||
.isPresent("org.springframework.security.oauth2.jwt.DPoPProofJwtDecoderFactory", classLoader);
|
||||
}
|
||||
|
||||
private static final RequestHeaderRequestMatcher X_REQUESTED_WITH = new RequestHeaderRequestMatcher(
|
||||
"X-Requested-With", "XMLHttpRequest");
|
||||
|
||||
@@ -292,10 +283,6 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
||||
filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
|
||||
filter = postProcess(filter);
|
||||
http.addFilter(filter);
|
||||
if (dPoPAuthenticationAvailable) {
|
||||
DPoPAuthenticationConfigurer<H> dPoPAuthenticationConfigurer = new DPoPAuthenticationConfigurer<>();
|
||||
dPoPAuthenticationConfigurer.configure(http);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateConfiguration() {
|
||||
@@ -373,10 +360,6 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
||||
return http.getSharedObject(AuthenticationManager.class);
|
||||
}
|
||||
|
||||
AuthenticationManagerResolver<HttpServletRequest> getAuthenticationManagerResolver() {
|
||||
return this.authenticationManagerResolver;
|
||||
}
|
||||
|
||||
BearerTokenResolver getBearerTokenResolver() {
|
||||
if (this.bearerTokenResolver == null) {
|
||||
if (this.context.getBeanNamesForType(BearerTokenResolver.class).length > 0) {
|
||||
|
||||
+84
-139
@@ -23,156 +23,125 @@ import jakarta.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.authentication.ott.GenerateOneTimeTokenRequest;
|
||||
import org.springframework.security.authentication.ott.InMemoryOneTimeTokenService;
|
||||
import org.springframework.security.authentication.ott.OneTimeToken;
|
||||
import org.springframework.security.authentication.ott.OneTimeTokenAuthenticationProvider;
|
||||
import org.springframework.security.authentication.ott.OneTimeTokenService;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
|
||||
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.configurers.AbstractAuthenticationFilterConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.AuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.ott.DefaultGenerateOneTimeTokenRequestResolver;
|
||||
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenFilter;
|
||||
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenRequestResolver;
|
||||
import org.springframework.security.web.authentication.ott.OneTimeTokenAuthenticationConverter;
|
||||
import org.springframework.security.web.authentication.ott.OneTimeTokenAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.security.web.authentication.ui.DefaultOneTimeTokenSubmitPageGeneratingFilter;
|
||||
import org.springframework.security.web.authentication.ui.DefaultResourcesFilter;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.web.context.SecurityContextRepository;
|
||||
import org.springframework.security.web.csrf.CsrfToken;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* An {@link AbstractHttpConfigurer} for One-Time Token Login.
|
||||
*
|
||||
* <p>
|
||||
* One-Time Token Login provides an application with the capability to have users log in
|
||||
* by obtaining a single-use token out of band, for example through email.
|
||||
*
|
||||
* <p>
|
||||
* Defaults are provided for all configuration options, with the only required
|
||||
* configuration being
|
||||
* {@link #tokenGenerationSuccessHandler(OneTimeTokenGenerationSuccessHandler)}.
|
||||
* Alternatively, a {@link OneTimeTokenGenerationSuccessHandler} {@code @Bean} may be
|
||||
* registered instead.
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following {@code Filter}s are populated:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link DefaultOneTimeTokenSubmitPageGeneratingFilter}</li>
|
||||
* <li>{@link GenerateOneTimeTokenFilter}</li>
|
||||
* <li>{@link OneTimeTokenAuthenticationFilter}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* The following shared objects are used:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link DefaultLoginPageGeneratingFilter} - if {@link #loginPage(String)} is not
|
||||
* configured and {@code DefaultLoginPageGeneratingFilter} is available, then a default
|
||||
* login page will be made available</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Marcus Da Coregio
|
||||
* @author Daniel Garnier-Moiroux
|
||||
* @since 6.4
|
||||
* @see HttpSecurity#oneTimeTokenLogin(Customizer)
|
||||
* @see DefaultOneTimeTokenSubmitPageGeneratingFilter
|
||||
* @see GenerateOneTimeTokenFilter
|
||||
* @see OneTimeTokenAuthenticationFilter
|
||||
* @see AbstractAuthenticationFilterConfigurer
|
||||
*/
|
||||
public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
|
||||
AbstractAuthenticationFilterConfigurer<H, OneTimeTokenLoginConfigurer<H>, OneTimeTokenAuthenticationFilter> {
|
||||
import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
|
||||
|
||||
public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
extends AbstractHttpConfigurer<OneTimeTokenLoginConfigurer<H>, H> {
|
||||
|
||||
private final ApplicationContext context;
|
||||
|
||||
private OneTimeTokenService oneTimeTokenService;
|
||||
|
||||
private String defaultSubmitPageUrl = DefaultOneTimeTokenSubmitPageGeneratingFilter.DEFAULT_SUBMIT_PAGE_URL;
|
||||
private AuthenticationConverter authenticationConverter = new OneTimeTokenAuthenticationConverter();
|
||||
|
||||
private AuthenticationFailureHandler authenticationFailureHandler;
|
||||
|
||||
private AuthenticationSuccessHandler authenticationSuccessHandler = new SavedRequestAwareAuthenticationSuccessHandler();
|
||||
|
||||
private String defaultSubmitPageUrl = "/login/ott";
|
||||
|
||||
private boolean submitPageEnabled = true;
|
||||
|
||||
private String loginProcessingUrl = OneTimeTokenAuthenticationFilter.DEFAULT_LOGIN_PROCESSING_URL;
|
||||
private String loginProcessingUrl = "/login/ott";
|
||||
|
||||
private String tokenGeneratingUrl = GenerateOneTimeTokenFilter.DEFAULT_GENERATE_URL;
|
||||
private String tokenGeneratingUrl = "/ott/generate";
|
||||
|
||||
private OneTimeTokenGenerationSuccessHandler oneTimeTokenGenerationSuccessHandler;
|
||||
|
||||
private AuthenticationProvider authenticationProvider;
|
||||
|
||||
private GenerateOneTimeTokenRequestResolver requestResolver;
|
||||
|
||||
public OneTimeTokenLoginConfigurer(ApplicationContext context) {
|
||||
super(new OneTimeTokenAuthenticationFilter(), OneTimeTokenAuthenticationFilter.DEFAULT_LOGIN_PROCESSING_URL);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(H http) throws Exception {
|
||||
super.init(http);
|
||||
AuthenticationProvider authenticationProvider = getAuthenticationProvider();
|
||||
public void init(H http) {
|
||||
AuthenticationProvider authenticationProvider = getAuthenticationProvider(http);
|
||||
http.authenticationProvider(postProcess(authenticationProvider));
|
||||
intiDefaultLoginFilter(http);
|
||||
configureDefaultLoginPage(http);
|
||||
}
|
||||
|
||||
private void intiDefaultLoginFilter(H http) {
|
||||
private void configureDefaultLoginPage(H http) {
|
||||
DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http
|
||||
.getSharedObject(DefaultLoginPageGeneratingFilter.class);
|
||||
if (loginPageGeneratingFilter == null || isCustomLoginPage()) {
|
||||
if (loginPageGeneratingFilter == null) {
|
||||
return;
|
||||
}
|
||||
loginPageGeneratingFilter.setOneTimeTokenEnabled(true);
|
||||
loginPageGeneratingFilter.setOneTimeTokenGenerationUrl(this.tokenGeneratingUrl);
|
||||
|
||||
if (!StringUtils.hasText(loginPageGeneratingFilter.getLoginPageUrl())) {
|
||||
loginPageGeneratingFilter.setLoginPageUrl(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL);
|
||||
loginPageGeneratingFilter.setFailureUrl(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL + "?"
|
||||
+ DefaultLoginPageGeneratingFilter.ERROR_PARAMETER_NAME);
|
||||
loginPageGeneratingFilter
|
||||
.setLogoutSuccessUrl(DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL + "?logout");
|
||||
if (this.authenticationFailureHandler == null
|
||||
&& StringUtils.hasText(loginPageGeneratingFilter.getLoginPageUrl())) {
|
||||
this.authenticationFailureHandler = new SimpleUrlAuthenticationFailureHandler(
|
||||
loginPageGeneratingFilter.getLoginPageUrl() + "?error");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(H http) throws Exception {
|
||||
super.configure(http);
|
||||
public void configure(H http) {
|
||||
configureSubmitPage(http);
|
||||
configureOttGenerateFilter(http);
|
||||
configureOttAuthenticationFilter(http);
|
||||
}
|
||||
|
||||
private void configureOttAuthenticationFilter(H http) {
|
||||
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
|
||||
AuthenticationFilter oneTimeTokenAuthenticationFilter = new AuthenticationFilter(authenticationManager,
|
||||
this.authenticationConverter);
|
||||
oneTimeTokenAuthenticationFilter.setSecurityContextRepository(getSecurityContextRepository(http));
|
||||
oneTimeTokenAuthenticationFilter.setRequestMatcher(antMatcher(HttpMethod.POST, this.loginProcessingUrl));
|
||||
oneTimeTokenAuthenticationFilter.setFailureHandler(getAuthenticationFailureHandler());
|
||||
oneTimeTokenAuthenticationFilter.setSuccessHandler(this.authenticationSuccessHandler);
|
||||
http.addFilter(postProcess(oneTimeTokenAuthenticationFilter));
|
||||
}
|
||||
|
||||
private SecurityContextRepository getSecurityContextRepository(H http) {
|
||||
SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
|
||||
if (securityContextRepository != null) {
|
||||
return securityContextRepository;
|
||||
}
|
||||
return new HttpSessionSecurityContextRepository();
|
||||
}
|
||||
|
||||
private void configureOttGenerateFilter(H http) {
|
||||
GenerateOneTimeTokenFilter generateFilter = new GenerateOneTimeTokenFilter(getOneTimeTokenService(),
|
||||
getOneTimeTokenGenerationSuccessHandler());
|
||||
generateFilter.setRequestMatcher(RequestMatcherFactory.matcher(HttpMethod.POST, this.tokenGeneratingUrl));
|
||||
generateFilter.setRequestResolver(getGenerateRequestResolver());
|
||||
GenerateOneTimeTokenFilter generateFilter = new GenerateOneTimeTokenFilter(getOneTimeTokenService(http),
|
||||
getOneTimeTokenGenerationSuccessHandler(http));
|
||||
generateFilter.setRequestMatcher(antMatcher(HttpMethod.POST, this.tokenGeneratingUrl));
|
||||
http.addFilter(postProcess(generateFilter));
|
||||
http.addFilter(DefaultResourcesFilter.css());
|
||||
}
|
||||
|
||||
private OneTimeTokenGenerationSuccessHandler getOneTimeTokenGenerationSuccessHandler() {
|
||||
private OneTimeTokenGenerationSuccessHandler getOneTimeTokenGenerationSuccessHandler(H http) {
|
||||
if (this.oneTimeTokenGenerationSuccessHandler == null) {
|
||||
this.oneTimeTokenGenerationSuccessHandler = this.context
|
||||
.getBeanProvider(OneTimeTokenGenerationSuccessHandler.class)
|
||||
.getIfUnique();
|
||||
this.oneTimeTokenGenerationSuccessHandler = getBeanOrNull(http, OneTimeTokenGenerationSuccessHandler.class);
|
||||
}
|
||||
if (this.oneTimeTokenGenerationSuccessHandler == null) {
|
||||
throw new IllegalStateException("""
|
||||
@@ -189,26 +158,21 @@ public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
}
|
||||
DefaultOneTimeTokenSubmitPageGeneratingFilter submitPage = new DefaultOneTimeTokenSubmitPageGeneratingFilter();
|
||||
submitPage.setResolveHiddenInputs(this::hiddenInputs);
|
||||
submitPage.setRequestMatcher(RequestMatcherFactory.matcher(HttpMethod.GET, this.defaultSubmitPageUrl));
|
||||
submitPage.setLoginProcessingUrl(this.getLoginProcessingUrl());
|
||||
submitPage.setRequestMatcher(antMatcher(HttpMethod.GET, this.defaultSubmitPageUrl));
|
||||
submitPage.setLoginProcessingUrl(this.loginProcessingUrl);
|
||||
http.addFilter(postProcess(submitPage));
|
||||
}
|
||||
|
||||
private AuthenticationProvider getAuthenticationProvider() {
|
||||
private AuthenticationProvider getAuthenticationProvider(H http) {
|
||||
if (this.authenticationProvider != null) {
|
||||
return this.authenticationProvider;
|
||||
}
|
||||
UserDetailsService userDetailsService = this.context.getBean(UserDetailsService.class);
|
||||
this.authenticationProvider = new OneTimeTokenAuthenticationProvider(getOneTimeTokenService(),
|
||||
UserDetailsService userDetailsService = getContext().getBean(UserDetailsService.class);
|
||||
this.authenticationProvider = new OneTimeTokenAuthenticationProvider(getOneTimeTokenService(http),
|
||||
userDetailsService);
|
||||
return this.authenticationProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
|
||||
return RequestMatcherFactory.matcher(HttpMethod.POST, loginProcessingUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the {@link AuthenticationProvider} to use when authenticating the user.
|
||||
* @param authenticationProvider
|
||||
@@ -246,25 +210,14 @@ public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
* Only POST requests are processed, for that reason make sure that you pass a valid
|
||||
* CSRF token if CSRF protection is enabled.
|
||||
* @param loginProcessingUrl
|
||||
* @see HttpSecurity#csrf(Customizer)
|
||||
* @see org.springframework.security.config.annotation.web.builders.HttpSecurity#csrf(Customizer)
|
||||
*/
|
||||
public OneTimeTokenLoginConfigurer<H> loginProcessingUrl(String loginProcessingUrl) {
|
||||
Assert.hasText(loginProcessingUrl, "loginProcessingUrl cannot be null or empty");
|
||||
super.loginProcessingUrl(loginProcessingUrl);
|
||||
this.loginProcessingUrl = loginProcessingUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the URL to send users to if login is required. If used with
|
||||
* {@link EnableWebSecurity} a default login page will be generated when this
|
||||
* attribute is not specified.
|
||||
* @param loginPage
|
||||
*/
|
||||
@Override
|
||||
public OneTimeTokenLoginConfigurer<H> loginPage(String loginPage) {
|
||||
return super.loginPage(loginPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures whether the default one-time token submit page should be shown. This
|
||||
* will prevent the {@link DefaultOneTimeTokenSubmitPageGeneratingFilter} to be
|
||||
@@ -309,7 +262,7 @@ public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
*/
|
||||
public OneTimeTokenLoginConfigurer<H> authenticationConverter(AuthenticationConverter authenticationConverter) {
|
||||
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
|
||||
this.getAuthenticationFilter().setAuthenticationConverter(authenticationConverter);
|
||||
this.authenticationConverter = authenticationConverter;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -319,13 +272,11 @@ public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
* {@link SimpleUrlAuthenticationFailureHandler}
|
||||
* @param authenticationFailureHandler the {@link AuthenticationFailureHandler} to use
|
||||
* when authentication fails.
|
||||
* @deprecated Use {@link #failureHandler(AuthenticationFailureHandler)} instead
|
||||
*/
|
||||
@Deprecated(since = "6.5")
|
||||
public OneTimeTokenLoginConfigurer<H> authenticationFailureHandler(
|
||||
AuthenticationFailureHandler authenticationFailureHandler) {
|
||||
Assert.notNull(authenticationFailureHandler, "authenticationFailureHandler cannot be null");
|
||||
super.failureHandler(authenticationFailureHandler);
|
||||
this.authenticationFailureHandler = authenticationFailureHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -334,57 +285,51 @@ public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
* {@link SavedRequestAwareAuthenticationSuccessHandler} with no additional properties
|
||||
* set.
|
||||
* @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler}.
|
||||
* @deprecated Use {@link #successHandler(AuthenticationSuccessHandler)} instead
|
||||
*/
|
||||
@Deprecated(since = "6.5")
|
||||
public OneTimeTokenLoginConfigurer<H> authenticationSuccessHandler(
|
||||
AuthenticationSuccessHandler authenticationSuccessHandler) {
|
||||
Assert.notNull(authenticationSuccessHandler, "authenticationSuccessHandler cannot be null");
|
||||
super.successHandler(authenticationSuccessHandler);
|
||||
this.authenticationSuccessHandler = authenticationSuccessHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@link GenerateOneTimeTokenRequestResolver} when resolving
|
||||
* {@link GenerateOneTimeTokenRequest} from {@link HttpServletRequest}. By default,
|
||||
* the {@link DefaultGenerateOneTimeTokenRequestResolver} is used.
|
||||
* @param requestResolver the {@link GenerateOneTimeTokenRequestResolver}
|
||||
* @since 6.5
|
||||
*/
|
||||
public OneTimeTokenLoginConfigurer<H> generateRequestResolver(GenerateOneTimeTokenRequestResolver requestResolver) {
|
||||
Assert.notNull(requestResolver, "requestResolver cannot be null");
|
||||
this.requestResolver = requestResolver;
|
||||
return this;
|
||||
}
|
||||
|
||||
private GenerateOneTimeTokenRequestResolver getGenerateRequestResolver() {
|
||||
if (this.requestResolver != null) {
|
||||
return this.requestResolver;
|
||||
private AuthenticationFailureHandler getAuthenticationFailureHandler() {
|
||||
if (this.authenticationFailureHandler != null) {
|
||||
return this.authenticationFailureHandler;
|
||||
}
|
||||
this.requestResolver = this.context.getBeanProvider(GenerateOneTimeTokenRequestResolver.class)
|
||||
.getIfUnique(DefaultGenerateOneTimeTokenRequestResolver::new);
|
||||
return this.requestResolver;
|
||||
this.authenticationFailureHandler = new SimpleUrlAuthenticationFailureHandler("/login?error");
|
||||
return this.authenticationFailureHandler;
|
||||
}
|
||||
|
||||
private OneTimeTokenService getOneTimeTokenService() {
|
||||
private OneTimeTokenService getOneTimeTokenService(H http) {
|
||||
if (this.oneTimeTokenService != null) {
|
||||
return this.oneTimeTokenService;
|
||||
}
|
||||
this.oneTimeTokenService = this.context.getBeanProvider(OneTimeTokenService.class)
|
||||
.getIfUnique(InMemoryOneTimeTokenService::new);
|
||||
OneTimeTokenService bean = getBeanOrNull(http, OneTimeTokenService.class);
|
||||
if (bean != null) {
|
||||
this.oneTimeTokenService = bean;
|
||||
}
|
||||
else {
|
||||
this.oneTimeTokenService = new InMemoryOneTimeTokenService();
|
||||
}
|
||||
return this.oneTimeTokenService;
|
||||
}
|
||||
|
||||
private <C> C getBeanOrNull(H http, Class<C> clazz) {
|
||||
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
|
||||
if (context == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return context.getBeanProvider(clazz).getIfUnique();
|
||||
}
|
||||
|
||||
private Map<String, String> hiddenInputs(HttpServletRequest request) {
|
||||
CsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
|
||||
return (token != null) ? Collections.singletonMap(token.getParameterName(), token.getToken())
|
||||
: Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use this.context instead
|
||||
*/
|
||||
@Deprecated
|
||||
public ApplicationContext getContext() {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
+23
-46
@@ -28,7 +28,6 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
@@ -52,12 +51,12 @@ import org.springframework.security.saml2.provider.service.web.authentication.Op
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.PortResolver;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.ParameterRequestMatcher;
|
||||
@@ -128,11 +127,15 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
|
||||
private String[] authenticationRequestParams = { "registrationId={registrationId}" };
|
||||
|
||||
private RequestMatcher authenticationRequestMatcher;
|
||||
private RequestMatcher authenticationRequestMatcher = RequestMatchers.anyOf(
|
||||
new AntPathRequestMatcher(Saml2AuthenticationRequestResolver.DEFAULT_AUTHENTICATION_REQUEST_URI),
|
||||
new AntPathQueryRequestMatcher(this.authenticationRequestUri, this.authenticationRequestParams));
|
||||
|
||||
private Saml2AuthenticationRequestResolver authenticationRequestResolver;
|
||||
|
||||
private RequestMatcher loginProcessingUrl;
|
||||
private RequestMatcher loginProcessingUrl = RequestMatchers.anyOf(
|
||||
new AntPathRequestMatcher(Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI),
|
||||
new AntPathRequestMatcher("/login/saml2/sso"));
|
||||
|
||||
private RelyingPartyRegistrationRepository relyingPartyRegistrationRepository;
|
||||
|
||||
@@ -235,8 +238,8 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
this.authenticationRequestUri = parts[0];
|
||||
this.authenticationRequestParams = new String[parts.length - 1];
|
||||
System.arraycopy(parts, 1, this.authenticationRequestParams, 0, parts.length - 1);
|
||||
this.authenticationRequestMatcher = new PathQueryRequestMatcher(
|
||||
RequestMatcherFactory.matcher(this.authenticationRequestUri), this.authenticationRequestParams);
|
||||
this.authenticationRequestMatcher = new AntPathQueryRequestMatcher(this.authenticationRequestUri,
|
||||
this.authenticationRequestParams);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -253,13 +256,13 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
@Override
|
||||
public Saml2LoginConfigurer<B> loginProcessingUrl(String loginProcessingUrl) {
|
||||
Assert.hasText(loginProcessingUrl, "loginProcessingUrl cannot be empty");
|
||||
this.loginProcessingUrl = RequestMatcherFactory.matcher(loginProcessingUrl);
|
||||
this.loginProcessingUrl = new AntPathRequestMatcher(loginProcessingUrl);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
|
||||
return RequestMatcherFactory.matcher(loginProcessingUrl);
|
||||
return new AntPathRequestMatcher(loginProcessingUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -281,7 +284,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
relyingPartyRegistrationRepository(http);
|
||||
this.saml2WebSsoAuthenticationFilter = new Saml2WebSsoAuthenticationFilter(getAuthenticationConverter(http));
|
||||
this.saml2WebSsoAuthenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
|
||||
this.saml2WebSsoAuthenticationFilter.setRequiresAuthenticationRequestMatcher(getLoginProcessingEndpoint());
|
||||
this.saml2WebSsoAuthenticationFilter.setRequiresAuthenticationRequestMatcher(this.loginProcessingUrl);
|
||||
setAuthenticationRequestRepository(http, this.saml2WebSsoAuthenticationFilter);
|
||||
setAuthenticationFilter(this.saml2WebSsoAuthenticationFilter);
|
||||
if (StringUtils.hasText(this.loginPage)) {
|
||||
@@ -337,21 +340,16 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
}
|
||||
|
||||
private AuthenticationEntryPoint getLoginEntryPoint(B http, String providerLoginPage) {
|
||||
RequestMatcher loginPageMatcher = RequestMatcherFactory.matcher(this.getLoginPage());
|
||||
RequestMatcher faviconMatcher = RequestMatcherFactory.matcher("/favicon.ico");
|
||||
RequestMatcher loginPageMatcher = new AntPathRequestMatcher(this.getLoginPage());
|
||||
RequestMatcher faviconMatcher = new AntPathRequestMatcher("/favicon.ico");
|
||||
RequestMatcher defaultEntryPointMatcher = this.getAuthenticationEntryPointMatcher(http);
|
||||
RequestMatcher defaultLoginPageMatcher = new AndRequestMatcher(
|
||||
new OrRequestMatcher(loginPageMatcher, faviconMatcher), defaultEntryPointMatcher);
|
||||
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
|
||||
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
|
||||
LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints = new LinkedHashMap<>();
|
||||
LoginUrlAuthenticationEntryPoint loginUrlEntryPoint = new LoginUrlAuthenticationEntryPoint(providerLoginPage);
|
||||
PortResolver portResolver = getBeanOrNull(http, PortResolver.class);
|
||||
if (portResolver != null) {
|
||||
loginUrlEntryPoint.setPortResolver(portResolver);
|
||||
}
|
||||
entryPoints.put(new AndRequestMatcher(notXRequestedWith, new NegatedRequestMatcher(defaultLoginPageMatcher)),
|
||||
loginUrlEntryPoint);
|
||||
new LoginUrlAuthenticationEntryPoint(providerLoginPage));
|
||||
DelegatingAuthenticationEntryPoint loginEntryPoint = new DelegatingAuthenticationEntryPoint(entryPoints);
|
||||
loginEntryPoint.setDefaultEntryPoint(this.getAuthenticationEntryPoint());
|
||||
return loginEntryPoint;
|
||||
@@ -378,38 +376,17 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
if (USE_OPENSAML_5) {
|
||||
OpenSaml5AuthenticationRequestResolver openSamlAuthenticationRequestResolver = new OpenSaml5AuthenticationRequestResolver(
|
||||
relyingPartyRegistrationRepository(http));
|
||||
openSamlAuthenticationRequestResolver.setRequestMatcher(getAuthenticationRequestMatcher());
|
||||
openSamlAuthenticationRequestResolver.setRequestMatcher(this.authenticationRequestMatcher);
|
||||
return openSamlAuthenticationRequestResolver;
|
||||
}
|
||||
else {
|
||||
OpenSaml4AuthenticationRequestResolver openSamlAuthenticationRequestResolver = new OpenSaml4AuthenticationRequestResolver(
|
||||
relyingPartyRegistrationRepository(http));
|
||||
openSamlAuthenticationRequestResolver.setRequestMatcher(getAuthenticationRequestMatcher());
|
||||
openSamlAuthenticationRequestResolver.setRequestMatcher(this.authenticationRequestMatcher);
|
||||
return openSamlAuthenticationRequestResolver;
|
||||
}
|
||||
}
|
||||
|
||||
private RequestMatcher getAuthenticationRequestMatcher() {
|
||||
if (this.authenticationRequestMatcher == null) {
|
||||
this.authenticationRequestMatcher = RequestMatchers.anyOf(
|
||||
RequestMatcherFactory
|
||||
.matcher(Saml2AuthenticationRequestResolver.DEFAULT_AUTHENTICATION_REQUEST_URI),
|
||||
new PathQueryRequestMatcher(RequestMatcherFactory.matcher(this.authenticationRequestUri),
|
||||
this.authenticationRequestParams));
|
||||
}
|
||||
return this.authenticationRequestMatcher;
|
||||
}
|
||||
|
||||
private RequestMatcher getLoginProcessingEndpoint() {
|
||||
if (this.loginProcessingUrl == null) {
|
||||
this.loginProcessingUrl = RequestMatchers.anyOf(
|
||||
RequestMatcherFactory.matcher(Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI),
|
||||
RequestMatcherFactory.matcher("/login/saml2/sso"));
|
||||
}
|
||||
|
||||
return this.loginProcessingUrl;
|
||||
}
|
||||
|
||||
private AuthenticationConverter getAuthenticationConverter(B http) {
|
||||
if (this.authenticationConverter != null) {
|
||||
return this.authenticationConverter;
|
||||
@@ -430,7 +407,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
OpenSaml5AuthenticationTokenConverter converter = new OpenSaml5AuthenticationTokenConverter(
|
||||
this.relyingPartyRegistrationRepository);
|
||||
converter.setAuthenticationRequestRepository(getAuthenticationRequestRepository(http));
|
||||
converter.setRequestMatcher(getLoginProcessingEndpoint());
|
||||
converter.setRequestMatcher(this.loginProcessingUrl);
|
||||
return converter;
|
||||
}
|
||||
authenticationConverterBean = getBeanOrNull(http, OpenSaml4AuthenticationTokenConverter.class);
|
||||
@@ -440,7 +417,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
OpenSaml4AuthenticationTokenConverter converter = new OpenSaml4AuthenticationTokenConverter(
|
||||
this.relyingPartyRegistrationRepository);
|
||||
converter.setAuthenticationRequestRepository(getAuthenticationRequestRepository(http));
|
||||
converter.setRequestMatcher(getLoginProcessingEndpoint());
|
||||
converter.setRequestMatcher(this.loginProcessingUrl);
|
||||
return converter;
|
||||
}
|
||||
|
||||
@@ -464,7 +441,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
if (csrf == null) {
|
||||
return;
|
||||
}
|
||||
csrf.ignoringRequestMatchers(getLoginProcessingEndpoint());
|
||||
csrf.ignoringRequestMatchers(this.loginProcessingUrl);
|
||||
}
|
||||
|
||||
private void initDefaultLoginFilter(B http) {
|
||||
@@ -532,13 +509,13 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
}
|
||||
}
|
||||
|
||||
static class PathQueryRequestMatcher implements RequestMatcher {
|
||||
static class AntPathQueryRequestMatcher implements RequestMatcher {
|
||||
|
||||
private final RequestMatcher matcher;
|
||||
|
||||
PathQueryRequestMatcher(RequestMatcher pathMatcher, String... params) {
|
||||
AntPathQueryRequestMatcher(String path, String... params) {
|
||||
List<RequestMatcher> matchers = new ArrayList<>();
|
||||
matchers.add(pathMatcher);
|
||||
matchers.add(new AntPathRequestMatcher(path));
|
||||
for (String param : params) {
|
||||
String[] parts = param.split("=");
|
||||
if (parts.length == 1) {
|
||||
|
||||
+4
-5
@@ -23,11 +23,9 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.opensaml.core.Version;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
|
||||
@@ -66,6 +64,7 @@ import org.springframework.security.web.csrf.CsrfFilter;
|
||||
import org.springframework.security.web.csrf.CsrfLogoutHandler;
|
||||
import org.springframework.security.web.csrf.CsrfTokenRepository;
|
||||
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.ParameterRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
@@ -305,19 +304,19 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
}
|
||||
|
||||
private RequestMatcher createLogoutMatcher() {
|
||||
RequestMatcher logout = RequestMatcherFactory.matcher(HttpMethod.POST, this.logoutUrl);
|
||||
RequestMatcher logout = new AntPathRequestMatcher(this.logoutUrl, "POST");
|
||||
RequestMatcher saml2 = new Saml2RequestMatcher(getSecurityContextHolderStrategy());
|
||||
return new AndRequestMatcher(logout, saml2);
|
||||
}
|
||||
|
||||
private RequestMatcher createLogoutRequestMatcher() {
|
||||
RequestMatcher logout = RequestMatcherFactory.matcher(this.logoutRequestConfigurer.logoutUrl);
|
||||
RequestMatcher logout = new AntPathRequestMatcher(this.logoutRequestConfigurer.logoutUrl);
|
||||
RequestMatcher samlRequest = new ParameterRequestMatcher("SAMLRequest");
|
||||
return new AndRequestMatcher(logout, samlRequest);
|
||||
}
|
||||
|
||||
private RequestMatcher createLogoutResponseMatcher() {
|
||||
RequestMatcher logout = RequestMatcherFactory.matcher(this.logoutResponseConfigurer.logoutUrl);
|
||||
RequestMatcher logout = new AntPathRequestMatcher(this.logoutResponseConfigurer.logoutUrl);
|
||||
RequestMatcher samlResponse = new ParameterRequestMatcher("SAMLResponse");
|
||||
return new AndRequestMatcher(logout, samlResponse);
|
||||
}
|
||||
|
||||
+3
-3
@@ -22,7 +22,6 @@ import org.opensaml.core.Version;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.RequestMatcherFactory;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.saml2.provider.service.metadata.OpenSaml4MetadataResolver;
|
||||
@@ -33,6 +32,7 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP
|
||||
import org.springframework.security.saml2.provider.service.web.Saml2MetadataFilter;
|
||||
import org.springframework.security.saml2.provider.service.web.metadata.RequestMatcherMetadataResponseResolver;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -111,12 +111,12 @@ public class Saml2MetadataConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
if (USE_OPENSAML_5) {
|
||||
RequestMatcherMetadataResponseResolver metadata = new RequestMatcherMetadataResponseResolver(
|
||||
registrations, new OpenSaml5MetadataResolver());
|
||||
metadata.setRequestMatcher(RequestMatcherFactory.matcher(metadataUrl));
|
||||
metadata.setRequestMatcher(new AntPathRequestMatcher(metadataUrl));
|
||||
return metadata;
|
||||
}
|
||||
RequestMatcherMetadataResponseResolver metadata = new RequestMatcherMetadataResponseResolver(registrations,
|
||||
new OpenSaml4MetadataResolver());
|
||||
metadata.setRequestMatcher(RequestMatcherFactory.matcher(metadataUrl));
|
||||
metadata.setRequestMatcher(new AntPathRequestMatcher(metadataUrl));
|
||||
return metadata;
|
||||
};
|
||||
return this;
|
||||
|
||||
-5
@@ -21,7 +21,6 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler;
|
||||
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;
|
||||
import org.springframework.security.messaging.util.matcher.MessageMatcherFactory;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
final class MessageMatcherAuthorizationManagerConfiguration {
|
||||
@@ -30,10 +29,6 @@ final class MessageMatcherAuthorizationManagerConfiguration {
|
||||
@Scope("prototype")
|
||||
MessageMatcherDelegatingAuthorizationManager.Builder messageAuthorizationManagerBuilder(
|
||||
ApplicationContext context) {
|
||||
MessageMatcherFactory.setApplicationContext(context);
|
||||
if (MessageMatcherFactory.usesPathPatterns()) {
|
||||
return MessageMatcherDelegatingAuthorizationManager.builder();
|
||||
}
|
||||
return MessageMatcherDelegatingAuthorizationManager.builder()
|
||||
.simpDestPathMatcher(
|
||||
() -> (context.getBeanNamesForType(SimpAnnotationMethodMessageHandler.class).length > 0)
|
||||
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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
|
||||
*
|
||||
* https://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.aot.hint;
|
||||
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
|
||||
/**
|
||||
* Runtime hints for
|
||||
* {@link org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration}
|
||||
*
|
||||
* @author Marcus da Coregio
|
||||
*/
|
||||
class WebSecurityConfigurationRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
hints.reflection()
|
||||
.registerType(TypeReference
|
||||
.of("org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy"),
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
|
||||
hints.reflection()
|
||||
.registerType(TypeReference.of("org.springframework.web.filter.ServletRequestPathFilter"),
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -65,7 +65,8 @@ public class AuthenticationManagerFactoryBean implements FactoryBean<Authenticat
|
||||
if (uds == null) {
|
||||
throw new NoSuchBeanDefinitionException(BeanIds.AUTHENTICATION_MANAGER, MISSING_BEAN_ERROR_MESSAGE);
|
||||
}
|
||||
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(uds);
|
||||
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
|
||||
provider.setUserDetailsService(uds);
|
||||
PasswordEncoder passwordEncoder = this.bf.getBeanProvider(PasswordEncoder.class).getIfUnique();
|
||||
if (passwordEncoder != null) {
|
||||
provider.setPasswordEncoder(passwordEncoder);
|
||||
|
||||
-9
@@ -21,8 +21,6 @@ import java.util.List;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.annotation.SecurityAnnotationScanner;
|
||||
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
|
||||
|
||||
/**
|
||||
@@ -31,14 +29,7 @@ import org.springframework.security.web.access.channel.ChannelDecisionManagerImp
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
* @deprecated In modern Spring Security APIs, each API manages its own configuration
|
||||
* context. As such there is no direct replacement for this interface. In the case of
|
||||
* method security, please see {@link SecurityAnnotationScanner} and
|
||||
* {@link AuthorizationManager}. In the case of channel security, please see
|
||||
* {@code HttpsRedirectFilter}. In the case of web security, please see
|
||||
* {@link AuthorizationManager}.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class ChannelAttributeFactory {
|
||||
|
||||
private static final String OPT_REQUIRES_HTTP = "http";
|
||||
|
||||
+13
-49
@@ -39,7 +39,6 @@ import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.UnreachableFilterChainException;
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
||||
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
||||
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
|
||||
@@ -54,6 +53,7 @@ import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
public class DefaultFilterChainValidator implements FilterChainProxy.FilterChainValidator {
|
||||
|
||||
@@ -69,67 +69,31 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
|
||||
}
|
||||
checkPathOrder(new ArrayList<>(fcp.getFilterChains()));
|
||||
checkForDuplicateMatchers(new ArrayList<>(fcp.getFilterChains()));
|
||||
checkAuthorizationFilters(new ArrayList<>(fcp.getFilterChains()));
|
||||
}
|
||||
|
||||
private void checkPathOrder(List<SecurityFilterChain> filterChains) {
|
||||
// Check that the universal pattern is listed at the end, if at all
|
||||
Iterator<SecurityFilterChain> chains = filterChains.iterator();
|
||||
while (chains.hasNext()) {
|
||||
if (chains.next() instanceof DefaultSecurityFilterChain securityFilterChain) {
|
||||
if (AnyRequestMatcher.INSTANCE.equals(securityFilterChain.getRequestMatcher()) && chains.hasNext()) {
|
||||
throw new UnreachableFilterChainException("A universal match pattern ('/**') is defined "
|
||||
+ " before other patterns in the filter chain, causing them to be ignored. Please check the "
|
||||
+ "ordering in your <security:http> namespace or FilterChainProxy bean configuration",
|
||||
securityFilterChain, chains.next());
|
||||
}
|
||||
RequestMatcher matcher = ((DefaultSecurityFilterChain) chains.next()).getRequestMatcher();
|
||||
if (AnyRequestMatcher.INSTANCE.equals(matcher) && chains.hasNext()) {
|
||||
throw new IllegalArgumentException("A universal match pattern ('/**') is defined "
|
||||
+ " before other patterns in the filter chain, causing them to be ignored. Please check the "
|
||||
+ "ordering in your <security:http> namespace or FilterChainProxy bean configuration");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForDuplicateMatchers(List<SecurityFilterChain> chains) {
|
||||
DefaultSecurityFilterChain filterChain = null;
|
||||
for (SecurityFilterChain chain : chains) {
|
||||
if (filterChain != null) {
|
||||
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
|
||||
if (defaultChain.getRequestMatcher().equals(filterChain.getRequestMatcher())) {
|
||||
throw new UnreachableFilterChainException(
|
||||
"The FilterChainProxy contains two filter chains using the" + " matcher "
|
||||
+ defaultChain.getRequestMatcher()
|
||||
+ ". If you are using multiple <http> namespace "
|
||||
+ "elements, you must use a 'pattern' attribute to define the request patterns to which they apply.",
|
||||
defaultChain, chain);
|
||||
}
|
||||
while (chains.size() > 1) {
|
||||
DefaultSecurityFilterChain chain = (DefaultSecurityFilterChain) chains.remove(0);
|
||||
for (SecurityFilterChain test : chains) {
|
||||
if (chain.getRequestMatcher().equals(((DefaultSecurityFilterChain) test).getRequestMatcher())) {
|
||||
throw new IllegalArgumentException("The FilterChainProxy contains two filter chains using the"
|
||||
+ " matcher " + chain.getRequestMatcher() + ". If you are using multiple <http> namespace "
|
||||
+ "elements, you must use a 'pattern' attribute to define the request patterns to which they apply.");
|
||||
}
|
||||
}
|
||||
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
|
||||
filterChain = defaultChain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAuthorizationFilters(List<SecurityFilterChain> chains) {
|
||||
Filter authorizationFilter = null;
|
||||
Filter filterSecurityInterceptor = null;
|
||||
for (SecurityFilterChain chain : chains) {
|
||||
for (Filter filter : chain.getFilters()) {
|
||||
if (filter instanceof AuthorizationFilter) {
|
||||
authorizationFilter = filter;
|
||||
}
|
||||
if (filter instanceof FilterSecurityInterceptor) {
|
||||
filterSecurityInterceptor = filter;
|
||||
}
|
||||
}
|
||||
if (authorizationFilter != null && filterSecurityInterceptor != null) {
|
||||
this.logger.warn(
|
||||
"It is not recommended to use authorizeRequests in the configuration. Please only use authorizeHttpRequests");
|
||||
}
|
||||
if (filterSecurityInterceptor != null) {
|
||||
this.logger.warn(
|
||||
"Usage of authorizeRequests is deprecated. Please use authorizeHttpRequests in the configuration");
|
||||
}
|
||||
authorizationFilter = null;
|
||||
filterSecurityInterceptor = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -178,7 +178,7 @@ public class FormLoginBeanDefinitionParser {
|
||||
}
|
||||
this.loginProcessingUrl = loginUrl;
|
||||
BeanDefinitionBuilder matcherBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(RequestMatcherFactoryBean.class);
|
||||
.rootBeanDefinition("org.springframework.security.web.util.matcher.AntPathRequestMatcher");
|
||||
matcherBuilder.addConstructorArgValue(loginUrl);
|
||||
if (this.loginMethod != null) {
|
||||
matcherBuilder.addConstructorArgValue("POST");
|
||||
|
||||
+4
-49
@@ -51,7 +51,6 @@ import org.springframework.security.core.session.SessionRegistryImpl;
|
||||
import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.HandlerMappingIntrospectorRequestTransformer;
|
||||
import org.springframework.security.web.access.PathPatternRequestTransformer;
|
||||
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
|
||||
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
|
||||
import org.springframework.security.web.access.channel.InsecureChannelProcessor;
|
||||
@@ -83,7 +82,7 @@ import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
|
||||
import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;
|
||||
import org.springframework.security.web.transport.HttpsRedirectFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -123,10 +122,6 @@ class HttpConfigurationBuilder {
|
||||
|
||||
private static final String ATT_SESSION_AUTH_STRATEGY_REF = "session-authentication-strategy-ref";
|
||||
|
||||
private static final String ATT_MAX_SESSIONS_REF = "max-sessions-ref";
|
||||
|
||||
private static final String ATT_MAX_SESSIONS = "max-sessions";
|
||||
|
||||
private static final String ATT_SESSION_AUTH_ERROR_URL = "session-authentication-error-url";
|
||||
|
||||
private static final String ATT_SECURITY_CONTEXT_HOLDER_STRATEGY = "security-context-holder-strategy-ref";
|
||||
@@ -177,8 +172,6 @@ class HttpConfigurationBuilder {
|
||||
|
||||
private BeanDefinition cpf;
|
||||
|
||||
private BeanDefinition httpsRedirectFilter;
|
||||
|
||||
private BeanDefinition securityContextPersistenceFilter;
|
||||
|
||||
private BeanDefinition forceEagerSessionCreationFilter;
|
||||
@@ -255,7 +248,6 @@ class HttpConfigurationBuilder {
|
||||
createServletApiFilter(authenticationManager);
|
||||
createJaasApiFilter();
|
||||
createChannelProcessingFilter();
|
||||
createHttpsRedirectFilter();
|
||||
createFilterSecurity(authenticationManager);
|
||||
createAddHeadersFilter();
|
||||
createCorsFilter();
|
||||
@@ -493,16 +485,10 @@ class HttpConfigurationBuilder {
|
||||
concurrentSessionStrategy.addConstructorArgValue(this.sessionRegistryRef);
|
||||
String maxSessions = this.pc.getReaderContext()
|
||||
.getEnvironment()
|
||||
.resolvePlaceholders(sessionCtrlElt.getAttribute(ATT_MAX_SESSIONS));
|
||||
.resolvePlaceholders(sessionCtrlElt.getAttribute("max-sessions"));
|
||||
if (StringUtils.hasText(maxSessions)) {
|
||||
concurrentSessionStrategy.addPropertyValue("maximumSessions", maxSessions);
|
||||
}
|
||||
String maxSessionsRef = this.pc.getReaderContext()
|
||||
.getEnvironment()
|
||||
.resolvePlaceholders(sessionCtrlElt.getAttribute(ATT_MAX_SESSIONS_REF));
|
||||
if (StringUtils.hasText(maxSessionsRef)) {
|
||||
concurrentSessionStrategy.addPropertyReference("maximumSessions", maxSessionsRef);
|
||||
}
|
||||
String exceptionIfMaximumExceeded = sessionCtrlElt.getAttribute("error-if-maximum-exceeded");
|
||||
if (StringUtils.hasText(exceptionIfMaximumExceeded)) {
|
||||
concurrentSessionStrategy.addPropertyValue("exceptionIfMaximumExceeded", exceptionIfMaximumExceeded);
|
||||
@@ -605,12 +591,6 @@ class HttpConfigurationBuilder {
|
||||
.error("Cannot use 'expired-url' attribute and 'expired-session-strategy-ref'" + " attribute together.",
|
||||
source);
|
||||
}
|
||||
String maxSessions = element.getAttribute(ATT_MAX_SESSIONS);
|
||||
String maxSessionsRef = element.getAttribute(ATT_MAX_SESSIONS_REF);
|
||||
if (StringUtils.hasText(maxSessions) && StringUtils.hasText(maxSessionsRef)) {
|
||||
this.pc.getReaderContext()
|
||||
.error("Cannot use 'max-sessions' attribute and 'max-sessions-ref' attribute together.", source);
|
||||
}
|
||||
if (StringUtils.hasText(expiryUrl)) {
|
||||
BeanDefinitionBuilder expiredSessionBldr = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(SimpleRedirectSessionInformationExpiredStrategy.class);
|
||||
@@ -660,19 +640,6 @@ class HttpConfigurationBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
private void createHttpsRedirectFilter() {
|
||||
String ref = this.httpElt
|
||||
.getAttribute(HttpSecurityBeanDefinitionParser.ATT_REDIRECT_TO_HTTPS_REQUEST_MATCHER_REF);
|
||||
if (!StringUtils.hasText(ref)) {
|
||||
return;
|
||||
}
|
||||
RootBeanDefinition channelFilter = new RootBeanDefinition(HttpsRedirectFilter.class);
|
||||
channelFilter.getPropertyValues().addPropertyValue("requestMatcher", new RuntimeBeanReference(ref));
|
||||
channelFilter.getPropertyValues().addPropertyValue("portMapper", this.portMapper);
|
||||
this.httpsRedirectFilter = channelFilter;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private void createChannelProcessingFilter() {
|
||||
ManagedMap<BeanMetadataElement, BeanDefinition> channelRequestMap = parseInterceptUrlsForChannelSecurity();
|
||||
if (channelRequestMap.isEmpty()) {
|
||||
@@ -708,9 +675,7 @@ class HttpConfigurationBuilder {
|
||||
* Parses the intercept-url elements to obtain the map used by channel security. This
|
||||
* will be empty unless the <tt>requires-channel</tt> attribute has been used on a URL
|
||||
* path.
|
||||
* @deprecated please use {@link #createHttpsRedirectFilter} instead
|
||||
*/
|
||||
@Deprecated
|
||||
private ManagedMap<BeanMetadataElement, BeanDefinition> parseInterceptUrlsForChannelSecurity() {
|
||||
ManagedMap<BeanMetadataElement, BeanDefinition> channelRequestMap = new ManagedMap<>();
|
||||
for (Element urlElt : this.interceptUrls) {
|
||||
@@ -751,7 +716,7 @@ class HttpConfigurationBuilder {
|
||||
requestCacheBldr.addPropertyValue("portResolver", this.portResolver);
|
||||
if (this.csrfFilter != null) {
|
||||
BeanDefinitionBuilder requestCacheMatcherBldr = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(RequestMatcherFactoryBean.class);
|
||||
.rootBeanDefinition(AntPathRequestMatcher.class);
|
||||
requestCacheMatcherBldr.addConstructorArgValue("/**");
|
||||
requestCacheMatcherBldr.addConstructorArgValue("GET");
|
||||
requestCacheBldr.addPropertyValue("requestMatcher", requestCacheMatcherBldr.getBeanDefinition());
|
||||
@@ -916,9 +881,6 @@ class HttpConfigurationBuilder {
|
||||
if (this.disableUrlRewriteFilter != null) {
|
||||
filters.add(new OrderDecorator(this.disableUrlRewriteFilter, SecurityFilters.DISABLE_ENCODE_URL_FILTER));
|
||||
}
|
||||
if (this.httpsRedirectFilter != null) {
|
||||
filters.add(new OrderDecorator(this.httpsRedirectFilter, SecurityFilters.HTTPS_REDIRECT_FILTER));
|
||||
}
|
||||
if (this.cpf != null) {
|
||||
filters.add(new OrderDecorator(this.cpf, SecurityFilters.CHANNEL_FILTER));
|
||||
}
|
||||
@@ -975,17 +937,10 @@ class HttpConfigurationBuilder {
|
||||
@Override
|
||||
public AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer getObject()
|
||||
throws Exception {
|
||||
AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer requestTransformer = this.applicationContext
|
||||
.getBeanProvider(
|
||||
AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer.class)
|
||||
.getIfUnique();
|
||||
if (requestTransformer != null) {
|
||||
return requestTransformer;
|
||||
}
|
||||
HandlerMappingIntrospector hmi = this.applicationContext.getBeanProvider(HandlerMappingIntrospector.class)
|
||||
.getIfAvailable();
|
||||
return (hmi != null) ? new HandlerMappingIntrospectorRequestTransformer(hmi)
|
||||
: new PathPatternRequestTransformer();
|
||||
: AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer.IDENTITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
-6
@@ -81,8 +81,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
static final String ATT_REQUEST_MATCHER_REF = "request-matcher-ref";
|
||||
|
||||
static final String ATT_REDIRECT_TO_HTTPS_REQUEST_MATCHER_REF = "redirect-to-https-request-matcher-ref";
|
||||
|
||||
static final String ATT_PATH_PATTERN = "pattern";
|
||||
|
||||
static final String ATT_HTTP_METHOD = "method";
|
||||
@@ -242,10 +240,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
||||
}
|
||||
|
||||
private RuntimeBeanReference createPortResolver(BeanReference portMapper, ParserContext pc) {
|
||||
String beanName = "portResolver";
|
||||
if (pc.getRegistry().containsBeanDefinition(beanName)) {
|
||||
return new RuntimeBeanReference(beanName);
|
||||
}
|
||||
RootBeanDefinition portResolver = new RootBeanDefinition(PortResolverImpl.class);
|
||||
portResolver.getPropertyValues().addPropertyValue("portMapper", portMapper);
|
||||
String portResolverName = pc.getReaderContext().generateBeanName(portResolver);
|
||||
|
||||
+1
-1
@@ -134,7 +134,7 @@ class LogoutBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private BeanDefinition getLogoutRequestMatcher(String logoutUrl) {
|
||||
BeanDefinitionBuilder matcherBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(RequestMatcherFactoryBean.class);
|
||||
.rootBeanDefinition("org.springframework.security.web.util.matcher.AntPathRequestMatcher");
|
||||
matcherBuilder.addConstructorArgValue(logoutUrl);
|
||||
if (this.csrfEnabled) {
|
||||
matcherBuilder.addConstructorArgValue("POST");
|
||||
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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
|
||||
*
|
||||
* https://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.beans.factory.FactoryBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.messaging.simp.SimpMessageType;
|
||||
import org.springframework.security.messaging.util.matcher.MessageMatcher;
|
||||
import org.springframework.security.messaging.util.matcher.PathPatternMessageMatcher;
|
||||
import org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
|
||||
@Deprecated
|
||||
public final class MessageMatcherFactoryBean implements FactoryBean<MessageMatcher<?>>, ApplicationContextAware {
|
||||
|
||||
private PathPatternMessageMatcher.Builder builder;
|
||||
|
||||
private final SimpMessageType method;
|
||||
|
||||
private final String path;
|
||||
|
||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
public MessageMatcherFactoryBean(String path) {
|
||||
this(path, null);
|
||||
}
|
||||
|
||||
public MessageMatcherFactoryBean(String path, SimpMessageType method) {
|
||||
this.method = method;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageMatcher<?> getObject() throws Exception {
|
||||
if (this.builder != null) {
|
||||
return this.builder.matcher(this.method, this.path);
|
||||
}
|
||||
if (this.method == SimpMessageType.SUBSCRIBE) {
|
||||
return SimpDestinationMessageMatcher.createSubscribeMatcher(this.path, this.pathMatcher);
|
||||
}
|
||||
if (this.method == SimpMessageType.MESSAGE) {
|
||||
return SimpDestinationMessageMatcher.createMessageMatcher(this.path, this.pathMatcher);
|
||||
}
|
||||
return new SimpDestinationMessageMatcher(this.path, this.pathMatcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setPathMatcher(PathMatcher pathMatcher) {
|
||||
this.pathMatcher = pathMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
this.builder = context.getBeanProvider(PathPatternMessageMatcher.Builder.class).getIfUnique();
|
||||
}
|
||||
|
||||
}
|
||||
-7
@@ -34,7 +34,6 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.security.oauth2.client.AuthorizationCodeOAuth2AuthorizedClientProvider;
|
||||
@@ -198,12 +197,6 @@ final class OAuth2AuthorizedClientManagerRegistrar implements BeanDefinitionRegi
|
||||
authorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);
|
||||
}
|
||||
|
||||
ApplicationEventPublisher applicationEventPublisher = getBeanOfType(
|
||||
ResolvableType.forClass(ApplicationEventPublisher.class));
|
||||
if (applicationEventPublisher != null) {
|
||||
authorizedClientProvider.setApplicationEventPublisher(applicationEventPublisher);
|
||||
}
|
||||
|
||||
return authorizedClientProvider;
|
||||
}
|
||||
|
||||
|
||||
+28
-59
@@ -19,6 +19,7 @@ package org.springframework.security.config.http;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -26,13 +27,11 @@ import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -58,6 +57,7 @@ import org.springframework.security.web.authentication.DelegatingAuthenticationE
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||
@@ -235,7 +235,7 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser {
|
||||
.getBeanDefinition();
|
||||
}
|
||||
else {
|
||||
Map<BeanDefinition, AuthenticationEntryPoint> entryPoint = getLoginEntryPoint(element);
|
||||
Map<RequestMatcher, AuthenticationEntryPoint> entryPoint = getLoginEntryPoint(element);
|
||||
if (entryPoint != null) {
|
||||
this.oauth2LoginAuthenticationEntryPoint = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(DelegatingAuthenticationEntryPoint.class)
|
||||
@@ -364,35 +364,42 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser {
|
||||
return this.oauth2LoginLinks;
|
||||
}
|
||||
|
||||
private Map<BeanDefinition, AuthenticationEntryPoint> getLoginEntryPoint(Element element) {
|
||||
Map<BeanDefinition, AuthenticationEntryPoint> entryPoints = null;
|
||||
private Map<RequestMatcher, AuthenticationEntryPoint> getLoginEntryPoint(Element element) {
|
||||
Map<RequestMatcher, AuthenticationEntryPoint> entryPoints = null;
|
||||
Element clientRegsElt = DomUtils.getChildElementByTagName(element.getOwnerDocument().getDocumentElement(),
|
||||
Elements.CLIENT_REGISTRATIONS);
|
||||
if (clientRegsElt != null) {
|
||||
List<Element> clientRegList = DomUtils.getChildElementsByTagName(clientRegsElt, ELT_CLIENT_REGISTRATION);
|
||||
if (clientRegList.size() == 1) {
|
||||
BeanDefinition loginPageMatcher = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(RequestMatcherFactoryBean.class)
|
||||
.addConstructorArgValue(DEFAULT_LOGIN_URI)
|
||||
.getBeanDefinition();
|
||||
BeanDefinition faviconMatcher = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(RequestMatcherFactoryBean.class)
|
||||
.addConstructorArgValue("/favicon.ico")
|
||||
.getBeanDefinition();
|
||||
BeanDefinition entryPointMatcher = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(EntryPointMatcherFactoryBean.class)
|
||||
.addConstructorArgValue(loginPageMatcher)
|
||||
.addConstructorArgValue(faviconMatcher)
|
||||
.getBeanDefinition();
|
||||
RequestMatcher loginPageMatcher = new AntPathRequestMatcher(DEFAULT_LOGIN_URI);
|
||||
RequestMatcher faviconMatcher = new AntPathRequestMatcher("/favicon.ico");
|
||||
RequestMatcher defaultEntryPointMatcher = this.getAuthenticationEntryPointMatcher();
|
||||
RequestMatcher defaultLoginPageMatcher = new AndRequestMatcher(
|
||||
new OrRequestMatcher(loginPageMatcher, faviconMatcher), defaultEntryPointMatcher);
|
||||
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
|
||||
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
|
||||
Element clientRegElt = clientRegList.get(0);
|
||||
entryPoints = new ManagedMap<>();
|
||||
entryPoints.put(entryPointMatcher, new LoginUrlAuthenticationEntryPoint(
|
||||
DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/" + clientRegElt.getAttribute(ATT_REGISTRATION_ID)));
|
||||
entryPoints = new LinkedHashMap<>();
|
||||
entryPoints.put(
|
||||
new AndRequestMatcher(notXRequestedWith, new NegatedRequestMatcher(defaultLoginPageMatcher)),
|
||||
new LoginUrlAuthenticationEntryPoint(DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/"
|
||||
+ clientRegElt.getAttribute(ATT_REGISTRATION_ID)));
|
||||
}
|
||||
}
|
||||
return entryPoints;
|
||||
}
|
||||
|
||||
private RequestMatcher getAuthenticationEntryPointMatcher() {
|
||||
ContentNegotiationStrategy contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
|
||||
MediaTypeRequestMatcher mediaMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,
|
||||
MediaType.APPLICATION_XHTML_XML, new MediaType("image", "*"), MediaType.TEXT_HTML,
|
||||
MediaType.TEXT_PLAIN);
|
||||
mediaMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
|
||||
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
|
||||
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
|
||||
return new AndRequestMatcher(Arrays.asList(notXRequestedWith, mediaMatcher));
|
||||
}
|
||||
|
||||
private static class OidcAuthenticationRequestChecker implements AuthenticationProvider {
|
||||
|
||||
@Override
|
||||
@@ -456,42 +463,4 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
static class EntryPointMatcherFactoryBean implements FactoryBean<RequestMatcher> {
|
||||
|
||||
private final RequestMatcher entryPointMatcher;
|
||||
|
||||
EntryPointMatcherFactoryBean(RequestMatcher loginPageMatcher, RequestMatcher faviconMatcher) {
|
||||
RequestMatcher defaultEntryPointMatcher = getAuthenticationEntryPointMatcher();
|
||||
RequestMatcher defaultLoginPageMatcher = new AndRequestMatcher(
|
||||
new OrRequestMatcher(loginPageMatcher, faviconMatcher), defaultEntryPointMatcher);
|
||||
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
|
||||
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
|
||||
this.entryPointMatcher = new AndRequestMatcher(notXRequestedWith,
|
||||
new NegatedRequestMatcher(defaultLoginPageMatcher));
|
||||
}
|
||||
|
||||
private RequestMatcher getAuthenticationEntryPointMatcher() {
|
||||
ContentNegotiationStrategy contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
|
||||
MediaTypeRequestMatcher mediaMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,
|
||||
MediaType.APPLICATION_XHTML_XML, new MediaType("image", "*"), MediaType.TEXT_HTML,
|
||||
MediaType.TEXT_PLAIN);
|
||||
mediaMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
|
||||
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
|
||||
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
|
||||
return new AndRequestMatcher(Arrays.asList(notXRequestedWith, mediaMatcher));
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestMatcher getObject() {
|
||||
return this.entryPointMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return RequestMatcher.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -40,7 +40,7 @@ import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
|
||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
|
||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
||||
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
|
||||
@@ -300,7 +300,7 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
|
||||
String clientId = element.getAttribute(CLIENT_ID);
|
||||
String clientSecret = element.getAttribute(CLIENT_SECRET);
|
||||
BeanDefinitionBuilder introspectorBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(SpringOpaqueTokenIntrospector.class);
|
||||
.rootBeanDefinition(NimbusOpaqueTokenIntrospector.class);
|
||||
introspectorBuilder.addConstructorArgValue(introspectionUri);
|
||||
introspectorBuilder.addConstructorArgValue(clientId);
|
||||
introspectorBuilder.addConstructorArgValue(clientSecret);
|
||||
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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
|
||||
*
|
||||
* https://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.beans.factory.FactoryBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
@Deprecated
|
||||
public final class RequestMatcherFactoryBean implements FactoryBean<RequestMatcher>, ApplicationContextAware {
|
||||
|
||||
private PathPatternRequestMatcher.Builder builder;
|
||||
|
||||
private final HttpMethod method;
|
||||
|
||||
private final String path;
|
||||
|
||||
public RequestMatcherFactoryBean(String path) {
|
||||
this(path, null);
|
||||
}
|
||||
|
||||
public RequestMatcherFactoryBean(String path, HttpMethod method) {
|
||||
this.method = method;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestMatcher getObject() throws Exception {
|
||||
if (this.builder != null) {
|
||||
return this.builder.matcher(this.method, this.path);
|
||||
}
|
||||
return new AntPathRequestMatcher(this.path, (this.method != null) ? this.method.name() : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
this.builder = context.getBeanProvider(PathPatternRequestMatcher.Builder.class).getIfUnique();
|
||||
}
|
||||
|
||||
}
|
||||
+3
-2
@@ -43,6 +43,7 @@ import org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthen
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
|
||||
@@ -215,7 +216,7 @@ final class Saml2LoginBeanDefinitionParser implements BeanDefinitionParser {
|
||||
}
|
||||
if (saml2LoginAuthenticationEntryPoint != null) {
|
||||
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(RequestMatcherFactoryBean.class);
|
||||
.rootBeanDefinition(AntPathRequestMatcher.class);
|
||||
requestMatcherBuilder.addConstructorArgValue(this.loginProcessingUrl);
|
||||
BeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition();
|
||||
this.entryPoints.put(requestMatcher, saml2LoginAuthenticationEntryPoint);
|
||||
@@ -259,7 +260,7 @@ final class Saml2LoginBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private void registerDefaultCsrfOverride() {
|
||||
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(RequestMatcherFactoryBean.class);
|
||||
.rootBeanDefinition(AntPathRequestMatcher.class);
|
||||
requestMatcherBuilder.addConstructorArgValue(this.loginProcessingUrl);
|
||||
BeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition();
|
||||
this.csrfIgnoreRequestMatchers.add(requestMatcher);
|
||||
|
||||
+4
-4
@@ -41,6 +41,7 @@ import org.springframework.security.web.authentication.logout.LogoutSuccessEvent
|
||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.ParameterRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -170,7 +171,7 @@ final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
|
||||
}
|
||||
|
||||
private BeanMetadataElement createLogoutRequestMatcher() {
|
||||
BeanMetadataElement logoutMatcher = BeanDefinitionBuilder.rootBeanDefinition(RequestMatcherFactoryBean.class)
|
||||
BeanMetadataElement logoutMatcher = BeanDefinitionBuilder.rootBeanDefinition(AntPathRequestMatcher.class)
|
||||
.addConstructorArgValue(this.logoutUrl)
|
||||
.addConstructorArgValue("POST")
|
||||
.getBeanDefinition();
|
||||
@@ -183,8 +184,7 @@ final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
|
||||
}
|
||||
|
||||
private BeanMetadataElement createSaml2LogoutRequestMatcher() {
|
||||
BeanMetadataElement logoutRequestMatcher = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(RequestMatcherFactoryBean.class)
|
||||
BeanMetadataElement logoutRequestMatcher = BeanDefinitionBuilder.rootBeanDefinition(AntPathRequestMatcher.class)
|
||||
.addConstructorArgValue(this.logoutRequestUrl)
|
||||
.getBeanDefinition();
|
||||
BeanMetadataElement saml2RequestMatcher = BeanDefinitionBuilder
|
||||
@@ -198,7 +198,7 @@ final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private BeanMetadataElement createSaml2LogoutResponseMatcher() {
|
||||
BeanMetadataElement logoutResponseMatcher = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(RequestMatcherFactoryBean.class)
|
||||
.rootBeanDefinition(AntPathRequestMatcher.class)
|
||||
.addConstructorArgValue(this.logoutResponseUrl)
|
||||
.getBeanDefinition();
|
||||
BeanMetadataElement saml2ResponseMatcher = BeanDefinitionBuilder
|
||||
|
||||
@@ -33,11 +33,8 @@ enum SecurityFilters {
|
||||
|
||||
FORCE_EAGER_SESSION_FILTER,
|
||||
|
||||
@Deprecated
|
||||
CHANNEL_FILTER,
|
||||
|
||||
HTTPS_REDIRECT_FILTER,
|
||||
|
||||
SECURITY_CONTEXT_FILTER,
|
||||
|
||||
CONCURRENT_SESSION_FILTER,
|
||||
|
||||
+2
-4
@@ -23,6 +23,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.security.web.RequestMatcherRedirectFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -44,12 +45,9 @@ public final class WellKnownChangePasswordBeanDefinitionParser implements BeanDe
|
||||
*/
|
||||
@Override
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
BeanDefinition requestMatcher = BeanDefinitionBuilder.rootBeanDefinition(RequestMatcherFactoryBean.class)
|
||||
.addConstructorArgValue(WELL_KNOWN_CHANGE_PASSWORD_PATTERN)
|
||||
.getBeanDefinition();
|
||||
BeanDefinition changePasswordFilter = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(RequestMatcherRedirectFilter.class)
|
||||
.addConstructorArgValue(requestMatcher)
|
||||
.addConstructorArgValue(new AntPathRequestMatcher(WELL_KNOWN_CHANGE_PASSWORD_PATTERN))
|
||||
.addConstructorArgValue(getChangePasswordPage(element))
|
||||
.getBeanDefinition();
|
||||
parserContext.getReaderContext().registerWithGeneratedName(changePasswordFilter);
|
||||
|
||||
@@ -24,7 +24,6 @@ import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.access.vote.AffirmativeBased;
|
||||
import org.springframework.security.access.vote.AuthenticatedVoter;
|
||||
import org.springframework.security.access.vote.RoleVoter;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.config.BeanIds;
|
||||
|
||||
/**
|
||||
@@ -33,9 +32,7 @@ import org.springframework.security.config.BeanIds;
|
||||
* @author Luke Taylor
|
||||
* @author Ben Alex
|
||||
* @author Rob Winch
|
||||
* @deprecated Please use {@link AuthorizationManager} instead
|
||||
*/
|
||||
@Deprecated
|
||||
abstract class MethodConfigUtils {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
+1
-1
@@ -125,7 +125,7 @@ public final class ClientRegistrationsBeanDefinitionParser implements BeanDefini
|
||||
getOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_CLIENT_SECRET))
|
||||
.ifPresent(builder::clientSecret);
|
||||
getOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_CLIENT_AUTHENTICATION_METHOD))
|
||||
.map(ClientAuthenticationMethod::valueOf)
|
||||
.map(ClientAuthenticationMethod::new)
|
||||
.ifPresent(builder::clientAuthenticationMethod);
|
||||
getOptionalIfNotEmpty(parserContext, clientRegistrationElt.getAttribute(ATT_AUTHORIZATION_GRANT_TYPE))
|
||||
.map(AuthorizationGrantType::new)
|
||||
|
||||
-133
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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
|
||||
*
|
||||
* https://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.web;
|
||||
|
||||
import reactor.util.annotation.NonNull;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||
import org.springframework.web.util.pattern.PathPatternParser;
|
||||
|
||||
/**
|
||||
* Use this factory bean to configure the {@link PathPatternRequestMatcher.Builder} bean
|
||||
* used to create request matchers in {@link AuthorizeHttpRequestsConfigurer} and other
|
||||
* parts of the DSL.
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 6.5
|
||||
*/
|
||||
public final class PathPatternRequestMatcherBuilderFactoryBean implements
|
||||
FactoryBean<PathPatternRequestMatcher.Builder>, ApplicationContextAware, BeanNameAware, BeanFactoryAware {
|
||||
|
||||
static final String MVC_PATTERN_PARSER_BEAN_NAME = "mvcPatternParser";
|
||||
|
||||
private final PathPatternParser parser;
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
private String beanName;
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
/**
|
||||
* Construct this factory bean using the default {@link PathPatternParser}
|
||||
*
|
||||
* <p>
|
||||
* If you are using Spring MVC, it will use the Spring MVC instance.
|
||||
*/
|
||||
public PathPatternRequestMatcherBuilderFactoryBean() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct this factory bean using this {@link PathPatternParser}.
|
||||
*
|
||||
* <p>
|
||||
* If you are using Spring MVC, it is likely incorrect to call this constructor.
|
||||
* Please call the default constructor instead.
|
||||
* @param parser the {@link PathPatternParser} to use
|
||||
*/
|
||||
public PathPatternRequestMatcherBuilderFactoryBean(PathPatternParser parser) {
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathPatternRequestMatcher.Builder getObject() throws Exception {
|
||||
if (!this.context.containsBean(MVC_PATTERN_PARSER_BEAN_NAME)) {
|
||||
PathPatternParser parser = (this.parser != null) ? this.parser : PathPatternParser.defaultInstance;
|
||||
return PathPatternRequestMatcher.withPathPatternParser(parser);
|
||||
}
|
||||
PathPatternParser mvc = this.context.getBean(MVC_PATTERN_PARSER_BEAN_NAME, PathPatternParser.class);
|
||||
PathPatternParser parser = (this.parser != null) ? this.parser : mvc;
|
||||
if (mvc.equals(parser)) {
|
||||
return PathPatternRequestMatcher.withPathPatternParser(parser);
|
||||
}
|
||||
throw new IllegalArgumentException("Spring Security and Spring MVC must use the same path pattern parser. "
|
||||
+ "To have Spring Security use Spring MVC's [" + describe(mvc, MVC_PATTERN_PARSER_BEAN_NAME)
|
||||
+ "] simply publish this bean [" + describe(this, this.beanName) + "] using its default constructor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return PathPatternRequestMatcher.Builder.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanName(@NonNull String name) {
|
||||
this.beanName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
if (beanFactory instanceof ConfigurableListableBeanFactory listable) {
|
||||
this.beanFactory = listable;
|
||||
}
|
||||
}
|
||||
|
||||
private String describe(Object bean, String name) {
|
||||
String text = bean.getClass().getSimpleName();
|
||||
if (name == null) {
|
||||
return text;
|
||||
}
|
||||
text += "defined as '" + name + "'";
|
||||
if (this.beanFactory == null) {
|
||||
return text;
|
||||
}
|
||||
BeanDefinition bd = this.beanFactory.getBeanDefinition(name);
|
||||
String description = bd.getResourceDescription();
|
||||
if (description == null) {
|
||||
return text;
|
||||
}
|
||||
text += " in [" + description + "]";
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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
|
||||
*
|
||||
* https://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.web.messaging;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;
|
||||
import org.springframework.security.messaging.util.matcher.PathPatternMessageMatcher;
|
||||
import org.springframework.web.util.pattern.PathPatternParser;
|
||||
|
||||
/**
|
||||
* Use this factory bean to configure the {@link PathPatternMessageMatcher.Builder} bean
|
||||
* used to create request matchers in {@link MessageMatcherDelegatingAuthorizationManager}
|
||||
* and other parts of the DSL.
|
||||
*
|
||||
* @author Pat McCusker
|
||||
* @since 6.5
|
||||
*/
|
||||
public final class PathPatternMessageMatcherBuilderFactoryBean
|
||||
implements FactoryBean<PathPatternMessageMatcher.Builder> {
|
||||
|
||||
private PathPatternParser parser;
|
||||
|
||||
/**
|
||||
* Create {@link PathPatternMessageMatcher}s using
|
||||
* {@link PathPatternParser#defaultInstance}
|
||||
*/
|
||||
public PathPatternMessageMatcherBuilderFactoryBean() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create {@link PathPatternMessageMatcher}s using the given {@link PathPatternParser}
|
||||
* @param parser the {@link PathPatternParser} to use
|
||||
*/
|
||||
public PathPatternMessageMatcherBuilderFactoryBean(PathPatternParser parser) {
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathPatternMessageMatcher.Builder getObject() throws Exception {
|
||||
if (this.parser == null) {
|
||||
return PathPatternMessageMatcher.withDefaults();
|
||||
}
|
||||
return PathPatternMessageMatcher.withPathPatternParser(this.parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return PathPatternMessageMatcher.Builder.class;
|
||||
}
|
||||
|
||||
}
|
||||
+16
-84
@@ -29,7 +29,6 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
@@ -54,7 +53,6 @@ import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.authentication.DelegatingReactiveAuthenticationManager;
|
||||
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
||||
import org.springframework.security.authentication.ReactiveAuthenticationManagerResolver;
|
||||
import org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;
|
||||
import org.springframework.security.authentication.ott.OneTimeToken;
|
||||
import org.springframework.security.authentication.ott.reactive.InMemoryReactiveOneTimeTokenService;
|
||||
import org.springframework.security.authentication.ott.reactive.OneTimeTokenReactiveAuthenticationManager;
|
||||
@@ -111,9 +109,9 @@ import org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtReactiveAuthenticationManager;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenReactiveAuthenticationManager;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.NimbusReactiveOpaqueTokenIntrospector;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.SpringReactiveOpaqueTokenIntrospector;
|
||||
import org.springframework.security.oauth2.server.resource.web.access.server.BearerTokenServerAccessDeniedHandler;
|
||||
import org.springframework.security.oauth2.server.resource.web.server.BearerTokenServerAuthenticationEntryPoint;
|
||||
import org.springframework.security.oauth2.server.resource.web.server.authentication.ServerBearerTokenAuthenticationConverter;
|
||||
@@ -158,9 +156,7 @@ import org.springframework.security.web.server.authentication.logout.LogoutWebFi
|
||||
import org.springframework.security.web.server.authentication.logout.SecurityContextServerLogoutHandler;
|
||||
import org.springframework.security.web.server.authentication.logout.ServerLogoutHandler;
|
||||
import org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler;
|
||||
import org.springframework.security.web.server.authentication.ott.DefaultServerGenerateOneTimeTokenRequestResolver;
|
||||
import org.springframework.security.web.server.authentication.ott.GenerateOneTimeTokenWebFilter;
|
||||
import org.springframework.security.web.server.authentication.ott.ServerGenerateOneTimeTokenRequestResolver;
|
||||
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenAuthenticationConverter;
|
||||
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler;
|
||||
import org.springframework.security.web.server.authorization.AuthorizationContext;
|
||||
@@ -2736,7 +2732,7 @@ public class ServerHttpSecurity {
|
||||
ServerHttpSecurity.this.defaultEntryPoints.add(new DelegateEntry(preferredMatcher, this.entryPoint));
|
||||
AuthenticationWebFilter authenticationFilter = new AuthenticationWebFilter(this.authenticationManager);
|
||||
authenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
|
||||
authenticationFilter.setServerAuthenticationConverter(new ServerHttpBasicAuthenticationConverter());
|
||||
authenticationFilter.setAuthenticationConverter(new ServerHttpBasicAuthenticationConverter());
|
||||
authenticationFilter.setSecurityContextRepository(this.securityContextRepository);
|
||||
authenticationFilter.setAuthenticationSuccessHandler(getAuthenticationSuccessHandler(http));
|
||||
http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.HTTP_BASIC);
|
||||
@@ -3014,7 +3010,7 @@ public class ServerHttpSecurity {
|
||||
AuthenticationWebFilter authenticationFilter = new AuthenticationWebFilter(this.authenticationManager);
|
||||
authenticationFilter.setRequiresAuthenticationMatcher(this.requiresAuthenticationMatcher);
|
||||
authenticationFilter.setAuthenticationFailureHandler(this.authenticationFailureHandler);
|
||||
authenticationFilter.setServerAuthenticationConverter(new ServerFormLoginAuthenticationConverter());
|
||||
authenticationFilter.setAuthenticationConverter(new ServerFormLoginAuthenticationConverter());
|
||||
authenticationFilter.setAuthenticationSuccessHandler(getAuthenticationSuccessHandler(http));
|
||||
authenticationFilter.setSecurityContextRepository(this.securityContextRepository);
|
||||
http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.FORM_LOGIN);
|
||||
@@ -3039,8 +3035,7 @@ public class ServerHttpSecurity {
|
||||
return;
|
||||
}
|
||||
if (http.formLogin != null && http.formLogin.isEntryPointExplicit
|
||||
|| http.oauth2Login != null && StringUtils.hasText(http.oauth2Login.loginPage)
|
||||
|| http.oneTimeTokenLogin != null && StringUtils.hasText(http.oneTimeTokenLogin.loginPage)) {
|
||||
|| http.oauth2Login != null && StringUtils.hasText(http.oauth2Login.loginPage)) {
|
||||
return;
|
||||
}
|
||||
LoginPageGeneratingWebFilter loginPage = null;
|
||||
@@ -3055,13 +3050,6 @@ public class ServerHttpSecurity {
|
||||
}
|
||||
loginPage.setOauth2AuthenticationUrlToClientName(urlToText);
|
||||
}
|
||||
if (http.oneTimeTokenLogin != null) {
|
||||
if (loginPage == null) {
|
||||
loginPage = new LoginPageGeneratingWebFilter();
|
||||
}
|
||||
loginPage.setOneTimeTokenEnabled(true);
|
||||
loginPage.setGenerateOneTimeTokenUrl(http.oneTimeTokenLogin.tokenGeneratingUrl);
|
||||
}
|
||||
if (loginPage != null) {
|
||||
http.addFilterAt(loginPage, SecurityWebFiltersOrder.LOGIN_PAGE_GENERATING);
|
||||
http.addFilterBefore(DefaultResourcesWebFilter.css(), SecurityWebFiltersOrder.LOGIN_PAGE_GENERATING);
|
||||
@@ -5443,7 +5431,7 @@ public class ServerHttpSecurity {
|
||||
public OpaqueTokenSpec introspectionUri(String introspectionUri) {
|
||||
Assert.hasText(introspectionUri, "introspectionUri cannot be empty");
|
||||
this.introspectionUri = introspectionUri;
|
||||
this.introspector = () -> new SpringReactiveOpaqueTokenIntrospector(this.introspectionUri,
|
||||
this.introspector = () -> new NimbusReactiveOpaqueTokenIntrospector(this.introspectionUri,
|
||||
this.clientId, this.clientSecret);
|
||||
return this;
|
||||
}
|
||||
@@ -5459,7 +5447,7 @@ public class ServerHttpSecurity {
|
||||
Assert.notNull(clientSecret, "clientSecret cannot be null");
|
||||
this.clientId = clientId;
|
||||
this.clientSecret = clientSecret;
|
||||
this.introspector = () -> new SpringReactiveOpaqueTokenIntrospector(this.introspectionUri,
|
||||
this.introspector = () -> new NimbusReactiveOpaqueTokenIntrospector(this.introspectionUri,
|
||||
this.clientId, this.clientSecret);
|
||||
return this;
|
||||
}
|
||||
@@ -5952,8 +5940,6 @@ public class ServerHttpSecurity {
|
||||
|
||||
private ServerSecurityContextRepository securityContextRepository;
|
||||
|
||||
private ServerGenerateOneTimeTokenRequestResolver requestResolver;
|
||||
|
||||
private String loginProcessingUrl = "/login/ott";
|
||||
|
||||
private String defaultSubmitPageUrl = "/login/ott";
|
||||
@@ -5962,13 +5948,11 @@ public class ServerHttpSecurity {
|
||||
|
||||
private boolean submitPageEnabled = true;
|
||||
|
||||
private String loginPage;
|
||||
|
||||
protected void configure(ServerHttpSecurity http) {
|
||||
configureSubmitPage(http);
|
||||
configureOttGenerateFilter(http);
|
||||
configureOttAuthenticationFilter(http);
|
||||
configureDefaultEntryPoint(http);
|
||||
configureDefaultLoginPage(http);
|
||||
}
|
||||
|
||||
private void configureOttAuthenticationFilter(ServerHttpSecurity http) {
|
||||
@@ -6001,33 +5985,20 @@ public class ServerHttpSecurity {
|
||||
getTokenGenerationSuccessHandler());
|
||||
generateFilter
|
||||
.setRequestMatcher(ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, this.tokenGeneratingUrl));
|
||||
generateFilter.setGenerateRequestResolver(getRequestResolver());
|
||||
http.addFilterAt(generateFilter, SecurityWebFiltersOrder.ONE_TIME_TOKEN);
|
||||
}
|
||||
|
||||
private void configureDefaultEntryPoint(ServerHttpSecurity http) {
|
||||
MediaTypeServerWebExchangeMatcher htmlMatcher = new MediaTypeServerWebExchangeMatcher(
|
||||
MediaType.APPLICATION_XHTML_XML, new MediaType("image", "*"), MediaType.TEXT_HTML,
|
||||
MediaType.TEXT_PLAIN);
|
||||
htmlMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
|
||||
ServerWebExchangeMatcher xhrMatcher = (exchange) -> {
|
||||
if (exchange.getRequest().getHeaders().getOrEmpty("X-Requested-With").contains("XMLHttpRequest")) {
|
||||
return ServerWebExchangeMatcher.MatchResult.match();
|
||||
private void configureDefaultLoginPage(ServerHttpSecurity http) {
|
||||
if (http.formLogin != null) {
|
||||
for (WebFilter webFilter : http.webFilters) {
|
||||
OrderedWebFilter orderedWebFilter = (OrderedWebFilter) webFilter;
|
||||
if (orderedWebFilter.webFilter instanceof LoginPageGeneratingWebFilter loginPageGeneratingFilter) {
|
||||
loginPageGeneratingFilter.setOneTimeTokenEnabled(true);
|
||||
loginPageGeneratingFilter.setGenerateOneTimeTokenUrl(this.tokenGeneratingUrl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ServerWebExchangeMatcher.MatchResult.notMatch();
|
||||
};
|
||||
ServerWebExchangeMatcher notXhrMatcher = new NegatedServerWebExchangeMatcher(xhrMatcher);
|
||||
ServerWebExchangeMatcher defaultEntryPointMatcher = new AndServerWebExchangeMatcher(notXhrMatcher,
|
||||
htmlMatcher);
|
||||
String loginPage = "/login";
|
||||
if (this.loginPage != null) {
|
||||
loginPage = this.loginPage;
|
||||
}
|
||||
RedirectServerAuthenticationEntryPoint defaultEntryPoint = new RedirectServerAuthenticationEntryPoint(
|
||||
loginPage);
|
||||
defaultEntryPoint.setRequestCache(http.requestCache.requestCache);
|
||||
http.defaultEntryPoints.add(new DelegateEntry(defaultEntryPointMatcher, defaultEntryPoint));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6141,32 +6112,6 @@ public class ServerHttpSecurity {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@link ServerGenerateOneTimeTokenRequestResolver} when resolving
|
||||
* {@link GenerateOneTimeTokenRequest} from {@link ServerWebExchange}. By default,
|
||||
* the {@link DefaultServerGenerateOneTimeTokenRequestResolver} is used.
|
||||
* @param requestResolver the
|
||||
* {@link DefaultServerGenerateOneTimeTokenRequestResolver} to use
|
||||
* @since 6.5
|
||||
*/
|
||||
public OneTimeTokenLoginSpec generateRequestResolver(
|
||||
ServerGenerateOneTimeTokenRequestResolver requestResolver) {
|
||||
Assert.notNull(requestResolver, "generateRequestResolver cannot be null");
|
||||
this.requestResolver = requestResolver;
|
||||
return this;
|
||||
}
|
||||
|
||||
private ServerGenerateOneTimeTokenRequestResolver getRequestResolver() {
|
||||
if (this.requestResolver != null) {
|
||||
return this.requestResolver;
|
||||
}
|
||||
ServerGenerateOneTimeTokenRequestResolver bean = getBeanOrNull(
|
||||
ServerGenerateOneTimeTokenRequestResolver.class);
|
||||
this.requestResolver = Objects.requireNonNullElseGet(bean,
|
||||
DefaultServerGenerateOneTimeTokenRequestResolver::new);
|
||||
return this.requestResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the URL to process the login request, defaults to {@code /login/ott}.
|
||||
* Only POST requests are processed, for that reason make sure that you pass a
|
||||
@@ -6255,19 +6200,6 @@ public class ServerHttpSecurity {
|
||||
return this.tokenGenerationSuccessHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the URL to send users to if login is required. A default login page
|
||||
* will be generated when this attribute is not specified.
|
||||
* @param loginPage the URL to send users to if login is required
|
||||
* @return the {@link OAuth2LoginSpec} for further configuration
|
||||
* @since 6.5
|
||||
*/
|
||||
public OneTimeTokenLoginSpec loginPage(String loginPage) {
|
||||
Assert.hasText(loginPage, "loginPage cannot be empty");
|
||||
this.loginPage = loginPage;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+14
-7
@@ -50,7 +50,6 @@ import org.springframework.security.access.vote.ConsensusBased;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.config.Elements;
|
||||
import org.springframework.security.config.http.MessageMatcherFactoryBean;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
@@ -64,6 +63,7 @@ import org.springframework.security.messaging.access.intercept.MessageMatcherDel
|
||||
import org.springframework.security.messaging.context.AuthenticationPrincipalArgumentResolver;
|
||||
import org.springframework.security.messaging.context.SecurityContextChannelInterceptor;
|
||||
import org.springframework.security.messaging.util.matcher.MessageMatcher;
|
||||
import org.springframework.security.messaging.util.matcher.SimpDestinationMessageMatcher;
|
||||
import org.springframework.security.messaging.util.matcher.SimpMessageTypeMatcher;
|
||||
import org.springframework.security.messaging.web.csrf.CsrfChannelInterceptor;
|
||||
import org.springframework.security.messaging.web.socket.server.CsrfTokenHandshakeInterceptor;
|
||||
@@ -270,18 +270,25 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
|
||||
matcher.addConstructorArgValue(messageType);
|
||||
return matcher.getBeanDefinition();
|
||||
}
|
||||
BeanDefinitionBuilder matcher = BeanDefinitionBuilder.rootBeanDefinition(MessageMatcherFactoryBean.class);
|
||||
matcher.addConstructorArgValue(matcherPattern);
|
||||
if (hasMessageType) {
|
||||
String factoryName = null;
|
||||
if (hasPattern && hasMessageType) {
|
||||
SimpMessageType type = SimpMessageType.valueOf(messageType);
|
||||
matcher.addConstructorArgValue(type);
|
||||
if (SimpMessageType.SUBSCRIBE != type && SimpMessageType.MESSAGE != type) {
|
||||
if (SimpMessageType.MESSAGE == type) {
|
||||
factoryName = "createMessageMatcher";
|
||||
}
|
||||
else if (SimpMessageType.SUBSCRIBE == type) {
|
||||
factoryName = "createSubscribeMatcher";
|
||||
}
|
||||
else {
|
||||
parserContext.getReaderContext()
|
||||
.error("Cannot use intercept-websocket@message-type=" + messageType
|
||||
+ " with a pattern because the type does not have a destination.", interceptMessage);
|
||||
}
|
||||
}
|
||||
matcher.addPropertyValue("pathMatcher", new RuntimeBeanReference("springSecurityMessagePathMatcher"));
|
||||
BeanDefinitionBuilder matcher = BeanDefinitionBuilder.rootBeanDefinition(SimpDestinationMessageMatcher.class);
|
||||
matcher.setFactoryMethod(factoryName);
|
||||
matcher.addConstructorArgValue(matcherPattern);
|
||||
matcher.addConstructorArgValue(new RuntimeBeanReference("springSecurityMessagePathMatcher"));
|
||||
return matcher.getBeanDefinition();
|
||||
}
|
||||
|
||||
|
||||
-7
@@ -275,13 +275,6 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl {
|
||||
val authenticated: AuthorizationManager<RequestAuthorizationContext> =
|
||||
AuthenticatedAuthorizationManager.authenticated()
|
||||
|
||||
/**
|
||||
* Specify that URLs are allowed by users who have authenticated and were not "remembered".
|
||||
* @since 6.5
|
||||
*/
|
||||
val fullyAuthenticated: AuthorizationManager<RequestAuthorizationContext> =
|
||||
AuthenticatedAuthorizationManager.fullyAuthenticated()
|
||||
|
||||
internal fun get(): (AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry) -> Unit {
|
||||
return { requests ->
|
||||
authorizationRules.forEach { rule ->
|
||||
|
||||
+1
-36
@@ -192,7 +192,7 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
|
||||
* @Bean
|
||||
* fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
* http {
|
||||
* securityMatcher(PathPatternRequestMatcher.withDefaults().matcher("/private/**"))
|
||||
* securityMatcher(AntPathRequestMatcher("/private/**"))
|
||||
* formLogin {
|
||||
* loginPage = "/log-in"
|
||||
* }
|
||||
@@ -527,47 +527,12 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
|
||||
* @param requiresChannelConfiguration custom configuration that specifies
|
||||
* channel security
|
||||
* @see [RequiresChannelDsl]
|
||||
* @deprecated please use [redirectToHttps] instead
|
||||
*/
|
||||
@Deprecated(message="since 6.5 use redirectToHttps instead")
|
||||
fun requiresChannel(requiresChannelConfiguration: RequiresChannelDsl.() -> Unit) {
|
||||
val requiresChannelCustomizer = RequiresChannelDsl().apply(requiresChannelConfiguration).get()
|
||||
this.http.requiresChannel(requiresChannelCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures channel security. In order for this configuration to be useful at least
|
||||
* one mapping to a required channel must be provided.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* The example below demonstrates how to require HTTPS for every request. Only
|
||||
* requiring HTTPS for some requests is supported, for example if you need to differentiate
|
||||
* between local and production deployments.
|
||||
*
|
||||
* ```
|
||||
* @Configuration
|
||||
* @EnableWebSecurity
|
||||
* class RequireHttpsConfig {
|
||||
*
|
||||
* @Bean
|
||||
* fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
* http {
|
||||
* redirectToHttps { }
|
||||
* }
|
||||
* return http.build();
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @param httpsRedirectConfiguration custom configuration to apply to HTTPS redirect rules
|
||||
* @see [HttpsRedirectDsl]
|
||||
* @since 6.5
|
||||
*/
|
||||
fun redirectToHttps(httpsRedirectConfiguration: HttpsRedirectDsl.() -> Unit) {
|
||||
val httpsRedirectCustomizer = HttpsRedirectDsl().apply(httpsRedirectConfiguration).get()
|
||||
this.http.redirectToHttps(httpsRedirectCustomizer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds X509 based pre authentication to an application
|
||||
*
|
||||
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.web
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.HttpsRedirectConfigurer
|
||||
import org.springframework.security.web.PortMapper
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [ServerHttpSecurity] HTTPS redirection rules using idiomatic
|
||||
* Kotlin code.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.4
|
||||
* @property portMapper the [PortMapper] that specifies a custom HTTPS port to redirect to.
|
||||
*/
|
||||
@SecurityMarker
|
||||
class HttpsRedirectDsl {
|
||||
var requestMatchers: Array<out RequestMatcher>? = null
|
||||
|
||||
internal fun get(): (HttpsRedirectConfigurer<HttpSecurity>) -> Unit {
|
||||
return { https ->
|
||||
requestMatchers?.also { https.requestMatchers(*requestMatchers!!) }
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-10
@@ -23,7 +23,6 @@ import org.springframework.security.config.annotation.web.configurers.ott.OneTim
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler
|
||||
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenRequestResolver
|
||||
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler
|
||||
|
||||
/**
|
||||
@@ -35,7 +34,6 @@ import org.springframework.security.web.authentication.ott.OneTimeTokenGeneratio
|
||||
* @property authenticationConverter Use this [AuthenticationConverter] when converting incoming requests to an authentication
|
||||
* @property authenticationFailureHandler the [AuthenticationFailureHandler] to use when authentication
|
||||
* @property authenticationSuccessHandler the [AuthenticationSuccessHandler] to be used
|
||||
* @property generateRequestResolver the [GenerateOneTimeTokenRequestResolver] to be used
|
||||
* @property defaultSubmitPageUrl sets the URL that the default submit page will be generated
|
||||
* @property showDefaultSubmitPage configures whether the default one-time token submit page should be shown
|
||||
* @property loginProcessingUrl the URL to process the login request
|
||||
@@ -49,7 +47,6 @@ class OneTimeTokenLoginDsl {
|
||||
var authenticationConverter: AuthenticationConverter? = null
|
||||
var authenticationFailureHandler: AuthenticationFailureHandler? = null
|
||||
var authenticationSuccessHandler: AuthenticationSuccessHandler? = null
|
||||
var generateRequestResolver: GenerateOneTimeTokenRequestResolver? = null
|
||||
var defaultSubmitPageUrl: String? = null
|
||||
var loginProcessingUrl: String? = null
|
||||
var tokenGeneratingUrl: String? = null
|
||||
@@ -62,20 +59,15 @@ class OneTimeTokenLoginDsl {
|
||||
tokenService?.also { oneTimeTokenLoginConfigurer.tokenService(tokenService) }
|
||||
authenticationConverter?.also { oneTimeTokenLoginConfigurer.authenticationConverter(authenticationConverter) }
|
||||
authenticationFailureHandler?.also {
|
||||
oneTimeTokenLoginConfigurer.failureHandler(
|
||||
oneTimeTokenLoginConfigurer.authenticationFailureHandler(
|
||||
authenticationFailureHandler
|
||||
)
|
||||
}
|
||||
authenticationSuccessHandler?.also {
|
||||
oneTimeTokenLoginConfigurer.successHandler(
|
||||
oneTimeTokenLoginConfigurer.authenticationSuccessHandler(
|
||||
authenticationSuccessHandler
|
||||
)
|
||||
}
|
||||
generateRequestResolver?.also {
|
||||
oneTimeTokenLoginConfigurer.generateRequestResolver(
|
||||
generateRequestResolver
|
||||
)
|
||||
}
|
||||
defaultSubmitPageUrl?.also { oneTimeTokenLoginConfigurer.defaultSubmitPageUrl(defaultSubmitPageUrl) }
|
||||
showDefaultSubmitPage?.also { oneTimeTokenLoginConfigurer.showDefaultSubmitPage(showDefaultSubmitPage!!) }
|
||||
loginProcessingUrl?.also { oneTimeTokenLoginConfigurer.loginProcessingUrl(loginProcessingUrl) }
|
||||
|
||||
-1
@@ -35,7 +35,6 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector
|
||||
* @property channelProcessors the [ChannelProcessor] instances to use in
|
||||
* [ChannelDecisionManagerImpl]
|
||||
*/
|
||||
@Deprecated(message="since 6.5 use redirectToHttps instead")
|
||||
class RequiresChannelDsl : AbstractRequestMatcherDsl() {
|
||||
private val channelSecurityRules = mutableListOf<AuthorizationRule>()
|
||||
|
||||
|
||||
+1
-7
@@ -16,16 +16,14 @@
|
||||
|
||||
package org.springframework.security.config.annotation.web
|
||||
|
||||
import org.springframework.http.converter.HttpMessageConverter
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.WebAuthnConfigurer
|
||||
import org.springframework.security.web.webauthn.registration.PublicKeyCredentialCreationOptionsRepository
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] webauthn using idiomatic Kotlin code.
|
||||
* @property rpName the relying party name
|
||||
* @property rpId the relying party id
|
||||
* @property allowedOrigins allowed origins
|
||||
* @property the allowed origins
|
||||
* @property disableDefaultRegistrationPage disable default webauthn registration page
|
||||
* @since 6.4
|
||||
* @author Rob Winch
|
||||
@@ -37,8 +35,6 @@ class WebAuthnDsl {
|
||||
var rpId: String? = null
|
||||
var allowedOrigins: Set<String>? = null
|
||||
var disableDefaultRegistrationPage: Boolean? = false
|
||||
var creationOptionsRepository: PublicKeyCredentialCreationOptionsRepository? = null
|
||||
var messageConverter: HttpMessageConverter<Any>? = null
|
||||
|
||||
internal fun get(): (WebAuthnConfigurer<HttpSecurity>) -> Unit {
|
||||
return { webAuthn ->
|
||||
@@ -46,8 +42,6 @@ class WebAuthnDsl {
|
||||
rpId?.also { webAuthn.rpId(rpId) }
|
||||
allowedOrigins?.also { webAuthn.allowedOrigins(allowedOrigins) }
|
||||
disableDefaultRegistrationPage?.also { webAuthn.disableDefaultRegistrationPage(disableDefaultRegistrationPage!!) }
|
||||
creationOptionsRepository?.also { webAuthn.creationOptionsRepository(creationOptionsRepository) }
|
||||
messageConverter?.also { webAuthn.messageConverter(messageConverter) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-11
@@ -19,9 +19,7 @@ package org.springframework.security.config.annotation.web.session
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer
|
||||
import org.springframework.security.core.session.SessionRegistry
|
||||
import org.springframework.security.web.authentication.session.SessionLimit
|
||||
import org.springframework.security.web.session.SessionInformationExpiredStrategy
|
||||
import org.springframework.util.Assert
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure the behaviour of multiple sessions using idiomatic
|
||||
@@ -46,21 +44,12 @@ class SessionConcurrencyDsl {
|
||||
var expiredSessionStrategy: SessionInformationExpiredStrategy? = null
|
||||
var maxSessionsPreventsLogin: Boolean? = null
|
||||
var sessionRegistry: SessionRegistry? = null
|
||||
private var sessionLimit: SessionLimit? = null
|
||||
|
||||
fun maximumSessions(max: SessionLimit) {
|
||||
this.sessionLimit = max
|
||||
}
|
||||
|
||||
internal fun get(): (SessionManagementConfigurer<HttpSecurity>.ConcurrencyControlConfigurer) -> Unit {
|
||||
Assert.isTrue(maximumSessions == null || sessionLimit == null, "You cannot specify maximumSessions as both an Int and a SessionLimit. Please use only one.")
|
||||
return { sessionConcurrencyControl ->
|
||||
maximumSessions?.also {
|
||||
sessionConcurrencyControl.maximumSessions(maximumSessions!!)
|
||||
}
|
||||
sessionLimit?.also {
|
||||
sessionConcurrencyControl.maximumSessions(sessionLimit!!)
|
||||
}
|
||||
expiredUrl?.also {
|
||||
sessionConcurrencyControl.expiredUrl(expiredUrl)
|
||||
}
|
||||
|
||||
-15
@@ -17,7 +17,6 @@
|
||||
package org.springframework.security.config.web.server
|
||||
|
||||
import org.springframework.security.web.server.header.CacheControlServerHttpHeadersWriter
|
||||
import org.springframework.security.web.server.header.ServerHttpHeadersWriter
|
||||
import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter
|
||||
import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter
|
||||
import org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter
|
||||
@@ -44,7 +43,6 @@ class ServerHeadersDsl {
|
||||
private var crossOriginOpenerPolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginOpenerPolicySpec) -> Unit)? = null
|
||||
private var crossOriginEmbedderPolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginEmbedderPolicySpec) -> Unit)? = null
|
||||
private var crossOriginResourcePolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginResourcePolicySpec) -> Unit)? = null
|
||||
private var writers = mutableListOf<ServerHttpHeadersWriter>()
|
||||
|
||||
private var disabled = false
|
||||
|
||||
@@ -200,16 +198,6 @@ class ServerHeadersDsl {
|
||||
this.crossOriginResourcePolicy = ServerCrossOriginResourcePolicyDsl().apply(crossOriginResourcePolicyConfig).get()
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures custom headers writer
|
||||
*
|
||||
* @since 6.5
|
||||
* @param writer the [ServerHttpHeadersWriter] to provide custom headers writer
|
||||
*/
|
||||
fun writer(writer: ServerHttpHeadersWriter) {
|
||||
this.writers.add(writer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables HTTP response headers.
|
||||
*/
|
||||
@@ -256,9 +244,6 @@ class ServerHeadersDsl {
|
||||
crossOriginResourcePolicy?.also {
|
||||
headers.crossOriginResourcePolicy(crossOriginResourcePolicy)
|
||||
}
|
||||
writers.also {
|
||||
writers.forEach { writer -> headers.writer(writer) }
|
||||
}
|
||||
if (disabled) {
|
||||
headers.disable()
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user