Have you ever noticed the unscubscribe url pattern when you unsubscribe from an online promotion or retail marketing campaign? It looks something like this:
www.somesite.com/unsubscribe/u/NSFAHPF/aW5aaW1pdHk
Ideally, you would want the user to click that (personalized) URL and the system should automatically unsubscribe, or perform any simple non-secure operations like registering for a poll. How would you then create a url that is unique to the user id, yet obfuscated enough to make it secure and personalized?
Here’s a Java utility class that will do encode a url and decode a url based on the java persistence id of the user. It’s based on Java’s inbuilt hash algorithm.
Of course, for a fine grain control over the URL generation, a Strategy interface could be used that can be passed to the PURLGenerator.
import java.net.URI;
public class PURLGenerator {
// Maximum bits used by the value
private static final int VALUE_BITS = Integer.SIZE - 1;
// Maximum bits used by the encoded value
private static final int ENCODED_BITS = Long.SIZE - 1;
// Bits available for each encoding bucket
private static final int BUCKET_BITS = ENCODED_BITS - VALUE_BITS;
// Size of the encoding bucket
private static final long BUCKET_SIZE = 1L << BUCKET_BITS;
// Hash factor for encoding within the bucket
private static final long BUCKET_HASH = (BUCKET_SIZE / 2) - 37;
// Radix used for text encoding
private static final int ENCODED_RADIX = 35;
// Base URI
private URI baseUri;
public URI generateUri(Integer id) {
if (id == null) {
throw new NullPointerException();
}
if (id < 1) {
throw new IllegalArgumentException("Id must be greater then zero");
}
// Find the base for the mapped encoding bucket
final long valueBase = (id - 1) * BUCKET_SIZE;
// Hash the value within the bucket
final long valueHash = (id * BUCKET_HASH) % BUCKET_SIZE;
// Generate the encoded value
final long value = valueBase + valueHash;
// Serialize encoded value as text
final String code = Long.toString(value, ENCODED_RADIX);
// Return the appended URI
return this.baseUri.resolve(code);
}
public Integer parseUri(URI uri) {
if (uri == null) {
throw new NullPointerException();
}
if (!uri.getPath().startsWith(this.baseUri.getPath())) {
throw new IllegalArgumentException("Invalid URI format: "
+ uri);
}
// Get the serialized value
final String code = uri.getPath().substring(
this.baseUri.getPath().length());
// Deserialize the encoded value
final long value = Long.parseLong(code, ENCODED_RADIX);
// Decode the id
final int id = (int) ((value / BUCKET_SIZE) + 1);
// Decode the id hash
final long idHash = value % BUCKET_SIZE;
// Validate the hash
if (idHash != ((id * BUCKET_HASH) % BUCKET_SIZE)) {
return null;
}
return id;
}
}
One Comment
I was curious if you ever considered changing the structure of your website?
Its very well written; I love what youve got to say. But maybe you
could a little more in the way of content so people could connect with it better.
Youve got an awful lot of text for only having one or 2
images. Maybe you could space it out better?
Post a Comment