update oidc+partey
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package de.bstly.we.businesslogic;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import de.bstly.we.model.User;
|
||||
import de.bstly.we.security.LocalRememberMeServices;
|
||||
import de.bstly.we.security.LocalRememberMeServices.ConcurrentToken;
|
||||
|
||||
/**
|
||||
* The Class SessionManager.
|
||||
*/
|
||||
@Component
|
||||
public class SessionManager {
|
||||
|
||||
@Autowired
|
||||
private FindByIndexNameSessionRepository<? extends Session> sessionRepository;
|
||||
@Autowired
|
||||
private LocalRememberMeServices rememberMeServices;
|
||||
|
||||
/**
|
||||
* Delete sessions for user.
|
||||
*
|
||||
* @param user the user
|
||||
*/
|
||||
public void deleteSessionsForUser(User user) {
|
||||
Map<String, ? extends Session> usersSessions = sessionRepository.findByPrincipalName(user.getUsername());
|
||||
for (Session session : usersSessions.values()) {
|
||||
if (session.getAttribute("userId") != null && session.getAttribute("userId").equals(user.getId())) {
|
||||
sessionRepository.deleteById(session.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Concurrent clean up.
|
||||
*/
|
||||
@Scheduled(cron = "${rememberme.concurrent.cron:0 */5 * * * * }")
|
||||
public void concurrentCleanUp() {
|
||||
Set<String> delete = Sets.newHashSet();
|
||||
|
||||
for (Entry<String, ConcurrentToken> entry : rememberMeServices.getConcurrentTokens().entrySet()) {
|
||||
if (Instant.now().minus(rememberMeServices.getConcurrentTimeout(), ChronoUnit.SECONDS)
|
||||
.isAfter(entry.getValue().getRenewed())) {
|
||||
delete.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
for (String key : delete) {
|
||||
rememberMeServices.getConcurrentTokens().remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,8 +39,8 @@ import de.bstly.we.model.PermissionMapping;
|
||||
import de.bstly.we.model.ProfileFieldType;
|
||||
import de.bstly.we.model.User;
|
||||
import de.bstly.we.model.UserProfileField;
|
||||
import de.bstly.we.model.Visibility;
|
||||
import de.bstly.we.model.UserStatus;
|
||||
import de.bstly.we.model.Visibility;
|
||||
|
||||
/**
|
||||
* The Class UserController.
|
||||
|
||||
@@ -3,17 +3,42 @@
|
||||
*/
|
||||
package de.bstly.we.security;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.web.authentication.rememberme.CookieTheftException;
|
||||
import org.springframework.security.web.authentication.rememberme.InvalidCookieException;
|
||||
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
|
||||
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
|
||||
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
|
||||
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationException;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* The Class LocalRememberMeServices.
|
||||
*/
|
||||
public class LocalRememberMeServices extends PersistentTokenBasedRememberMeServices {
|
||||
|
||||
private PersistentTokenRepository tokenRepository;
|
||||
|
||||
@Value("${rememberme.concurrent.enabled:true}")
|
||||
private boolean concurrent;
|
||||
@Value("${rememberme.concurrent.timeout:120}")
|
||||
private long concurrentTimeout; // default 2 minutes
|
||||
|
||||
private Map<String, ConcurrentToken> concurrentTokens = Maps.newHashMap();
|
||||
|
||||
/**
|
||||
* Instantiates a new local remember me services.
|
||||
*
|
||||
@@ -24,6 +49,7 @@ public class LocalRememberMeServices extends PersistentTokenBasedRememberMeServi
|
||||
public LocalRememberMeServices(String key, UserDetailsService userDetailsService,
|
||||
PersistentTokenRepository tokenRepository) {
|
||||
super(key, userDetailsService, tokenRepository);
|
||||
this.tokenRepository = tokenRepository;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -45,4 +71,174 @@ public class LocalRememberMeServices extends PersistentTokenBasedRememberMeServi
|
||||
return super.rememberMeRequested(request, parameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
if (cookieTokens.length != 2) {
|
||||
throw new InvalidCookieException("Cookie token did not contain " + 2 + " tokens, but contained '"
|
||||
+ Arrays.asList(cookieTokens) + "'");
|
||||
}
|
||||
String presentedSeries = cookieTokens[0];
|
||||
String presentedToken = cookieTokens[1];
|
||||
PersistentRememberMeToken token = this.tokenRepository.getTokenForSeries(presentedSeries);
|
||||
if (token == null) {
|
||||
// No series match, so we can't authenticate using this cookie
|
||||
throw new RememberMeAuthenticationException("No persistent token found for series id: " + presentedSeries);
|
||||
}
|
||||
|
||||
boolean concurrentToken = false;
|
||||
String newTokenData = generateTokenData();
|
||||
// We have a match for this user/series combination
|
||||
|
||||
this.logger.trace("concurrentTokens: " + concurrentTokens);
|
||||
|
||||
if (!presentedToken.equals(token.getTokenValue())) {
|
||||
if (concurrent && concurrentTokens.containsKey(presentedToken)) {
|
||||
this.logger.debug("found token '" + presentedToken + "' in concurrent tokens");
|
||||
if (concurrentTokens.get(presentedToken).getSeries().equals(presentedSeries)) {
|
||||
this.logger.debug("found series '" + presentedSeries + "' in concurrent tokens");
|
||||
if (Instant.now().minus(concurrentTimeout, ChronoUnit.SECONDS)
|
||||
.isBefore(concurrentTokens.get(presentedToken).getRenewed())) {
|
||||
newTokenData = token.getTokenValue();
|
||||
concurrentToken = true;
|
||||
} else {
|
||||
this.logger.debug("cocurrent token expired!");
|
||||
concurrentTokens.remove(presentedToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!concurrentToken) {
|
||||
// Token doesn't match series value. Delete all logins for this user and throw
|
||||
// an exception to warn them.
|
||||
this.tokenRepository.removeUserTokens(token.getUsername());
|
||||
throw new CookieTheftException(this.messages.getMessage(
|
||||
"PersistentTokenBasedRememberMeServices.cookieStolen",
|
||||
"Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack."));
|
||||
}
|
||||
}
|
||||
if (token.getDate().getTime() + getTokenValiditySeconds() * 1000L < System.currentTimeMillis()) {
|
||||
throw new RememberMeAuthenticationException("Remember-me login has expired");
|
||||
}
|
||||
// Token also matches, so login is valid. Update the token value, keeping the
|
||||
// *same* series number.
|
||||
this.logger.debug(LogMessage.format("Refreshing persistent login token for user '%s', series '%s'",
|
||||
token.getUsername(), token.getSeries()));
|
||||
|
||||
if (!concurrentToken && !concurrentTokens.containsKey(presentedToken)) {
|
||||
concurrentTokens.put(presentedToken, new ConcurrentToken(presentedSeries, Instant.now()));
|
||||
}
|
||||
|
||||
PersistentRememberMeToken newToken = new PersistentRememberMeToken(token.getUsername(), token.getSeries(),
|
||||
newTokenData, new Date());
|
||||
try {
|
||||
this.tokenRepository.updateToken(newToken.getSeries(), newToken.getTokenValue(), newToken.getDate());
|
||||
addCookie(newToken, request, response);
|
||||
} catch (Exception ex) {
|
||||
this.logger.error("Failed to update token: ", ex);
|
||||
throw new RememberMeAuthenticationException("Autologin failed due to data access problem");
|
||||
}
|
||||
return getUserDetailsService().loadUserByUsername(token.getUsername());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the user tokens.
|
||||
*
|
||||
* @param username the username
|
||||
*/
|
||||
public void removeUserTokens(String username) {
|
||||
tokenRepository.removeUserTokens(username);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the cookie.
|
||||
*
|
||||
* @param token the token
|
||||
* @param request the request
|
||||
* @param response the response
|
||||
*/
|
||||
private void addCookie(PersistentRememberMeToken token, HttpServletRequest request, HttpServletResponse response) {
|
||||
setCookie(new String[] { token.getSeries(), token.getTokenValue() }, getTokenValiditySeconds(), request,
|
||||
response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the concurrent tokens.
|
||||
*
|
||||
* @return the concurrent tokens
|
||||
*/
|
||||
public Map<String, ConcurrentToken> getConcurrentTokens() {
|
||||
if (concurrentTokens == null) {
|
||||
concurrentTokens = Maps.newHashMap();
|
||||
}
|
||||
return concurrentTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the concurrent timeout.
|
||||
*
|
||||
* @return the concurrent timeout
|
||||
*/
|
||||
public long getConcurrentTimeout() {
|
||||
return concurrentTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Class ConcurrentToken.
|
||||
*/
|
||||
public static class ConcurrentToken {
|
||||
|
||||
private String series;
|
||||
private Instant renewed;
|
||||
|
||||
/**
|
||||
* Instantiates a new concurrent token.
|
||||
*
|
||||
* @param series the series
|
||||
* @param renewed the renewed
|
||||
*/
|
||||
public ConcurrentToken(String series, Instant renewed) {
|
||||
super();
|
||||
this.series = series;
|
||||
this.renewed = renewed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the series.
|
||||
*
|
||||
* @return the series
|
||||
*/
|
||||
public String getSeries() {
|
||||
return series;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the series.
|
||||
*
|
||||
* @param series the new series
|
||||
*/
|
||||
public void setSeries(String series) {
|
||||
this.series = series;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the renewed.
|
||||
*
|
||||
* @return the renewed
|
||||
*/
|
||||
public Instant getRenewed() {
|
||||
return renewed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the renewed.
|
||||
*
|
||||
* @param renewed the new renewed
|
||||
*/
|
||||
public void setRenewed(Instant renewed) {
|
||||
this.renewed = renewed;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user