Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.DeferredSecurityContext;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
Expand Down Expand Up @@ -490,7 +491,8 @@ public void getWhenExplicitSaveAndRepositoryAndAuthenticatingThenConsultsCustomS
this.spring.configLocations(xml("ExplicitSaveAndExplicitRepository")).autowire();
SecurityContextRepository repository = this.spring.getContext().getBean(SecurityContextRepository.class);
SecurityContext context = new SecurityContextImpl(new TestingAuthenticationToken("user", "password"));
given(repository.loadContext(any(HttpServletRequest.class))).willReturn(() -> context);
given(repository.loadDeferredContext(any(HttpServletRequest.class)))
.willReturn(new TestDeferredSecurityContext(context, false));
// @formatter:off
MvcResult result = this.mvc.perform(formLogin())
.andExpect(status().is3xxRedirection())
Expand All @@ -500,6 +502,29 @@ public void getWhenExplicitSaveAndRepositoryAndAuthenticatingThenConsultsCustomS
any(HttpServletResponse.class));
}

static class TestDeferredSecurityContext implements DeferredSecurityContext {

private SecurityContext securityContext;

private boolean isGenerated;

public TestDeferredSecurityContext(SecurityContext securityContext, boolean isGenerated) {
this.securityContext = securityContext;
this.isGenerated = isGenerated;
}

@Override
public SecurityContext get() {
return this.securityContext;
}

@Override
public boolean isGenerated() {
return this.isGenerated;
}

}

@Test
public void getWhenExplicitSaveAndExplicitSaveAndAuthenticatingThenConsultsCustomSecurityContextRepository()
throws Exception {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2002-2022 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.core.context;

import java.util.function.Supplier;

/**
* An interface that allows delayed access to a {@link SecurityContext} that may be
* generated.
*
* @author Steve Riesenberg
* @since 5.8
*/
public interface DeferredSecurityContext extends Supplier<SecurityContext> {

/**
* Returns true if {@link #get()} refers to a generated {@link SecurityContext} or
* false if it already existed.
* @return true if {@link #get()} refers to a generated {@link SecurityContext} or
* false if it already existed
*/
boolean isGenerated();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright 2002-2022 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.web.context;

import java.util.Arrays;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.context.DeferredSecurityContext;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.util.Assert;

/**
* @author Steve Riesenberg
* @author Josh Cummings
* @since 5.8
*/
public final class DelegatingSecurityContextRepository implements SecurityContextRepository {

private final List<SecurityContextRepository> delegates;

public DelegatingSecurityContextRepository(SecurityContextRepository... delegates) {
this(Arrays.asList(delegates));
}

public DelegatingSecurityContextRepository(List<SecurityContextRepository> delegates) {
Assert.notEmpty(delegates, "delegates cannot be empty");
this.delegates = delegates;
}

@Override
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
return loadContext(requestResponseHolder.getRequest()).get();
}

@Override
public DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {
DeferredSecurityContext deferredSecurityContext = null;
for (SecurityContextRepository delegate : this.delegates) {
if (deferredSecurityContext == null) {
deferredSecurityContext = delegate.loadDeferredContext(request);
}
else {
DeferredSecurityContext next = delegate.loadDeferredContext(request);
deferredSecurityContext = new DelegatingDeferredSecurityContext(deferredSecurityContext, next);
}
}
return deferredSecurityContext;
}

@Override
public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
for (SecurityContextRepository delegate : this.delegates) {
delegate.saveContext(context, request, response);
}
}

@Override
public boolean containsContext(HttpServletRequest request) {
for (SecurityContextRepository delegate : this.delegates) {
if (delegate.containsContext(request)) {
return true;
}
}
return false;
}

static final class DelegatingDeferredSecurityContext implements DeferredSecurityContext {

private final DeferredSecurityContext previous;

private final DeferredSecurityContext next;

DelegatingDeferredSecurityContext(DeferredSecurityContext previous, DeferredSecurityContext next) {
this.previous = previous;
this.next = next;
}

@Override
public SecurityContext get() {
SecurityContext securityContext = this.previous.get();
if (!this.previous.isGenerated()) {
return securityContext;
}
return this.next.get();
}

@Override
public boolean isGenerated() {
return this.previous.isGenerated() && this.next.isGenerated();
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
*
* @author Luke Taylor
* @since 3.0
* @deprecated Use {@link SecurityContextRepository#loadContext(HttpServletRequest)}
* @deprecated Use
* {@link SecurityContextRepository#loadDeferredContext(HttpServletRequest)}
*/
@Deprecated
public final class HttpRequestResponseHolder {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.springframework.security.web.context;

import java.util.function.Supplier;

import javax.servlet.AsyncContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
Expand All @@ -33,6 +35,7 @@
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.Transient;
import org.springframework.security.core.context.DeferredSecurityContext;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
Expand Down Expand Up @@ -136,6 +139,12 @@ public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHold
return context;
}

@Override
public DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {
Supplier<SecurityContext> supplier = () -> readSecurityContextFromSession(request.getSession(false));
return new SupplierDeferredSecurityContext(supplier, this.securityContextHolderStrategy);
}

@Override
public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
SaveContextOnUpdateOrErrorResponseWrapper responseWrapper = WebUtils.getNativeResponse(response,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.context.DeferredSecurityContext;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
Expand Down Expand Up @@ -76,17 +77,13 @@ public boolean containsContext(HttpServletRequest request) {

@Override
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
return getContextOrEmpty(requestResponseHolder.getRequest());
return loadDeferredContext(requestResponseHolder.getRequest()).get();
}

@Override
public Supplier<SecurityContext> loadContext(HttpServletRequest request) {
return () -> getContextOrEmpty(request);
}

private SecurityContext getContextOrEmpty(HttpServletRequest request) {
SecurityContext context = getContext(request);
return (context != null) ? context : this.securityContextHolderStrategy.createEmptyContext();
public DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {
Supplier<SecurityContext> supplier = () -> getContext(request);
return new SupplierDeferredSecurityContext(supplier, this.securityContextHolderStrategy);
}

private SecurityContext getContext(HttpServletRequest request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
* @author Marten Algesten
* @author Rob Winch
* @since 3.0
* @deprecated Use {@link SecurityContextRepository#loadContext(HttpServletRequest)}
* instead.
* @deprecated Use
* {@link SecurityContextRepository#loadDeferredContext(HttpServletRequest)} instead.
*/
@Deprecated
public abstract class SaveContextOnUpdateOrErrorResponseWrapper extends OnCommittedResponseWrapper {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public SecurityContextHolderFilter(SecurityContextRepository securityContextRepo
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
Supplier<SecurityContext> deferredContext = this.securityContextRepository.loadContext(request);
Supplier<SecurityContext> deferredContext = this.securityContextRepository.loadDeferredContext(request);
try {
this.securityContextHolderStrategy.setDeferredContext(deferredContext);
filterChain.doFilter(request, response);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2022 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.
Expand All @@ -21,7 +21,9 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.context.DeferredSecurityContext;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.function.SingletonSupplier;

/**
Expand Down Expand Up @@ -61,7 +63,7 @@ public interface SecurityContextRepository {
* the context should be loaded.
* @return The security context which should be used for the current request, never
* null.
* @deprecated Use {@link #loadContext(HttpServletRequest)} instead.
* @deprecated Use {@link #loadDeferredContext(HttpServletRequest)} instead.
*/
@Deprecated
SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);
Expand All @@ -75,9 +77,27 @@ public interface SecurityContextRepository {
* @return a {@link Supplier} that returns the {@link SecurityContext} which cannot be
* null.
* @since 5.7
* @deprecated Use
* {@link SecurityContextRepository#loadDeferredContext(HttpServletRequest)} instead
*/
@Deprecated
default Supplier<SecurityContext> loadContext(HttpServletRequest request) {
return SingletonSupplier.of(() -> loadContext(new HttpRequestResponseHolder(request, null)));
return loadDeferredContext(request);
}

/**
* Defers loading the {@link SecurityContext} using the {@link HttpServletRequest}
* until it is needed by the application.
* @param request the {@link HttpServletRequest} to load the {@link SecurityContext}
* from
* @return a {@link DeferredSecurityContext} that returns the {@link SecurityContext}
* which cannot be null
* @since 5.8
*/
default DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {
Supplier<SecurityContext> supplier = () -> loadContext(new HttpRequestResponseHolder(request, null));
return new SupplierDeferredSecurityContext(SingletonSupplier.of(supplier),
SecurityContextHolder.getContextHolderStrategy());
}

/**
Expand Down
Loading