1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 module hunt.shiro.session.mgt.DefaultSessionManager; 20 21 import hunt.shiro.session.mgt.AbstractValidatingSessionManager; 22 import hunt.shiro.session.mgt.SessionContext; 23 import hunt.shiro.session.mgt.SessionFactory; 24 import hunt.shiro.session.mgt.SessionKey; 25 import hunt.shiro.session.mgt.SimpleSession; 26 import hunt.shiro.session.mgt.SimpleSessionFactory; 27 28 29 30 import hunt.shiro.cache.CacheManager; 31 import hunt.shiro.cache.CacheManagerAware; 32 import hunt.shiro.session.Session; 33 import hunt.shiro.Exceptions; 34 import hunt.shiro.session.mgt.eis.MemorySessionDAO; 35 import hunt.shiro.session.mgt.eis.SessionDAO; 36 import hunt.logging.Logger; 37 38 import hunt.util.Common; 39 import hunt.collection; 40 import hunt.collection.Collections; 41 42 import std.array; 43 // import java.util.Date; 44 45 /** 46 * Default business-tier implementation of a {@link ValidatingSessionManager}. All session CRUD operations are 47 * delegated to an internal {@link SessionDAO}. 48 * 49 */ 50 class DefaultSessionManager : AbstractValidatingSessionManager, CacheManagerAware { 51 52 private SessionFactory sessionFactory; 53 54 protected SessionDAO sessionDAO; //todo - move SessionDAO up to AbstractValidatingSessionManager? 55 56 private CacheManager cacheManager; 57 58 private bool deleteInvalidSessions; 59 60 this() { 61 this.deleteInvalidSessions = true; 62 this.sessionFactory = new SimpleSessionFactory(); 63 this.sessionDAO = new MemorySessionDAO(); 64 } 65 66 void setSessionDAO(SessionDAO sessionDAO) { 67 this.sessionDAO = sessionDAO; 68 applyCacheManagerToSessionDAO(); 69 } 70 71 SessionDAO getSessionDAO() { 72 return this.sessionDAO; 73 } 74 75 /** 76 * Returns the {@code SessionFactory} used to generate new {@link Session} instances. The default instance 77 * is a {@link SimpleSessionFactory}. 78 * 79 * @return the {@code SessionFactory} used to generate new {@link Session} instances. 80 */ 81 SessionFactory getSessionFactory() { 82 return sessionFactory; 83 } 84 85 /** 86 * Sets the {@code SessionFactory} used to generate new {@link Session} instances. The default instance 87 * is a {@link SimpleSessionFactory}. 88 * 89 * @param sessionFactory the {@code SessionFactory} used to generate new {@link Session} instances. 90 */ 91 void setSessionFactory(SessionFactory sessionFactory) { 92 this.sessionFactory = sessionFactory; 93 } 94 95 /** 96 * Returns {@code true} if sessions should be automatically deleted after they are discovered to be invalid, 97 * {@code false} if invalid sessions will be manually deleted by some process external to Shiro's control. The 98 * default is {@code true} to ensure no orphans exist in the underlying data store. 99 * <h4>Usage</h4> 100 * It is ok to set this to {@code false} <b><em>ONLY</em></b> if you have some other process that you manage yourself 101 * that periodically deletes invalid sessions from the backing data store over time, such as via a Quartz or Cron 102 * job. If you do not do this, the invalid sessions will become 'orphans' and fill up the data store over time. 103 * <p/> 104 * This property is provided because some systems need the ability to perform querying/reporting against sessions in 105 * the data store, even after they have stopped or expired. Setting this attribute to {@code false} will allow 106 * such querying, but with the caveat that the application developer/configurer deletes the sessions themselves by 107 * some other means (cron, quartz, etc). 108 * 109 * @return {@code true} if sessions should be automatically deleted after they are discovered to be invalid, 110 * {@code false} if invalid sessions will be manually deleted by some process external to Shiro's control. 111 */ 112 bool isDeleteInvalidSessions() { 113 return deleteInvalidSessions; 114 } 115 116 /** 117 * Sets whether or not sessions should be automatically deleted after they are discovered to be invalid. Default 118 * value is {@code true} to ensure no orphans will exist in the underlying data store. 119 * <h4>WARNING</h4> 120 * Only set this value to {@code false} if you are manually going to remove sessions yourself by some process 121 * (quartz, cron, etc) external to Shiro's control. See the 122 * {@link #isDeleteInvalidSessions() isDeleteInvalidSessions()} JavaDoc for more. 123 * 124 * @param deleteInvalidSessions whether or not sessions should be automatically deleted after they are discovered 125 * to be invalid. 126 */ 127 //@SuppressWarnings({"UnusedDeclaration"}) 128 void setDeleteInvalidSessions(bool deleteInvalidSessions) { 129 this.deleteInvalidSessions = deleteInvalidSessions; 130 } 131 132 void setCacheManager(CacheManager cacheManager) { 133 this.cacheManager = cacheManager; 134 applyCacheManagerToSessionDAO(); 135 } 136 137 /** 138 * Sets the internal {@code CacheManager} on the {@code SessionDAO} if it implements the 139 * {@link hunt.shiro.cache.CacheManagerAware CacheManagerAware} interface. 140 * <p/> 141 * This method is called after setting a cacheManager via the 142 * {@link #setCacheManager(hunt.shiro.cache.CacheManager) setCacheManager} method <em>em</em> when 143 * setting a {@code SessionDAO} via the {@link #setSessionDAO} method to allow it to be propagated 144 * in either case. 145 * 146 */ 147 private void applyCacheManagerToSessionDAO() { 148 CacheManagerAware cma = cast(CacheManagerAware) this.sessionDAO; 149 if (this.cacheManager !is null && this.sessionDAO !is null && cma !is null) { 150 cma.setCacheManager(this.cacheManager); 151 } 152 } 153 154 override protected Session doCreateSession(SessionContext context) { 155 Session s = newSessionInstance(context); 156 version(HUNT_SHIRO_DEBUG) { 157 tracef("Creating session for host %s", s.getHost()); 158 } 159 create(s); 160 return s; 161 } 162 163 protected Session newSessionInstance(SessionContext context) { 164 return getSessionFactory().createSession(context); 165 } 166 167 /** 168 * Persists the given session instance to an underlying EIS (Enterprise Information System). This implementation 169 * delegates and calls 170 * <code>this.{@link SessionDAO sessionDAO}.{@link SessionDAO#create(hunt.shiro.session.Session) create}(session);<code> 171 * 172 * @param session the Session instance to persist to the underlying EIS. 173 */ 174 protected void create(Session session) { 175 version(HUNT_SHIRO_DEBUG) { 176 tracef("Creating new EIS record for new session instance [" ~ (cast(Object)session).toString() ~ "]"); 177 } 178 sessionDAO.create(session); 179 } 180 181 override 182 protected void onStop(Session session) { 183 SimpleSession ss = cast(SimpleSession) session; 184 if (ss !is null) { 185 Date stopTs = ss.getStopTimestamp().value; 186 ss.setLastAccessTime(stopTs); 187 } 188 onChange(session); 189 } 190 191 override 192 protected void afterStopped(Session session) { 193 if (isDeleteInvalidSessions()) { 194 remove(session); 195 } 196 } 197 198 override protected void onExpiration(Session session) { 199 SimpleSession ss = cast(SimpleSession) session; 200 if (ss !is null) { 201 ss.setExpired(true); 202 } 203 onChange(session); 204 } 205 206 override 207 protected void afterExpired(Session session) { 208 if (isDeleteInvalidSessions()) { 209 remove(session); 210 } 211 } 212 213 override protected void onChange(Session session) { 214 sessionDAO.update(session); 215 } 216 217 override protected Session retrieveSession(SessionKey sessionKey) { 218 string sessionId = getSessionId(sessionKey); 219 if (sessionId.empty()) { 220 warningf("Unable to resolve session ID from SessionKey [%s]. Returning null to indicate a " ~ 221 "session could not be found.", sessionKey); 222 return null; 223 } 224 Session s = retrieveSessionFromDataSource(sessionId); 225 if (s is null) { 226 //session ID was provided, meaning one is expected to be found, but we couldn't find one: 227 string msg = "Could not find session with ID [" ~ sessionId ~ "]"; 228 throw new UnknownSessionException(msg); 229 } 230 return s; 231 } 232 233 protected string getSessionId(SessionKey sessionKey) { 234 return sessionKey.getSessionId(); 235 } 236 237 protected Session retrieveSessionFromDataSource(string sessionId){ 238 return sessionDAO.readSession(sessionId); 239 } 240 241 protected void remove(Session session) { 242 sessionDAO.remove(session); 243 } 244 245 override protected Session[] getActiveSessions() { 246 Session[] active = sessionDAO.getActiveSessions(); 247 return active; 248 } 249 250 }