You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 8 Next »

An explanation of how to transparently switch from http to https.

Table of contents

Change Render Strategy

Change the rendering strategy in your application.

 getRequestCycleSettings().setRenderStrategy(Settings.ONE_PASS_RENDER);

I could not get this to work with the other strategies because you cannot send more than one redirect in the same request. You lose some features like avoiding double submit, but you can avoid that with some javascript. You gain some performance because the rendering is also done in the same request.

Create RequiredSSL Annotation

Create an annotation.

 @Retention(RetentionPolicy.RUNTIME)
 public @interface RequiredSSL { }

Create New Response Strategy

Check if the response page's class has the @RequiredSSL annotation. If it does, redirect to the response page in SSL mode.

The following goes in your extended Application class.

 @Override
 protected IRequestCycleProcessor newRequestCycleProcessor() {
 	                return new DefaultWebRequestCycleProcessor() {
 	                        @Override
 	                        protected IResponseStrategy newResponseStrategy() {
 	                                return new IResponseStrategy() {
 	                                        public void respond(RequestCycle requestCycle) {
 	                                                IRequestTarget requestTarget = requestCycle
 	                                                                .getRequestTarget();
 	                                                if (requestTarget != null) {
 	                                                        Application.get().logResponseTarget(requestTarget);
 	
 	                                                        WebRequest webRequest = (WebRequest) requestCycle
 	                                                                        .getRequest();
 	                                                        WebResponse webResponse = (WebResponse) requestCycle
 	                                                                        .getResponse();
 	
 	                                                        HttpServletRequest httpServletRequest = webRequest
 	                                                                        .getHttpServletRequest();
 	
 	                                                        Class pageClass = null;
 	
 	                                                        if (requestTarget instanceof IPageRequestTarget) {
 	                                                                IPageRequestTarget pageTarget = 
                                                                                     (IPageRequestTarget) requestTarget;
 	                                                                pageClass = pageTarget.getPage().getClass();
 	                                                        } else if (requestTarget instanceof IBookmarkablePageRequestTarget) {
 	                                                                IBookmarkablePageRequestTarget bookmarkableTarget = 
                                                                                     (IBookmarkablePageRequestTarget) requestTarget;
 	                                                                pageClass = bookmarkableTarget.getPageClass();
 	                                                        }
                                                                if (pageClass != null
 	                                                                        && !httpServletRequest.isSecure()
 	                                                                        && pageClass.isAnnotationPresent(RequiredSSL.class)) {
 	                                                                StringBuffer url = new StringBuffer("https://"
 	                                                                                + httpServletRequest.getServerName());
 	
 	                                                                url.append(":" + MyApplication.get().getSslPort());
                                                                        String q = RequestCycle.get().urlFor(
 	                                                                                requestTarget).toString();
 	                                                                url.append(q);
 	                                                                webResponse.redirect(url.toString());
 	                                                        }
                                                                else /* else added */
 	                                                                requestTarget.respond(requestCycle);
 	                                                }
                                                 }
 	                                };
 	                        }
 	                };
 	        }

There are 2 important pieces of configuration that will change based on your environment.

1. SSL Port: There is no way to determine the SSL port being used via the servlet spec API, so this needs to be set manually. Grab the SSL port from your configuration. (Or better yet, set it with Spring in your applicationContext.xml from a properties file)

2. Hostname: In the code below, httpServletRequest.getServerName() is used to determine the hostname. This may not always work, for example in a clustered environment where your website's hostname resolves to an IP address on a router and each application server has a unique hostname like appserver1 and appserver2. If you have this kind of setup, it would be best to grab the hostname from a configuration file or set it with Spring.

Annotate Your Pages

Add @RequiredSSL to any Page that requires SSL!

Edit:

I tried to apply this but I think there was a bug (at least it didn't work for me). A else was missing before the requestTarget.respond(requestCycle); (see else added in the code).
Additionally, a switch back to non-ssl mode should/could be added by adding a if clause, more ore less like this:

                                                                else if (pageClass != null
 	                                                                        && httpServletRequest.isSecure()
 	                                                                        && !pageClass.isAnnotationPresent(RequiredSSL.class)) {
 	                                                                StringBuffer url = new StringBuffer("http://"
 	                                                                                + httpServletRequest.getServerName());
 	
                                                                        String q = RequestCycle.get().urlFor(
 	                                                                                requestTarget).toString();
 	                                                                url.append(q);
 	                                                                webResponse.redirect(url.toString());
 	                                                        }

Edit (Wicket 1.3.x):

I was not able to get the above to work with 1.3. However, with a few tweaks, I was able to get the proper URL generation. I followed the RequiredSSL annotation requirement. Removed the One Pass Through Render Strategy suggestion. I then copied the WebRequestCycleProcessor locally. Modified the

encode(final RequestCycle requestCycle, final IRequestTarget requestTarget) 

and added a

doPostEncode() 
	public final CharSequence encode(final RequestCycle requestCycle,
			final IRequestTarget requestTarget)
	{
		// First check to see whether the target is mounted
		CharSequence url = pathForTarget(requestTarget);

		if (url != null)
		{
			// Do nothing - we've found the URL and it's mounted.
		}
		else if (requestTarget instanceof IBookmarkablePageRequestTarget)
		{
			url = encode(requestCycle, (IBookmarkablePageRequestTarget)requestTarget);
		}
		else if (requestTarget instanceof ISharedResourceRequestTarget)
		{
			url = encode(requestCycle, (ISharedResourceRequestTarget)requestTarget);
		}
		else if (requestTarget instanceof IListenerInterfaceRequestTarget)
		{
			url = encode(requestCycle, (IListenerInterfaceRequestTarget)requestTarget);
		}
		else if (requestTarget instanceof IPageRequestTarget)
		{
			// This calls page.urlFor(IRedirectListener.INTERFACE), which calls
			// the function we're in again. We therefore need to jump out here
			// and return the url immediately, otherwise we end up prefixing it
			// with relative path or absolute prefixes twice.
			return encode(requestCycle, (IPageRequestTarget)requestTarget);
		}
		// fallthough for non-default request targets
		else
		{
			url = doEncode(requestCycle, requestTarget);
		}

		if (url != null)
		{
			// Add the actual URL. This will be relative to the Wicket
			// Servlet/Filter, with no leading '/'.
			PrependingStringBuffer prepender = new PrependingStringBuffer(url.toString());

			// Prepend prefix to the URL to make it relative to the current
			// request.
			prepender.prepend(requestCycle.getRequest().getRelativePathPrefixToWicketHandler());

// CHANGES vvvvv
			// We need to special-case links to the home page if we're at the
			// same level.
			if (prepender.toString().length() == 0)
			{
				prepender.prepend("./");
			}			
			
			String result = prepender.toString();			

			return doPostEncode( requestCycle, requestTarget, requestCycle.getOriginalResponse().encodeURL(result) );

// CHANGES ^^^^^
			
		}

		// Just return null intead of throwing an exception. So that it can be
		// handled better
		return null;
	}

// CHANGES vvvvv	
	protected CharSequence doPostEncode( final RequestCycle requestCycle,
			final IRequestTarget requestTarget, CharSequence result ) {
		
		return result;
	}
	
// CHANGES ^^^^^

Then used the following SecureWebRequestCycleProcessor

public class SecureWebRequestCodingStrategy extends WebRequestCodingStrategy {
	private int publicPort = 80;
	private int securePort = 443;

	public SecureWebRequestCodingStrategy(int publicPort, int securePort) {
		this.publicPort = publicPort;
		this.securePort = securePort;
	}

	@Override
	protected CharSequence doPostEncode(final RequestCycle requestCycle,
			final IRequestTarget requestTarget, final CharSequence result) {
		PrependingStringBuffer buf = new PrependingStringBuffer(
				stripWicketPath(result.toString()));
		if (requestTarget != null) {
			WebRequest webRequest = (WebRequest) requestCycle.getRequest();

			HttpServletRequest httpServletRequest = webRequest
					.getHttpServletRequest();

			Class targetClass = null;

			if (requestTarget instanceof IBookmarkablePageRequestTarget) {
				targetClass = ((IBookmarkablePageRequestTarget) requestTarget)
						.getPageClass();
			} else if (requestTarget instanceof ISharedResourceRequestTarget) {
				targetClass = ((ISharedResourceRequestTarget) requestTarget)
						.getClass();
			} else if (requestTarget instanceof IListenerInterfaceRequestTarget) {
				targetClass = ((IListenerInterfaceRequestTarget) requestTarget)
						.getTarget().getClass();
			} else if (requestTarget instanceof IPageRequestTarget) {
				targetClass = ((IPageRequestTarget) requestTarget).getPage()
						.getClass();
			}

			if (targetClass != null && !httpServletRequest.isSecure()
					&& targetClass.isAnnotationPresent(RequiredSSL.class)) {
				StringBuffer url = new StringBuffer("https://"
						+ httpServletRequest.getServerName());

				if (443 != securePort) {
					url.append(":" + securePort);
				}
				url.append(httpServletRequest.getContextPath());

				url
						.append(stripServletPath(httpServletRequest
								.getServletPath()));

				buf.prepend(url.toString());
			} else if (targetClass != null && httpServletRequest.isSecure()
					&& !targetClass.isAnnotationPresent(RequiredSSL.class)) {
				StringBuffer url = new StringBuffer("http://");
				url.append(httpServletRequest.getServerName());
				if (80 != publicPort) {
					url.append(":" + publicPort);
				}
				url.append(httpServletRequest.getContextPath());
				url
						.append(stripServletPath(httpServletRequest
								.getServletPath()));

				buf.prepend(url.toString());
			} else {
				if (!buf.toString().startsWith("/")) {
					buf.prepend("/");
				}
				buf.prepend(stripServletPath(httpServletRequest
						.getServletPath()));
				buf.prepend(httpServletRequest.getContextPath());
			}
		}

		return buf.toString();
	}

	private String stripServletPath(String fullPath) {
		int idx = fullPath.indexOf("/", 1);

		return (idx == -1) ? fullPath : fullPath.substring(0, idx);
	}

	private String stripWicketPath(String fullPath) {
		String tmp = fullPath;

		int idx = -1;

		if ((idx = tmp.indexOf("?wicket")) != -1) {
			tmp = "/" + tmp.substring(idx);
		} else if ((idx = tmp.lastIndexOf("../")) != -1) {
			tmp = "/" + tmp.substring(idx + 3);
		}

		return tmp;
	}
}

In my application class, I did the following:


	protected IRequestCycleProcessor newRequestCycleProcessor() {
		return new WebRequestCycleProcessor() {
			@Override
			protected IRequestCodingStrategy newRequestCodingStrategy() {
				
				String publicPort = MyApplication.this.getPublicPort();
				int publicPortVal = 80;
				
				if ( publicPort != null && !publicPort.equals( "" )) {
					publicPortVal = new Integer( publicPort ).intValue();
				}
				String securePort = MyApplication.this.getSslPort();
				
				if ( securePort == null || securePort.equals( "" )) {
					return super.newRequestCodingStrategy();
				}
				return new SecureWebRequestCodingStrategy( publicPortVal, new Integer( securePort ).intValue() );
			}
		};
	}

By using the above solution, anytime I placed a @RequiredSSL on a Form, Link, Button declaration, the proper URL will be generated (https for secured items and http for non-secure items).

BTW...to get Button/SubmitLink to generate https URLs you will need to use the following classes:

@RequiredSSL
public class SecureButton extends Button {

	public SecureButton(String id, IModel model) {
		super(id, model);
	}

	public SecureButton(String id) {
		super(id);
	}

}

@RequiredSSL
public class SecureSubmitLink extends SubmitLink {

	public SecureSubmitLink(String id, Form form) {
		super(id, form);
	}

	public SecureSubmitLink(String id, IModel model, Form form) {
		super(id, model, form);
	}

	public SecureSubmitLink(String id, IModel model) {
		super(id, model);
	}

	public SecureSubmitLink(String id) {
		super(id);
	}

}

Edit (Wicket 1.3.x) alternative:

An alternative implementation for 1.3 is to do as in the original solution but just override the newRequestCycleProcessor(...) in your application like this:

@Override
protected IRequestCycleProcessor newRequestCycleProcessor() {
    return new WebRequestCycleProcessor() {
        public void respond(RequestCycle requestCycle) {
            IRequestTarget requestTarget = requestCycle.getRequestTarget();
            if (requestTarget != null) {
                WebRequest webRequest = (WebRequest) requestCycle .getRequest();
                WebResponse webResponse = (WebResponse) requestCycle .getResponse();
                HttpServletRequest httpServletRequest = webRequest.getHttpServletRequest();

                Class pageClass = null;
                if (requestTarget instanceof IPageRequestTarget) {
                    IPageRequestTarget pageTarget = (IPageRequestTarget) requestTarget;
                    pageClass = pageTarget.getPage().getClass();
                } else if (requestTarget instanceof IBookmarkablePageRequestTarget) {
                    IBookmarkablePageRequestTarget bookmarkableTarget = (IBookmarkablePageRequestTarget) requestTarget;
                    pageClass = bookmarkableTarget.getPageClass();
                }

                if (pageClass != null && !httpServletRequest.isSecure() &&
                        pageClass.isAnnotationPresent(RequiredSSL.class)) {
                    // We should switch to https
                    StringBuffer url = new StringBuffer("https://");
                    url.append(httpServletRequest.getServerName());
                    url.append(":" + MyApplication.get().getHttpsPort());

                    url.append(webRequest.getHttpServletRequest().getContextPath());
                    url.append(webRequest.getServletPath());
                    webResponse.redirect(url.toString());
                } else if (pageClass != null && httpServletRequest.isSecure() &&
                        !pageClass.isAnnotationPresent(RequiredSSL.class)) {
                    // We should switch to http
                    StringBuffer url = new StringBuffer("http://");
                    url.append(httpServletRequest.getServerName());
                    url.append(":" + MyApplication.get().getHttpPort());

                    url.append(webRequest.getHttpServletRequest().getContextPath());
                    url.append(webRequest.getServletPath());
                    webResponse.redirect(url.toString());
                } else {
                    // We should just respond!
                    requestTarget.respond(requestCycle);
                }
            }
        }
    };
}

  • No labels