Merge remote-tracking branch 'origin/5.8.x'

This commit is contained in:
Josh Cummings
2022-09-30 17:01:22 -06:00
4 changed files with 176 additions and 24 deletions

View File

@@ -18,6 +18,8 @@ package org.springframework.security.core.context;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import org.springframework.util.Assert;
@@ -127,9 +129,9 @@ public final class ListeningSecurityContextHolderStrategy implements SecurityCon
*/
@Override
public void clearContext() {
SecurityContext from = getContext();
Supplier<SecurityContext> deferred = this.delegate.getDeferredContext();
this.delegate.clearContext();
publish(from, null);
publish(new SecurityContextChangedEvent(deferred, SecurityContextChangedEvent.NO_CONTEXT));
}
/**
@@ -140,14 +142,28 @@ public final class ListeningSecurityContextHolderStrategy implements SecurityCon
return this.delegate.getContext();
}
/**
* {@inheritDoc}
*/
@Override
public Supplier<SecurityContext> getDeferredContext() {
return this.delegate.getDeferredContext();
}
/**
* {@inheritDoc}
*/
@Override
public void setContext(SecurityContext context) {
SecurityContext from = getContext();
this.delegate.setContext(context);
publish(from, context);
setDeferredContext(() -> context);
}
/**
* {@inheritDoc}
*/
@Override
public void setDeferredContext(Supplier<SecurityContext> deferredContext) {
this.delegate.setDeferredContext(new PublishOnceSupplier(getDeferredContext(), deferredContext));
}
/**
@@ -158,14 +174,42 @@ public final class ListeningSecurityContextHolderStrategy implements SecurityCon
return this.delegate.createEmptyContext();
}
private void publish(SecurityContext previous, SecurityContext current) {
if (previous == current) {
return;
}
SecurityContextChangedEvent event = new SecurityContextChangedEvent(previous, current);
private void publish(SecurityContextChangedEvent event) {
for (SecurityContextChangedListener listener : this.listeners) {
listener.securityContextChanged(event);
}
}
class PublishOnceSupplier implements Supplier<SecurityContext> {
private final AtomicBoolean isPublished = new AtomicBoolean(false);
private final Supplier<SecurityContext> old;
private final Supplier<SecurityContext> updated;
PublishOnceSupplier(Supplier<SecurityContext> old, Supplier<SecurityContext> updated) {
if (old instanceof PublishOnceSupplier) {
this.old = ((PublishOnceSupplier) old).updated;
}
else {
this.old = old;
}
this.updated = updated;
}
@Override
public SecurityContext get() {
SecurityContext updated = this.updated.get();
if (this.isPublished.compareAndSet(false, true)) {
SecurityContext old = this.old.get();
if (old != updated) {
publish(new SecurityContextChangedEvent(old, updated));
}
}
return updated;
}
}
}

View File

@@ -16,6 +16,8 @@
package org.springframework.security.core.context;
import java.util.function.Supplier;
import org.springframework.context.ApplicationEvent;
/**
@@ -26,9 +28,24 @@ import org.springframework.context.ApplicationEvent;
*/
public class SecurityContextChangedEvent extends ApplicationEvent {
private final SecurityContext oldContext;
public static final Supplier<SecurityContext> NO_CONTEXT = () -> null;
private final SecurityContext newContext;
private final Supplier<SecurityContext> oldContext;
private final Supplier<SecurityContext> newContext;
/**
* Construct an event
* @param oldContext the old security context
* @param newContext the new security context, use
* {@link SecurityContextChangedEvent#NO_CONTEXT} for if the context is cleared
* @since 5.8
*/
public SecurityContextChangedEvent(Supplier<SecurityContext> oldContext, Supplier<SecurityContext> newContext) {
super(SecurityContextHolder.class);
this.oldContext = oldContext;
this.newContext = newContext;
}
/**
* Construct an event
@@ -36,9 +53,7 @@ public class SecurityContextChangedEvent extends ApplicationEvent {
* @param newContext the new security context
*/
public SecurityContextChangedEvent(SecurityContext oldContext, SecurityContext newContext) {
super(SecurityContextHolder.class);
this.oldContext = oldContext;
this.newContext = newContext;
this(() -> oldContext, (newContext != null) ? () -> newContext : NO_CONTEXT);
}
/**
@@ -47,7 +62,7 @@ public class SecurityContextChangedEvent extends ApplicationEvent {
* @return the previous {@link SecurityContext}
*/
public SecurityContext getOldContext() {
return this.oldContext;
return this.oldContext.get();
}
/**
@@ -56,7 +71,21 @@ public class SecurityContextChangedEvent extends ApplicationEvent {
* @return the current {@link SecurityContext}
*/
public SecurityContext getNewContext() {
return this.newContext;
return this.newContext.get();
}
/**
* Say whether the event is a context-clearing event.
*
* <p>
* This method is handy for avoiding looking up the new context to confirm it is a
* cleared event.
* @return {@code true} if the new context is
* {@link SecurityContextChangedEvent#NO_CONTEXT}
* @since 5.8
*/
public boolean isCleared() {
return this.newContext == NO_CONTEXT;
}
}