Add Max Sessions on WebFlux
Closes gh-6192
This commit is contained in:
committed by
Marcus Hert Da Coregio
parent
64feedf67e
commit
57ab15127a
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.session;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Provides an in-memory implementation of {@link ReactiveSessionRegistry}.
|
||||
*
|
||||
* @author Marcus da Coregio
|
||||
* @since 6.3
|
||||
*/
|
||||
public class InMemoryReactiveSessionRegistry implements ReactiveSessionRegistry {
|
||||
|
||||
private final ConcurrentMap<Object, Set<String>> sessionIdsByPrincipal;
|
||||
|
||||
private final Map<String, ReactiveSessionInformation> sessionById;
|
||||
|
||||
public InMemoryReactiveSessionRegistry() {
|
||||
this.sessionIdsByPrincipal = new ConcurrentHashMap<>();
|
||||
this.sessionById = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public InMemoryReactiveSessionRegistry(ConcurrentMap<Object, Set<String>> sessionIdsByPrincipal,
|
||||
Map<String, ReactiveSessionInformation> sessionById) {
|
||||
this.sessionIdsByPrincipal = sessionIdsByPrincipal;
|
||||
this.sessionById = sessionById;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ReactiveSessionInformation> getAllSessions(Object principal, boolean includeExpiredSessions) {
|
||||
return Flux.fromIterable(this.sessionIdsByPrincipal.getOrDefault(principal, Collections.emptySet()))
|
||||
.map(this.sessionById::get)
|
||||
.filter((sessionInformation) -> includeExpiredSessions || !sessionInformation.isExpired());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> saveSessionInformation(ReactiveSessionInformation information) {
|
||||
this.sessionById.put(information.getSessionId(), information);
|
||||
this.sessionIdsByPrincipal.computeIfAbsent(information.getPrincipal(), (key) -> new CopyOnWriteArraySet<>())
|
||||
.add(information.getSessionId());
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ReactiveSessionInformation> getSessionInformation(String sessionId) {
|
||||
return Mono.justOrEmpty(this.sessionById.get(sessionId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ReactiveSessionInformation> removeSessionInformation(String sessionId) {
|
||||
return getSessionInformation(sessionId).doOnNext((sessionInformation) -> {
|
||||
this.sessionById.remove(sessionId);
|
||||
Set<String> sessionsUsedByPrincipal = this.sessionIdsByPrincipal.get(sessionInformation.getPrincipal());
|
||||
if (sessionsUsedByPrincipal != null) {
|
||||
sessionsUsedByPrincipal.remove(sessionId);
|
||||
if (sessionsUsedByPrincipal.isEmpty()) {
|
||||
this.sessionIdsByPrincipal.remove(sessionInformation.getPrincipal());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ReactiveSessionInformation> updateLastAccessTime(String sessionId) {
|
||||
ReactiveSessionInformation session = this.sessionById.get(sessionId);
|
||||
if (session != null) {
|
||||
return session.refreshLastRequest().thenReturn(session);
|
||||
}
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.session;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class ReactiveSessionInformation implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private Instant lastAccessTime;
|
||||
|
||||
private final Object principal;
|
||||
|
||||
private final String sessionId;
|
||||
|
||||
private boolean expired = false;
|
||||
|
||||
public ReactiveSessionInformation(Object principal, String sessionId, Instant lastAccessTime) {
|
||||
Assert.notNull(principal, "principal cannot be null");
|
||||
Assert.hasText(sessionId, "sessionId cannot be null");
|
||||
Assert.notNull(lastAccessTime, "lastAccessTime cannot be null");
|
||||
this.principal = principal;
|
||||
this.sessionId = sessionId;
|
||||
this.lastAccessTime = lastAccessTime;
|
||||
}
|
||||
|
||||
public ReactiveSessionInformation withSessionId(String sessionId) {
|
||||
return new ReactiveSessionInformation(this.principal, sessionId, this.lastAccessTime);
|
||||
}
|
||||
|
||||
public Mono<Void> invalidate() {
|
||||
return Mono.fromRunnable(() -> this.expired = true);
|
||||
}
|
||||
|
||||
public Mono<Void> refreshLastRequest() {
|
||||
this.lastAccessTime = Instant.now();
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
public Instant getLastAccessTime() {
|
||||
return this.lastAccessTime;
|
||||
}
|
||||
|
||||
public Object getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
public String getSessionId() {
|
||||
return this.sessionId;
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return this.expired;
|
||||
}
|
||||
|
||||
public void setLastAccessTime(Instant lastAccessTime) {
|
||||
this.lastAccessTime = lastAccessTime;
|
||||
}
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.session;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Maintains a registry of {@link ReactiveSessionInformation} instances.
|
||||
*
|
||||
* @author Marcus da Coregio
|
||||
* @since 6.3
|
||||
*/
|
||||
public interface ReactiveSessionRegistry {
|
||||
|
||||
/**
|
||||
* Gets all the known {@link ReactiveSessionInformation} instances for the specified
|
||||
* principal.
|
||||
* @param principal the principal
|
||||
* @return the {@link ReactiveSessionInformation} instances associated with the
|
||||
* principal
|
||||
*/
|
||||
Flux<ReactiveSessionInformation> getAllSessions(Object principal, boolean includeExpiredSessions);
|
||||
|
||||
/**
|
||||
* Saves the {@link ReactiveSessionInformation}
|
||||
* @param information the {@link ReactiveSessionInformation} to save
|
||||
* @return a {@link Mono} that completes when the session is saved
|
||||
*/
|
||||
Mono<Void> saveSessionInformation(ReactiveSessionInformation information);
|
||||
|
||||
/**
|
||||
* Gets the {@link ReactiveSessionInformation} for the specified session identifier.
|
||||
* @param sessionId the session identifier
|
||||
* @return the {@link ReactiveSessionInformation} for the session.
|
||||
*/
|
||||
Mono<ReactiveSessionInformation> getSessionInformation(String sessionId);
|
||||
|
||||
/**
|
||||
* Removes the specified session from the registry.
|
||||
* @param sessionId the session identifier
|
||||
* @return a {@link Mono} that completes when the session is removed
|
||||
*/
|
||||
Mono<ReactiveSessionInformation> removeSessionInformation(String sessionId);
|
||||
|
||||
/**
|
||||
* Updates the last accessed time of the {@link ReactiveSessionInformation}
|
||||
* @param sessionId the session identifier
|
||||
* @return a {@link Mono} that completes when the session is updated
|
||||
*/
|
||||
Mono<ReactiveSessionInformation> updateLastAccessTime(String sessionId);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user