1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.acegisecurity.concurrent;
17
18 import org.acegisecurity.AcegiMessageSource;
19 import org.acegisecurity.Authentication;
20 import org.acegisecurity.AuthenticationException;
21 import org.springframework.beans.factory.InitializingBean;
22 import org.springframework.context.MessageSource;
23 import org.springframework.context.MessageSourceAware;
24 import org.springframework.context.support.MessageSourceAccessor;
25 import org.springframework.util.Assert;
26
27
28 /***
29 * Base implementation of {@link ConcurrentSessionControllerImpl} which
30 * prohibits simultaneous logins.
31 *
32 * <p>
33 * By default uses {@link org.acegisecurity.concurrent.SessionRegistryImpl},
34 * although any <code>SessionRegistry</code> may be used.
35 * </p>
36 */
37 public class ConcurrentSessionControllerImpl
38 implements ConcurrentSessionController, InitializingBean,
39 MessageSourceAware {
40
41
42 protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
43 private SessionRegistry sessionRegistry = new SessionRegistryImpl();
44 private boolean exceptionIfMaximumExceeded = false;
45 private int maximumSessions = 1;
46
47
48
49 public void afterPropertiesSet() throws Exception {
50 Assert.notNull(sessionRegistry, "SessionRegistry required");
51 Assert.isTrue(maximumSessions != 0,
52 "MaximumLogins must be either -1 to allow unlimited logins, or a positive integer to specify a maximum");
53 Assert.notNull(this.messages, "A message source must be set");
54 }
55
56 /***
57 * Allows subclasses to customise behaviour when too many sessions are
58 * detected.
59 *
60 * @param sessionId the session ID of the present request
61 * @param sessions either <code>null</code> or all unexpired sessions
62 * associated with the principal
63 * @param allowableSessions DOCUMENT ME!
64 * @param registry an instance of the <code>SessionRegistry</code> for
65 * subclass use
66 *
67 * @throws ConcurrentLoginException DOCUMENT ME!
68 */
69 protected void allowableSessionsExceeded(String sessionId,
70 SessionInformation[] sessions, int allowableSessions,
71 SessionRegistry registry) {
72 if (exceptionIfMaximumExceeded || (sessions == null)) {
73 throw new ConcurrentLoginException(messages.getMessage(
74 "ConcurrentSessionControllerImpl.exceededAllowed",
75 new Object[] {new Integer(allowableSessions)},
76 "Maximum sessions of {0} for this principal exceeded"));
77 }
78
79
80 SessionInformation leastRecentlyUsed = null;
81
82 for (int i = 0; i < sessions.length; i++) {
83 if ((leastRecentlyUsed == null)
84 || sessions[i].getLastRequest()
85 .before(leastRecentlyUsed.getLastRequest())) {
86 leastRecentlyUsed = sessions[i];
87 }
88 }
89
90 leastRecentlyUsed.expireNow();
91 }
92
93 public void checkAuthenticationAllowed(Authentication request)
94 throws AuthenticationException {
95 Assert.notNull(request,
96 "Authentication request cannot be null (violation of interface contract)");
97
98 Object principal = SessionRegistryUtils
99 .obtainPrincipalFromAuthentication(request);
100 String sessionId = SessionRegistryUtils
101 .obtainSessionIdFromAuthentication(request);
102
103 SessionInformation[] sessions = sessionRegistry.getAllSessions(principal);
104
105 int sessionCount = 0;
106
107 if (sessions != null) {
108 sessionCount = sessions.length;
109 }
110
111 int allowableSessions = getMaximumSessionsForThisUser(request);
112 Assert.isTrue(allowableSessions != 0,
113 "getMaximumSessionsForThisUser() must return either -1 to allow unlimited logins, or a positive integer to specify a maximum");
114
115 if (sessionCount < allowableSessions) {
116 return;
117 } else if (sessionCount == allowableSessions) {
118
119 for (int i = 0; i < sessionCount; i++) {
120 if (sessions[i].getSessionId().equals(sessionId)) {
121 return;
122 }
123 }
124 }
125
126 allowableSessionsExceeded(sessionId, sessions,
127 allowableSessions, sessionRegistry);
128 }
129
130 /***
131 * Method intended for use by subclasses to override the maximum
132 * number of sessions that are permitted for a particular
133 * authentication. The default implementation simply returns the
134 * <code>maximumSessions</code> value for the bean.
135 *
136 * @param authentication to determine the maximum sessions for
137 *
138 * @return either -1 meaning unlimited, or a positive integer to
139 * limit (never zero)
140 */
141 protected int getMaximumSessionsForThisUser(
142 Authentication authentication) {
143 return maximumSessions;
144 }
145
146 public void registerSuccessfulAuthentication(
147 Authentication authentication) {
148 Assert.notNull(authentication,
149 "Authentication cannot be null (violation of interface contract)");
150
151 Object principal = SessionRegistryUtils
152 .obtainPrincipalFromAuthentication(authentication);
153 String sessionId = SessionRegistryUtils
154 .obtainSessionIdFromAuthentication(authentication);
155
156 sessionRegistry.removeSessionInformation(sessionId);
157 sessionRegistry.registerNewSession(sessionId, principal);
158 }
159
160 public void setExceptionIfMaximumExceeded(
161 boolean exceptionIfMaximumExceeded) {
162 this.exceptionIfMaximumExceeded = exceptionIfMaximumExceeded;
163 }
164
165 public void setMaximumSessions(int maximumSessions) {
166 this.maximumSessions = maximumSessions;
167 }
168
169 public void setMessageSource(MessageSource messageSource) {
170 this.messages = new MessageSourceAccessor(messageSource);
171 }
172
173 public void setSessionRegistry(
174 SessionRegistry sessionRegistry) {
175 this.sessionRegistry = sessionRegistry;
176 }
177 }