2626import org .apache .commons .logging .LogFactory ;
2727import org .springframework .boot .autoconfigure .condition .ConditionalOnProperty ;
2828import org .springframework .data .redis .core .RedisTemplate ;
29+ import org .springframework .security .core .Authentication ;
2930import org .springframework .security .core .context .SecurityContext ;
3031import org .springframework .session .Session ;
3132import org .springframework .session .data .redis .RedisIndexedSessionRepository ;
3233import org .springframework .stereotype .Component ;
3334
3435import javax .annotation .PostConstruct ;
3536import javax .inject .Inject ;
37+ import java .time .Instant ;
38+ import java .util .HashSet ;
3639import java .util .Objects ;
3740import java .util .Set ;
3841import java .util .Timer ;
4346
4447@ Component
4548@ ConditionalOnProperty (name = "spring.session.store-type" , havingValue = "redis" )
46- public class RedisSessionService implements ISessionService {
49+ public class RedisSessionService implements ISessionService {
4750
4851 private static final Pattern SESSION_ID_PATTERN = Pattern .compile ("^.*sessions:([a-z0-9-]*)$" );
49- private static final int CACHE_UPDATE_INTERVAL = 60 * 1000 ; // update cache every minutes
52+ private static final int CACHE_UPDATE_INTERVAL = 20 * 1000 ; // update cache every minutes
5053
5154 private final Log logger = LogFactory .getLog (RedisSessionService .class );
5255
@@ -60,6 +63,7 @@ public class RedisSessionService implements ISessionService {
6063 private RedisTemplate <Object , Object > redisTemplate ;
6164
6265 private Integer cachedUsersLoggedInCount = null ; // default value;
66+ private Integer cachedActiveUsersCount = null ; // default value;
6367
6468 @ PostConstruct
6569 public void init () {
@@ -78,6 +82,11 @@ public Integer getLoggedInUsersCount() {
7882 return cachedUsersLoggedInCount ;
7983 }
8084
85+ @ Override
86+ public Integer getActiveUsersCount () {
87+ return cachedActiveUsersCount ;
88+ }
89+
8190 /**
8291 * Updates the cached count of users.
8392 * We only update this value every CACHE_UPDATE_INTERVAL because this is a relative heavy computation to do.
@@ -93,30 +102,33 @@ private void updateCachedUsersLoggedInCount() {
93102 return ;
94103 }
95104
96- Set <String > authenticatedUsers = keys
97- .stream ()
98- .map ((keyId ) -> {
99- String sessionId = extractSessionId (keyId );
100- if (sessionId != null ) {
101- logger .debug (String .format ("Extracted sessionId %s " , sessionId ));
102- Session session = redisIndexedSessionRepository .findById (sessionId );
103- if (session != null ) {
104- Object object = session .getAttribute ("SPRING_SECURITY_CONTEXT" );
105- if (object instanceof SecurityContext ) {
106- SecurityContext securityContext = (SecurityContext ) object ;
107- if (securityContext .getAuthentication ().isAuthenticated ()) {
108- return securityContext .getAuthentication ().getName ();
109- }
110- }
111- }
112- }
113- return null ;
114- })
115- .filter (Objects ::nonNull )
116- .collect (Collectors .toSet ());
105+ // users are only counted as active, if they are active after this time.
106+ Instant minimumInstantTime = Instant .now ().minusSeconds (60 );
107+
108+ Set <String > authenticatedUsers = new HashSet <>();
109+ Set <String > activeUsers = new HashSet <>();
110+
111+ for (Object keyId : keys ) {
112+ String sessionId = extractSessionId (keyId );
113+ if (sessionId == null ) continue ;
114+
115+ Session session = redisIndexedSessionRepository .findById (sessionId );
116+ if (session == null ) continue ;
117+
118+ Authentication authentication = extractAuthenticationIfAuthenticated (session );
119+ if (authentication == null ) continue ;
120+
121+ authenticatedUsers .add (authentication .getName ());
122+
123+ if (session .getLastAccessedTime ().isAfter (minimumInstantTime )) {
124+ activeUsers .add (authentication .getName ());
125+ }
126+ }
117127
118128 logger .debug (String .format ("Logged in users count %s, all users: %s " , authenticatedUsers .size (), authenticatedUsers ));
119129 cachedUsersLoggedInCount = authenticatedUsers .size ();
130+ logger .debug (String .format ("Active users count %s, %s " , activeUsers .size (), activeUsers ));
131+ cachedActiveUsersCount = activeUsers .size ();
120132 }
121133
122134 /**
@@ -133,4 +145,24 @@ private String extractSessionId(Object keyId) {
133145 return null ;
134146 }
135147
148+ /**
149+ * Extracts the {@link Authentication} object from the given Session, if and only if the user is authenticated.
150+ * @param session the session
151+ * @return Authentication|null
152+ */
153+ private Authentication extractAuthenticationIfAuthenticated (Session session ) {
154+ Object object = session .getAttribute ("SPRING_SECURITY_CONTEXT" );
155+
156+ if (object instanceof SecurityContext ) {
157+ SecurityContext securityContext = (SecurityContext ) object ;
158+
159+ if (securityContext .getAuthentication ().isAuthenticated ()) {
160+
161+ return securityContext .getAuthentication ();
162+ }
163+ }
164+
165+ return null ;
166+ }
167+
136168}
0 commit comments