1 // 2 // ======================================================================== 3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. 4 // ------------------------------------------------------------------------ 5 // All rights reserved. This program and the accompanying materials 6 // are made available under the terms of the Eclipse Public License v1.0 7 // and Apache License v2.0 which accompanies this distribution. 8 // 9 // The Eclipse Public License is available at 10 // http://www.eclipse.org/legal/epl-v10.html 11 // 12 // The Apache License v2.0 is available at 13 // http://www.opensource.org/licenses/apache2.0.php 14 // 15 // You may elect to redistribute this code under either of these licenses. 16 // ======================================================================== 17 // 18 19 package org.eclipse.jetty.server.session; 20 21 import java.security.SecureRandom; 22 import java.util.Random; 23 24 import javax.servlet.http.HttpServletRequest; 25 26 import org.eclipse.jetty.server.SessionIdManager; 27 import org.eclipse.jetty.util.component.AbstractLifeCycle; 28 import org.eclipse.jetty.util.log.Log; 29 import org.eclipse.jetty.util.log.Logger; 30 31 public abstract class AbstractSessionIdManager extends AbstractLifeCycle implements SessionIdManager 32 { 33 private static final Logger LOG = Log.getLogger(AbstractSessionIdManager.class); 34 35 private final static String __NEW_SESSION_ID="org.eclipse.jetty.server.newSessionId"; 36 37 protected Random _random; 38 protected boolean _weakRandom; 39 protected String _workerName; 40 protected long _reseed=100000L; 41 42 /* ------------------------------------------------------------ */ AbstractSessionIdManager()43 public AbstractSessionIdManager() 44 { 45 } 46 47 /* ------------------------------------------------------------ */ AbstractSessionIdManager(Random random)48 public AbstractSessionIdManager(Random random) 49 { 50 _random=random; 51 } 52 53 54 /* ------------------------------------------------------------ */ 55 /** 56 * @return the reseed probability 57 */ getReseed()58 public long getReseed() 59 { 60 return _reseed; 61 } 62 63 /* ------------------------------------------------------------ */ 64 /** Set the reseed probability. 65 * @param reseed If non zero then when a random long modulo the reseed value == 1, the {@link SecureRandom} will be reseeded. 66 */ setReseed(long reseed)67 public void setReseed(long reseed) 68 { 69 _reseed = reseed; 70 } 71 72 /* ------------------------------------------------------------ */ 73 /** 74 * Get the workname. If set, the workername is dot appended to the session 75 * ID and can be used to assist session affinity in a load balancer. 76 * 77 * @return String or null 78 */ getWorkerName()79 public String getWorkerName() 80 { 81 return _workerName; 82 } 83 84 /* ------------------------------------------------------------ */ 85 /** 86 * Set the workname. If set, the workername is dot appended to the session 87 * ID and can be used to assist session affinity in a load balancer. 88 * 89 * @param workerName 90 */ setWorkerName(String workerName)91 public void setWorkerName(String workerName) 92 { 93 if (workerName.contains(".")) 94 throw new IllegalArgumentException("Name cannot contain '.'"); 95 _workerName=workerName; 96 } 97 98 /* ------------------------------------------------------------ */ getRandom()99 public Random getRandom() 100 { 101 return _random; 102 } 103 104 /* ------------------------------------------------------------ */ setRandom(Random random)105 public synchronized void setRandom(Random random) 106 { 107 _random=random; 108 _weakRandom=false; 109 } 110 111 /* ------------------------------------------------------------ */ 112 /** 113 * Create a new session id if necessary. 114 * 115 * @see org.eclipse.jetty.server.SessionIdManager#newSessionId(javax.servlet.http.HttpServletRequest, long) 116 */ newSessionId(HttpServletRequest request, long created)117 public String newSessionId(HttpServletRequest request, long created) 118 { 119 synchronized (this) 120 { 121 if (request!=null) 122 { 123 // A requested session ID can only be used if it is in use already. 124 String requested_id=request.getRequestedSessionId(); 125 if (requested_id!=null) 126 { 127 String cluster_id=getClusterId(requested_id); 128 if (idInUse(cluster_id)) 129 return cluster_id; 130 } 131 132 // Else reuse any new session ID already defined for this request. 133 String new_id=(String)request.getAttribute(__NEW_SESSION_ID); 134 if (new_id!=null&&idInUse(new_id)) 135 return new_id; 136 } 137 138 // pick a new unique ID! 139 String id=null; 140 while (id==null||id.length()==0||idInUse(id)) 141 { 142 long r0=_weakRandom 143 ?(hashCode()^Runtime.getRuntime().freeMemory()^_random.nextInt()^(((long)request.hashCode())<<32)) 144 :_random.nextLong(); 145 if (r0<0) 146 r0=-r0; 147 148 // random chance to reseed 149 if (_reseed>0 && (r0%_reseed)== 1L) 150 { 151 LOG.debug("Reseeding {}",this); 152 if (_random instanceof SecureRandom) 153 { 154 SecureRandom secure = (SecureRandom)_random; 155 secure.setSeed(secure.generateSeed(8)); 156 } 157 else 158 { 159 _random.setSeed(_random.nextLong()^System.currentTimeMillis()^request.hashCode()^Runtime.getRuntime().freeMemory()); 160 } 161 } 162 163 long r1=_weakRandom 164 ?(hashCode()^Runtime.getRuntime().freeMemory()^_random.nextInt()^(((long)request.hashCode())<<32)) 165 :_random.nextLong(); 166 if (r1<0) 167 r1=-r1; 168 id=Long.toString(r0,36)+Long.toString(r1,36); 169 170 //add in the id of the node to ensure unique id across cluster 171 //NOTE this is different to the node suffix which denotes which node the request was received on 172 if (_workerName!=null) 173 id=_workerName + id; 174 } 175 176 request.setAttribute(__NEW_SESSION_ID,id); 177 return id; 178 } 179 } 180 181 /* ------------------------------------------------------------ */ 182 @Override doStart()183 protected void doStart() throws Exception 184 { 185 initRandom(); 186 } 187 188 /* ------------------------------------------------------------ */ 189 @Override doStop()190 protected void doStop() throws Exception 191 { 192 } 193 194 /* ------------------------------------------------------------ */ 195 /** 196 * Set up a random number generator for the sessionids. 197 * 198 * By preference, use a SecureRandom but allow to be injected. 199 */ initRandom()200 public void initRandom () 201 { 202 if (_random==null) 203 { 204 try 205 { 206 _random=new SecureRandom(); 207 } 208 catch (Exception e) 209 { 210 LOG.warn("Could not generate SecureRandom for session-id randomness",e); 211 _random=new Random(); 212 _weakRandom=true; 213 } 214 } 215 else 216 _random.setSeed(_random.nextLong()^System.currentTimeMillis()^hashCode()^Runtime.getRuntime().freeMemory()); 217 } 218 219 220 } 221