diff --git a/docs/src/test/java/docs/security/SecurityConfiguration.java b/docs/src/test/java/docs/security/SecurityConfiguration.java index 8bc3512e..23950a91 100644 --- a/docs/src/test/java/docs/security/SecurityConfiguration.java +++ b/docs/src/test/java/docs/security/SecurityConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2016 the original author or authors. + * Copyright 2014-2017 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. @@ -33,20 +33,23 @@ import org.springframework.session.security.SpringSessionBackedSessionRegistry; public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired - FindByIndexNameSessionRepository sessionRepository; + private FindByIndexNameSessionRepository sessionRepository; @Override protected void configure(HttpSecurity http) throws Exception { + // @formatter:off http // other config goes here... .sessionManagement() .maximumSessions(2) .sessionRegistry(sessionRegistry()); + // @formatter:on } @Bean SpringSessionBackedSessionRegistry sessionRegistry() { - return new SpringSessionBackedSessionRegistry(this.sessionRepository); + return new SpringSessionBackedSessionRegistry( + this.sessionRepository); } } // end::class[] diff --git a/spring-session/src/main/java/org/springframework/session/security/SpringSessionBackedSessionInformation.java b/spring-session/src/main/java/org/springframework/session/security/SpringSessionBackedSessionInformation.java index e789d044..ea3fc57c 100644 --- a/spring-session/src/main/java/org/springframework/session/security/SpringSessionBackedSessionInformation.java +++ b/spring-session/src/main/java/org/springframework/session/security/SpringSessionBackedSessionInformation.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2016 the original author or authors. + * Copyright 2014-2017 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. @@ -23,27 +23,43 @@ import org.apache.commons.logging.LogFactory; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.session.SessionInformation; - import org.springframework.session.ExpiringSession; import org.springframework.session.FindByIndexNameSessionRepository; import org.springframework.session.Session; import org.springframework.session.SessionRepository; /** - * Ensures that calling {@link #expireNow()} propagates to Spring Session, - * since this session information contains only derived data and is not the authoritative source. + * Ensures that calling {@link #expireNow()} propagates to Spring Session, since this + * session information contains only derived data and is not the authoritative source. * + * @param the {@link ExpiringSession} type. * @author Joris Kuipers + * @author Vedran Pavic * @since 1.3 */ -class SpringSessionBackedSessionInformation extends SessionInformation { +class SpringSessionBackedSessionInformation + extends SessionInformation { - static final String EXPIRED_ATTR = SpringSessionBackedSessionInformation.class.getName() + ".EXPIRED"; + static final String EXPIRED_ATTR = SpringSessionBackedSessionInformation.class + .getName() + ".EXPIRED"; - private static final Log logger = LogFactory.getLog(SpringSessionBackedSessionInformation.class); + private static final Log logger = LogFactory + .getLog(SpringSessionBackedSessionInformation.class); private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT"; + private final SessionRepository sessionRepository; + + SpringSessionBackedSessionInformation(S session, + SessionRepository sessionRepository) { + super(resolvePrincipal(session), session.getId(), + new Date(session.getLastAccessedTime())); + this.sessionRepository = sessionRepository; + if (Boolean.TRUE.equals(session.getAttribute(EXPIRED_ATTR))) { + super.expireNow(); + } + } + /** * Tries to determine the principal's name from the given Session. * @@ -51,7 +67,8 @@ class SpringSessionBackedSessionInformation extends SessionInformation { * @return the principal's name, or empty String if it couldn't be determined */ private static String resolvePrincipal(Session session) { - String principalName = session.getAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME); + String principalName = session + .getAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME); if (principalName != null) { return principalName; } @@ -62,30 +79,22 @@ class SpringSessionBackedSessionInformation extends SessionInformation { return ""; } - private final SessionRepository sessionRepository; - - SpringSessionBackedSessionInformation(ExpiringSession session, SessionRepository sessionRepository) { - super(resolvePrincipal(session), session.getId(), new Date(session.getLastAccessedTime())); - this.sessionRepository = sessionRepository; - if (Boolean.TRUE.equals(session.getAttribute(EXPIRED_ATTR))) { - super.expireNow(); - } - } - @Override public void expireNow() { if (logger.isDebugEnabled()) { - logger.debug("Expiring session " + getSessionId() + " for user '" + getPrincipal() + - "', presumably because maximum allowed concurrent sessions was exceeded"); + logger.debug("Expiring session " + getSessionId() + " for user '" + + getPrincipal() + "', presumably because maximum allowed concurrent " + + "sessions was exceeded"); } super.expireNow(); - ExpiringSession session = this.sessionRepository.getSession(getSessionId()); + S session = this.sessionRepository.getSession(getSessionId()); if (session != null) { session.setAttribute(EXPIRED_ATTR, Boolean.TRUE); this.sessionRepository.save(session); } else { - logger.info("Could not find Session with id " + getSessionId() + " to mark as expired"); + logger.info("Could not find Session with id " + getSessionId() + + " to mark as expired"); } } diff --git a/spring-session/src/main/java/org/springframework/session/security/SpringSessionBackedSessionRegistry.java b/spring-session/src/main/java/org/springframework/session/security/SpringSessionBackedSessionRegistry.java index 8f70e648..1aced741 100644 --- a/spring-session/src/main/java/org/springframework/session/security/SpringSessionBackedSessionRegistry.java +++ b/spring-session/src/main/java/org/springframework/session/security/SpringSessionBackedSessionRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2016 the original author or authors. + * Copyright 2014-2017 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. @@ -17,7 +17,6 @@ package org.springframework.session.security; import java.security.Principal; - import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -25,54 +24,64 @@ import java.util.List; import org.springframework.security.core.session.SessionInformation; import org.springframework.security.core.session.SessionRegistry; import org.springframework.security.core.userdetails.UserDetails; - import org.springframework.session.ExpiringSession; import org.springframework.session.FindByIndexNameSessionRepository; - import org.springframework.util.Assert; /** - * A {@link SessionRegistry} that retrieves session information from Spring Session, rather than maintaining it itself. - * This allows concurrent session management with Spring Security in a clustered environment. + * A {@link SessionRegistry} that retrieves session information from Spring Session, + * rather than maintaining it itself. This allows concurrent session management with + * Spring Security in a clustered environment. *

- * Relies on being able to derive the same String-based representation of the principal given to - * {@link #getAllSessions(Object, boolean)} as used by Spring Session in order to look up the user's sessions. + * Relies on being able to derive the same String-based representation of the principal + * given to {@link #getAllSessions(Object, boolean)} as used by Spring Session in order to + * look up the user's sessions. *

* Does not support {@link #getAllPrincipals()}, since that information is not available. * + * @param the {@link ExpiringSession} type. * @author Joris Kuipers + * @author Vedran Pavic * @since 1.3 */ -public class SpringSessionBackedSessionRegistry implements SessionRegistry { +public class SpringSessionBackedSessionRegistry + implements SessionRegistry { - private final FindByIndexNameSessionRepository sessionRepository; + private final FindByIndexNameSessionRepository sessionRepository; - public SpringSessionBackedSessionRegistry(FindByIndexNameSessionRepository sessionRepository) { + public SpringSessionBackedSessionRegistry( + FindByIndexNameSessionRepository sessionRepository) { Assert.notNull(sessionRepository, "sessionRepository cannot be null"); this.sessionRepository = sessionRepository; } public List getAllPrincipals() { - throw new UnsupportedOperationException("SpringSessionBackedSessionRegistry does not support retrieving all principals, " + - "since Spring Session provides no way to obtain that information"); + throw new UnsupportedOperationException("SpringSessionBackedSessionRegistry does " + + "not support retrieving all principals, since Spring Session provides " + + "no way to obtain that information"); } - public List getAllSessions(Object principal, boolean includeExpiredSessions) { - Collection sessions = - this.sessionRepository.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, name(principal)).values(); + public List getAllSessions(Object principal, + boolean includeExpiredSessions) { + Collection sessions = this.sessionRepository.findByIndexNameAndIndexValue( + FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, + name(principal)).values(); List infos = new ArrayList(); - for (ExpiringSession session : sessions) { - if (includeExpiredSessions || !Boolean.TRUE.equals(session.getAttribute(SpringSessionBackedSessionInformation.EXPIRED_ATTR))) { - infos.add(new SpringSessionBackedSessionInformation(session, this.sessionRepository)); + for (S session : sessions) { + if (includeExpiredSessions || !Boolean.TRUE.equals(session + .getAttribute(SpringSessionBackedSessionInformation.EXPIRED_ATTR))) { + infos.add(new SpringSessionBackedSessionInformation(session, + this.sessionRepository)); } } return infos; } public SessionInformation getSessionInformation(String sessionId) { - ExpiringSession session = this.sessionRepository.getSession(sessionId); + S session = this.sessionRepository.getSession(sessionId); if (session != null) { - return new SpringSessionBackedSessionInformation(session, this.sessionRepository); + return new SpringSessionBackedSessionInformation(session, + this.sessionRepository); } return null; } @@ -99,7 +108,8 @@ public class SpringSessionBackedSessionRegistry implements SessionRegistry { * Derives a String name for the given principal. * * @param principal as provided by Spring Security - * @return name of the principal, or its {@code toString()} representation if no name could be derived + * @return name of the principal, or its {@code toString()} representation if no name + * could be derived */ protected String name(Object principal) { if (principal instanceof UserDetails) { @@ -110,4 +120,5 @@ public class SpringSessionBackedSessionRegistry implements SessionRegistry { } return principal.toString(); } + } diff --git a/spring-session/src/test/java/org/springframework/session/security/SpringSessionBackedSessionRegistryTest.java b/spring-session/src/test/java/org/springframework/session/security/SpringSessionBackedSessionRegistryTest.java index 4c2ae522..5f66352f 100644 --- a/spring-session/src/test/java/org/springframework/session/security/SpringSessionBackedSessionRegistryTest.java +++ b/spring-session/src/test/java/org/springframework/session/security/SpringSessionBackedSessionRegistryTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2016 the original author or authors. + * Copyright 2014-2017 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. @@ -24,7 +24,6 @@ import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; - import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; @@ -44,27 +43,36 @@ import static org.mockito.BDDMockito.mock; import static org.mockito.BDDMockito.verify; import static org.mockito.BDDMockito.when; +/** + * Tests for {@link SpringSessionBackedSessionRegistry}. + */ @RunWith(MockitoJUnitRunner.class) public class SpringSessionBackedSessionRegistryTest { - static final String SESSION_ID = "sessionId"; - static final String SESSION_ID2 = "otherSessionId"; - static final String USER_NAME = "userName"; - static final User PRINCIPAL = new User(USER_NAME, "password", Collections.emptyList()); - static final Date NOW = new Date(); + private static final String SESSION_ID = "sessionId"; + + private static final String SESSION_ID2 = "otherSessionId"; + + private static final String USER_NAME = "userName"; + + private static final User PRINCIPAL = new User(USER_NAME, "password", + Collections.emptyList()); + + private static final Date NOW = new Date(); @Mock - FindByIndexNameSessionRepository sessionRepository; + private FindByIndexNameSessionRepository sessionRepository; @InjectMocks - SpringSessionBackedSessionRegistry sessionRegistry; + private SpringSessionBackedSessionRegistry sessionRegistry; @Test public void sessionInformationForExistingSession() { ExpiringSession session = createSession(SESSION_ID, USER_NAME, NOW.getTime()); when(this.sessionRepository.getSession(SESSION_ID)).thenReturn(session); - SessionInformation sessionInfo = this.sessionRegistry.getSessionInformation(SESSION_ID); + SessionInformation sessionInfo = this.sessionRegistry + .getSessionInformation(SESSION_ID); assertThat(sessionInfo.getSessionId()).isEqualTo(SESSION_ID); assertThat(sessionInfo.getLastRequest()).isEqualTo(NOW); @@ -75,10 +83,12 @@ public class SpringSessionBackedSessionRegistryTest { @Test public void sessionInformationForExpiredSession() { ExpiringSession session = createSession(SESSION_ID, USER_NAME, NOW.getTime()); - session.setAttribute(SpringSessionBackedSessionInformation.EXPIRED_ATTR, Boolean.TRUE); + session.setAttribute(SpringSessionBackedSessionInformation.EXPIRED_ATTR, + Boolean.TRUE); when(this.sessionRepository.getSession(SESSION_ID)).thenReturn(session); - SessionInformation sessionInfo = this.sessionRegistry.getSessionInformation(SESSION_ID); + SessionInformation sessionInfo = this.sessionRegistry + .getSessionInformation(SESSION_ID); assertThat(sessionInfo.getSessionId()).isEqualTo(SESSION_ID); assertThat(sessionInfo.getLastRequest()).isEqualTo(NOW); @@ -88,25 +98,30 @@ public class SpringSessionBackedSessionRegistryTest { @Test public void noSessionInformationForMissingSession() { - assertThat(this.sessionRegistry.getSessionInformation("nonExistingSessionId")).isNull(); + assertThat(this.sessionRegistry.getSessionInformation("nonExistingSessionId")) + .isNull(); } @Test public void getAllSessions() { setUpSessions(); - List allSessionInfos = this.sessionRegistry.getAllSessions(PRINCIPAL, true); + List allSessionInfos = this.sessionRegistry + .getAllSessions(PRINCIPAL, true); - assertThat(allSessionInfos).extracting("sessionId").containsExactly(SESSION_ID, SESSION_ID2); + assertThat(allSessionInfos).extracting("sessionId").containsExactly(SESSION_ID, + SESSION_ID2); } @Test public void getNonExpiredSessions() { setUpSessions(); - List nonExpiredSessionInfos = this.sessionRegistry.getAllSessions(PRINCIPAL, false); + List nonExpiredSessionInfos = this.sessionRegistry + .getAllSessions(PRINCIPAL, false); - assertThat(nonExpiredSessionInfos).extracting("sessionId").containsExactly(SESSION_ID2); + assertThat(nonExpiredSessionInfos).extracting("sessionId") + .containsExactly(SESSION_ID2); } @Test @@ -114,18 +129,23 @@ public class SpringSessionBackedSessionRegistryTest { ExpiringSession session = createSession(SESSION_ID, USER_NAME, NOW.getTime()); when(this.sessionRepository.getSession(SESSION_ID)).thenReturn(session); - SessionInformation sessionInfo = this.sessionRegistry.getSessionInformation(SESSION_ID); + SessionInformation sessionInfo = this.sessionRegistry + .getSessionInformation(SESSION_ID); assertThat(sessionInfo.isExpired()).isFalse(); sessionInfo.expireNow(); assertThat(sessionInfo.isExpired()).isTrue(); - ArgumentCaptor captor = ArgumentCaptor.forClass(ExpiringSession.class); + ArgumentCaptor captor = ArgumentCaptor + .forClass(ExpiringSession.class); verify(this.sessionRepository).save(captor.capture()); - assertThat(captor.getValue().getAttribute(SpringSessionBackedSessionInformation.EXPIRED_ATTR)).isEqualTo(Boolean.TRUE); + assertThat(captor.getValue() + .getAttribute(SpringSessionBackedSessionInformation.EXPIRED_ATTR)) + .isEqualTo(Boolean.TRUE); } - private ExpiringSession createSession(String sessionId, String userName, Long lastAccessed) { + private ExpiringSession createSession(String sessionId, String userName, + Long lastAccessed) { MapSession session = new MapSession(sessionId); session.setLastAccessedTime(lastAccessed); Authentication authentication = mock(Authentication.class); @@ -138,12 +158,15 @@ public class SpringSessionBackedSessionRegistryTest { private void setUpSessions() { ExpiringSession session1 = createSession(SESSION_ID, USER_NAME, NOW.getTime()); - session1.setAttribute(SpringSessionBackedSessionInformation.EXPIRED_ATTR, Boolean.TRUE); + session1.setAttribute(SpringSessionBackedSessionInformation.EXPIRED_ATTR, + Boolean.TRUE); ExpiringSession session2 = createSession(SESSION_ID2, USER_NAME, NOW.getTime()); Map sessions = new LinkedHashMap(); sessions.put(session1.getId(), session1); sessions.put(session2.getId(), session2); - when(this.sessionRepository.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, USER_NAME)).thenReturn(sessions); + when(this.sessionRepository.findByIndexNameAndIndexValue( + FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, USER_NAME)) + .thenReturn(sessions); } }