[jboss-user] [Tomcat, HTTPD, Servlets & JSP] - Re: Session is getting merge with other session
bstansberry@jboss.com
do-not-reply at jboss.com
Thu Dec 20 21:09:47 EST 2007
If at the end of your application workflow, there is a natural place to do session.invalidate(), then sure, I encourage you to do that. That will save you memory.
Session ids are generated from a VM singleton, using a random generator seeded by a timestamp and the VM's free memory (which are unlikely to be duplicated across VMs in a cluster). The class follows, in case you are interested. I've never heard a case of this algorithm generating noticeable duplicate ids. I have seen reports on this forum of non-clustered webapps getting duplicate ids when more than one JBoss instance is on the same machine. But clustered session ids use a different algorithm.
Have you heard of more than 1 case of this? If you have, that would in my mind pretty much rule out a chance session id collision.
Anyway, invalidating the session if you know its done would reduce the already extremely low chance of a problem from session id collision.
Here's the SessionIDGenerator class:
| /*
| * JBoss, Home of Professional Open Source.
| * Copyright 2006, Red Hat Middleware LLC, and individual contributors
| * as indicated by the @author tags. See the copyright.txt file in the
| * distribution for a full listing of individual contributors.
| *
| * This is free software; you can redistribute it and/or modify it
| * under the terms of the GNU Lesser General Public License as
| * published by the Free Software Foundation; either version 2.1 of
| * the License, or (at your option) any later version.
| *
| * This software is distributed in the hope that it will be useful,
| * but WITHOUT ANY WARRANTY; without even the implied warranty of
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
| * Lesser General Public License for more details.
| *
| * You should have received a copy of the GNU Lesser General Public
| * License along with this software; if not, write to the Free
| * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
| * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
| */
| package org.jboss.web.tomcat.service.session;
|
| import java.util.Collection;
| import java.util.HashSet;
| import java.util.Iterator;
| import java.util.Random;
| import java.security.MessageDigest;
| import java.security.NoSuchAlgorithmException;
| import java.security.SecureRandom;
|
| import org.jboss.logging.Logger;
|
| /**
| * Unique session id generator
| *
| * @author Ben Wang
| */
| public class SessionIDGenerator
| {
| protected final static int SESSION_ID_BYTES = 16; // We want 16 Bytes for the session-id
| protected final static String SESSION_ID_HASH_ALGORITHM = "MD5";
| protected final static String SESSION_ID_RANDOM_ALGORITHM = "SHA1PRNG";
| protected final static String SESSION_ID_RANDOM_ALGORITHM_ALT = "IBMSecureRandom";
| protected Logger log = Logger.getLogger(SessionIDGenerator.class);
|
| protected MessageDigest digest = null;
| protected Random random = null;
| protected static final SessionIDGenerator s_ = new SessionIDGenerator();
|
| protected String sessionIdAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-*";
|
| public static SessionIDGenerator getInstance()
| {
| return s_;
| }
|
| /**
| * The SessionIdAlphabet is the set of characters used to create a session Id
| */
| public void setSessionIdAlphabet(String sessionIdAlphabet)
| {
| if (sessionIdAlphabet.length() != 65) {
| throw new IllegalArgumentException("SessionIdAlphabet must be exactly 65 characters long");
| }
|
| checkDuplicateChars(sessionIdAlphabet);
|
| this.sessionIdAlphabet = sessionIdAlphabet;
| }
|
| protected void checkDuplicateChars(String sessionIdAlphabet) {
| char[] alphabet = sessionIdAlphabet.toCharArray();
| for (int i=0; i < alphabet.length; i++) {
| if (!uniqueChar(alphabet, sessionIdAlphabet)) {
| throw new IllegalArgumentException("All chars in SessionIdAlphabet must be unique");
| }
| }
| }
|
| // does a character appear in the String once and only once?
| protected boolean uniqueChar(char c, String s) {
| int firstIndex = s.indexOf(c);
| if (firstIndex == -1) return false;
| return s.indexOf(c, firstIndex + 1) == -1;
| }
|
| /**
| * The SessionIdAlphabet is the set of characters used to create a session Id
| */
| public String getSessionIdAlphabet() {
| return this.sessionIdAlphabet;
| }
|
| public synchronized String getSessionId()
| {
| String id = generateSessionId();
| if (log.isDebugEnabled())
| log.debug("getSessionId called: " + id);
| return id;
| }
|
|
| /**
| * Generate a session-id that is not guessable
| *
| * @return generated session-id
| */
| protected synchronized String generateSessionId()
| {
| if (this.digest == null)
| {
| this.digest = getDigest();
| }
|
| if (this.random == null)
| {
| this.random = getRandom();
| }
|
| byte[] bytes = new byte[SESSION_ID_BYTES];
|
| // get random bytes
| this.random.nextBytes(bytes);
|
| // Hash the random bytes
| bytes = this.digest.digest(bytes);
|
| // Render the result as a String of hexadecimal digits
| return encode(bytes);
| }
|
| /**
| * Encode the bytes into a String with a slightly modified Base64-algorithm
| * This code was written by Kevin Kelley <kelley at ruralnet.net>
| * and adapted by Thomas Peuss <jboss at peuss.de>
| *
| * @param data The bytes you want to encode
| * @return the encoded String
| */
| protected String encode(byte[] data)
| {
| char[] out = new char[((data.length + 2) / 3) * 4];
| char[] alphabet = this.sessionIdAlphabet.toCharArray();
|
| //
| // 3 bytes encode to 4 chars. Output is always an even
| // multiple of 4 characters.
| //
| for (int i = 0, index = 0; i < data.length; i += 3, index += 4)
| {
| boolean quad = false;
| boolean trip = false;
|
| int val = (0xFF & (int) data);
| val <<= 8;
| if ((i + 1) < data.length)
| {
| val |= (0xFF & (int) data[i + 1]);
| trip = true;
| }
| val <<= 8;
| if ((i + 2) < data.length)
| {
| val |= (0xFF & (int) data[i + 2]);
| quad = true;
| }
| out[index + 3] = alphabet[(quad ? (val & 0x3F) : 64)];
| val >>= 6;
| out[index + 2] = alphabet[(trip ? (val & 0x3F) : 64)];
| val >>= 6;
| out[index + 1] = alphabet[val & 0x3F];
| val >>= 6;
| out[index + 0] = alphabet[val & 0x3F];
| }
| return new String(out);
| }
|
| /**
| * get a random-number generator
| *
| * @return a random-number generator
| */
| protected synchronized Random getRandom()
| {
| long seed;
| Random random = null;
|
| // Mix up the seed a bit
| seed = System.currentTimeMillis();
| seed ^= Runtime.getRuntime().freeMemory();
|
| try
| {
| random = SecureRandom.getInstance(SESSION_ID_RANDOM_ALGORITHM);
| }
| catch (NoSuchAlgorithmException e)
| {
| try
| {
| random = SecureRandom.getInstance(SESSION_ID_RANDOM_ALGORITHM_ALT);
| }
| catch (NoSuchAlgorithmException e_alt)
| {
| log.error("Could not generate SecureRandom for session-id randomness", e);
| log.error("Could not generate SecureRandom for session-id randomness", e_alt);
| return null;
| }
| }
|
| // set the generated seed for this PRNG
| random.setSeed(seed);
|
| return random;
| }
|
| /**
| * get a MessageDigest hash-generator
| *
| * @return a hash generator
| */
| protected synchronized MessageDigest getDigest()
| {
| MessageDigest digest = null;
|
| try
| {
| digest = MessageDigest.getInstance(SESSION_ID_HASH_ALGORITHM);
| }
| catch (NoSuchAlgorithmException e)
| {
| log.error("Could not generate MessageDigest for session-id hashing", e);
| return null;
| }
|
| return digest;
| }
|
| public static void main(String[] args)
| {
| SessionIDGenerator gen = SessionIDGenerator.getInstance();
|
| HashSet set = new HashSet();
| String id = null;
| for (int i = 0; i > -1; i++)
| {
| id = gen.getSessionId();
|
| if (set.add(id) == false)
| {
| System.out.println(i + " FAILED -- duplicate id " + id);
| break;
| }
|
| if (i % 1000 == 0 && i > 0)
| {
| System.out.println("Tested " + i + " session ids");
| }
| if (i % 50000 == 0 && set.size() > 450000)
| {
| System.out.println("Discarding...");
| int k = 0;
| int discarded = 0;
| for (Iterator it = set.iterator(); it.hasNext() && discarded < 50000; k++)
| {
| it.next();
| if (k % 10 == 0)
| {
| it.remove();
| discarded++;
| }
| }
| System.out.println("Discarding completed");
| }
| }
| }
| }
|
View the original post : http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4114862#4114862
Reply to the post : http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&p=4114862
More information about the jboss-user
mailing list