Adding HttpOnly to RESTful response (Jee6)
Consider the following classes:
- javax.servlet.http.Cookie ( jee 6, servlet spec 3.0)
- javax.ws.rs.core.NewCookie (jsr311 on which currently Jersey is based on)
As you might see RESTful version lacks in HTTPOnly attribute (introduced with RFC 6265) because it only supports the first old RFC 2109. I don’t know which is the rational behind Jersey’s choice (latest version on RFC not implemented) but I need to solve the problem anyhow!
So, as “plain-old-jee-developer” :-D , the first solution I found was to write a servlet Filter and it worked great! Using new servlet 3.0 @WebFilter is not bad but…. googling around, I realized a more neat approach: injecting my own com.sun.jersey.spi.HeaderDelegateProvider implementation.
You only have to add a text file named
META-INF/services/com.sun.jersey.spi.HeaderDelegateProvider
containing the fully qualified name of your implementation. Your implementation must beĀ marked as Jersey @Provider.
Here my own implementation:
@Provider
public class NewCookie2HeaderDelegateProvider implements
HeaderDelegateProvider<NewCookie2> {private NewCookieProvider newCookieProvider = null;
public NewCookie2HeaderDelegateProvider(){
super();
newCookieProvider = new NewCookieProvider();
}@Override
public NewCookie2 fromString(String header) throws IllegalArgumentException {
if (header == null)
throw new IllegalArgumentException(“header must not be null!”);
return NewCookie2Parser.parseNewCookie2(header);
}@Override
public String toString(NewCookie2 newCookie2) {
StringBuilder b = new StringBuilder();
b.append(newCookieProvider.toString(newCookie2));
if (newCookie2.isHttpOnly()){
b.append(“;HTTPOnly”);
}
return b.toString();
}@Override
public boolean supports(Class<?> type) {
return type == NewCookie2.class;
}
where my “NewCookie2″ extends Jersey “NewCookie”:
public class NewCookie2 extends NewCookie {
private boolean httpOnly = false;
public NewCookie2(Cookie cookie) {
super(cookie);
}
…}
and a new parser takes care ok HTTPOnly attribute also:
public class NewCookie2Parser {
private static class MutableNewCookie2 {
String name = null;
String value = null;
String path = null;
String domain = null;
int version = Cookie.DEFAULT_VERSION;
String comment = null;
int maxAge = NewCookie.DEFAULT_MAX_AGE;
boolean secure = false;
boolean httpOnly = false;public MutableNewCookie2(String name, String value) {
this.name = name;
this.value = value;
}public NewCookie2 getImmutableNewCookie2() {
return new NewCookie2(name, value, path, domain, version, comment, maxAge, secure, httpOnly);
}
}public static NewCookie2 parseNewCookie2(String header) {
String bites[] = header.split(“[;,]“);MutableNewCookie2 cookie = null;
for (String bite: bites) {
String crumbs[] = bite.split(“=”, 2);
String name = crumbs.length>0 ? crumbs[0].trim() : “”;
String value = crumbs.length>1 ? crumbs[1].trim() : “”;
if (value.startsWith(“\”") && value.endsWith(“\”") && value.length()>1)
value = value.substring(1,value.length()-1);if (cookie == null)
cookie = new MutableNewCookie2(name, value);
else if (name.startsWith(“Comment”))
cookie.comment = value;
else if (name.startsWith(“Domain”))
cookie.domain = value;
else if (name.startsWith(“Max-Age”))
cookie.maxAge = Integer.parseInt(value);
else if (name.startsWith(“Path”))
cookie.path = value;
else if (name.startsWith(“Secure”))
cookie.secure = true;
else if (name.startsWith(“Version”))
cookie.version = Integer.parseInt(value);
else if (name.startsWith(“Domain”))
cookie.domain = value;
else if (name.startsWith(“HTTPOnly”))
cookie.httpOnly = true;
}return cookie.getImmutableNewCookie2();
}
}
Obviously this is not meant to be a complete RFC 6265 implementation but it fits my needs.
As an example of a usage inside your own jersey resource, here is a snippet:
@POST
public javax.ws.rs.core.Response loginRedirect(@FormParam(“user”) String userName,
@FormParam(“password”) String password){
Response.ResponseBuilder responseBuilder = null;//check username and password and if correct:
responseBuilder = addCookie(Response.created(uriOK).status(Status.MOVED_PERMANENTLY));
return responseBuilder.build();
}
where addCookie is:
protected static Response.ResponseBuilder addCookie(Response.ResponseBuilder responseToAddTo){
Cookie authCookie = new Cookie(“AUTH”, “134142134″, “/”, “.mydomain.it”);
NewCookie2 newCookie = new NewCookie2(authCookie, true);
responseToAddTo.cookie(newCookie);
CacheControl cc=new CacheControl();
cc.setMaxAge(7200);
responseToAddTo.cacheControl(cc);
return responseToAddTo;
}
Hope this helps.
Let me know!