diff --git a/src/main/java/com/nonononoki/alovoa/config/SecurityConfig.java b/src/main/java/com/nonononoki/alovoa/config/SecurityConfig.java index 3d9c6eec..ab6116ac 100644 --- a/src/main/java/com/nonononoki/alovoa/config/SecurityConfig.java +++ b/src/main/java/com/nonononoki/alovoa/config/SecurityConfig.java @@ -1,5 +1,8 @@ package com.nonononoki.alovoa.config; +import java.util.ArrayList; +import java.util.List; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -7,10 +10,18 @@ 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.WebSecurityConfigurerAdapter; +import org.springframework.security.core.session.SessionRegistry; +import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices; +import org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy; +import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; +import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; +import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy; +import org.springframework.security.web.session.SessionInformationExpiredStrategy; +import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy; import com.nonononoki.alovoa.component.AuthFilter; import com.nonononoki.alovoa.component.AuthProvider; @@ -74,6 +85,8 @@ public void configure(HttpSecurity http) throws Exception { .addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class).rememberMe() .rememberMeServices(rememberMeServices()).key(rememberKey); + http.sessionManagement().maximumSessions(10).expiredSessionStrategy(getSessionInformationExpiredStrategy()) + .sessionRegistry(sessionRegistry()); http.csrf().ignoringAntMatchers("/donate/received/**"); http.requiresChannel().anyRequest().requiresSecure(); } @@ -90,9 +103,32 @@ public AuthFilter authenticationFilter() throws Exception { filter.setAuthenticationSuccessHandler(successHandler); filter.setAuthenticationFailureHandler(failureHandler); filter.setRememberMeServices(rememberMeServices()); + filter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy()); return filter; } + // cf. + // https://stackoverflow.com/questions/32463022/sessionregistry-is-empty-when-i-use-concurrentsessioncontrolauthenticationstrate + public SessionAuthenticationStrategy sessionAuthenticationStrategy() { + List stratList = new ArrayList<>(); + SessionFixationProtectionStrategy concStrat = new SessionFixationProtectionStrategy(); + stratList.add(concStrat); + RegisterSessionAuthenticationStrategy regStrat = new RegisterSessionAuthenticationStrategy(sessionRegistry()); + stratList.add(regStrat); + CompositeSessionAuthenticationStrategy compStrat = new CompositeSessionAuthenticationStrategy(stratList); + return compStrat; + } + + public SessionInformationExpiredStrategy getSessionInformationExpiredStrategy() { + SessionInformationExpiredStrategy strat = new SimpleRedirectSessionInformationExpiredStrategy("/logout"); + return strat; + } + + @Bean + public SessionRegistry sessionRegistry() { + return new SessionRegistryImpl(); + } + @Bean public TokenBasedRememberMeServices rememberMeServices() throws Exception { return new TokenBasedRememberMeServices(rememberKey, customUserDetailsService); diff --git a/src/main/java/com/nonononoki/alovoa/service/PasswordService.java b/src/main/java/com/nonononoki/alovoa/service/PasswordService.java index 039f14a2..6cebcf11 100644 --- a/src/main/java/com/nonononoki/alovoa/service/PasswordService.java +++ b/src/main/java/com/nonononoki/alovoa/service/PasswordService.java @@ -3,6 +3,8 @@ import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; import javax.mail.MessagingException; import javax.servlet.http.HttpSession; @@ -11,7 +13,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.DisabledException; +import org.springframework.security.core.session.SessionRegistry; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.core.user.DefaultOAuth2User; import org.springframework.stereotype.Service; import com.nonononoki.alovoa.entity.User; @@ -43,6 +47,9 @@ public class PasswordService { @Autowired private MailService mailService; + @Autowired + private SessionRegistry sessionRegistry; + @Value("${app.password-token.length}") private int tokenLength; @@ -98,7 +105,31 @@ public void changePasword(PasswordChangeDto dto, HttpSession session) throws Alo user.setRegisterToken(null); } + List principals = sessionRegistry.getAllPrincipals().stream().filter(auth -> { + try { + String email; + if (auth instanceof DefaultOAuth2User) { + DefaultOAuth2User oauth2User = (DefaultOAuth2User) auth; + email = oauth2User.getAttribute("email"); + } else { + email = (String) auth; + } + if (user.getEmail().equals(email)) { + return true; + } else { + return false; + } + } catch (Exception e) { + return false; + } + }).collect(Collectors.toList()); + + for (Object p : principals) { + sessionRegistry.getAllSessions(p, false).forEach(sessionInfo -> { + sessionInfo.expireNow(); + }); + } + userRepo.saveAndFlush(user); - session.invalidate(); } }