mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-09-08 20:51:41 +00:00
Compare commits
108 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a4f813ab29 | ||
|
686f8398dd | ||
|
653f22d4a1 | ||
|
f54c293078 | ||
|
34fccf45c2 | ||
|
f840ee06eb | ||
|
8429c23108 | ||
|
97f3567702 | ||
|
2cfdcb9d95 | ||
|
3c344ff491 | ||
|
f30cc9c5a9 | ||
|
c64b086878 | ||
|
de10e08348 | ||
|
bd119ac411 | ||
|
24ffda28d8 | ||
|
6a84f96930 | ||
|
194be8ffb6 | ||
|
47b4b155da | ||
|
0a991a91ce | ||
|
d2e934ca54 | ||
|
fee4d08de3 | ||
|
3dbcf266e9 | ||
|
eeb67650ee | ||
|
b4fc01194f | ||
|
3534b74945 | ||
|
dc0ab4c805 | ||
|
c982753d46 | ||
|
910df479be | ||
|
9866435946 | ||
|
5370f1190f | ||
|
f13d8d5c75 | ||
|
1216ee598f | ||
|
a4a4908d71 | ||
|
0ff9f10696 | ||
|
7e3bf9662c | ||
|
be64c67af5 | ||
|
a58f3282d9 | ||
|
c2ba662b91 | ||
|
49f308adb0 | ||
|
4cbe8de7ea | ||
|
559b73b39f | ||
|
3278f3a410 | ||
|
36f1de945f | ||
|
6663eea65f | ||
|
89b2f9cf54 | ||
|
0e39685b9c | ||
|
9d64880ea9 | ||
|
8b2a453301 | ||
|
d1962201b5 | ||
|
857ca9c412 | ||
|
894105aab5 | ||
|
f7f41ba6c4 | ||
|
f496ded4e5 | ||
|
583e668c6b | ||
|
d6a0e3bf78 | ||
|
312c2049f3 | ||
|
3dd4686feb | ||
|
29bb4919ca | ||
|
d9210c6596 | ||
|
b8b1a92ad4 | ||
|
bbcdb23698 | ||
|
9bbf837c7c | ||
|
8a1e2a22f9 | ||
|
0404996f87 | ||
|
0f63d98c84 | ||
|
fbfbb1e571 | ||
|
d002e68231 | ||
|
41162aa7e3 | ||
|
d86f2c957d | ||
|
62b5b1a77c | ||
|
523222c24d | ||
|
69f38d4933 | ||
|
0179a811c7 | ||
|
7ce2bdd701 | ||
|
de4ceffc4f | ||
|
8c920a7ee7 | ||
|
b9653346a1 | ||
|
9e6bcbd1d0 | ||
|
8d888edc71 | ||
|
f82fe9c8c6 | ||
|
a8f045eb50 | ||
|
5c5efc9092 | ||
|
5453224377 | ||
|
a14ad770ab | ||
|
806297da23 | ||
|
8846b44b81 | ||
|
7f103b2d0a | ||
|
c06ff59747 | ||
|
f1194de45e | ||
|
1f16009c8d | ||
|
3e13437b1a | ||
|
20a6863e07 | ||
|
e145c07d5b | ||
|
68a7f1702f | ||
|
3e8b606aac | ||
|
ef5c703010 | ||
|
47be93e694 | ||
|
9310153d16 | ||
|
a933089ec2 | ||
|
a8da9ec7f7 | ||
|
3396890d8b | ||
|
c45bc384da | ||
|
4da98dde2b | ||
|
7575e4ef1c | ||
|
acaed1ad96 | ||
|
0549bf566b | ||
|
44037c0ea4 | ||
|
01c8cea00f |
@ -17,8 +17,6 @@ package io.spring.gradle;
|
|||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.gradle.testkit.runner.GradleRunner;
|
import org.gradle.testkit.runner.GradleRunner;
|
||||||
import org.junit.runner.Description;
|
|
||||||
import org.junit.runners.model.Statement;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -23,8 +23,6 @@ import org.gradle.testfixtures.ProjectBuilder;
|
|||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,7 +5,6 @@ import org.apache.commons.io.FileUtils;
|
|||||||
import org.gradle.testkit.runner.BuildResult;
|
import org.gradle.testkit.runner.BuildResult;
|
||||||
import org.gradle.testkit.runner.TaskOutcome;
|
import org.gradle.testkit.runner.TaskOutcome;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
plugins {
|
||||||
|
id 'security-nullability'
|
||||||
|
}
|
||||||
|
|
||||||
apply plugin: 'io.spring.convention.spring-module'
|
apply plugin: 'io.spring.convention.spring-module'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.security.cas;
|
package org.springframework.security.cas;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@ -34,7 +36,7 @@ public class ServiceProperties implements InitializingBean {
|
|||||||
|
|
||||||
public static final String DEFAULT_CAS_SERVICE_PARAMETER = "service";
|
public static final String DEFAULT_CAS_SERVICE_PARAMETER = "service";
|
||||||
|
|
||||||
private String service;
|
private @Nullable String service;
|
||||||
|
|
||||||
private boolean authenticateAllArtifacts;
|
private boolean authenticateAllArtifacts;
|
||||||
|
|
||||||
@ -62,7 +64,7 @@ public class ServiceProperties implements InitializingBean {
|
|||||||
* </pre>
|
* </pre>
|
||||||
* @return the URL of the service the user is authenticating to
|
* @return the URL of the service the user is authenticating to
|
||||||
*/
|
*/
|
||||||
public final String getService() {
|
public final @Nullable String getService() {
|
||||||
return this.service;
|
return this.service;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,8 @@ import org.apache.commons.logging.LogFactory;
|
|||||||
import org.apereo.cas.client.validation.Assertion;
|
import org.apereo.cas.client.validation.Assertion;
|
||||||
import org.apereo.cas.client.validation.TicketValidationException;
|
import org.apereo.cas.client.validation.TicketValidationException;
|
||||||
import org.apereo.cas.client.validation.TicketValidator;
|
import org.apereo.cas.client.validation.TicketValidator;
|
||||||
|
import org.jspecify.annotations.NullUnmarked;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.context.MessageSource;
|
import org.springframework.context.MessageSource;
|
||||||
@ -62,6 +64,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
|||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);
|
private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService;
|
private AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService;
|
||||||
|
|
||||||
private UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
|
private UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
|
||||||
@ -70,11 +73,13 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
|||||||
|
|
||||||
private StatelessTicketCache statelessTicketCache = new NullStatelessTicketCache();
|
private StatelessTicketCache statelessTicketCache = new NullStatelessTicketCache();
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private String key;
|
private String key;
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private TicketValidator ticketValidator;
|
private TicketValidator ticketValidator;
|
||||||
|
|
||||||
private ServiceProperties serviceProperties;
|
private @Nullable ServiceProperties serviceProperties;
|
||||||
|
|
||||||
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
|
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
|
||||||
|
|
||||||
@ -89,7 +94,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
public @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||||
if (!supports(authentication.getClass())) {
|
if (!supports(authentication.getClass())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -129,11 +134,14 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
|||||||
|
|
||||||
private CasAuthenticationToken authenticateNow(final Authentication authentication) throws AuthenticationException {
|
private CasAuthenticationToken authenticateNow(final Authentication authentication) throws AuthenticationException {
|
||||||
try {
|
try {
|
||||||
Assertion assertion = this.ticketValidator.validate(authentication.getCredentials().toString(),
|
Object credentials = authentication.getCredentials();
|
||||||
getServiceUrl(authentication));
|
if (credentials == null) {
|
||||||
|
throw new BadCredentialsException("Authentication.getCredentials() cannot be null");
|
||||||
|
}
|
||||||
|
Assertion assertion = this.ticketValidator.validate(credentials.toString(), getServiceUrl(authentication));
|
||||||
UserDetails userDetails = loadUserByAssertion(assertion);
|
UserDetails userDetails = loadUserByAssertion(assertion);
|
||||||
this.userDetailsChecker.check(userDetails);
|
this.userDetailsChecker.check(userDetails);
|
||||||
return new CasAuthenticationToken(this.key, userDetails, authentication.getCredentials(),
|
return new CasAuthenticationToken(this.key, userDetails, credentials,
|
||||||
this.authoritiesMapper.mapAuthorities(userDetails.getAuthorities()), userDetails, assertion);
|
this.authoritiesMapper.mapAuthorities(userDetails.getAuthorities()), userDetails, assertion);
|
||||||
}
|
}
|
||||||
catch (TicketValidationException ex) {
|
catch (TicketValidationException ex) {
|
||||||
@ -149,7 +157,8 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
|||||||
* @param authentication
|
* @param authentication
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private String getServiceUrl(Authentication authentication) {
|
@NullUnmarked
|
||||||
|
private @Nullable String getServiceUrl(Authentication authentication) {
|
||||||
String serviceUrl;
|
String serviceUrl;
|
||||||
if (authentication.getDetails() instanceof ServiceAuthenticationDetails) {
|
if (authentication.getDetails() instanceof ServiceAuthenticationDetails) {
|
||||||
return ((ServiceAuthenticationDetails) authentication.getDetails()).getServiceUrl();
|
return ((ServiceAuthenticationDetails) authentication.getDetails()).getServiceUrl();
|
||||||
@ -215,7 +224,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
|||||||
return this.statelessTicketCache;
|
return this.statelessTicketCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TicketValidator getTicketValidator() {
|
protected @Nullable TicketValidator getTicketValidator() {
|
||||||
return this.ticketValidator;
|
return this.ticketValidator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,8 @@ package org.springframework.security.cas.authentication;
|
|||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
@ -41,7 +43,7 @@ public class CasServiceTicketAuthenticationToken extends AbstractAuthenticationT
|
|||||||
|
|
||||||
private final String identifier;
|
private final String identifier;
|
||||||
|
|
||||||
private Object credentials;
|
private @Nullable Object credentials;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor can be safely used by any code that wishes to create a
|
* This constructor can be safely used by any code that wishes to create a
|
||||||
@ -86,7 +88,7 @@ public class CasServiceTicketAuthenticationToken extends AbstractAuthenticationT
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getCredentials() {
|
public @Nullable Object getCredentials() {
|
||||||
return this.credentials;
|
return this.credentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.security.cas.authentication;
|
package org.springframework.security.cas.authentication;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of @link {@link StatelessTicketCache} that has no backing cache. Useful
|
* Implementation of @link {@link StatelessTicketCache} that has no backing cache. Useful
|
||||||
* in instances where storing of tickets for stateless session management is not required.
|
* in instances where storing of tickets for stateless session management is not required.
|
||||||
@ -33,7 +35,7 @@ public final class NullStatelessTicketCache implements StatelessTicketCache {
|
|||||||
* @return null since we are not storing any tickets.
|
* @return null since we are not storing any tickets.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
|
public @Nullable CasAuthenticationToken getByTicketId(final String serviceTicket) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ package org.springframework.security.cas.authentication;
|
|||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.cache.Cache;
|
import org.springframework.cache.Cache;
|
||||||
import org.springframework.core.log.LogMessage;
|
import org.springframework.core.log.LogMessage;
|
||||||
@ -42,7 +43,7 @@ public class SpringCacheBasedTicketCache implements StatelessTicketCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
|
public @Nullable CasAuthenticationToken getByTicketId(final String serviceTicket) {
|
||||||
final Cache.ValueWrapper element = (serviceTicket != null) ? this.cache.get(serviceTicket) : null;
|
final Cache.ValueWrapper element = (serviceTicket != null) ? this.cache.get(serviceTicket) : null;
|
||||||
logger.debug(LogMessage.of(() -> "Cache hit: " + (element != null) + "; service ticket: " + serviceTicket));
|
logger.debug(LogMessage.of(() -> "Cache hit: " + (element != null) + "; service ticket: " + serviceTicket));
|
||||||
return (element != null) ? (CasAuthenticationToken) element.get() : null;
|
return (element != null) ? (CasAuthenticationToken) element.get() : null;
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.security.cas.authentication;
|
package org.springframework.security.cas.authentication;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Caches CAS service tickets and CAS proxy tickets for stateless connections.
|
* Caches CAS service tickets and CAS proxy tickets for stateless connections.
|
||||||
*
|
*
|
||||||
@ -69,7 +71,7 @@ public interface StatelessTicketCache {
|
|||||||
* </p>
|
* </p>
|
||||||
* @return the fully populated authentication token
|
* @return the fully populated authentication token
|
||||||
*/
|
*/
|
||||||
CasAuthenticationToken getByTicketId(String serviceTicket);
|
@Nullable CasAuthenticationToken getByTicketId(String serviceTicket);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the specified <code>CasAuthenticationToken</code> to the cache.
|
* Adds the specified <code>CasAuthenticationToken</code> to the cache.
|
||||||
|
@ -18,4 +18,7 @@
|
|||||||
* An {@code AuthenticationProvider} that can process CAS service tickets and proxy
|
* An {@code AuthenticationProvider} that can process CAS service tickets and proxy
|
||||||
* tickets.
|
* tickets.
|
||||||
*/
|
*/
|
||||||
|
@NullMarked
|
||||||
package org.springframework.security.cas.authentication;
|
package org.springframework.security.cas.authentication;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jackson support for CAS.
|
||||||
|
*/
|
||||||
|
@NullMarked
|
||||||
|
package org.springframework.security.cas.jackson2;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
@ -18,4 +18,7 @@
|
|||||||
* Spring Security support for Apereo's Central Authentication Service
|
* Spring Security support for Apereo's Central Authentication Service
|
||||||
* (<a href="https://github.com/apereo/cas">CAS</a>).
|
* (<a href="https://github.com/apereo/cas">CAS</a>).
|
||||||
*/
|
*/
|
||||||
|
@NullMarked
|
||||||
package org.springframework.security.cas;
|
package org.springframework.security.cas;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link org.springframework.security.core.userdetails.UserDetails} abstractions for CAS.
|
||||||
|
*/
|
||||||
|
@NullMarked
|
||||||
|
package org.springframework.security.cas.userdetails;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
@ -22,6 +22,7 @@ import jakarta.servlet.http.HttpServletRequest;
|
|||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.apereo.cas.client.util.CommonUtils;
|
import org.apereo.cas.client.util.CommonUtils;
|
||||||
import org.apereo.cas.client.util.WebUtils;
|
import org.apereo.cas.client.util.WebUtils;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.security.cas.ServiceProperties;
|
import org.springframework.security.cas.ServiceProperties;
|
||||||
@ -47,9 +48,10 @@ import org.springframework.util.Assert;
|
|||||||
*/
|
*/
|
||||||
public class CasAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean {
|
public class CasAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean {
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private ServiceProperties serviceProperties;
|
private ServiceProperties serviceProperties;
|
||||||
|
|
||||||
private String loginUrl;
|
private @Nullable String loginUrl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether the Service URL should include the session id for the specific
|
* Determines whether the Service URL should include the session id for the specific
|
||||||
@ -117,7 +119,7 @@ public class CasAuthenticationEntryPoint implements AuthenticationEntryPoint, In
|
|||||||
* <code>https://www.mycompany.com/cas/login</code>.
|
* <code>https://www.mycompany.com/cas/login</code>.
|
||||||
* @return the enterprise-wide CAS login URL
|
* @return the enterprise-wide CAS login URL
|
||||||
*/
|
*/
|
||||||
public final String getLoginUrl() {
|
public final @Nullable String getLoginUrl() {
|
||||||
return this.loginUrl;
|
return this.loginUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import jakarta.servlet.http.HttpSession;
|
|||||||
import org.apereo.cas.client.proxy.ProxyGrantingTicketStorage;
|
import org.apereo.cas.client.proxy.ProxyGrantingTicketStorage;
|
||||||
import org.apereo.cas.client.util.WebUtils;
|
import org.apereo.cas.client.util.WebUtils;
|
||||||
import org.apereo.cas.client.validation.TicketValidator;
|
import org.apereo.cas.client.validation.TicketValidator;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.core.log.LogMessage;
|
import org.springframework.core.log.LogMessage;
|
||||||
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||||
@ -190,12 +191,12 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
|||||||
/**
|
/**
|
||||||
* The last portion of the receptor url, i.e. /proxy/receptor
|
* The last portion of the receptor url, i.e. /proxy/receptor
|
||||||
*/
|
*/
|
||||||
private RequestMatcher proxyReceptorMatcher;
|
private @Nullable RequestMatcher proxyReceptorMatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The backing storage to store ProxyGrantingTicket requests.
|
* The backing storage to store ProxyGrantingTicket requests.
|
||||||
*/
|
*/
|
||||||
private ProxyGrantingTicketStorage proxyGrantingTicketStorage;
|
private @Nullable ProxyGrantingTicketStorage proxyGrantingTicketStorage;
|
||||||
|
|
||||||
private String artifactParameter = ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER;
|
private String artifactParameter = ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER;
|
||||||
|
|
||||||
@ -244,7 +245,7 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
|
public @Nullable Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
|
||||||
throws AuthenticationException, IOException {
|
throws AuthenticationException, IOException {
|
||||||
// if the request is a proxy request process it and return null to indicate the
|
// if the request is a proxy request process it and return null to indicate the
|
||||||
// request has been processed
|
// request has been processed
|
||||||
@ -422,6 +423,7 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
|||||||
* @param request
|
* @param request
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("NullAway") // Dataflow analysis limitation
|
||||||
private boolean proxyReceptorRequest(HttpServletRequest request) {
|
private boolean proxyReceptorRequest(HttpServletRequest request) {
|
||||||
final boolean result = proxyReceptorConfigured() && this.proxyReceptorMatcher.matches(request);
|
final boolean result = proxyReceptorConfigured() && this.proxyReceptorMatcher.matches(request);
|
||||||
this.logger.debug(LogMessage.format("proxyReceptorRequest = %s", result));
|
this.logger.debug(LogMessage.format("proxyReceptorRequest = %s", result));
|
||||||
|
@ -21,6 +21,7 @@ import java.net.URL;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.cas.authentication.ServiceAuthenticationDetails;
|
import org.springframework.security.cas.authentication.ServiceAuthenticationDetails;
|
||||||
import org.springframework.security.web.authentication.WebAuthenticationDetails;
|
import org.springframework.security.web.authentication.WebAuthenticationDetails;
|
||||||
@ -49,8 +50,8 @@ final class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails
|
|||||||
* string from containing the artifact name and value. This can be created using
|
* string from containing the artifact name and value. This can be created using
|
||||||
* {@link #createArtifactPattern(String)}.
|
* {@link #createArtifactPattern(String)}.
|
||||||
*/
|
*/
|
||||||
DefaultServiceAuthenticationDetails(String casService, HttpServletRequest request, Pattern artifactPattern)
|
DefaultServiceAuthenticationDetails(@Nullable String casService, HttpServletRequest request,
|
||||||
throws MalformedURLException {
|
Pattern artifactPattern) throws MalformedURLException {
|
||||||
super(request);
|
super(request);
|
||||||
URL casServiceUrl = new URL(casService);
|
URL casServiceUrl = new URL(casService);
|
||||||
int port = getServicePort(casServiceUrl);
|
int port = getServicePort(casServiceUrl);
|
||||||
@ -104,7 +105,7 @@ final class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails
|
|||||||
* @return the query String minus the artifactParameterName and the corresponding
|
* @return the query String minus the artifactParameterName and the corresponding
|
||||||
* value.
|
* value.
|
||||||
*/
|
*/
|
||||||
private String getQueryString(final HttpServletRequest request, final Pattern artifactPattern) {
|
private @Nullable String getQueryString(final HttpServletRequest request, final Pattern artifactPattern) {
|
||||||
final String query = request.getQueryString();
|
final String query = request.getQueryString();
|
||||||
if (query == null) {
|
if (query == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -18,4 +18,7 @@
|
|||||||
* Authentication processing mechanisms which respond to the submission of authentication
|
* Authentication processing mechanisms which respond to the submission of authentication
|
||||||
* credentials using CAS.
|
* credentials using CAS.
|
||||||
*/
|
*/
|
||||||
|
@NullMarked
|
||||||
package org.springframework.security.cas.web.authentication;
|
package org.springframework.security.cas.web.authentication;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
@ -17,4 +17,7 @@
|
|||||||
/**
|
/**
|
||||||
* Authenticates standard web browser users via CAS.
|
* Authenticates standard web browser users via CAS.
|
||||||
*/
|
*/
|
||||||
|
@NullMarked
|
||||||
package org.springframework.security.cas.web;
|
package org.springframework.security.cas.web;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
@ -25,7 +25,6 @@ dependencies {
|
|||||||
optional project(':spring-security-ldap')
|
optional project(':spring-security-ldap')
|
||||||
optional project(':spring-security-messaging')
|
optional project(':spring-security-messaging')
|
||||||
optional project(path: ':spring-security-saml2-service-provider')
|
optional project(path: ':spring-security-saml2-service-provider')
|
||||||
opensaml5 project(path: ':spring-security-saml2-service-provider', configuration: 'opensamlFiveMain')
|
|
||||||
optional project(':spring-security-oauth2-client')
|
optional project(':spring-security-oauth2-client')
|
||||||
optional project(':spring-security-oauth2-jose')
|
optional project(':spring-security-oauth2-jose')
|
||||||
optional project(':spring-security-oauth2-resource-server')
|
optional project(':spring-security-oauth2-resource-server')
|
||||||
@ -170,15 +169,3 @@ test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register("opensaml5Test", Test) {
|
|
||||||
filter {
|
|
||||||
includeTestsMatching "org.springframework.security.config.annotation.web.configurers.saml2.*"
|
|
||||||
}
|
|
||||||
useJUnitPlatform()
|
|
||||||
classpath = sourceSets.main.output + sourceSets.test.output + configurations.opensaml5
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named("check") {
|
|
||||||
dependsOn opensaml5Test
|
|
||||||
}
|
|
||||||
|
@ -18,7 +18,6 @@ package org.springframework.security.config.annotation.authentication.ldap;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.naming.directory.SearchControls;
|
import javax.naming.directory.SearchControls;
|
||||||
@ -39,7 +38,6 @@ import org.springframework.security.config.annotation.configuration.ObjectPostPr
|
|||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.config.test.SpringTestContext;
|
import org.springframework.security.config.test.SpringTestContext;
|
||||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
|
||||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||||
@ -120,8 +118,7 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
|
|||||||
this.spring.register(BindAuthenticationConfig.class).autowire();
|
this.spring.register(BindAuthenticationConfig.class).autowire();
|
||||||
|
|
||||||
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
|
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
|
||||||
.andExpect(authenticated().withUsername("bob")
|
.andExpect(authenticated().withUsername("bob").withRoles("DEVELOPERS"));
|
||||||
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS"))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SEC-2472
|
// SEC-2472
|
||||||
@ -130,8 +127,7 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
|
|||||||
this.spring.register(PasswordEncoderConfig.class).autowire();
|
this.spring.register(PasswordEncoderConfig.class).autowire();
|
||||||
|
|
||||||
this.mockMvc.perform(formLogin().user("bcrypt").password("password"))
|
this.mockMvc.perform(formLogin().user("bcrypt").password("password"))
|
||||||
.andExpect(authenticated().withUsername("bcrypt")
|
.andExpect(authenticated().withUsername("bcrypt").withRoles("DEVELOPERS"));
|
||||||
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS"))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private LdapAuthenticationProvider ldapProvider() {
|
private LdapAuthenticationProvider ldapProvider() {
|
||||||
|
@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package org.springframework.security.config.annotation.authentication.ldap;
|
package org.springframework.security.config.annotation.authentication.ldap;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
@ -28,8 +26,6 @@ import org.springframework.security.config.annotation.authentication.ldap.LdapAu
|
|||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.config.test.SpringTestContext;
|
import org.springframework.security.config.test.SpringTestContext;
|
||||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
|
||||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders;
|
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders;
|
||||||
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers;
|
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
@ -64,7 +60,7 @@ public class LdapAuthenticationProviderConfigurerTests {
|
|||||||
.password("bobspassword");
|
.password("bobspassword");
|
||||||
SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
|
SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
|
||||||
.withUsername("bob")
|
.withUsername("bob")
|
||||||
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS")));
|
.withRoles("DEVELOPERS");
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
this.mockMvc.perform(request).andExpect(expectedUser);
|
this.mockMvc.perform(request).andExpect(expectedUser);
|
||||||
}
|
}
|
||||||
@ -79,7 +75,7 @@ public class LdapAuthenticationProviderConfigurerTests {
|
|||||||
.password("bobspassword");
|
.password("bobspassword");
|
||||||
SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
|
SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
|
||||||
.withUsername("bob")
|
.withUsername("bob")
|
||||||
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROL_DEVELOPERS")));
|
.withRoles("ROL_", new String[] { "DEVELOPERS" });
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
this.mockMvc.perform(request).andExpect(expectedUser);
|
this.mockMvc.perform(request).andExpect(expectedUser);
|
||||||
}
|
}
|
||||||
@ -108,8 +104,7 @@ public class LdapAuthenticationProviderConfigurerTests {
|
|||||||
.password("otherbenspassword");
|
.password("otherbenspassword");
|
||||||
SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
|
SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
|
||||||
.withUsername("otherben")
|
.withUsername("otherben")
|
||||||
.withAuthorities(
|
.withRoles("SUBMANAGERS", "MANAGERS", "DEVELOPERS");
|
||||||
AuthorityUtils.createAuthorityList("ROLE_SUBMANAGERS", "ROLE_MANAGERS", "ROLE_DEVELOPERS"));
|
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
this.mockMvc.perform(request).andExpect(expectedUser);
|
this.mockMvc.perform(request).andExpect(expectedUser);
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package org.springframework.security.config.annotation.authentication.ldap;
|
package org.springframework.security.config.annotation.authentication.ldap;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@ -34,7 +33,6 @@ import org.springframework.security.config.test.SpringTestContext;
|
|||||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
|
||||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||||
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
|
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
|
||||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders;
|
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders;
|
||||||
@ -79,7 +77,7 @@ public class NamespaceLdapAuthenticationProviderTests {
|
|||||||
.user("bob")
|
.user("bob")
|
||||||
.password("bobspassword");
|
.password("bobspassword");
|
||||||
SecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated()
|
SecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated()
|
||||||
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("PREFIX_DEVELOPERS")));
|
.withRoles("PREFIX_", new String[] { "DEVELOPERS" });
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
this.mockMvc.perform(request).andExpect(user);
|
this.mockMvc.perform(request).andExpect(user);
|
||||||
}
|
}
|
||||||
@ -103,7 +101,7 @@ public class NamespaceLdapAuthenticationProviderTests {
|
|||||||
.user("bob")
|
.user("bob")
|
||||||
.password("bobspassword");
|
.password("bobspassword");
|
||||||
SecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated()
|
SecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated()
|
||||||
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_EXTRA")));
|
.withRoles("EXTRA");
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
this.mockMvc.perform(request).andExpect(user);
|
this.mockMvc.perform(request).andExpect(user);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* 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.rsocket;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.rsocket.core.RSocketServer;
|
||||||
|
import io.rsocket.exceptions.RejectedSetupException;
|
||||||
|
import io.rsocket.frame.decoder.PayloadDecoder;
|
||||||
|
import io.rsocket.transport.netty.server.CloseableChannel;
|
||||||
|
import io.rsocket.transport.netty.server.TcpServerTransport;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||||
|
import org.springframework.messaging.rsocket.RSocketRequester;
|
||||||
|
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
|
||||||
|
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||||
|
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||||
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
||||||
|
import org.springframework.security.rsocket.core.PayloadSocketAcceptorInterceptor;
|
||||||
|
import org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;
|
||||||
|
import org.springframework.security.rsocket.util.matcher.PayloadExchangeAuthorizationContext;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Andrey Litvitski
|
||||||
|
*/
|
||||||
|
@ContextConfiguration
|
||||||
|
@ExtendWith(SpringExtension.class)
|
||||||
|
public class AnonymousAuthenticationITests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
RSocketMessageHandler handler;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
SecuritySocketAcceptorInterceptor interceptor;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
ServerController controller;
|
||||||
|
|
||||||
|
private CloseableChannel server;
|
||||||
|
|
||||||
|
private RSocketRequester requester;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup() {
|
||||||
|
// @formatter:off
|
||||||
|
this.server = RSocketServer.create()
|
||||||
|
.payloadDecoder(PayloadDecoder.ZERO_COPY)
|
||||||
|
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
|
||||||
|
)
|
||||||
|
.acceptor(this.handler.responder())
|
||||||
|
.bind(TcpServerTransport.create("localhost", 0))
|
||||||
|
.block();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void dispose() {
|
||||||
|
this.requester.rsocket().dispose();
|
||||||
|
this.server.dispose();
|
||||||
|
this.controller.payloads.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestWhenAnonymousDisabledThenRespondsWithForbidden() {
|
||||||
|
this.requester = RSocketRequester.builder()
|
||||||
|
.rsocketStrategies(this.handler.getRSocketStrategies())
|
||||||
|
.connectTcp("localhost", this.server.address().getPort())
|
||||||
|
.block();
|
||||||
|
String data = "andrew";
|
||||||
|
assertThatExceptionOfType(RejectedSetupException.class).isThrownBy(
|
||||||
|
() -> this.requester.route("secure.retrieve-mono").data(data).retrieveMono(String.class).block());
|
||||||
|
assertThat(this.controller.payloads).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableRSocketSecurity
|
||||||
|
static class Config {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ServerController controller() {
|
||||||
|
return new ServerController();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
RSocketMessageHandler messageHandler() {
|
||||||
|
return new RSocketMessageHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
|
||||||
|
AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
||||||
|
ReactiveAuthorizationManager<PayloadExchangeAuthorizationContext> anonymous = (authentication,
|
||||||
|
exchange) -> authentication.map(trustResolver::isAnonymous).map(AuthorizationDecision::new);
|
||||||
|
rsocket.authorizePayload((authorize) -> authorize.anyExchange().access(anonymous));
|
||||||
|
rsocket.anonymous((anonymousAuthentication) -> anonymousAuthentication.disable());
|
||||||
|
return rsocket.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
static class ServerController {
|
||||||
|
|
||||||
|
private List<String> payloads = new ArrayList<>();
|
||||||
|
|
||||||
|
@MessageMapping("**")
|
||||||
|
String retrieveMono(String payload) {
|
||||||
|
add(payload);
|
||||||
|
return "Hi " + payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add(String p) {
|
||||||
|
this.payloads.add(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -169,7 +169,7 @@ public class LdapProviderBeanDefinitionParserTests {
|
|||||||
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server />" + "<authentication-manager>"
|
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server />" + "<authentication-manager>"
|
||||||
+ " <ldap-authentication-provider user-dn-pattern='uid={0},ou=${udp}' group-search-filter='${gsf}={0}' />"
|
+ " <ldap-authentication-provider user-dn-pattern='uid={0},ou=${udp}' group-search-filter='${gsf}={0}' />"
|
||||||
+ "</authentication-manager>"
|
+ "</authentication-manager>"
|
||||||
+ "<b:bean id='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer' class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer' />");
|
+ "<b:bean id='org.springframework.context.support.PropertySourcesPlaceholderConfigurer' class='org.springframework.context.support.PropertySourcesPlaceholderConfigurer' />");
|
||||||
|
|
||||||
ProviderManager providerManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class);
|
ProviderManager providerManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class);
|
||||||
assertThat(providerManager.getProviders()).hasSize(1);
|
assertThat(providerManager.getProviders()).hasSize(1);
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Customizer} that allows invocation of code that throws a checked exception.
|
||||||
|
*
|
||||||
|
* @param <T> The type of input.
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ThrowingCustomizer<T> extends Customizer<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default {@link Customizer#customize(Object)} that wraps any thrown checked
|
||||||
|
* exceptions (by default in a {@link RuntimeException}).
|
||||||
|
* @param t the object to customize
|
||||||
|
*/
|
||||||
|
default void customize(T t) {
|
||||||
|
try {
|
||||||
|
customizeWithException(t);
|
||||||
|
}
|
||||||
|
catch (RuntimeException ex) {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the customization on the given object, possibly throwing a checked
|
||||||
|
* exception.
|
||||||
|
* @param t the object to customize
|
||||||
|
* @throws Exception on error
|
||||||
|
*/
|
||||||
|
void customizeWithException(T t) throws Exception;
|
||||||
|
|
||||||
|
}
|
@ -102,9 +102,7 @@ class AuthorizationProxyWebConfiguration implements WebMvcConfigurer {
|
|||||||
Throwable accessDeniedException = this.throwableAnalyzer
|
Throwable accessDeniedException = this.throwableAnalyzer
|
||||||
.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
|
.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
|
||||||
if (accessDeniedException != null) {
|
if (accessDeniedException != null) {
|
||||||
return new ModelAndView((model, req, res) -> {
|
throw (AccessDeniedException) accessDeniedException;
|
||||||
throw ex;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -109,6 +109,7 @@ import org.springframework.security.rsocket.util.matcher.RoutePayloadExchangeMat
|
|||||||
* @author Manuel Tejeda
|
* @author Manuel Tejeda
|
||||||
* @author Ebert Toribio
|
* @author Ebert Toribio
|
||||||
* @author Ngoc Nhan
|
* @author Ngoc Nhan
|
||||||
|
* @author Andrey Litvitski
|
||||||
* @since 5.2
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
public class RSocketSecurity {
|
public class RSocketSecurity {
|
||||||
@ -119,6 +120,8 @@ public class RSocketSecurity {
|
|||||||
|
|
||||||
private SimpleAuthenticationSpec simpleAuthSpec;
|
private SimpleAuthenticationSpec simpleAuthSpec;
|
||||||
|
|
||||||
|
private AnonymousAuthenticationSpec anonymousAuthSpec = new AnonymousAuthenticationSpec(this);
|
||||||
|
|
||||||
private JwtSpec jwtSpec;
|
private JwtSpec jwtSpec;
|
||||||
|
|
||||||
private AuthorizePayloadsSpec authorizePayload;
|
private AuthorizePayloadsSpec authorizePayload;
|
||||||
@ -164,6 +167,20 @@ public class RSocketSecurity {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds anonymous authentication
|
||||||
|
* @param anonymous a customizer
|
||||||
|
* @return this instance
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public RSocketSecurity anonymous(Customizer<AnonymousAuthenticationSpec> anonymous) {
|
||||||
|
if (this.anonymousAuthSpec == null) {
|
||||||
|
this.anonymousAuthSpec = new AnonymousAuthenticationSpec(this);
|
||||||
|
}
|
||||||
|
anonymous.customize(this.anonymousAuthSpec);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds authentication with BasicAuthenticationPayloadExchangeConverter.
|
* Adds authentication with BasicAuthenticationPayloadExchangeConverter.
|
||||||
* @param basic
|
* @param basic
|
||||||
@ -214,7 +231,9 @@ public class RSocketSecurity {
|
|||||||
if (this.jwtSpec != null) {
|
if (this.jwtSpec != null) {
|
||||||
result.addAll(this.jwtSpec.build());
|
result.addAll(this.jwtSpec.build());
|
||||||
}
|
}
|
||||||
result.add(anonymous());
|
if (this.anonymousAuthSpec != null) {
|
||||||
|
result.add(this.anonymousAuthSpec.build());
|
||||||
|
}
|
||||||
if (this.authorizePayload != null) {
|
if (this.authorizePayload != null) {
|
||||||
result.add(this.authorizePayload.build());
|
result.add(this.authorizePayload.build());
|
||||||
}
|
}
|
||||||
@ -222,12 +241,6 @@ public class RSocketSecurity {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AnonymousPayloadInterceptor anonymous() {
|
|
||||||
AnonymousPayloadInterceptor result = new AnonymousPayloadInterceptor("anonymousUser");
|
|
||||||
result.setOrder(PayloadInterceptorOrder.ANONYMOUS.getOrder());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> T getBean(Class<T> beanClass) {
|
private <T> T getBean(Class<T> beanClass) {
|
||||||
if (this.context == null) {
|
if (this.context == null) {
|
||||||
return null;
|
return null;
|
||||||
@ -283,6 +296,26 @@ public class RSocketSecurity {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class AnonymousAuthenticationSpec {
|
||||||
|
|
||||||
|
private RSocketSecurity parent;
|
||||||
|
|
||||||
|
private AnonymousAuthenticationSpec(RSocketSecurity parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AnonymousPayloadInterceptor build() {
|
||||||
|
AnonymousPayloadInterceptor result = new AnonymousPayloadInterceptor("anonymousUser");
|
||||||
|
result.setOrder(PayloadInterceptorOrder.ANONYMOUS.getOrder());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disable() {
|
||||||
|
this.parent.anonymousAuthSpec = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public final class BasicAuthenticationSpec {
|
public final class BasicAuthenticationSpec {
|
||||||
|
|
||||||
private ReactiveAuthenticationManager authenticationManager;
|
private ReactiveAuthenticationManager authenticationManager;
|
||||||
|
@ -16,19 +16,24 @@
|
|||||||
|
|
||||||
package org.springframework.security.config.annotation.web.configuration;
|
package org.springframework.security.config.annotation.web.configuration;
|
||||||
|
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
||||||
|
import org.springframework.security.config.Customizer;
|
||||||
import org.springframework.security.config.ObjectPostProcessor;
|
import org.springframework.security.config.ObjectPostProcessor;
|
||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||||
@ -46,6 +51,7 @@ import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
|||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
|
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
|
||||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.util.function.ThrowingSupplier;
|
import org.springframework.util.function.ThrowingSupplier;
|
||||||
import org.springframework.web.accept.ContentNegotiationStrategy;
|
import org.springframework.web.accept.ContentNegotiationStrategy;
|
||||||
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
||||||
@ -131,6 +137,8 @@ class HttpSecurityConfiguration {
|
|||||||
// @formatter:on
|
// @formatter:on
|
||||||
applyCorsIfAvailable(http);
|
applyCorsIfAvailable(http);
|
||||||
applyDefaultConfigurers(http);
|
applyDefaultConfigurers(http);
|
||||||
|
applyHttpSecurityCustomizers(this.context, http);
|
||||||
|
applyTopLevelCustomizers(this.context, http);
|
||||||
return http;
|
return http;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,6 +168,73 @@ class HttpSecurityConfiguration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies all {@code Customizer<HttpSecurity>} Bean instances to the
|
||||||
|
* {@link HttpSecurity} instance.
|
||||||
|
* @param applicationContext the {@link ApplicationContext} to lookup Bean instances
|
||||||
|
* @param http the {@link HttpSecurity} to apply the Beans to.
|
||||||
|
*/
|
||||||
|
private void applyHttpSecurityCustomizers(ApplicationContext applicationContext, HttpSecurity http) {
|
||||||
|
ResolvableType httpSecurityCustomizerType = ResolvableType.forClassWithGenerics(Customizer.class,
|
||||||
|
HttpSecurity.class);
|
||||||
|
ObjectProvider<Customizer<HttpSecurity>> customizerProvider = this.context
|
||||||
|
.getBeanProvider(httpSecurityCustomizerType);
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
customizerProvider.orderedStream().forEach((customizer) ->
|
||||||
|
customizer.customize(http)
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies all {@link Customizer} Beans to {@link HttpSecurity}. For each public,
|
||||||
|
* non-static method in HttpSecurity that accepts a Customizer
|
||||||
|
* <ul>
|
||||||
|
* <li>Use the {@link MethodParameter} (this preserves generics) to resolve all Beans
|
||||||
|
* for that type</li>
|
||||||
|
* <li>For each {@link Customizer} Bean invoke the {@link java.lang.reflect.Method}
|
||||||
|
* with the {@link Customizer} Bean as the argument</li>
|
||||||
|
* </ul>
|
||||||
|
* @param context the {@link ApplicationContext}
|
||||||
|
* @param http the {@link HttpSecurity}
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private void applyTopLevelCustomizers(ApplicationContext context, HttpSecurity http) {
|
||||||
|
ReflectionUtils.MethodFilter isCustomizerMethod = (method) -> {
|
||||||
|
if (Modifier.isStatic(method.getModifiers())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!Modifier.isPublic(method.getModifiers())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!method.canAccess(http)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (method.getParameterCount() != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (method.getParameterTypes()[0] == Customizer.class) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
ReflectionUtils.MethodCallback invokeWithEachCustomizerBean = (customizerMethod) -> {
|
||||||
|
|
||||||
|
MethodParameter customizerParameter = new MethodParameter(customizerMethod, 0);
|
||||||
|
ResolvableType customizerType = ResolvableType.forMethodParameter(customizerParameter);
|
||||||
|
ObjectProvider<?> customizerProvider = context.getBeanProvider(customizerType);
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
customizerProvider.orderedStream().forEach((customizer) ->
|
||||||
|
ReflectionUtils.invokeMethod(customizerMethod, http, customizer)
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
};
|
||||||
|
ReflectionUtils.doWithMethods(HttpSecurity.class, invokeWithEachCustomizerBean, isCustomizerMethod);
|
||||||
|
}
|
||||||
|
|
||||||
private Map<Class<?>, Object> createSharedObjects() {
|
private Map<Class<?>, Object> createSharedObjects() {
|
||||||
Map<Class<?>, Object> sharedObjects = new HashMap<>();
|
Map<Class<?>, Object> sharedObjects = new HashMap<>();
|
||||||
sharedObjects.put(ApplicationContext.class, this.context);
|
sharedObjects.put(ApplicationContext.class, this.context);
|
||||||
|
@ -33,7 +33,6 @@ import org.springframework.beans.BeansException;
|
|||||||
import org.springframework.beans.factory.ObjectProvider;
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
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.config.ConfigurableListableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||||
@ -79,6 +78,7 @@ import org.springframework.web.filter.ServletRequestPathFilter;
|
|||||||
*
|
*
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
* @author Keesun Baik
|
* @author Keesun Baik
|
||||||
|
* @author Yanming Zhou
|
||||||
* @since 3.2
|
* @since 3.2
|
||||||
* @see EnableWebSecurity
|
* @see EnableWebSecurity
|
||||||
* @see WebSecurity
|
* @see WebSecurity
|
||||||
@ -190,7 +190,7 @@ public class WebSecurityConfiguration implements ImportAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public static BeanFactoryPostProcessor conversionServicePostProcessor() {
|
public static RsaKeyConversionServicePostProcessor conversionServicePostProcessor() {
|
||||||
return new RsaKeyConversionServicePostProcessor();
|
return new RsaKeyConversionServicePostProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.security.config.annotation.web.reactive;
|
package org.springframework.security.config.annotation.web.reactive;
|
||||||
|
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
@ -28,10 +29,13 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.context.expression.BeanFactoryResolver;
|
import org.springframework.context.expression.BeanFactoryResolver;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.ReactiveAdapterRegistry;
|
import org.springframework.core.ReactiveAdapterRegistry;
|
||||||
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
||||||
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
|
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
|
||||||
import org.springframework.security.authentication.password.ReactiveCompromisedPasswordChecker;
|
import org.springframework.security.authentication.password.ReactiveCompromisedPasswordChecker;
|
||||||
|
import org.springframework.security.config.Customizer;
|
||||||
import org.springframework.security.config.ObjectPostProcessor;
|
import org.springframework.security.config.ObjectPostProcessor;
|
||||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||||
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
|
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
|
||||||
@ -40,6 +44,7 @@ import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
|
|||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.security.web.reactive.result.method.annotation.AuthenticationPrincipalArgumentResolver;
|
import org.springframework.security.web.reactive.result.method.annotation.AuthenticationPrincipalArgumentResolver;
|
||||||
import org.springframework.security.web.reactive.result.method.annotation.CurrentSecurityContextArgumentResolver;
|
import org.springframework.security.web.reactive.result.method.annotation.CurrentSecurityContextArgumentResolver;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
||||||
import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;
|
import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;
|
||||||
|
|
||||||
@ -154,6 +159,83 @@ class ServerHttpSecurityConfiguration {
|
|||||||
|
|
||||||
@Bean(HTTPSECURITY_BEAN_NAME)
|
@Bean(HTTPSECURITY_BEAN_NAME)
|
||||||
@Scope("prototype")
|
@Scope("prototype")
|
||||||
|
ServerHttpSecurity httpSecurity(ApplicationContext context) {
|
||||||
|
ServerHttpSecurity http = httpSecurity();
|
||||||
|
applyServerHttpSecurityCustomizers(context, http);
|
||||||
|
applyTopLevelBeanCustomizers(context, http);
|
||||||
|
return http;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies all {@code Custmizer<ServerHttpSecurity>} Beans to
|
||||||
|
* {@link ServerHttpSecurity}.
|
||||||
|
* @param context the {@link ApplicationContext}
|
||||||
|
* @param http the {@link ServerHttpSecurity}
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private void applyServerHttpSecurityCustomizers(ApplicationContext context, ServerHttpSecurity http) {
|
||||||
|
ResolvableType httpSecurityCustomizerType = ResolvableType.forClassWithGenerics(Customizer.class,
|
||||||
|
ServerHttpSecurity.class);
|
||||||
|
ObjectProvider<Customizer<ServerHttpSecurity>> customizerProvider = context
|
||||||
|
.getBeanProvider(httpSecurityCustomizerType);
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
customizerProvider.orderedStream().forEach((customizer) ->
|
||||||
|
customizer.customize(http)
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies all {@link Customizer} Beans to top level {@link ServerHttpSecurity}
|
||||||
|
* method.
|
||||||
|
*
|
||||||
|
* For each public, non-static method in ServerHttpSecurity that accepts a Customizer
|
||||||
|
* <ul>
|
||||||
|
* <li>Use the {@link MethodParameter} (this preserves generics) to resolve all Beans
|
||||||
|
* for that type</li>
|
||||||
|
* <li>For each {@link Customizer} Bean invoke the {@link java.lang.reflect.Method}
|
||||||
|
* with the {@link Customizer} Bean as the argument</li>
|
||||||
|
* </ul>
|
||||||
|
* @param context the {@link ApplicationContext}
|
||||||
|
* @param http the {@link ServerHttpSecurity}
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private void applyTopLevelBeanCustomizers(ApplicationContext context, ServerHttpSecurity http) {
|
||||||
|
ReflectionUtils.MethodFilter isCustomizerMethod = (method) -> {
|
||||||
|
if (Modifier.isStatic(method.getModifiers())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!Modifier.isPublic(method.getModifiers())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!method.canAccess(http)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (method.getParameterCount() != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (method.getParameterTypes()[0] == Customizer.class) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
ReflectionUtils.MethodCallback invokeWithEachCustomizerBean = (customizerMethod) -> {
|
||||||
|
|
||||||
|
MethodParameter customizerParameter = new MethodParameter(customizerMethod, 0);
|
||||||
|
ResolvableType customizerType = ResolvableType.forMethodParameter(customizerParameter);
|
||||||
|
ObjectProvider<?> customizerProvider = context.getBeanProvider(customizerType);
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
customizerProvider.orderedStream().forEach((customizer) ->
|
||||||
|
ReflectionUtils.invokeMethod(customizerMethod, http, customizer)
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
};
|
||||||
|
ReflectionUtils.doWithMethods(ServerHttpSecurity.class, invokeWithEachCustomizerBean, isCustomizerMethod);
|
||||||
|
}
|
||||||
|
|
||||||
ServerHttpSecurity httpSecurity() {
|
ServerHttpSecurity httpSecurity() {
|
||||||
ContextAwareServerHttpSecurity http = new ContextAwareServerHttpSecurity();
|
ContextAwareServerHttpSecurity http = new ContextAwareServerHttpSecurity();
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
|
@ -21,7 +21,6 @@ import java.util.List;
|
|||||||
|
|
||||||
import org.springframework.beans.factory.ObjectProvider;
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@ -44,12 +43,13 @@ import static org.springframework.security.config.Customizer.withDefaults;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
|
* @author Yanming Zhou
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
*/
|
*/
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
class WebFluxSecurityConfiguration {
|
class WebFluxSecurityConfiguration {
|
||||||
|
|
||||||
public static final int WEB_FILTER_CHAIN_FILTER_ORDER = 0 - 100;
|
public static final int WEB_FILTER_CHAIN_FILTER_ORDER = -100;
|
||||||
|
|
||||||
private static final String BEAN_NAME_PREFIX = "org.springframework.security.config.annotation.web.reactive.WebFluxSecurityConfiguration.";
|
private static final String BEAN_NAME_PREFIX = "org.springframework.security.config.annotation.web.reactive.WebFluxSecurityConfiguration.";
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ class WebFluxSecurityConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
static BeanFactoryPostProcessor conversionServicePostProcessor() {
|
static RsaKeyConversionServicePostProcessor conversionServicePostProcessor() {
|
||||||
return new RsaKeyConversionServicePostProcessor();
|
return new RsaKeyConversionServicePostProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import java.util.Map;
|
|||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.aop.Pointcut;
|
import org.springframework.aop.Pointcut;
|
||||||
import org.springframework.aop.support.AopUtils;
|
import org.springframework.aop.support.AopUtils;
|
||||||
@ -37,7 +38,8 @@ class PointcutDelegatingAuthorizationManager implements AuthorizationManager<Met
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthorizationResult authorize(Supplier<Authentication> authentication, MethodInvocation object) {
|
public AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,
|
||||||
|
MethodInvocation object) {
|
||||||
for (Map.Entry<Pointcut, AuthorizationManager<MethodInvocation>> entry : this.managers.entrySet()) {
|
for (Map.Entry<Pointcut, AuthorizationManager<MethodInvocation>> entry : this.managers.entrySet()) {
|
||||||
Class<?> targetClass = (object.getThis() != null) ? AopUtils.getTargetClass(object.getThis()) : null;
|
Class<?> targetClass = (object.getThis() != null) ? AopUtils.getTargetClass(object.getThis()) : null;
|
||||||
if (entry.getKey().getClassFilter().matches(targetClass)
|
if (entry.getKey().getClassFilter().matches(targetClass)
|
||||||
|
@ -1316,6 +1316,10 @@ public class ServerHttpSecurity {
|
|||||||
return this.context.getBeanNamesForType(beanClass);
|
return this.context.getBeanNamesForType(beanClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApplicationContext getApplicationContext() {
|
||||||
|
return this.context;
|
||||||
|
}
|
||||||
|
|
||||||
protected void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
protected void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||||
this.context = applicationContext;
|
this.context = applicationContext;
|
||||||
}
|
}
|
||||||
@ -3029,7 +3033,8 @@ public class ServerHttpSecurity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the logout handler. Default is
|
* Configures the logout handler. Default is
|
||||||
* {@code SecurityContextServerLogoutHandler}
|
* {@code SecurityContextServerLogoutHandler}. This clears any previous handlers
|
||||||
|
* configured.
|
||||||
* @param logoutHandler
|
* @param logoutHandler
|
||||||
* @return the {@link LogoutSpec} to configure
|
* @return the {@link LogoutSpec} to configure
|
||||||
*/
|
*/
|
||||||
@ -3045,6 +3050,18 @@ public class ServerHttpSecurity {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows managing the list of {@link ServerLogoutHandler} instances.
|
||||||
|
* @param handlersConsumer {@link Consumer} for managing the list of handlers.
|
||||||
|
* @return the {@link LogoutSpec} to configure
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public LogoutSpec logoutHandler(Consumer<List<ServerLogoutHandler>> handlersConsumer) {
|
||||||
|
Assert.notNull(handlersConsumer, "consumer cannot be null");
|
||||||
|
handlersConsumer.accept(this.logoutHandlers);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures what URL a POST to will trigger a log out.
|
* Configures what URL a POST to will trigger a log out.
|
||||||
* @param logoutUrl the url to trigger a log out (i.e. "/signout" would mean a
|
* @param logoutUrl the url to trigger a log out (i.e. "/signout" would mean a
|
||||||
|
@ -25,6 +25,7 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
@ -458,7 +459,7 @@ public final class WebSocketMessageBrokerSecurityBeanDefinitionParser implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthorizationResult authorize(Supplier<Authentication> authentication,
|
public AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,
|
||||||
MessageAuthorizationContext<?> object) {
|
MessageAuthorizationContext<?> object) {
|
||||||
EvaluationContext context = this.expressionHandler.createEvaluationContext(authentication, object);
|
EvaluationContext context = this.expressionHandler.createEvaluationContext(authentication, object);
|
||||||
boolean granted = ExpressionUtils.evaluateAsBoolean(this.expression, context);
|
boolean granted = ExpressionUtils.evaluateAsBoolean(this.expression, context);
|
||||||
|
@ -29,7 +29,6 @@ import org.springframework.security.config.annotation.web.configurers.AuthorizeH
|
|||||||
import org.springframework.security.config.core.GrantedAuthorityDefaults
|
import org.springframework.security.config.core.GrantedAuthorityDefaults
|
||||||
import org.springframework.security.core.Authentication
|
import org.springframework.security.core.Authentication
|
||||||
import org.springframework.security.web.access.IpAddressAuthorizationManager
|
import org.springframework.security.web.access.IpAddressAuthorizationManager
|
||||||
import org.springframework.security.web.access.intercept.AuthorizationFilter
|
|
||||||
import org.springframework.security.web.access.intercept.RequestAuthorizationContext
|
import org.springframework.security.web.access.intercept.RequestAuthorizationContext
|
||||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher
|
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher
|
||||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher
|
import org.springframework.security.web.util.matcher.AnyRequestMatcher
|
||||||
@ -235,13 +234,13 @@ class AuthorizeHttpRequestsDsl : AbstractRequestMatcherDsl {
|
|||||||
* Specify that URLs are allowed by anyone.
|
* Specify that URLs are allowed by anyone.
|
||||||
*/
|
*/
|
||||||
val permitAll: AuthorizationManager<RequestAuthorizationContext> =
|
val permitAll: AuthorizationManager<RequestAuthorizationContext> =
|
||||||
AuthorizationManager { _: Supplier<Authentication>, _: RequestAuthorizationContext -> AuthorizationDecision(true) }
|
AuthorizationManager { _: Supplier<out Authentication>, _: RequestAuthorizationContext -> AuthorizationDecision(true) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify that URLs are not allowed by anyone.
|
* Specify that URLs are not allowed by anyone.
|
||||||
*/
|
*/
|
||||||
val denyAll: AuthorizationManager<RequestAuthorizationContext> =
|
val denyAll: AuthorizationManager<RequestAuthorizationContext> =
|
||||||
AuthorizationManager { _: Supplier<Authentication>, _: RequestAuthorizationContext -> AuthorizationDecision(false) }
|
AuthorizationManager { _: Supplier<out Authentication>, _: RequestAuthorizationContext -> AuthorizationDecision(false) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify that URLs are allowed by any authenticated user.
|
* Specify that URLs are allowed by any authenticated user.
|
||||||
|
@ -18,14 +18,22 @@ package org.springframework.security.config.annotation.web
|
|||||||
|
|
||||||
import jakarta.servlet.Filter
|
import jakarta.servlet.Filter
|
||||||
import jakarta.servlet.http.HttpServletRequest
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import org.springframework.beans.factory.ObjectProvider
|
||||||
import org.springframework.context.ApplicationContext
|
import org.springframework.context.ApplicationContext
|
||||||
|
import org.springframework.core.MethodParameter
|
||||||
|
import org.springframework.core.ResolvableType
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils
|
||||||
import org.springframework.security.authentication.AuthenticationManager
|
import org.springframework.security.authentication.AuthenticationManager
|
||||||
|
import org.springframework.security.config.Customizer
|
||||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter
|
import org.springframework.security.config.annotation.SecurityConfigurerAdapter
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository
|
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository
|
||||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository
|
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository
|
||||||
import org.springframework.security.web.DefaultSecurityFilterChain
|
import org.springframework.security.web.DefaultSecurityFilterChain
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher
|
import org.springframework.security.web.util.matcher.RequestMatcher
|
||||||
|
import org.springframework.util.ReflectionUtils
|
||||||
|
import java.lang.reflect.Method
|
||||||
|
import java.lang.reflect.Modifier
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures [HttpSecurity] using a [HttpSecurity Kotlin DSL][HttpSecurityDsl].
|
* Configures [HttpSecurity] using a [HttpSecurity Kotlin DSL][HttpSecurityDsl].
|
||||||
@ -77,6 +85,117 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
|
|||||||
var authenticationManager: AuthenticationManager? = null
|
var authenticationManager: AuthenticationManager? = null
|
||||||
val context: ApplicationContext = http.getSharedObject(ApplicationContext::class.java)
|
val context: ApplicationContext = http.getSharedObject(ApplicationContext::class.java)
|
||||||
|
|
||||||
|
init {
|
||||||
|
applyFunction1HttpSecurityDslBeans(this.context, this)
|
||||||
|
applyTopLevelFunction1SecurityDslBeans(this.context, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies all `Function1<HttpSecurity,Unit>` Beans which
|
||||||
|
* allows exposing the DSL as Beans to be applied.
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Bean
|
||||||
|
* fun httpSecurityDslBean(): HttpSecurityDsl.() -> Unit {
|
||||||
|
* return {
|
||||||
|
* headers {
|
||||||
|
* contentSecurityPolicy {
|
||||||
|
* policyDirectives = "object-src 'none'"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* redirectToHttps { }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
private fun applyFunction1HttpSecurityDslBeans(context: ApplicationContext, http: HttpSecurityDsl) : Unit {
|
||||||
|
val httpSecurityDslFnType = ResolvableType.forClassWithGenerics(Function1::class.java,
|
||||||
|
HttpSecurityDsl::class.java, Unit::class.java)
|
||||||
|
val httpSecurityDslFnProvider = context
|
||||||
|
.getBeanProvider<Function1<HttpSecurityDsl,Unit>>(httpSecurityDslFnType)
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
httpSecurityDslFnProvider.orderedStream().forEach { fn -> fn.invoke(http) }
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies all `Function1<T,Unit>` Beans such that `T` is a top level
|
||||||
|
* DSL on `HttpSecurityDsl`. This allows exposing the top level
|
||||||
|
* DSLs as Beans to be applied.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Bean
|
||||||
|
* fun httpSecurityCustomizer(): ThrowingCustomizer<HttpSecurity> {
|
||||||
|
* return ThrowingCustomizer { http -> http
|
||||||
|
* .headers { headers -> headers
|
||||||
|
* .contentSecurityPolicy { csp -> csp
|
||||||
|
* .policyDirectives("object-src 'none'")
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* .redirectToHttps(Customizer.withDefaults())
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param context the [ApplicationContext]
|
||||||
|
* @param http the [HttpSecurity]
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private fun applyTopLevelFunction1SecurityDslBeans(context: ApplicationContext, http: HttpSecurityDsl) {
|
||||||
|
val isCustomizerMethod = ReflectionUtils.MethodFilter { method: Method ->
|
||||||
|
if (Modifier.isStatic(method.modifiers)) {
|
||||||
|
return@MethodFilter false
|
||||||
|
}
|
||||||
|
if (!Modifier.isPublic(method.modifiers)) {
|
||||||
|
return@MethodFilter false
|
||||||
|
}
|
||||||
|
if (!method.canAccess(http)) {
|
||||||
|
return@MethodFilter false
|
||||||
|
}
|
||||||
|
if (method.parameterCount != 1) {
|
||||||
|
return@MethodFilter false
|
||||||
|
}
|
||||||
|
return@MethodFilter extractDslType(method) != null
|
||||||
|
}
|
||||||
|
val invokeWithEachDslBean = ReflectionUtils.MethodCallback { dslMethod: Method ->
|
||||||
|
val dslFunctionType = firstMethodResolvableType(dslMethod)!!
|
||||||
|
val dslFunctionProvider: ObjectProvider<*> = context.getBeanProvider<Any>(dslFunctionType)
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
dslFunctionProvider.orderedStream().forEach {customizer: Any -> ReflectionUtils.invokeMethod(dslMethod, http, customizer)}
|
||||||
|
}
|
||||||
|
ReflectionUtils.doWithMethods(HttpSecurityDsl::class.java, invokeWithEachDslBean, isCustomizerMethod)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From a `Method` with the first argument `Function<T,Unit>` return `T` or `null`
|
||||||
|
* if the first argument is not a `Function`.
|
||||||
|
* @return From a `Method` with the first argument `Function<T,Unit>` return `T`.
|
||||||
|
*/
|
||||||
|
private fun extractDslType(method: Method): ResolvableType? {
|
||||||
|
val functionType = firstMethodResolvableType(method)
|
||||||
|
if (!Function::class.java.isAssignableFrom(functionType.toClass())) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val functionInputType = functionType.getGeneric(0)
|
||||||
|
val securityMarker = AnnotationUtils.findAnnotation(functionInputType.toClass(), SecurityMarker::class.java)
|
||||||
|
val isSecurityDsl = securityMarker != null
|
||||||
|
if (!isSecurityDsl) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return functionInputType
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun firstMethodResolvableType(method: Method): ResolvableType {
|
||||||
|
val parameter = MethodParameter(
|
||||||
|
method, 0
|
||||||
|
)
|
||||||
|
return ResolvableType.forMethodParameter(parameter)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies a [SecurityConfigurerAdapter] to this [HttpSecurity]
|
* Applies a [SecurityConfigurerAdapter] to this [HttpSecurity]
|
||||||
*
|
*
|
||||||
|
@ -33,6 +33,7 @@ import org.springframework.security.web.util.matcher.RequestMatcher
|
|||||||
* @property channelProcessors the [ChannelProcessor] instances to use in
|
* @property channelProcessors the [ChannelProcessor] instances to use in
|
||||||
* [ChannelDecisionManagerImpl]
|
* [ChannelDecisionManagerImpl]
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(message="since 6.5 use redirectToHttps instead")
|
||||||
class RequiresChannelDsl : AbstractRequestMatcherDsl() {
|
class RequiresChannelDsl : AbstractRequestMatcherDsl() {
|
||||||
private val channelSecurityRules = mutableListOf<AuthorizationRule>()
|
private val channelSecurityRules = mutableListOf<AuthorizationRule>()
|
||||||
|
|
||||||
|
@ -16,13 +16,23 @@
|
|||||||
|
|
||||||
package org.springframework.security.config.web.server
|
package org.springframework.security.config.web.server
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.ObjectProvider
|
||||||
|
import org.springframework.context.ApplicationContext
|
||||||
|
import org.springframework.core.MethodParameter
|
||||||
|
import org.springframework.core.ResolvableType
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils
|
||||||
import org.springframework.security.authentication.ReactiveAuthenticationManager
|
import org.springframework.security.authentication.ReactiveAuthenticationManager
|
||||||
|
import org.springframework.security.config.Customizer
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository
|
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain
|
import org.springframework.security.web.server.SecurityWebFilterChain
|
||||||
import org.springframework.security.web.server.context.ServerSecurityContextRepository
|
import org.springframework.security.web.server.context.ServerSecurityContextRepository
|
||||||
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher
|
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher
|
||||||
|
import org.springframework.util.ReflectionUtils
|
||||||
import org.springframework.web.server.ServerWebExchange
|
import org.springframework.web.server.ServerWebExchange
|
||||||
import org.springframework.web.server.WebFilter
|
import org.springframework.web.server.WebFilter
|
||||||
|
import java.lang.reflect.Method
|
||||||
|
import java.lang.reflect.Modifier
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures [ServerHttpSecurity] using a [ServerHttpSecurity Kotlin DSL][ServerHttpSecurityDsl].
|
* Configures [ServerHttpSecurity] using a [ServerHttpSecurity Kotlin DSL][ServerHttpSecurityDsl].
|
||||||
@ -68,6 +78,115 @@ class ServerHttpSecurityDsl(private val http: ServerHttpSecurity, private val in
|
|||||||
var authenticationManager: ReactiveAuthenticationManager? = null
|
var authenticationManager: ReactiveAuthenticationManager? = null
|
||||||
var securityContextRepository: ServerSecurityContextRepository? = null
|
var securityContextRepository: ServerSecurityContextRepository? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
applyFunction1HttpSecurityDslBeans(this.http.applicationContext, this)
|
||||||
|
applyTopLevelFunction1SecurityDslBeans(this.http.applicationContext, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies all `Function1<ServerHttpSecurityDsl,Unit>` Beans which
|
||||||
|
* allows exposing the DSL as Beans to be applied.
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Bean
|
||||||
|
* @Order(Ordered.LOWEST_PRECEDENCE)
|
||||||
|
* fun userAuthorization(): ServerHttpSecurityDsl.() -> Unit {
|
||||||
|
* // @formatter:off
|
||||||
|
* return {
|
||||||
|
* authorizeExchange {
|
||||||
|
* authorize("/user/profile", hasRole("USER"))
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* // @formatter:on
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
private fun applyFunction1HttpSecurityDslBeans(context: ApplicationContext, http: ServerHttpSecurityDsl) : Unit {
|
||||||
|
val httpSecurityDslFnType = ResolvableType.forClassWithGenerics(Function1::class.java,
|
||||||
|
ServerHttpSecurityDsl::class.java, Unit::class.java)
|
||||||
|
val httpSecurityDslFnProvider = context
|
||||||
|
.getBeanProvider<Function1<ServerHttpSecurityDsl,Unit>>(httpSecurityDslFnType)
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
httpSecurityDslFnProvider.orderedStream().forEach { fn -> fn.invoke(http) }
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies all `Function1<T,Unit>` Beans such that `T` is a top level
|
||||||
|
* DSL on `ServerHttpSecurityDsl`. This allows exposing the top level
|
||||||
|
* DSLs as Beans to be applied.
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Bean
|
||||||
|
* fun headersSecurity(): Customizer<ServerHttpSecurity.HeaderSpec> {
|
||||||
|
* // @formatter:off
|
||||||
|
* return Customizer { headers -> headers
|
||||||
|
* .contentSecurityPolicy { csp -> csp
|
||||||
|
* .policyDirectives("object-src 'none'")
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* // @formatter:on
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param context the [ApplicationContext]
|
||||||
|
* @param http the [HttpSecurity]
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private fun applyTopLevelFunction1SecurityDslBeans(context: ApplicationContext, http: ServerHttpSecurityDsl) {
|
||||||
|
val isCustomizerMethod = ReflectionUtils.MethodFilter { method: Method ->
|
||||||
|
if (Modifier.isStatic(method.modifiers)) {
|
||||||
|
return@MethodFilter false
|
||||||
|
}
|
||||||
|
if (!Modifier.isPublic(method.modifiers)) {
|
||||||
|
return@MethodFilter false
|
||||||
|
}
|
||||||
|
if (!method.canAccess(http)) {
|
||||||
|
return@MethodFilter false
|
||||||
|
}
|
||||||
|
if (method.parameterCount != 1) {
|
||||||
|
return@MethodFilter false
|
||||||
|
}
|
||||||
|
return@MethodFilter extractDslType(method) != null
|
||||||
|
}
|
||||||
|
|
||||||
|
val invokeWithEachDslBean = ReflectionUtils.MethodCallback { dslMethod: Method ->
|
||||||
|
val dslFunctionType = firstMethodResolvableType(dslMethod)!!
|
||||||
|
val dslFunctionProvider: ObjectProvider<*> = context.getBeanProvider<Any>(dslFunctionType)
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
dslFunctionProvider.orderedStream().forEach {customizer: Any -> ReflectionUtils.invokeMethod(dslMethod, http, customizer)}
|
||||||
|
}
|
||||||
|
ReflectionUtils.doWithMethods(ServerHttpSecurityDsl::class.java, invokeWithEachDslBean, isCustomizerMethod)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From a `Method` with the first argument `Function<T,Unit>` return `T` or `null`
|
||||||
|
* if the first argument is not a `Function`.
|
||||||
|
* @return From a `Method` with the first argument `Function<T,Unit>` return `T`.
|
||||||
|
*/
|
||||||
|
private fun extractDslType(method: Method): ResolvableType? {
|
||||||
|
val functionType = firstMethodResolvableType(method)
|
||||||
|
if (!Function::class.java.isAssignableFrom(functionType.toClass())) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val functionInputType = functionType.getGeneric(0)
|
||||||
|
val securityMarker = AnnotationUtils.findAnnotation(functionInputType.toClass(), ServerSecurityMarker::class.java)
|
||||||
|
val isSecurityDsl = securityMarker != null
|
||||||
|
if (!isSecurityDsl) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return functionInputType
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun firstMethodResolvableType(method: Method): ResolvableType {
|
||||||
|
val parameter = MethodParameter(
|
||||||
|
method, 0
|
||||||
|
)
|
||||||
|
return ResolvableType.forMethodParameter(parameter)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows configuring the [ServerHttpSecurity] to only be invoked when matching the
|
* Allows configuring the [ServerHttpSecurity] to only be invoked when matching the
|
||||||
* provided [ServerWebExchangeMatcher].
|
* provided [ServerWebExchangeMatcher].
|
||||||
|
@ -78,6 +78,7 @@ import org.springframework.security.authentication.jaas.event.JaasAuthentication
|
|||||||
import org.springframework.security.authentication.jaas.event.JaasAuthenticationSuccessEvent;
|
import org.springframework.security.authentication.jaas.event.JaasAuthenticationSuccessEvent;
|
||||||
import org.springframework.security.authentication.ott.DefaultOneTimeToken;
|
import org.springframework.security.authentication.ott.DefaultOneTimeToken;
|
||||||
import org.springframework.security.authentication.ott.InvalidOneTimeTokenException;
|
import org.springframework.security.authentication.ott.InvalidOneTimeTokenException;
|
||||||
|
import org.springframework.security.authentication.ott.OneTimeTokenAuthentication;
|
||||||
import org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;
|
import org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;
|
||||||
import org.springframework.security.authentication.password.CompromisedPasswordException;
|
import org.springframework.security.authentication.password.CompromisedPasswordException;
|
||||||
import org.springframework.security.authorization.AuthorityAuthorizationDecision;
|
import org.springframework.security.authorization.AuthorityAuthorizationDecision;
|
||||||
@ -189,6 +190,7 @@ import org.springframework.security.saml2.provider.service.registration.OpenSaml
|
|||||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
|
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
|
||||||
import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;
|
import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;
|
||||||
import org.springframework.security.web.PortResolverImpl;
|
import org.springframework.security.web.PortResolverImpl;
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationFilter;
|
||||||
import org.springframework.security.web.authentication.WebAuthenticationDetails;
|
import org.springframework.security.web.authentication.WebAuthenticationDetails;
|
||||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException;
|
import org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException;
|
||||||
@ -400,6 +402,8 @@ final class SerializationSamples {
|
|||||||
});
|
});
|
||||||
generatorByClassName.put(OneTimeTokenAuthenticationToken.class,
|
generatorByClassName.put(OneTimeTokenAuthenticationToken.class,
|
||||||
(r) -> applyDetails(new OneTimeTokenAuthenticationToken("username", "token")));
|
(r) -> applyDetails(new OneTimeTokenAuthenticationToken("username", "token")));
|
||||||
|
generatorByClassName.put(OneTimeTokenAuthentication.class,
|
||||||
|
(r) -> applyDetails(new OneTimeTokenAuthentication("username", authentication.getAuthorities())));
|
||||||
generatorByClassName.put(AccessDeniedException.class,
|
generatorByClassName.put(AccessDeniedException.class,
|
||||||
(r) -> new AccessDeniedException("access denied", new RuntimeException()));
|
(r) -> new AccessDeniedException("access denied", new RuntimeException()));
|
||||||
generatorByClassName.put(AuthorizationServiceException.class,
|
generatorByClassName.put(AuthorizationServiceException.class,
|
||||||
@ -453,7 +457,7 @@ final class SerializationSamples {
|
|||||||
generatorByClassName.put(AuthenticationSuccessEvent.class,
|
generatorByClassName.put(AuthenticationSuccessEvent.class,
|
||||||
(r) -> new AuthenticationSuccessEvent(authentication));
|
(r) -> new AuthenticationSuccessEvent(authentication));
|
||||||
generatorByClassName.put(InteractiveAuthenticationSuccessEvent.class,
|
generatorByClassName.put(InteractiveAuthenticationSuccessEvent.class,
|
||||||
(r) -> new InteractiveAuthenticationSuccessEvent(authentication, Authentication.class));
|
(r) -> new InteractiveAuthenticationSuccessEvent(authentication, AuthenticationFilter.class));
|
||||||
generatorByClassName.put(LogoutSuccessEvent.class, (r) -> new LogoutSuccessEvent(authentication));
|
generatorByClassName.put(LogoutSuccessEvent.class, (r) -> new LogoutSuccessEvent(authentication));
|
||||||
generatorByClassName.put(JaasAuthenticationFailedEvent.class,
|
generatorByClassName.put(JaasAuthenticationFailedEvent.class,
|
||||||
(r) -> new JaasAuthenticationFailedEvent(authentication, new RuntimeException("message")));
|
(r) -> new JaasAuthenticationFailedEvent(authentication, new RuntimeException("message")));
|
||||||
|
@ -32,6 +32,7 @@ import org.springframework.security.authentication.AuthenticationEventPublisher;
|
|||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
import org.springframework.security.authentication.ProviderManager;
|
import org.springframework.security.authentication.ProviderManager;
|
||||||
|
import org.springframework.security.authentication.SecurityAssertions;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||||
import org.springframework.security.config.ObjectPostProcessor;
|
import org.springframework.security.config.ObjectPostProcessor;
|
||||||
@ -44,7 +45,6 @@ import org.springframework.security.config.test.SpringTestContext;
|
|||||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
|
||||||
import org.springframework.security.core.userdetails.PasswordEncodedUser;
|
import org.springframework.security.core.userdetails.PasswordEncodedUser;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
|
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
|
||||||
@ -107,8 +107,7 @@ public class AuthenticationManagerBuilderTests {
|
|||||||
.getAuthenticationManager();
|
.getAuthenticationManager();
|
||||||
Authentication auth = manager
|
Authentication auth = manager
|
||||||
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("user", "password"));
|
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("user", "password"));
|
||||||
assertThat(auth.getName()).isEqualTo("user");
|
SecurityAssertions.assertThat(auth).name("user").hasAuthority("ROLE_USER");
|
||||||
assertThat(auth.getAuthorities()).extracting(GrantedAuthority::getAuthority).containsOnly("ROLE_USER");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -119,8 +118,7 @@ public class AuthenticationManagerBuilderTests {
|
|||||||
.getAuthenticationManager();
|
.getAuthenticationManager();
|
||||||
Authentication auth = manager
|
Authentication auth = manager
|
||||||
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("user", "password"));
|
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("user", "password"));
|
||||||
assertThat(auth.getName()).isEqualTo("user");
|
SecurityAssertions.assertThat(auth).name("user").hasAuthority("ROLE_USER");
|
||||||
assertThat(auth.getAuthorities()).extracting(GrantedAuthority::getAuthority).containsOnly("ROLE_USER");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -33,6 +33,7 @@ import io.micrometer.observation.ObservationHandler;
|
|||||||
import io.micrometer.observation.ObservationRegistry;
|
import io.micrometer.observation.ObservationRegistry;
|
||||||
import io.micrometer.observation.ObservationTextPublisher;
|
import io.micrometer.observation.ObservationTextPublisher;
|
||||||
import jakarta.annotation.security.DenyAll;
|
import jakarta.annotation.security.DenyAll;
|
||||||
|
import jakarta.servlet.RequestDispatcher;
|
||||||
import org.aopalliance.aop.Advice;
|
import org.aopalliance.aop.Advice;
|
||||||
import org.aopalliance.intercept.MethodInterceptor;
|
import org.aopalliance.intercept.MethodInterceptor;
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
@ -138,6 +139,7 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
|||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||||
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.atLeastOnce;
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
import static org.mockito.Mockito.clearInvocations;
|
import static org.mockito.Mockito.clearInvocations;
|
||||||
@ -149,6 +151,7 @@ import static org.mockito.Mockito.verify;
|
|||||||
import static org.mockito.Mockito.verifyNoInteractions;
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1279,6 +1282,19 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||||||
this.mvc.perform(requestWithUser).andExpect(status().isForbidden());
|
this.mvc.perform(requestWithUser).andExpect(status().isForbidden());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gh-17761
|
||||||
|
@Test
|
||||||
|
void getWhenPostAuthorizeAuthenticationNameNotMatchThenNoExceptionExposedInRequest() throws Exception {
|
||||||
|
this.spring.register(WebMvcMethodSecurityConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithUser = get("/authorized-person")
|
||||||
|
.param("name", "john")
|
||||||
|
.with(user("rob"));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithUser)
|
||||||
|
.andExpect(request().attribute(RequestDispatcher.ERROR_EXCEPTION, nullValue()));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getWhenPostAuthorizeWithinServiceAuthenticationNameMatchesThenRespondsWithOk() throws Exception {
|
void getWhenPostAuthorizeWithinServiceAuthenticationNameMatchesThenRespondsWithOk() throws Exception {
|
||||||
this.spring.register(WebMvcMethodSecurityConfig.class, BasicController.class, BasicService.class).autowire();
|
this.spring.register(WebMvcMethodSecurityConfig.class, BasicController.class, BasicService.class).autowire();
|
||||||
|
@ -25,6 +25,7 @@ import javax.security.auth.login.LoginContext;
|
|||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpSession;
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
@ -310,7 +311,7 @@ public class NamespaceHttpTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthorizationResult authorize(Supplier<Authentication> authentication,
|
public AuthorizationResult authorize(Supplier<? extends @Nullable Authentication> authentication,
|
||||||
RequestAuthorizationContext object) {
|
RequestAuthorizationContext object) {
|
||||||
HttpServletRequest request = object.getRequest();
|
HttpServletRequest request = object.getRequest();
|
||||||
FilterInvocation invocation = new FilterInvocation(request.getContextPath(), request.getServletPath(),
|
FilterInvocation invocation = new FilterInvocation(request.getContextPath(), request.getServletPath(),
|
||||||
|
@ -28,14 +28,20 @@ import jakarta.servlet.http.HttpServletRequest;
|
|||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.InOrder;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockedStatic;
|
import org.mockito.MockedStatic;
|
||||||
|
import org.mockito.Mockito;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.context.event.EventListener;
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||||
import org.springframework.mock.web.MockHttpSession;
|
import org.springframework.mock.web.MockHttpSession;
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
@ -54,6 +60,7 @@ import org.springframework.security.config.annotation.SecurityContextChangedList
|
|||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
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.AbstractHttpConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
|
||||||
import org.springframework.security.config.test.SpringTestContext;
|
import org.springframework.security.config.test.SpringTestContext;
|
||||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
@ -82,12 +89,15 @@ import org.springframework.web.cors.CorsConfiguration;
|
|||||||
import org.springframework.web.cors.CorsConfigurationSource;
|
import org.springframework.web.cors.CorsConfigurationSource;
|
||||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
import org.springframework.web.filter.CorsFilter;
|
import org.springframework.web.filter.CorsFilter;
|
||||||
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.BDDMockito.willAnswer;
|
||||||
import static org.mockito.Mockito.atLeastOnce;
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.withSettings;
|
||||||
import static org.springframework.security.config.Customizer.withDefaults;
|
import static org.springframework.security.config.Customizer.withDefaults;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
|
||||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
|
||||||
@ -425,6 +435,77 @@ public class HttpSecurityConfigurationTests {
|
|||||||
.andExpectAll(status().isFound(), redirectedUrl("/"), authenticated());
|
.andExpectAll(status().isFound(), redirectedUrl("/"), authenticated());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void authorizeHttpRequestsCustomizerBean() throws Exception {
|
||||||
|
this.spring.register(AuthorizeRequestsBeanConfiguration.class, UserDetailsConfig.class).autowire();
|
||||||
|
|
||||||
|
Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests = this.spring
|
||||||
|
.getContext()
|
||||||
|
.getBean("authorizeRequests", Customizer.class);
|
||||||
|
|
||||||
|
verify(authorizeRequests).customize(any());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void multiAuthorizeHttpRequestsCustomizerBean() throws Exception {
|
||||||
|
this.spring.register(MultiAuthorizeRequestsBeanConfiguration.class, UserDetailsConfig.class).autowire();
|
||||||
|
|
||||||
|
Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests0 = this.spring
|
||||||
|
.getContext()
|
||||||
|
.getBean("authorizeRequests0", Customizer.class);
|
||||||
|
Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests = this.spring
|
||||||
|
.getContext()
|
||||||
|
.getBean("authorizeRequests", Customizer.class);
|
||||||
|
InOrder inOrder = Mockito.inOrder(authorizeRequests0, authorizeRequests);
|
||||||
|
|
||||||
|
ArgumentCaptor<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> arg0 = ArgumentCaptor
|
||||||
|
.forClass(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry.class);
|
||||||
|
ArgumentCaptor<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> arg1 = ArgumentCaptor
|
||||||
|
.forClass(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry.class);
|
||||||
|
inOrder.verify(authorizeRequests0).customize(arg0.capture());
|
||||||
|
inOrder.verify(authorizeRequests).customize(arg1.capture());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void disableAuthorizeHttpRequestsCustomizerBean() throws Exception {
|
||||||
|
this.spring.register(AuthorizeRequestsBeanConfiguration.class, UserDetailsConfig.class).autowire();
|
||||||
|
|
||||||
|
Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests = this.spring
|
||||||
|
.getContext()
|
||||||
|
.getBean("authorizeRequests", Customizer.class);
|
||||||
|
|
||||||
|
verify(authorizeRequests).customize(any());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void httpSecurityCustomizerBean() throws Exception {
|
||||||
|
this.spring.register(HttpSecurityCustomizerBeanConfiguration.class, UserDetailsConfig.class).autowire();
|
||||||
|
|
||||||
|
Customizer<HttpSecurity> httpSecurityCustomizer = this.spring.getContext()
|
||||||
|
.getBean("httpSecurityCustomizer", Customizer.class);
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpSecurity> arg0 = ArgumentCaptor.forClass(HttpSecurity.class);
|
||||||
|
verify(httpSecurityCustomizer).customize(arg0.capture());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void multiHttpSecurityCustomizerBean() throws Exception {
|
||||||
|
this.spring.register(MultiHttpSecurityCustomizerBeanConfiguration.class, UserDetailsConfig.class).autowire();
|
||||||
|
|
||||||
|
Customizer<HttpSecurity> httpSecurityCustomizer = this.spring.getContext()
|
||||||
|
.getBean("httpSecurityCustomizer", Customizer.class);
|
||||||
|
Customizer<HttpSecurity> httpSecurityCustomizer0 = this.spring.getContext()
|
||||||
|
.getBean("httpSecurityCustomizer0", Customizer.class);
|
||||||
|
InOrder inOrder = Mockito.inOrder(httpSecurityCustomizer0, httpSecurityCustomizer);
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpSecurity> arg0 = ArgumentCaptor.forClass(HttpSecurity.class);
|
||||||
|
ArgumentCaptor<HttpSecurity> arg1 = ArgumentCaptor.forClass(HttpSecurity.class);
|
||||||
|
inOrder.verify(httpSecurityCustomizer0).customize(arg0.capture());
|
||||||
|
inOrder.verify(httpSecurityCustomizer).customize(arg1.capture());
|
||||||
|
}
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
static class NameController {
|
static class NameController {
|
||||||
|
|
||||||
@ -785,6 +866,134 @@ public class HttpSecurityConfigurationTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableWebMvc
|
||||||
|
static class AuthorizeRequestsBeanConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain noAuthorizeSecurity(HttpSecurity http) throws Exception {
|
||||||
|
http.httpBasic(withDefaults());
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
static Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests()
|
||||||
|
throws Exception {
|
||||||
|
Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authz = mock(
|
||||||
|
Customizer.class, withSettings().name("authz"));
|
||||||
|
// prevent validation errors of no authorization rules being defined
|
||||||
|
willAnswer(((invocation) -> {
|
||||||
|
AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry requests = invocation
|
||||||
|
.getArgument(0);
|
||||||
|
requests.anyRequest().authenticated();
|
||||||
|
return null;
|
||||||
|
})).given(authz).customize(any());
|
||||||
|
return authz;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class PublicController {
|
||||||
|
|
||||||
|
@GetMapping("/public")
|
||||||
|
String permitAll() {
|
||||||
|
return "public";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableWebMvc
|
||||||
|
static class DisableAuthorizeRequestsBeanConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
|
||||||
|
http.httpBasic(withDefaults());
|
||||||
|
// @formatter:off
|
||||||
|
http.authorizeHttpRequests((requests) -> requests
|
||||||
|
.anyRequest().permitAll()
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
static Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests()
|
||||||
|
throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
return (requests) -> requests
|
||||||
|
.anyRequest().denyAll();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class PublicController {
|
||||||
|
|
||||||
|
@GetMapping("/public")
|
||||||
|
String permitAll() {
|
||||||
|
return "public";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@Import(AuthorizeRequestsBeanConfiguration.class)
|
||||||
|
static class MultiAuthorizeRequestsBeanConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
static Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeRequests0()
|
||||||
|
throws Exception {
|
||||||
|
return mock(Customizer.class, withSettings().name("authz0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableWebMvc
|
||||||
|
static class HttpSecurityCustomizerBeanConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
|
||||||
|
http.httpBasic(withDefaults());
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
static Customizer<HttpSecurity> httpSecurityCustomizer() {
|
||||||
|
return mock(Customizer.class, withSettings().name("httpSecurityCustomizer"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class PublicController {
|
||||||
|
|
||||||
|
@GetMapping("/public")
|
||||||
|
String permitAll() {
|
||||||
|
return "public";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@Import(HttpSecurityCustomizerBeanConfiguration.class)
|
||||||
|
static class MultiHttpSecurityCustomizerBeanConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
static Customizer<HttpSecurity> httpSecurityCustomizer0() throws Exception {
|
||||||
|
return mock(Customizer.class, withSettings().name("httpSecurityCustomizer0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private static class TestCompromisedPasswordChecker implements CompromisedPasswordChecker {
|
private static class TestCompromisedPasswordChecker implements CompromisedPasswordChecker {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -24,6 +24,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.security.config.ObjectPostProcessor;
|
import org.springframework.security.config.ObjectPostProcessor;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
@ -34,6 +35,7 @@ import org.springframework.security.core.userdetails.AuthenticationUserDetailsSe
|
|||||||
import org.springframework.security.core.userdetails.User;
|
import org.springframework.security.core.userdetails.User;
|
||||||
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers;
|
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||||
import org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource;
|
import org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource;
|
||||||
import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter;
|
import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
@ -64,18 +66,16 @@ public class JeeConfigurerTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void configureWhenRegisteringObjectPostProcessorThenInvokedOnJ2eePreAuthenticatedProcessingFilter() {
|
public void configureWhenRegisteringObjectPostProcessorThenInvokedOnJ2eePreAuthenticatedProcessingFilter() {
|
||||||
ObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
|
|
||||||
this.spring.register(ObjectPostProcessorConfig.class).autowire();
|
this.spring.register(ObjectPostProcessorConfig.class).autowire();
|
||||||
verify(ObjectPostProcessorConfig.objectPostProcessor)
|
ObjectPostProcessor<Object> objectPostProcessor = this.spring.getContext().getBean(ObjectPostProcessor.class);
|
||||||
.postProcess(any(J2eePreAuthenticatedProcessingFilter.class));
|
verify(objectPostProcessor).postProcess(any(J2eePreAuthenticatedProcessingFilter.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void configureWhenRegisteringObjectPostProcessorThenInvokedOnJ2eeBasedPreAuthenticatedWebAuthenticationDetailsSource() {
|
public void configureWhenRegisteringObjectPostProcessorThenInvokedOnJ2eeBasedPreAuthenticatedWebAuthenticationDetailsSource() {
|
||||||
ObjectPostProcessorConfig.objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
|
|
||||||
this.spring.register(ObjectPostProcessorConfig.class).autowire();
|
this.spring.register(ObjectPostProcessorConfig.class).autowire();
|
||||||
verify(ObjectPostProcessorConfig.objectPostProcessor)
|
ObjectPostProcessor<Object> objectPostProcessor = this.spring.getContext().getBean(ObjectPostProcessor.class);
|
||||||
.postProcess(any(J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.class));
|
verify(objectPostProcessor).postProcess(any(J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -135,12 +135,14 @@ public class JeeConfigurerTests {
|
|||||||
public void requestWhenCustomAuthenticatedUserDetailsServiceInLambdaThenCustomAuthenticatedUserDetailsServiceUsed()
|
public void requestWhenCustomAuthenticatedUserDetailsServiceInLambdaThenCustomAuthenticatedUserDetailsServiceUsed()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
this.spring.register(JeeCustomAuthenticatedUserDetailsServiceConfig.class).autowire();
|
this.spring.register(JeeCustomAuthenticatedUserDetailsServiceConfig.class).autowire();
|
||||||
|
AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> userDetailsService = this.spring
|
||||||
|
.getContext()
|
||||||
|
.getBean(AuthenticationUserDetailsService.class);
|
||||||
Principal user = mock(Principal.class);
|
Principal user = mock(Principal.class);
|
||||||
User userDetails = new User("user", "N/A", true, true, true, true,
|
User userDetails = new User("user", "N/A", true, true, true, true,
|
||||||
AuthorityUtils.createAuthorityList("ROLE_USER"));
|
AuthorityUtils.createAuthorityList("ROLE_USER"));
|
||||||
given(user.getName()).willReturn("user");
|
given(user.getName()).willReturn("user");
|
||||||
given(JeeCustomAuthenticatedUserDetailsServiceConfig.authenticationUserDetailsService.loadUserDetails(any()))
|
given(userDetailsService.loadUserDetails(any())).willReturn(userDetails);
|
||||||
.willReturn(userDetails);
|
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
MockHttpServletRequestBuilder authRequest = get("/")
|
MockHttpServletRequestBuilder authRequest = get("/")
|
||||||
.principal(user)
|
.principal(user)
|
||||||
@ -157,7 +159,7 @@ public class JeeConfigurerTests {
|
|||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
static class ObjectPostProcessorConfig {
|
static class ObjectPostProcessorConfig {
|
||||||
|
|
||||||
static ObjectPostProcessor<Object> objectPostProcessor;
|
ObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
@ -169,8 +171,9 @@ public class JeeConfigurerTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
static ObjectPostProcessor<Object> objectPostProcessor() {
|
@Primary
|
||||||
return objectPostProcessor;
|
ObjectPostProcessor<Object> objectPostProcessor() {
|
||||||
|
return this.objectPostProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -245,7 +248,7 @@ public class JeeConfigurerTests {
|
|||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
public static class JeeCustomAuthenticatedUserDetailsServiceConfig {
|
public static class JeeCustomAuthenticatedUserDetailsServiceConfig {
|
||||||
|
|
||||||
static AuthenticationUserDetailsService authenticationUserDetailsService = mock(
|
private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService = mock(
|
||||||
AuthenticationUserDetailsService.class);
|
AuthenticationUserDetailsService.class);
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ -256,12 +259,17 @@ public class JeeConfigurerTests {
|
|||||||
.anyRequest().hasRole("USER")
|
.anyRequest().hasRole("USER")
|
||||||
)
|
)
|
||||||
.jee((jee) -> jee
|
.jee((jee) -> jee
|
||||||
.authenticatedUserDetailsService(authenticationUserDetailsService)
|
.authenticatedUserDetailsService(this.authenticationUserDetailsService)
|
||||||
);
|
);
|
||||||
return http.build();
|
return http.build();
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService() {
|
||||||
|
return this.authenticationUserDetailsService;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -300,7 +300,15 @@ public class WebAuthnConfigurerTests {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
return http.formLogin(Customizer.withDefaults()).webAuthn(Customizer.withDefaults()).build();
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.formLogin(Customizer.withDefaults())
|
||||||
|
.webAuthn((authn) -> authn
|
||||||
|
.rpId("spring.io")
|
||||||
|
.rpName("spring")
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -316,7 +324,14 @@ public class WebAuthnConfigurerTests {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
return http.webAuthn(Customizer.withDefaults()).build();
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.webAuthn((authn) -> authn
|
||||||
|
.rpId("spring.io")
|
||||||
|
.rpName("spring")
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -332,9 +347,16 @@ public class WebAuthnConfigurerTests {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
return http.formLogin(Customizer.withDefaults())
|
// @formatter:off
|
||||||
.webAuthn((webauthn) -> webauthn.disableDefaultRegistrationPage(true))
|
http
|
||||||
.build();
|
.formLogin(Customizer.withDefaults())
|
||||||
|
.webAuthn((authn) -> authn
|
||||||
|
.rpId("spring.io")
|
||||||
|
.rpName("spring")
|
||||||
|
.disableDefaultRegistrationPage(true)
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -350,9 +372,18 @@ public class WebAuthnConfigurerTests {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
return http.formLogin((login) -> login.loginPage("/custom-login-page"))
|
// @formatter:off
|
||||||
.webAuthn((webauthn) -> webauthn.disableDefaultRegistrationPage(true))
|
http
|
||||||
.build();
|
.formLogin((login) -> login
|
||||||
|
.loginPage("/custom-login-page")
|
||||||
|
)
|
||||||
|
.webAuthn((authn) -> authn
|
||||||
|
.rpId("spring.io")
|
||||||
|
.rpName("spring")
|
||||||
|
.disableDefaultRegistrationPage(true)
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ import org.springframework.mock.web.MockFilterChain;
|
|||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.mock.web.MockHttpServletResponse;
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
|
import org.springframework.security.authentication.SecurityAssertions;
|
||||||
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
|
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
|
||||||
import org.springframework.security.config.Customizer;
|
import org.springframework.security.config.Customizer;
|
||||||
import org.springframework.security.config.ObjectPostProcessor;
|
import org.springframework.security.config.ObjectPostProcessor;
|
||||||
@ -217,10 +218,9 @@ public class OAuth2LoginConfigurerTests {
|
|||||||
Authentication authentication = this.securityContextRepository
|
Authentication authentication = this.securityContextRepository
|
||||||
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
||||||
.getAuthentication();
|
.getAuthentication();
|
||||||
assertThat(authentication.getAuthorities()).hasSize(1);
|
SecurityAssertions.assertThat(authentication)
|
||||||
assertThat(authentication.getAuthorities()).first()
|
.hasAuthority("OAUTH2_USER")
|
||||||
.isInstanceOf(OAuth2UserAuthority.class)
|
.isInstanceOf(OAuth2UserAuthority.class);
|
||||||
.hasToString("OAUTH2_USER");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -234,10 +234,9 @@ public class OAuth2LoginConfigurerTests {
|
|||||||
Authentication authentication = this.securityContextRepository
|
Authentication authentication = this.securityContextRepository
|
||||||
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
||||||
.getAuthentication();
|
.getAuthentication();
|
||||||
assertThat(authentication.getAuthorities()).hasSize(1);
|
SecurityAssertions.assertThat(authentication)
|
||||||
assertThat(authentication.getAuthorities()).first()
|
.hasAuthority("OAUTH2_USER")
|
||||||
.isInstanceOf(OAuth2UserAuthority.class)
|
.isInstanceOf(OAuth2UserAuthority.class);
|
||||||
.hasToString("OAUTH2_USER");
|
|
||||||
SecurityContextHolderStrategy strategy = this.context.getBean(SecurityContextHolderStrategy.class);
|
SecurityContextHolderStrategy strategy = this.context.getBean(SecurityContextHolderStrategy.class);
|
||||||
verify(strategy, atLeastOnce()).getDeferredContext();
|
verify(strategy, atLeastOnce()).getDeferredContext();
|
||||||
SecurityContextChangedListener listener = this.context.getBean(SecurityContextChangedListener.class);
|
SecurityContextChangedListener listener = this.context.getBean(SecurityContextChangedListener.class);
|
||||||
@ -255,10 +254,9 @@ public class OAuth2LoginConfigurerTests {
|
|||||||
Authentication authentication = this.securityContextRepository
|
Authentication authentication = this.securityContextRepository
|
||||||
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
||||||
.getAuthentication();
|
.getAuthentication();
|
||||||
assertThat(authentication.getAuthorities()).hasSize(1);
|
SecurityAssertions.assertThat(authentication)
|
||||||
assertThat(authentication.getAuthorities()).first()
|
.hasAuthority("OAUTH2_USER")
|
||||||
.isInstanceOf(OAuth2UserAuthority.class)
|
.isInstanceOf(OAuth2UserAuthority.class);
|
||||||
.hasToString("OAUTH2_USER");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// gh-6009
|
// gh-6009
|
||||||
@ -296,9 +294,7 @@ public class OAuth2LoginConfigurerTests {
|
|||||||
Authentication authentication = this.securityContextRepository
|
Authentication authentication = this.securityContextRepository
|
||||||
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
||||||
.getAuthentication();
|
.getAuthentication();
|
||||||
assertThat(authentication.getAuthorities()).hasSize(2);
|
SecurityAssertions.assertThat(authentication).hasAuthorities("OAUTH2_USER", "ROLE_OAUTH2_USER");
|
||||||
assertThat(authentication.getAuthorities()).first().hasToString("OAUTH2_USER");
|
|
||||||
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -317,9 +313,7 @@ public class OAuth2LoginConfigurerTests {
|
|||||||
Authentication authentication = this.securityContextRepository
|
Authentication authentication = this.securityContextRepository
|
||||||
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
||||||
.getAuthentication();
|
.getAuthentication();
|
||||||
assertThat(authentication.getAuthorities()).hasSize(2);
|
SecurityAssertions.assertThat(authentication).hasAuthorities("OAUTH2_USER", "ROLE_OAUTH2_USER");
|
||||||
assertThat(authentication.getAuthorities()).first().hasToString("OAUTH2_USER");
|
|
||||||
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -338,9 +332,7 @@ public class OAuth2LoginConfigurerTests {
|
|||||||
Authentication authentication = this.securityContextRepository
|
Authentication authentication = this.securityContextRepository
|
||||||
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
||||||
.getAuthentication();
|
.getAuthentication();
|
||||||
assertThat(authentication.getAuthorities()).hasSize(2);
|
SecurityAssertions.assertThat(authentication).hasAuthorities("OAUTH2_USER", "ROLE_OAUTH2_USER");
|
||||||
assertThat(authentication.getAuthorities()).first().hasToString("OAUTH2_USER");
|
|
||||||
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// gh-5488
|
// gh-5488
|
||||||
@ -361,10 +353,9 @@ public class OAuth2LoginConfigurerTests {
|
|||||||
Authentication authentication = this.securityContextRepository
|
Authentication authentication = this.securityContextRepository
|
||||||
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
||||||
.getAuthentication();
|
.getAuthentication();
|
||||||
assertThat(authentication.getAuthorities()).hasSize(1);
|
SecurityAssertions.assertThat(authentication)
|
||||||
assertThat(authentication.getAuthorities()).first()
|
.hasAuthority("OAUTH2_USER")
|
||||||
.isInstanceOf(OAuth2UserAuthority.class)
|
.isInstanceOf(OAuth2UserAuthority.class);
|
||||||
.hasToString("OAUTH2_USER");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// gh-5521
|
// gh-5521
|
||||||
@ -570,10 +561,7 @@ public class OAuth2LoginConfigurerTests {
|
|||||||
Authentication authentication = this.securityContextRepository
|
Authentication authentication = this.securityContextRepository
|
||||||
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
||||||
.getAuthentication();
|
.getAuthentication();
|
||||||
assertThat(authentication.getAuthorities()).hasSize(1);
|
SecurityAssertions.assertThat(authentication).hasAuthority("OIDC_USER").isInstanceOf(OidcUserAuthority.class);
|
||||||
assertThat(authentication.getAuthorities()).first()
|
|
||||||
.isInstanceOf(OidcUserAuthority.class)
|
|
||||||
.hasToString("OIDC_USER");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -593,9 +581,7 @@ public class OAuth2LoginConfigurerTests {
|
|||||||
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
||||||
.getAuthentication();
|
.getAuthentication();
|
||||||
assertThat(authentication.getAuthorities()).hasSize(1);
|
assertThat(authentication.getAuthorities()).hasSize(1);
|
||||||
assertThat(authentication.getAuthorities()).first()
|
SecurityAssertions.assertThat(authentication).hasAuthority("OIDC_USER").isInstanceOf(OidcUserAuthority.class);
|
||||||
.isInstanceOf(OidcUserAuthority.class)
|
|
||||||
.hasToString("OIDC_USER");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -614,9 +600,7 @@ public class OAuth2LoginConfigurerTests {
|
|||||||
Authentication authentication = this.securityContextRepository
|
Authentication authentication = this.securityContextRepository
|
||||||
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
||||||
.getAuthentication();
|
.getAuthentication();
|
||||||
assertThat(authentication.getAuthorities()).hasSize(2);
|
SecurityAssertions.assertThat(authentication).hasAuthorities("OIDC_USER", "ROLE_OIDC_USER");
|
||||||
assertThat(authentication.getAuthorities()).first().hasToString("OIDC_USER");
|
|
||||||
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OIDC_USER");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -635,9 +619,7 @@ public class OAuth2LoginConfigurerTests {
|
|||||||
Authentication authentication = this.securityContextRepository
|
Authentication authentication = this.securityContextRepository
|
||||||
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
||||||
.getAuthentication();
|
.getAuthentication();
|
||||||
assertThat(authentication.getAuthorities()).hasSize(2);
|
SecurityAssertions.assertThat(authentication).hasAuthorities("OIDC_USER", "ROLE_OIDC_USER");
|
||||||
assertThat(authentication.getAuthorities()).first().hasToString("OIDC_USER");
|
|
||||||
assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OIDC_USER");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -690,11 +672,7 @@ public class OAuth2LoginConfigurerTests {
|
|||||||
Authentication authentication = this.securityContextRepository
|
Authentication authentication = this.securityContextRepository
|
||||||
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
.loadContext(new HttpRequestResponseHolder(this.request, this.response))
|
||||||
.getAuthentication();
|
.getAuthentication();
|
||||||
assertThat(authentication.getAuthorities()).hasSize(1);
|
SecurityAssertions.assertThat(authentication).hasAuthority("OIDC_USER").isInstanceOf(OidcUserAuthority.class);
|
||||||
assertThat(authentication.getAuthorities()).first()
|
|
||||||
.isInstanceOf(OidcUserAuthority.class)
|
|
||||||
.hasToString("OIDC_USER");
|
|
||||||
|
|
||||||
// Ensure shared objects set for OAuth2 Client are not used
|
// Ensure shared objects set for OAuth2 Client are not used
|
||||||
ClientRegistrationRepository clientRegistrationRepository = this.spring.getContext()
|
ClientRegistrationRepository clientRegistrationRepository = this.spring.getContext()
|
||||||
.getBean(ClientRegistrationRepository.class);
|
.getBean(ClientRegistrationRepository.class);
|
||||||
|
@ -2674,6 +2674,7 @@ public class OAuth2ResourceServerConfigurerTests {
|
|||||||
String requiresReadScope(JwtAuthenticationToken token) {
|
String requiresReadScope(JwtAuthenticationToken token) {
|
||||||
return token.getAuthorities()
|
return token.getAuthorities()
|
||||||
.stream()
|
.stream()
|
||||||
|
.filter((ga) -> ga.getAuthority().startsWith("SCOPE_"))
|
||||||
.map(GrantedAuthority::getAuthority)
|
.map(GrantedAuthority::getAuthority)
|
||||||
.collect(Collectors.toList())
|
.collect(Collectors.toList())
|
||||||
.toString();
|
.toString();
|
||||||
|
@ -29,12 +29,17 @@ import io.micrometer.observation.ObservationRegistry;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.InOrder;
|
||||||
|
import org.mockito.Mockito;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
|
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
|
||||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
import org.springframework.security.authentication.password.CompromisedPasswordDecision;
|
import org.springframework.security.authentication.password.CompromisedPasswordDecision;
|
||||||
@ -46,6 +51,7 @@ import org.springframework.security.config.test.SpringTestContext;
|
|||||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
import org.springframework.security.config.users.ReactiveAuthenticationTestConfiguration;
|
import org.springframework.security.config.users.ReactiveAuthenticationTestConfiguration;
|
||||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||||
|
import org.springframework.security.config.web.server.ServerHttpSecurity.AuthorizeExchangeSpec;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
|
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
@ -267,6 +273,47 @@ public class ServerHttpSecurityConfigurationTests {
|
|||||||
assertThat(contexts.next().getContextualName()).isEqualTo("security filterchain after");
|
assertThat(contexts.next().getContextualName()).isEqualTo("security filterchain after");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void authorizeExchangeCustomizerBean() {
|
||||||
|
this.spring.register(AuthorizeExchangeCustomizerBeanConfig.class).autowire();
|
||||||
|
Customizer<AuthorizeExchangeSpec> authzCustomizer = this.spring.getContext().getBean("authz", Customizer.class);
|
||||||
|
|
||||||
|
ArgumentCaptor<AuthorizeExchangeSpec> arg0 = ArgumentCaptor.forClass(AuthorizeExchangeSpec.class);
|
||||||
|
verify(authzCustomizer).customize(arg0.capture());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void multiAuthorizeExchangeCustomizerBean() {
|
||||||
|
this.spring.register(MultiAuthorizeExchangeCustomizerBeanConfig.class).autowire();
|
||||||
|
Customizer<AuthorizeExchangeSpec> authzCustomizer = this.spring.getContext().getBean("authz", Customizer.class);
|
||||||
|
|
||||||
|
ArgumentCaptor<AuthorizeExchangeSpec> arg0 = ArgumentCaptor.forClass(AuthorizeExchangeSpec.class);
|
||||||
|
verify(authzCustomizer).customize(arg0.capture());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void serverHttpSecurityCustomizerBean() {
|
||||||
|
this.spring.register(ServerHttpSecurityCustomizerConfig.class).autowire();
|
||||||
|
Customizer<ServerHttpSecurity> httpSecurityCustomizer = this.spring.getContext()
|
||||||
|
.getBean("httpSecurityCustomizer", Customizer.class);
|
||||||
|
|
||||||
|
ArgumentCaptor<ServerHttpSecurity> arg0 = ArgumentCaptor.forClass(ServerHttpSecurity.class);
|
||||||
|
verify(httpSecurityCustomizer).customize(arg0.capture());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void multiServerHttpSecurityCustomizerBean() {
|
||||||
|
this.spring.register(MultiServerHttpSecurityCustomizerConfig.class).autowire();
|
||||||
|
Customizer<ServerHttpSecurity> httpSecurityCustomizer = this.spring.getContext()
|
||||||
|
.getBean("httpSecurityCustomizer", Customizer.class);
|
||||||
|
Customizer<ServerHttpSecurity> httpSecurityCustomizer0 = this.spring.getContext()
|
||||||
|
.getBean("httpSecurityCustomizer0", Customizer.class);
|
||||||
|
InOrder inOrder = Mockito.inOrder(httpSecurityCustomizer0, httpSecurityCustomizer);
|
||||||
|
ArgumentCaptor<ServerHttpSecurity> arg0 = ArgumentCaptor.forClass(ServerHttpSecurity.class);
|
||||||
|
inOrder.verify(httpSecurityCustomizer0).customize(arg0.capture());
|
||||||
|
inOrder.verify(httpSecurityCustomizer).customize(arg0.capture());
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
static class SubclassConfig extends ServerHttpSecurityConfiguration {
|
static class SubclassConfig extends ServerHttpSecurityConfiguration {
|
||||||
|
|
||||||
@ -474,4 +521,64 @@ public class ServerHttpSecurityConfigurationTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@EnableWebFlux
|
||||||
|
@EnableWebFluxSecurity
|
||||||
|
@Import(UserDetailsConfig.class)
|
||||||
|
static class AuthorizeExchangeCustomizerBeanConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
static Customizer<AuthorizeExchangeSpec> authz() {
|
||||||
|
return mock(Customizer.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@Import(AuthorizeExchangeCustomizerBeanConfig.class)
|
||||||
|
static class MultiAuthorizeExchangeCustomizerBeanConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
Customizer<AuthorizeExchangeSpec> authz0() {
|
||||||
|
return mock(Customizer.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@EnableWebFlux
|
||||||
|
@EnableWebFluxSecurity
|
||||||
|
@Import(UserDetailsConfig.class)
|
||||||
|
static class ServerHttpSecurityCustomizerConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
static Customizer<ServerHttpSecurity> httpSecurityCustomizer() {
|
||||||
|
return mock(Customizer.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@Import(ServerHttpSecurityCustomizerConfig.class)
|
||||||
|
static class MultiServerHttpSecurityCustomizerConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
static Customizer<ServerHttpSecurity> httpSecurityCustomizer0() {
|
||||||
|
return mock(Customizer.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ public class UserServiceBeanDefinitionParserTests {
|
|||||||
System.setProperty("principal.pass", "joespassword");
|
System.setProperty("principal.pass", "joespassword");
|
||||||
System.setProperty("principal.authorities", "ROLE_A,ROLE_B");
|
System.setProperty("principal.authorities", "ROLE_A,ROLE_B");
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
setContext("<b:bean class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer'/>"
|
setContext("<b:bean class='org.springframework.context.support.PropertySourcesPlaceholderConfigurer'/>"
|
||||||
+ "<user-service id='service'>"
|
+ "<user-service id='service'>"
|
||||||
+ " <user name='${principal.name}' password='${principal.pass}' authorities='${principal.authorities}'/>"
|
+ " <user name='${principal.name}' password='${principal.pass}' authorities='${principal.authorities}'/>"
|
||||||
+ "</user-service>");
|
+ "</user-service>");
|
||||||
|
@ -92,7 +92,7 @@ public class FilterSecurityMetadataSourceBeanDefinitionParserTests {
|
|||||||
public void interceptUrlsSupportPropertyPlaceholders() {
|
public void interceptUrlsSupportPropertyPlaceholders() {
|
||||||
System.setProperty("secure.url", "/secure");
|
System.setProperty("secure.url", "/secure");
|
||||||
System.setProperty("secure.role", "ROLE_A");
|
System.setProperty("secure.role", "ROLE_A");
|
||||||
setContext("<b:bean class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer'/>"
|
setContext("<b:bean class='org.springframework.context.support.PropertySourcesPlaceholderConfigurer'/>"
|
||||||
+ "<filter-security-metadata-source id='fids' use-expressions='false'>"
|
+ "<filter-security-metadata-source id='fids' use-expressions='false'>"
|
||||||
+ " <intercept-url pattern='${secure.url}' access='${secure.role}'/>"
|
+ " <intercept-url pattern='${secure.url}' access='${secure.role}'/>"
|
||||||
+ "</filter-security-metadata-source>");
|
+ "</filter-security-metadata-source>");
|
||||||
|
@ -24,11 +24,11 @@ import org.junit.jupiter.api.BeforeEach;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
|
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||||
import org.springframework.mock.web.MockFilterChain;
|
import org.springframework.mock.web.MockFilterChain;
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.mock.web.MockHttpServletResponse;
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
@ -125,11 +125,11 @@ public class CustomHttpSecurityConfigurerTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
|
static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
|
||||||
// Typically externalize this as a properties file
|
// Typically externalize this as a properties file
|
||||||
Properties properties = new Properties();
|
Properties properties = new Properties();
|
||||||
properties.setProperty("permitAllPattern", "/public/**");
|
properties.setProperty("permitAllPattern", "/public/**");
|
||||||
PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer();
|
PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
|
||||||
propertyPlaceholderConfigurer.setProperties(properties);
|
propertyPlaceholderConfigurer.setProperties(properties);
|
||||||
return propertyPlaceholderConfigurer;
|
return propertyPlaceholderConfigurer;
|
||||||
}
|
}
|
||||||
@ -153,11 +153,11 @@ public class CustomHttpSecurityConfigurerTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
|
static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
|
||||||
// Typically externalize this as a properties file
|
// Typically externalize this as a properties file
|
||||||
Properties properties = new Properties();
|
Properties properties = new Properties();
|
||||||
properties.setProperty("permitAllPattern", "/public/**");
|
properties.setProperty("permitAllPattern", "/public/**");
|
||||||
PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer();
|
PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
|
||||||
propertyPlaceholderConfigurer.setProperties(properties);
|
propertyPlaceholderConfigurer.setProperties(properties);
|
||||||
return propertyPlaceholderConfigurer;
|
return propertyPlaceholderConfigurer;
|
||||||
}
|
}
|
||||||
|
@ -464,7 +464,9 @@ public class MethodSecurityBeanDefinitionParserTests {
|
|||||||
static class MyAuthorizationManager implements AuthorizationManager<MethodInvocation> {
|
static class MyAuthorizationManager implements AuthorizationManager<MethodInvocation> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthorizationResult authorize(Supplier<Authentication> authentication, MethodInvocation object) {
|
public AuthorizationResult authorize(
|
||||||
|
Supplier<? extends @org.jspecify.annotations.Nullable Authentication> authentication,
|
||||||
|
MethodInvocation object) {
|
||||||
return new AuthorizationDecision("bob".equals(authentication.get().getName()));
|
return new AuthorizationDecision("bob".equals(authentication.get().getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,8 +30,11 @@ import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostP
|
|||||||
import org.springframework.mock.web.MockServletConfig;
|
import org.springframework.mock.web.MockServletConfig;
|
||||||
import org.springframework.security.config.BeanIds;
|
import org.springframework.security.config.BeanIds;
|
||||||
import org.springframework.security.config.util.InMemoryXmlWebApplicationContext;
|
import org.springframework.security.config.util.InMemoryXmlWebApplicationContext;
|
||||||
|
import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers;
|
||||||
|
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
|
||||||
import org.springframework.security.web.servlet.MockServletContext;
|
import org.springframework.security.web.servlet.MockServletContext;
|
||||||
import org.springframework.test.context.web.GenericXmlWebContextLoader;
|
import org.springframework.test.context.web.GenericXmlWebContextLoader;
|
||||||
|
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.request.RequestPostProcessor;
|
import org.springframework.test.web.servlet.request.RequestPostProcessor;
|
||||||
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
|
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
|
||||||
@ -42,6 +45,7 @@ import org.springframework.web.context.WebApplicationContext;
|
|||||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||||
import org.springframework.web.context.support.XmlWebApplicationContext;
|
import org.springframework.web.context.support.XmlWebApplicationContext;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
import org.springframework.web.server.WebFilter;
|
||||||
|
|
||||||
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
|
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
|
||||||
|
|
||||||
@ -156,6 +160,18 @@ public class SpringTestContext implements Closeable {
|
|||||||
// @formatter:on
|
// @formatter:on
|
||||||
this.context.getBeanFactory().registerResolvableDependency(MockMvc.class, mockMvc);
|
this.context.getBeanFactory().registerResolvableDependency(MockMvc.class, mockMvc);
|
||||||
}
|
}
|
||||||
|
String webFluxSecurityBean = "org.springframework.security.config.annotation.web.reactive.WebFluxSecurityConfiguration.WebFilterChainFilter";
|
||||||
|
if (this.context.containsBean(webFluxSecurityBean)) {
|
||||||
|
WebFilter springSecurityFilter = this.context.getBean(webFluxSecurityBean, WebFilter.class);
|
||||||
|
// @formatter:off
|
||||||
|
WebTestClient webTest = WebTestClient
|
||||||
|
.bindToController(new WebTestClientBuilder.Http200RestController())
|
||||||
|
.webFilter(springSecurityFilter)
|
||||||
|
.apply(SecurityMockServerConfigurers.springSecurity())
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
this.context.getBeanFactory().registerResolvableDependency(WebTestClient.class, webTest);
|
||||||
|
}
|
||||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||||
bpp.setBeanFactory(this.context.getBeanFactory());
|
bpp.setBeanFactory(this.context.getBeanFactory());
|
||||||
bpp.processInjection(this.test);
|
bpp.processInjection(this.test);
|
||||||
|
@ -16,18 +16,27 @@
|
|||||||
|
|
||||||
package org.springframework.security.config.web.server;
|
package org.springframework.security.config.web.server;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.openqa.selenium.WebDriver;
|
import org.openqa.selenium.WebDriver;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder;
|
import org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.htmlunit.server.WebTestClientHtmlUnitDriverBuilder;
|
import org.springframework.security.htmlunit.server.WebTestClientHtmlUnitDriverBuilder;
|
||||||
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
|
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||||
|
import org.springframework.security.web.server.authentication.logout.ServerLogoutHandler;
|
||||||
|
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
|
||||||
import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
|
import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
|
||||||
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
|
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
|
||||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.springframework.security.config.Customizer.withDefaults;
|
import static org.springframework.security.config.Customizer.withDefaults;
|
||||||
@ -210,6 +219,84 @@ public class LogoutSpecTests {
|
|||||||
FormLoginTests.HomePage.to(driver, FormLoginTests.DefaultLoginPage.class).assertAt();
|
FormLoginTests.HomePage.to(driver, FormLoginTests.DefaultLoginPage.class).assertAt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multipleLogoutHandlers() {
|
||||||
|
InMemorySecurityContextRepository repository = new InMemorySecurityContextRepository();
|
||||||
|
MultiValueMap<String, String> logoutData = new LinkedMultiValueMap<>();
|
||||||
|
ServerLogoutHandler handler1 = (exchange, authentication) -> {
|
||||||
|
logoutData.add("handler-header", "value1");
|
||||||
|
return Mono.empty();
|
||||||
|
};
|
||||||
|
ServerLogoutHandler handler2 = (exchange, authentication) -> {
|
||||||
|
logoutData.add("handler-header", "value2");
|
||||||
|
return Mono.empty();
|
||||||
|
};
|
||||||
|
// @formatter:off
|
||||||
|
SecurityWebFilterChain securityWebFilter = this.http
|
||||||
|
.securityContextRepository(repository)
|
||||||
|
.authorizeExchange((authorize) -> authorize
|
||||||
|
.anyExchange().authenticated())
|
||||||
|
.formLogin(withDefaults())
|
||||||
|
.logout((logoutSpec) -> logoutSpec.logoutHandler((handlers) -> {
|
||||||
|
handlers.add(handler1);
|
||||||
|
handlers.add(0, handler2);
|
||||||
|
}))
|
||||||
|
.build();
|
||||||
|
WebTestClient webTestClient = WebTestClientBuilder
|
||||||
|
.bindToWebFilters(securityWebFilter)
|
||||||
|
.build();
|
||||||
|
WebDriver driver = WebTestClientHtmlUnitDriverBuilder
|
||||||
|
.webTestClientSetup(webTestClient)
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
FormLoginTests.DefaultLoginPage loginPage = FormLoginTests.HomePage
|
||||||
|
.to(driver, FormLoginTests.DefaultLoginPage.class)
|
||||||
|
.assertAt();
|
||||||
|
// @formatter:off
|
||||||
|
loginPage = loginPage.loginForm()
|
||||||
|
.username("user")
|
||||||
|
.password("invalid")
|
||||||
|
.submit(FormLoginTests.DefaultLoginPage.class)
|
||||||
|
.assertError();
|
||||||
|
FormLoginTests.HomePage homePage = loginPage.loginForm()
|
||||||
|
.username("user")
|
||||||
|
.password("password")
|
||||||
|
.submit(FormLoginTests.HomePage.class);
|
||||||
|
// @formatter:on
|
||||||
|
homePage.assertAt();
|
||||||
|
SecurityContext savedContext = repository.getSavedContext();
|
||||||
|
assertThat(savedContext).isNotNull();
|
||||||
|
assertThat(savedContext.getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class);
|
||||||
|
|
||||||
|
loginPage = FormLoginTests.DefaultLogoutPage.to(driver).assertAt().logout();
|
||||||
|
loginPage.assertAt().assertLogout();
|
||||||
|
assertThat(logoutData).hasSize(1);
|
||||||
|
assertThat(logoutData.get("handler-header")).containsExactly("value2", "value1");
|
||||||
|
savedContext = repository.getSavedContext();
|
||||||
|
assertThat(savedContext).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InMemorySecurityContextRepository implements ServerSecurityContextRepository {
|
||||||
|
|
||||||
|
@Nullable private SecurityContext savedContext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> save(ServerWebExchange exchange, SecurityContext context) {
|
||||||
|
this.savedContext = context;
|
||||||
|
return Mono.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<SecurityContext> load(ServerWebExchange exchange) {
|
||||||
|
return Mono.justOrEmpty(this.savedContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable private SecurityContext getSavedContext() {
|
||||||
|
return this.savedContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public static class HomeController {
|
public static class HomeController {
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import java.util.Map;
|
|||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.assertj.core.api.ThrowableAssert;
|
import org.assertj.core.api.ThrowableAssert;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
@ -735,7 +736,7 @@ public class WebSocketMessageBrokerConfigTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EvaluationContext createEvaluationContext(Supplier<Authentication> authentication,
|
public EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,
|
||||||
Message<Object> message) {
|
Message<Object> message) {
|
||||||
return new StandardEvaluationContext(new MessageSecurityExpressionRoot(authentication, message) {
|
return new StandardEvaluationContext(new MessageSecurityExpressionRoot(authentication, message) {
|
||||||
public boolean denyNile() {
|
public boolean denyNile() {
|
||||||
|
@ -193,7 +193,7 @@ class AuthorizeHttpRequestsDslTests {
|
|||||||
open class MvcMatcherPathVariablesConfig {
|
open class MvcMatcherPathVariablesConfig {
|
||||||
@Bean
|
@Bean
|
||||||
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
val access = AuthorizationManager { _: Supplier<Authentication>, context: RequestAuthorizationContext ->
|
val access = AuthorizationManager { _: Supplier<out Authentication>, context: RequestAuthorizationContext ->
|
||||||
AuthorizationDecision(context.variables["userName"] == "user")
|
AuthorizationDecision(context.variables["userName"] == "user")
|
||||||
}
|
}
|
||||||
http {
|
http {
|
||||||
|
@ -367,7 +367,7 @@ class HttpSecurityDslTests {
|
|||||||
this.spring.register(CustomFilterConfig::class.java).autowire()
|
this.spring.register(CustomFilterConfig::class.java).autowire()
|
||||||
|
|
||||||
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
|
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
|
||||||
val filters: List<Filter> = filterChain.getFilters("/")
|
val filters: List<Filter>? = filterChain.getFilters("/")
|
||||||
|
|
||||||
assertThat(filters).anyMatch { it is CustomFilter }
|
assertThat(filters).anyMatch { it is CustomFilter }
|
||||||
}
|
}
|
||||||
@ -390,7 +390,7 @@ class HttpSecurityDslTests {
|
|||||||
this.spring.register(CustomFilterConfigReified::class.java).autowire()
|
this.spring.register(CustomFilterConfigReified::class.java).autowire()
|
||||||
|
|
||||||
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
|
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
|
||||||
val filters: List<Filter> = filterChain.getFilters("/")
|
val filters: List<Filter>? = filterChain.getFilters("/")
|
||||||
|
|
||||||
assertThat(filters).anyMatch { it is CustomFilter }
|
assertThat(filters).anyMatch { it is CustomFilter }
|
||||||
}
|
}
|
||||||
@ -413,7 +413,7 @@ class HttpSecurityDslTests {
|
|||||||
this.spring.register(CustomFilterAfterConfig::class.java).autowire()
|
this.spring.register(CustomFilterAfterConfig::class.java).autowire()
|
||||||
|
|
||||||
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
|
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
|
||||||
val filters: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
|
val filters: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
|
||||||
|
|
||||||
assertThat(filters).containsSubsequence(
|
assertThat(filters).containsSubsequence(
|
||||||
UsernamePasswordAuthenticationFilter::class.java,
|
UsernamePasswordAuthenticationFilter::class.java,
|
||||||
@ -440,7 +440,7 @@ class HttpSecurityDslTests {
|
|||||||
this.spring.register(CustomFilterAfterConfigReified::class.java).autowire()
|
this.spring.register(CustomFilterAfterConfigReified::class.java).autowire()
|
||||||
|
|
||||||
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
|
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
|
||||||
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
|
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
|
||||||
|
|
||||||
assertThat(filterClasses).containsSubsequence(
|
assertThat(filterClasses).containsSubsequence(
|
||||||
UsernamePasswordAuthenticationFilter::class.java,
|
UsernamePasswordAuthenticationFilter::class.java,
|
||||||
@ -467,7 +467,7 @@ class HttpSecurityDslTests {
|
|||||||
this.spring.register(CustomFilterBeforeConfig::class.java).autowire()
|
this.spring.register(CustomFilterBeforeConfig::class.java).autowire()
|
||||||
|
|
||||||
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
|
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
|
||||||
val filters: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
|
val filters: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
|
||||||
|
|
||||||
assertThat(filters).containsSubsequence(
|
assertThat(filters).containsSubsequence(
|
||||||
CustomFilter::class.java,
|
CustomFilter::class.java,
|
||||||
@ -494,7 +494,7 @@ class HttpSecurityDslTests {
|
|||||||
this.spring.register(CustomFilterBeforeConfigReified::class.java).autowire()
|
this.spring.register(CustomFilterBeforeConfigReified::class.java).autowire()
|
||||||
|
|
||||||
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
|
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
|
||||||
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
|
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
|
||||||
|
|
||||||
assertThat(filterClasses).containsSubsequence(
|
assertThat(filterClasses).containsSubsequence(
|
||||||
CustomFilter::class.java,
|
CustomFilter::class.java,
|
||||||
@ -523,7 +523,7 @@ class HttpSecurityDslTests {
|
|||||||
this.spring.register(CustomSecurityConfigurerConfig::class.java).autowire()
|
this.spring.register(CustomSecurityConfigurerConfig::class.java).autowire()
|
||||||
|
|
||||||
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
|
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
|
||||||
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
|
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
|
||||||
|
|
||||||
assertThat(filterClasses).contains(
|
assertThat(filterClasses).contains(
|
||||||
CustomFilter::class.java
|
CustomFilter::class.java
|
||||||
@ -535,7 +535,7 @@ class HttpSecurityDslTests {
|
|||||||
this.spring.register(CustomSecurityConfigurerConfig::class.java).autowire()
|
this.spring.register(CustomSecurityConfigurerConfig::class.java).autowire()
|
||||||
|
|
||||||
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
|
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
|
||||||
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
|
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
|
||||||
|
|
||||||
assertThat(filterClasses).contains(
|
assertThat(filterClasses).contains(
|
||||||
CustomFilter::class.java
|
CustomFilter::class.java
|
||||||
@ -588,7 +588,7 @@ class HttpSecurityDslTests {
|
|||||||
this.spring.register(CustomDslUsingWithConfig::class.java).autowire()
|
this.spring.register(CustomDslUsingWithConfig::class.java).autowire()
|
||||||
|
|
||||||
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
|
val filterChain = spring.context.getBean(FilterChainProxy::class.java)
|
||||||
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/").map { it.javaClass }
|
val filterClasses: List<Class<out Filter>> = filterChain.getFilters("/")!!.map { it.javaClass }
|
||||||
|
|
||||||
assertThat(filterClasses).contains(
|
assertThat(filterClasses).contains(
|
||||||
UsernamePasswordAuthenticationFilter::class.java
|
UsernamePasswordAuthenticationFilter::class.java
|
||||||
@ -623,5 +623,38 @@ class HttpSecurityDslTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `HTTP security when Dsl Bean`() {
|
||||||
|
this.spring.register(DslBeanConfig::class.java).autowire()
|
||||||
|
|
||||||
|
this.mockMvc.get("/")
|
||||||
|
.andExpect {
|
||||||
|
header {
|
||||||
|
string("Content-Security-Policy", "object-src 'none'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableWebMvc
|
||||||
|
open class DslBeanConfig {
|
||||||
|
@Bean
|
||||||
|
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
http {
|
||||||
|
httpBasic { }
|
||||||
|
}
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun headersDsl(): HeadersDsl.() -> Unit {
|
||||||
|
return {
|
||||||
|
contentSecurityPolicy {
|
||||||
|
policyDirectives = "object-src 'none'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -350,8 +350,8 @@ class LogoutDslTests {
|
|||||||
|
|
||||||
class NoopLogoutHandler: LogoutHandler {
|
class NoopLogoutHandler: LogoutHandler {
|
||||||
override fun logout(
|
override fun logout(
|
||||||
request: HttpServletRequest?,
|
request: HttpServletRequest,
|
||||||
response: HttpServletResponse?,
|
response: HttpServletResponse,
|
||||||
authentication: Authentication?
|
authentication: Authentication?
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ import io.mockk.mockk
|
|||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import jakarta.servlet.http.HttpServletRequest
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
import jakarta.servlet.http.HttpServletResponse
|
import jakarta.servlet.http.HttpServletResponse
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.extension.ExtendWith
|
import org.junit.jupiter.api.extension.ExtendWith
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
@ -44,7 +43,6 @@ import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequ
|
|||||||
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers
|
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers
|
||||||
import org.springframework.security.web.SecurityFilterChain
|
import org.springframework.security.web.SecurityFilterChain
|
||||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler
|
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler
|
||||||
import org.springframework.security.web.authentication.ott.DefaultGenerateOneTimeTokenRequestResolver
|
|
||||||
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenRequestResolver
|
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenRequestResolver
|
||||||
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler
|
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler
|
||||||
import org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler
|
import org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler
|
||||||
@ -53,7 +51,6 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
|
|||||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
|
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.ZoneOffset
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for [OneTimeTokenLoginDsl]
|
* Tests for [OneTimeTokenLoginDsl]
|
||||||
@ -267,7 +264,7 @@ class OneTimeTokenLoginDslTests {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(redirectUrl: String?) {
|
constructor(redirectUrl: String) {
|
||||||
this.delegate =
|
this.delegate =
|
||||||
RedirectOneTimeTokenGenerationSuccessHandler(
|
RedirectOneTimeTokenGenerationSuccessHandler(
|
||||||
redirectUrl
|
redirectUrl
|
||||||
|
@ -132,8 +132,8 @@ class RequiresChannelDslTests {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val CHANNEL_PROCESSOR: ChannelProcessor = object : ChannelProcessor {
|
val CHANNEL_PROCESSOR: ChannelProcessor = object : ChannelProcessor {
|
||||||
override fun decide(invocation: FilterInvocation?, config: MutableCollection<ConfigAttribute>?) {}
|
override fun decide(invocation: FilterInvocation, config: MutableCollection<ConfigAttribute>) {}
|
||||||
override fun supports(attribute: ConfigAttribute?): Boolean = true
|
override fun supports(attribute: ConfigAttribute): Boolean = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ class SecurityContextDslTests {
|
|||||||
testContext.autowire()
|
testContext.autowire()
|
||||||
val filterChainProxy = testContext.context.getBean(FilterChainProxy::class.java)
|
val filterChainProxy = testContext.context.getBean(FilterChainProxy::class.java)
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
val filterTypes = filterChainProxy.getFilters("/").toList()
|
val filterTypes = filterChainProxy.getFilters("/")!!.toList()
|
||||||
|
|
||||||
assertThat(filterTypes)
|
assertThat(filterTypes)
|
||||||
.anyMatch { it is SecurityContextHolderFilter }
|
.anyMatch { it is SecurityContextHolderFilter }
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
* Copyright 2004-present the original author or authors.
|
* Copyright 2004-present the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -125,6 +126,8 @@ class WebAuthnDslTests {
|
|||||||
http{
|
http{
|
||||||
formLogin { }
|
formLogin { }
|
||||||
webAuthn {
|
webAuthn {
|
||||||
|
rpId = "spring.io"
|
||||||
|
rpName = "spring"
|
||||||
disableDefaultRegistrationPage = true
|
disableDefaultRegistrationPage = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,7 +147,10 @@ class WebAuthnDslTests {
|
|||||||
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
http{
|
http{
|
||||||
formLogin { }
|
formLogin { }
|
||||||
webAuthn { }
|
webAuthn {
|
||||||
|
rpId = "spring.io"
|
||||||
|
rpName = "spring"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return http.build()
|
return http.build()
|
||||||
}
|
}
|
||||||
|
@ -270,8 +270,8 @@ class ServerHttpBasicDslTests {
|
|||||||
|
|
||||||
open class MockServerAuthenticationFailureHandler: ServerAuthenticationFailureHandler {
|
open class MockServerAuthenticationFailureHandler: ServerAuthenticationFailureHandler {
|
||||||
override fun onAuthenticationFailure(
|
override fun onAuthenticationFailure(
|
||||||
webFilterExchange: WebFilterExchange?,
|
webFilterExchange: WebFilterExchange,
|
||||||
exception: AuthenticationException?
|
exception: AuthenticationException
|
||||||
): Mono<Void> {
|
): Mono<Void> {
|
||||||
return Mono.empty()
|
return Mono.empty()
|
||||||
}
|
}
|
||||||
|
@ -175,8 +175,8 @@ class ServerOAuth2ResourceServerDslTests {
|
|||||||
|
|
||||||
open class MockServerAuthenticationFailureHandler: ServerAuthenticationFailureHandler {
|
open class MockServerAuthenticationFailureHandler: ServerAuthenticationFailureHandler {
|
||||||
override fun onAuthenticationFailure(
|
override fun onAuthenticationFailure(
|
||||||
webFilterExchange: WebFilterExchange?,
|
webFilterExchange: WebFilterExchange,
|
||||||
exception: AuthenticationException?
|
exception: AuthenticationException
|
||||||
): Mono<Void> {
|
): Mono<Void> {
|
||||||
return Mono.empty()
|
return Mono.empty()
|
||||||
}
|
}
|
||||||
|
@ -280,11 +280,11 @@ class ServerOneTimeTokenLoginDslTests {
|
|||||||
this.delegate = ServerRedirectOneTimeTokenGenerationSuccessHandler("/login/ott")
|
this.delegate = ServerRedirectOneTimeTokenGenerationSuccessHandler("/login/ott")
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(redirectUrl: String?) {
|
constructor(redirectUrl: String) {
|
||||||
this.delegate = ServerRedirectOneTimeTokenGenerationSuccessHandler(redirectUrl)
|
this.delegate = ServerRedirectOneTimeTokenGenerationSuccessHandler(redirectUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handle(exchange: ServerWebExchange?, oneTimeToken: OneTimeToken?): Mono<Void> {
|
override fun handle(exchange: ServerWebExchange, oneTimeToken: OneTimeToken): Mono<Void> {
|
||||||
lastToken = oneTimeToken
|
lastToken = oneTimeToken
|
||||||
return delegate!!.handle(exchange, oneTimeToken)
|
return delegate!!.handle(exchange, oneTimeToken)
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
<intercept-url pattern="/**" access="permitAll"/>
|
<intercept-url pattern="/**" access="permitAll"/>
|
||||||
</http>
|
</http>
|
||||||
|
|
||||||
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
|
||||||
|
|
||||||
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
|
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
<intercept-url pattern="/**" access="permitAll"/>
|
<intercept-url pattern="/**" access="permitAll"/>
|
||||||
</http>
|
</http>
|
||||||
|
|
||||||
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
|
||||||
|
|
||||||
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
|
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
<custom-filter ref="userFilter" after="LOGOUT_FILTER"/>
|
<custom-filter ref="userFilter" after="LOGOUT_FILTER"/>
|
||||||
</http>
|
</http>
|
||||||
|
|
||||||
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
|
||||||
|
|
||||||
<b:bean name="userFilter" class="org.mockito.Mockito" factory-method="mock">
|
<b:bean name="userFilter" class="org.mockito.Mockito" factory-method="mock">
|
||||||
<b:constructor-arg value="jakarta.servlet.Filter" type="java.lang.Class"/>
|
<b:constructor-arg value="jakarta.servlet.Filter" type="java.lang.Class"/>
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
<intercept-url pattern="/**" access="authenticated"/>
|
<intercept-url pattern="/**" access="authenticated"/>
|
||||||
</http>
|
</http>
|
||||||
|
|
||||||
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
|
||||||
|
|
||||||
<b:import resource="MiscHttpConfigTests-controllers.xml"/>
|
<b:import resource="MiscHttpConfigTests-controllers.xml"/>
|
||||||
<b:import resource="userservice.xml"/>
|
<b:import resource="userservice.xml"/>
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
</b:constructor-arg>
|
</b:constructor-arg>
|
||||||
</b:bean>
|
</b:bean>
|
||||||
|
|
||||||
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
|
||||||
|
|
||||||
<b:import resource="MiscHttpConfigTests-controllers.xml"/>
|
<b:import resource="MiscHttpConfigTests-controllers.xml"/>
|
||||||
<b:import resource="userservice.xml"/>
|
<b:import resource="userservice.xml"/>
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
<access-denied-handler error-page="${accessDenied}"/>
|
<access-denied-handler error-page="${accessDenied}"/>
|
||||||
</http>
|
</http>
|
||||||
|
|
||||||
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
|
||||||
|
|
||||||
<b:bean name="sc" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>
|
<b:bean name="sc" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
<access-denied-handler error-page="#{'/go' + '-away'}"/>
|
<access-denied-handler error-page="#{'/go' + '-away'}"/>
|
||||||
</http>
|
</http>
|
||||||
|
|
||||||
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
|
||||||
|
|
||||||
<b:bean name="sc" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>
|
<b:bean name="sc" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
<csrf disabled="true"/>
|
<csrf disabled="true"/>
|
||||||
</http>
|
</http>
|
||||||
|
|
||||||
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
|
||||||
|
|
||||||
<b:bean name="unsecured" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>
|
<b:bean name="unsecured" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
<csrf disabled="true"/>
|
<csrf disabled="true"/>
|
||||||
</http>
|
</http>
|
||||||
|
|
||||||
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
|
||||||
|
|
||||||
<b:bean name="sc" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>
|
<b:bean name="sc" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
</port-mappings>
|
</port-mappings>
|
||||||
</http>
|
</http>
|
||||||
|
|
||||||
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
|
||||||
|
|
||||||
<b:bean name="sc" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>
|
<b:bean name="sc" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
<intercept-url pattern="${secure.url}" access="ROLE_USER" requires-channel="${required.channel}"/>
|
<intercept-url pattern="${secure.url}" access="ROLE_USER" requires-channel="${required.channel}"/>
|
||||||
</http>
|
</http>
|
||||||
|
|
||||||
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
|
||||||
|
|
||||||
<b:bean name="sc" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>
|
<b:bean name="sc" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
<intercept-url pattern="/**" access="ROLE_NUNYA"/>
|
<intercept-url pattern="/**" access="ROLE_NUNYA"/>
|
||||||
</http>
|
</http>
|
||||||
|
|
||||||
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
|
||||||
|
|
||||||
<b:bean name="unsecured" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>
|
<b:bean name="unsecured" class="org.springframework.security.config.http.PlaceHolderAndELConfigTests.SimpleController"/>
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
token-validity-seconds="${security.rememberme.ttl}"/>
|
token-validity-seconds="${security.rememberme.ttl}"/>
|
||||||
</http>
|
</http>
|
||||||
|
|
||||||
<b:bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
|
<b:bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
|
||||||
<b:property name="properties" value="security.rememberme.ttl=30"/>
|
<b:property name="properties" value="security.rememberme.ttl=30"/>
|
||||||
</b:bean>
|
</b:bean>
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
<b:bean name="transactionManager" class="org.springframework.security.config.MockTransactionManager" />
|
<b:bean name="transactionManager" class="org.springframework.security.config.MockTransactionManager" />
|
||||||
|
|
||||||
<b:bean class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer'/>
|
<b:bean class='org.springframework.context.support.PropertySourcesPlaceholderConfigurer'/>
|
||||||
|
|
||||||
<b:bean id="transactionalTarget" class="org.springframework.security.config.TransactionalTestBusinessBean">
|
<b:bean id="transactionalTarget" class="org.springframework.security.config.TransactionalTestBusinessBean">
|
||||||
<intercept-methods use-authorization-manager="false">
|
<intercept-methods use-authorization-manager="false">
|
||||||
|
@ -110,7 +110,7 @@
|
|||||||
</sec:filter-chain-map>
|
</sec:filter-chain-map>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />
|
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer" />
|
||||||
|
|
||||||
<bean id="sec1235FilterChainProxy" class="org.springframework.security.web.FilterChainProxy">
|
<bean id="sec1235FilterChainProxy" class="org.springframework.security.web.FilterChainProxy">
|
||||||
<constructor-arg>
|
<constructor-arg>
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -18,6 +18,8 @@ package org.springframework.security.access;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullUnmarked;
|
||||||
|
|
||||||
import org.springframework.security.access.intercept.RunAsManager;
|
import org.springframework.security.access.intercept.RunAsManager;
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
import org.springframework.security.core.annotation.SecurityAnnotationScanner;
|
import org.springframework.security.core.annotation.SecurityAnnotationScanner;
|
||||||
@ -45,6 +47,7 @@ import org.springframework.security.core.annotation.SecurityAnnotationScanner;
|
|||||||
* {@link AuthorizationManager}.
|
* {@link AuthorizationManager}.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
@NullUnmarked
|
||||||
public interface ConfigAttribute extends Serializable {
|
public interface ConfigAttribute extends Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,7 +70,7 @@ public abstract class AbstractSecurityExpressionHandler<T>
|
|||||||
* suitable root object.
|
* suitable root object.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public final EvaluationContext createEvaluationContext(Authentication authentication, T invocation) {
|
public final EvaluationContext createEvaluationContext(@Nullable Authentication authentication, T invocation) {
|
||||||
SecurityExpressionOperations root = createSecurityExpressionRoot(authentication, invocation);
|
SecurityExpressionOperations root = createSecurityExpressionRoot(authentication, invocation);
|
||||||
StandardEvaluationContext ctx = createEvaluationContextInternal(authentication, invocation);
|
StandardEvaluationContext ctx = createEvaluationContextInternal(authentication, invocation);
|
||||||
if (this.beanResolver != null) {
|
if (this.beanResolver != null) {
|
||||||
@ -91,7 +91,8 @@ public abstract class AbstractSecurityExpressionHandler<T>
|
|||||||
* @return A {@code StandardEvaluationContext} or potentially a custom subclass if
|
* @return A {@code StandardEvaluationContext} or potentially a custom subclass if
|
||||||
* overridden.
|
* overridden.
|
||||||
*/
|
*/
|
||||||
protected StandardEvaluationContext createEvaluationContextInternal(Authentication authentication, T invocation) {
|
protected StandardEvaluationContext createEvaluationContextInternal(@Nullable Authentication authentication,
|
||||||
|
T invocation) {
|
||||||
return new StandardEvaluationContext();
|
return new StandardEvaluationContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,8 +103,8 @@ public abstract class AbstractSecurityExpressionHandler<T>
|
|||||||
* @param invocation the invocation (filter, method, channel)
|
* @param invocation the invocation (filter, method, channel)
|
||||||
* @return the object
|
* @return the object
|
||||||
*/
|
*/
|
||||||
protected abstract SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
|
protected abstract SecurityExpressionOperations createSecurityExpressionRoot(
|
||||||
T invocation);
|
@Nullable Authentication authentication, T invocation);
|
||||||
|
|
||||||
protected @Nullable RoleHierarchy getRoleHierarchy() {
|
protected @Nullable RoleHierarchy getRoleHierarchy() {
|
||||||
return this.roleHierarchy;
|
return this.roleHierarchy;
|
||||||
|
@ -18,6 +18,8 @@ package org.springframework.security.access.expression;
|
|||||||
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.aop.framework.AopInfrastructureBean;
|
import org.springframework.aop.framework.AopInfrastructureBean;
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
import org.springframework.expression.ExpressionParser;
|
import org.springframework.expression.ExpressionParser;
|
||||||
@ -42,7 +44,7 @@ public interface SecurityExpressionHandler<T> extends AopInfrastructureBean {
|
|||||||
* Provides an evaluation context in which to evaluate security expressions for the
|
* Provides an evaluation context in which to evaluate security expressions for the
|
||||||
* invocation type.
|
* invocation type.
|
||||||
*/
|
*/
|
||||||
EvaluationContext createEvaluationContext(Authentication authentication, T invocation);
|
EvaluationContext createEvaluationContext(@Nullable Authentication authentication, T invocation);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides an evaluation context in which to evaluate security expressions for the
|
* Provides an evaluation context in which to evaluate security expressions for the
|
||||||
@ -55,7 +57,8 @@ public interface SecurityExpressionHandler<T> extends AopInfrastructureBean {
|
|||||||
* @return the {@link EvaluationContext} to use
|
* @return the {@link EvaluationContext} to use
|
||||||
* @since 5.8
|
* @since 5.8
|
||||||
*/
|
*/
|
||||||
default EvaluationContext createEvaluationContext(Supplier<Authentication> authentication, T invocation) {
|
default EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,
|
||||||
|
T invocation) {
|
||||||
return createEvaluationContext(authentication.get(), invocation);
|
return createEvaluationContext(authentication.get(), invocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
|
|||||||
* Creates a new instance
|
* Creates a new instance
|
||||||
* @param authentication the {@link Authentication} to use. Cannot be null.
|
* @param authentication the {@link Authentication} to use. Cannot be null.
|
||||||
*/
|
*/
|
||||||
public SecurityExpressionRoot(Authentication authentication) {
|
public SecurityExpressionRoot(@Nullable Authentication authentication) {
|
||||||
this(() -> authentication);
|
this(() -> authentication);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
|
|||||||
* Cannot be null.
|
* Cannot be null.
|
||||||
* @since 5.8
|
* @since 5.8
|
||||||
*/
|
*/
|
||||||
public SecurityExpressionRoot(Supplier<Authentication> authentication) {
|
public SecurityExpressionRoot(Supplier<? extends @Nullable Authentication> authentication) {
|
||||||
this.authentication = SingletonSupplier.of(() -> {
|
this.authentication = SingletonSupplier.of(() -> {
|
||||||
Authentication value = authentication.get();
|
Authentication value = authentication.get();
|
||||||
Assert.notNull(value, "Authentication object cannot be null");
|
Assert.notNull(value, "Authentication object cannot be null");
|
||||||
@ -177,7 +177,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
|
|||||||
this.trustResolver = trustResolver;
|
this.trustResolver = trustResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
|
public void setRoleHierarchy(@Nullable RoleHierarchy roleHierarchy) {
|
||||||
this.roleHierarchy = roleHierarchy;
|
this.roleHierarchy = roleHierarchy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,12 +79,15 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
|
|||||||
* implementation.
|
* implementation.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public StandardEvaluationContext createEvaluationContextInternal(Authentication auth, MethodInvocation mi) {
|
public StandardEvaluationContext createEvaluationContextInternal(@Nullable Authentication auth,
|
||||||
|
MethodInvocation mi) {
|
||||||
return new MethodSecurityEvaluationContext(auth, mi, getParameterNameDiscoverer());
|
return new MethodSecurityEvaluationContext(auth, mi, getParameterNameDiscoverer());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EvaluationContext createEvaluationContext(Supplier<Authentication> authentication, MethodInvocation mi) {
|
@SuppressWarnings("NullAway") // FIXME: Dataflow analysis limitation
|
||||||
|
public EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,
|
||||||
|
MethodInvocation mi) {
|
||||||
MethodSecurityExpressionOperations root = createSecurityExpressionRoot(authentication, mi);
|
MethodSecurityExpressionOperations root = createSecurityExpressionRoot(authentication, mi);
|
||||||
MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(root, mi,
|
MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(root, mi,
|
||||||
getParameterNameDiscoverer());
|
getParameterNameDiscoverer());
|
||||||
@ -96,13 +99,13 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
|
|||||||
* Creates the root object for expression evaluation.
|
* Creates the root object for expression evaluation.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
|
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(@Nullable Authentication authentication,
|
||||||
MethodInvocation invocation) {
|
MethodInvocation invocation) {
|
||||||
return createSecurityExpressionRoot(() -> authentication, invocation);
|
return createSecurityExpressionRoot(() -> authentication, invocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodSecurityExpressionOperations createSecurityExpressionRoot(Supplier<Authentication> authentication,
|
private MethodSecurityExpressionOperations createSecurityExpressionRoot(
|
||||||
MethodInvocation invocation) {
|
Supplier<? extends @Nullable Authentication> authentication, MethodInvocation invocation) {
|
||||||
MethodSecurityExpressionRoot root = new MethodSecurityExpressionRoot(authentication);
|
MethodSecurityExpressionRoot root = new MethodSecurityExpressionRoot(authentication);
|
||||||
root.setThis(invocation.getThis());
|
root.setThis(invocation.getThis());
|
||||||
root.setPermissionEvaluator(getPermissionEvaluator());
|
root.setPermissionEvaluator(getPermissionEvaluator());
|
||||||
|
@ -38,11 +38,11 @@ class MethodSecurityExpressionRoot extends SecurityExpressionRoot implements Met
|
|||||||
|
|
||||||
private @Nullable Object target;
|
private @Nullable Object target;
|
||||||
|
|
||||||
MethodSecurityExpressionRoot(Authentication a) {
|
MethodSecurityExpressionRoot(@Nullable Authentication a) {
|
||||||
super(a);
|
super(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodSecurityExpressionRoot(Supplier<Authentication> authentication) {
|
MethodSecurityExpressionRoot(Supplier<? extends @Nullable Authentication> authentication) {
|
||||||
super(authentication);
|
super(authentication);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ package org.springframework.security.access.vote;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullUnmarked;
|
||||||
|
|
||||||
import org.springframework.security.access.AccessDecisionVoter;
|
import org.springframework.security.access.AccessDecisionVoter;
|
||||||
import org.springframework.security.access.ConfigAttribute;
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
@ -53,6 +55,7 @@ import org.springframework.security.core.GrantedAuthority;
|
|||||||
* instead
|
* instead
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
@NullUnmarked
|
||||||
public class RoleVoter implements AccessDecisionVoter<Object> {
|
public class RoleVoter implements AccessDecisionVoter<Object> {
|
||||||
|
|
||||||
private String rolePrefix = "ROLE_";
|
private String rolePrefix = "ROLE_";
|
||||||
|
@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
package org.springframework.security.authentication;
|
package org.springframework.security.authentication;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import org.springframework.lang.Contract;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,7 +40,7 @@ public interface AuthenticationTrustResolver {
|
|||||||
* @return <code>true</code> the passed authentication token represented an anonymous
|
* @return <code>true</code> the passed authentication token represented an anonymous
|
||||||
* principal, <code>false</code> otherwise
|
* principal, <code>false</code> otherwise
|
||||||
*/
|
*/
|
||||||
boolean isAnonymous(Authentication authentication);
|
boolean isAnonymous(@Nullable Authentication authentication);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether the passed <code>Authentication</code> token represents user that
|
* Indicates whether the passed <code>Authentication</code> token represents user that
|
||||||
@ -51,7 +54,7 @@ public interface AuthenticationTrustResolver {
|
|||||||
* @return <code>true</code> the passed authentication token represented a principal
|
* @return <code>true</code> the passed authentication token represented a principal
|
||||||
* authenticated using a remember-me token, <code>false</code> otherwise
|
* authenticated using a remember-me token, <code>false</code> otherwise
|
||||||
*/
|
*/
|
||||||
boolean isRememberMe(Authentication authentication);
|
boolean isRememberMe(@Nullable Authentication authentication);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether the passed <code>Authentication</code> token represents a fully
|
* Indicates whether the passed <code>Authentication</code> token represents a fully
|
||||||
@ -66,7 +69,7 @@ public interface AuthenticationTrustResolver {
|
|||||||
* {@link #isRememberMe(Authentication)}, <code>false</code> otherwise
|
* {@link #isRememberMe(Authentication)}, <code>false</code> otherwise
|
||||||
* @since 6.1
|
* @since 6.1
|
||||||
*/
|
*/
|
||||||
default boolean isFullyAuthenticated(Authentication authentication) {
|
default boolean isFullyAuthenticated(@Nullable Authentication authentication) {
|
||||||
return isAuthenticated(authentication) && !isRememberMe(authentication);
|
return isAuthenticated(authentication) && !isRememberMe(authentication);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +81,8 @@ public interface AuthenticationTrustResolver {
|
|||||||
* {@link Authentication#isAuthenticated()} is true.
|
* {@link Authentication#isAuthenticated()} is true.
|
||||||
* @since 6.1.7
|
* @since 6.1.7
|
||||||
*/
|
*/
|
||||||
default boolean isAuthenticated(Authentication authentication) {
|
@Contract("null -> false")
|
||||||
|
default boolean isAuthenticated(@Nullable Authentication authentication) {
|
||||||
return authentication != null && authentication.isAuthenticated() && !isAnonymous(authentication);
|
return authentication != null && authentication.isAuthenticated() && !isAnonymous(authentication);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.security.authentication;
|
package org.springframework.security.authentication;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,7 +46,7 @@ public class AuthenticationTrustResolverImpl implements AuthenticationTrustResol
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAnonymous(Authentication authentication) {
|
public boolean isAnonymous(@Nullable Authentication authentication) {
|
||||||
if ((this.anonymousClass == null) || (authentication == null)) {
|
if ((this.anonymousClass == null) || (authentication == null)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -52,7 +54,7 @@ public class AuthenticationTrustResolverImpl implements AuthenticationTrustResol
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isRememberMe(Authentication authentication) {
|
public boolean isRememberMe(@Nullable Authentication authentication) {
|
||||||
if ((this.rememberMeClass == null) || (authentication == null)) {
|
if ((this.rememberMeClass == null) || (authentication == null)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@ package org.springframework.security.authentication;
|
|||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,7 +37,7 @@ public class BadCredentialsException extends AuthenticationException {
|
|||||||
* Constructs a <code>BadCredentialsException</code> with the specified message.
|
* Constructs a <code>BadCredentialsException</code> with the specified message.
|
||||||
* @param msg the detail message
|
* @param msg the detail message
|
||||||
*/
|
*/
|
||||||
public BadCredentialsException(String msg) {
|
public BadCredentialsException(@Nullable String msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +47,7 @@ public class BadCredentialsException extends AuthenticationException {
|
|||||||
* @param msg the detail message
|
* @param msg the detail message
|
||||||
* @param cause root cause
|
* @param cause root cause
|
||||||
*/
|
*/
|
||||||
public BadCredentialsException(String msg, Throwable cause) {
|
public BadCredentialsException(@Nullable String msg, Throwable cause) {
|
||||||
super(msg, cause);
|
super(msg, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
|
|||||||
|
|
||||||
private static final long serialVersionUID = 620L;
|
private static final long serialVersionUID = 620L;
|
||||||
|
|
||||||
private final Object principal;
|
private final @Nullable Object principal;
|
||||||
|
|
||||||
private @Nullable Object credentials;
|
private @Nullable Object credentials;
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
|
|||||||
* will return <code>false</code>.
|
* will return <code>false</code>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public UsernamePasswordAuthenticationToken(Object principal, @Nullable Object credentials) {
|
public UsernamePasswordAuthenticationToken(@Nullable Object principal, @Nullable Object credentials) {
|
||||||
super(null);
|
super(null);
|
||||||
this.principal = principal;
|
this.principal = principal;
|
||||||
this.credentials = credentials;
|
this.credentials = credentials;
|
||||||
@ -82,7 +82,8 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
|
|||||||
*
|
*
|
||||||
* @since 5.7
|
* @since 5.7
|
||||||
*/
|
*/
|
||||||
public static UsernamePasswordAuthenticationToken unauthenticated(Object principal, @Nullable Object credentials) {
|
public static UsernamePasswordAuthenticationToken unauthenticated(@Nullable Object principal,
|
||||||
|
@Nullable Object credentials) {
|
||||||
return new UsernamePasswordAuthenticationToken(principal, credentials);
|
return new UsernamePasswordAuthenticationToken(principal, credentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +107,7 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getPrincipal() {
|
public @Nullable Object getPrincipal() {
|
||||||
return this.principal;
|
return this.principal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,8 +178,10 @@ public abstract class AbstractJaasAuthenticationProvider implements Authenticati
|
|||||||
// applied.
|
// applied.
|
||||||
authorities = getAuthorities(principals);
|
authorities = getAuthorities(principals);
|
||||||
// Convert the authorities set back to an array and apply it to the token.
|
// Convert the authorities set back to an array and apply it to the token.
|
||||||
JaasAuthenticationToken result = new JaasAuthenticationToken(request.getPrincipal(),
|
Object principal = request.getPrincipal();
|
||||||
request.getCredentials(), new ArrayList<>(authorities), loginContext);
|
Assert.notNull(principal, "The principal cannot be null");
|
||||||
|
JaasAuthenticationToken result = new JaasAuthenticationToken(principal, request.getCredentials(),
|
||||||
|
new ArrayList<>(authorities), loginContext);
|
||||||
// Publish the success event
|
// Publish the success event
|
||||||
publishSuccessEvent(result);
|
publishSuccessEvent(result);
|
||||||
// we're done, return the token.
|
// we're done, return the token.
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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.authentication.ott;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result of a successful one-time-token authentication
|
||||||
|
*
|
||||||
|
* @author Josh Cummings
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public class OneTimeTokenAuthentication extends AbstractAuthenticationToken {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1195893764725073959L;
|
||||||
|
|
||||||
|
private final Object principal;
|
||||||
|
|
||||||
|
public OneTimeTokenAuthentication(Object principal, Collection<? extends GrantedAuthority> authorities) {
|
||||||
|
super(authorities);
|
||||||
|
this.principal = principal;
|
||||||
|
setAuthenticated(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getPrincipal() {
|
||||||
|
return this.principal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Object getCredentials() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -56,8 +56,7 @@ public final class OneTimeTokenAuthenticationProvider implements AuthenticationP
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
UserDetails user = this.userDetailsService.loadUserByUsername(consumed.getUsername());
|
UserDetails user = this.userDetailsService.loadUserByUsername(consumed.getUsername());
|
||||||
OneTimeTokenAuthenticationToken authenticated = OneTimeTokenAuthenticationToken.authenticated(user,
|
OneTimeTokenAuthentication authenticated = new OneTimeTokenAuthentication(user, user.getAuthorities());
|
||||||
user.getAuthorities());
|
|
||||||
authenticated.setDetails(otpAuthenticationToken.getDetails());
|
authenticated.setDetails(otpAuthenticationToken.getDetails());
|
||||||
return authenticated;
|
return authenticated;
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,10 @@ public class OneTimeTokenAuthenticationToken extends AbstractAuthenticationToken
|
|||||||
|
|
||||||
private @Nullable String tokenValue;
|
private @Nullable String tokenValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Please use constructor that takes a {@link String} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "7.0")
|
||||||
public OneTimeTokenAuthenticationToken(@Nullable Object principal, String tokenValue) {
|
public OneTimeTokenAuthenticationToken(@Nullable Object principal, String tokenValue) {
|
||||||
super(Collections.emptyList());
|
super(Collections.emptyList());
|
||||||
this.tokenValue = tokenValue;
|
this.tokenValue = tokenValue;
|
||||||
@ -50,6 +54,10 @@ public class OneTimeTokenAuthenticationToken extends AbstractAuthenticationToken
|
|||||||
this(null, tokenValue);
|
this(null, tokenValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Please use {@link OneTimeTokenAuthentication} instead
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "7.0")
|
||||||
public OneTimeTokenAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
|
public OneTimeTokenAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
|
||||||
super(authorities);
|
super(authorities);
|
||||||
this.principal = principal;
|
this.principal = principal;
|
||||||
@ -60,9 +68,11 @@ public class OneTimeTokenAuthenticationToken extends AbstractAuthenticationToken
|
|||||||
* Creates an unauthenticated token
|
* Creates an unauthenticated token
|
||||||
* @param tokenValue the one-time token value
|
* @param tokenValue the one-time token value
|
||||||
* @return an unauthenticated {@link OneTimeTokenAuthenticationToken}
|
* @return an unauthenticated {@link OneTimeTokenAuthenticationToken}
|
||||||
|
* @deprecated Please use constructor that takes a {@link String} instead
|
||||||
*/
|
*/
|
||||||
public static OneTimeTokenAuthenticationToken unauthenticated(String tokenValue) {
|
@Deprecated(forRemoval = true, since = "7.0")
|
||||||
return new OneTimeTokenAuthenticationToken(null, tokenValue);
|
public static OneTimeTokenAuthenticationToken unauthenticated(@Nullable String tokenValue) {
|
||||||
|
return new OneTimeTokenAuthenticationToken(null, (tokenValue != null) ? tokenValue : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,7 +80,9 @@ public class OneTimeTokenAuthenticationToken extends AbstractAuthenticationToken
|
|||||||
* @param principal the principal
|
* @param principal the principal
|
||||||
* @param tokenValue the one-time token value
|
* @param tokenValue the one-time token value
|
||||||
* @return an unauthenticated {@link OneTimeTokenAuthenticationToken}
|
* @return an unauthenticated {@link OneTimeTokenAuthenticationToken}
|
||||||
|
* @deprecated Please use constructor that takes a {@link String} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "7.0")
|
||||||
public static OneTimeTokenAuthenticationToken unauthenticated(Object principal, String tokenValue) {
|
public static OneTimeTokenAuthenticationToken unauthenticated(Object principal, String tokenValue) {
|
||||||
return new OneTimeTokenAuthenticationToken(principal, tokenValue);
|
return new OneTimeTokenAuthenticationToken(principal, tokenValue);
|
||||||
}
|
}
|
||||||
@ -80,7 +92,9 @@ public class OneTimeTokenAuthenticationToken extends AbstractAuthenticationToken
|
|||||||
* @param principal the principal
|
* @param principal the principal
|
||||||
* @param authorities the principal authorities
|
* @param authorities the principal authorities
|
||||||
* @return an authenticated {@link OneTimeTokenAuthenticationToken}
|
* @return an authenticated {@link OneTimeTokenAuthenticationToken}
|
||||||
|
* @deprecated Please use {@link OneTimeTokenAuthentication} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "7.0")
|
||||||
public static OneTimeTokenAuthenticationToken authenticated(Object principal,
|
public static OneTimeTokenAuthenticationToken authenticated(Object principal,
|
||||||
Collection<? extends GrantedAuthority> authorities) {
|
Collection<? extends GrantedAuthority> authorities) {
|
||||||
return new OneTimeTokenAuthenticationToken(principal, authorities);
|
return new OneTimeTokenAuthenticationToken(principal, authorities);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user