How to do proper URL Encoding in Javascript when using window.open()

In some cases you might want to do encoding of URL-parameters directly inside javascript, simply because you think it’s best to do it there or because you have no other options of getting the parameters encoded. But as I discovered when trying to do this myself, doing this in javascript is not like a walk in the park, or at least there are some important “black holes” you should be aware of.

In my example I use JSF, and I have a h:dataTable component which I build up programmatically in java. For each row I have a link that will open a new popup window that points to another page (jsp) passing some parameters. I needed to do javascript-encoding of parameters, because when I build my table I use expression language (EL – #{} ) and valueBindings (for those of you that are familiar with it). If you use expressions you don’t hold the value itself, but the expression will be “decoded” run-time when the page is rendered.

To show an example, I have a Java-class that has a hashmap called properties, an ordinary key-value thing. By using a get-method that returns the whole map it’s possible through EL to give an “input-parameter” on which property to get. My get-method:

public Map<String, Object> getProperties()
{
	return properties;
}

this would be the expression to get the url:

String url = "#{row.properties['"+SOME_KEY_THAT_POINTS_TO_OBJECT_WITH_URL+"']}";

And then I would create my link in java with value-binding like this:

HtmlOutputLink htmlOutputLink = new HtmlOutputLink();
ValueBinding vb = FacesContext.getCurrentInstance().getApplication().createValueBinding(
"popupWindow('"+ url +"','MY_POPUP_WINDOW', WIDTH,HEIGHT); return false;");
htmlOutputLink.setValueBinding("onclick",vb);

The “normal” way of doing encoding java-side would be something like this:

public String urlEncode(String urlToEncode)
{
	String encodedUrl = "";
	if(urlToEncode != null)
	{
		try
		{
 			encodedUrl = URLEncoder.encode(urlToEncode,"UTF-8");
		}
		catch (Exception e)
 		{
			log.error("Encode exception when encoding url: " + urlToEncode,e);
 			return urlToEncode; //Return unencodedUrl
		}

		return encodedUrl;
	}
}

But in my case, if I were to encode the url before passing it to the javascript, what I would encode would actually be my expression, not the value of it. The result of this encoding would be something like;

 %23%7Brow.properties%5B%27SOME_KEY_THAT_POINTS_TO_OBJECT_WITH_URL%27%5D%7D.

Thus, I was in the need of doing the encoding after the page has been rendered, and then I needed to do it inside the javascript.

I tried two different approaches for encoding the url, the first one which I expected would work, and the second one which I had to do to make it work. What you need to be aware of is that default encoding in javascript is the UTF-8 format, so if you try to decode “on the other side” you have to use UTF-8 in your decoder. I’ve also seen some examples around saying that the escape()-function does some sort of encoding, but this one just escapes (replaces) special characters, for example it converts white-spaces to %20%. Trying to use escape() on the URL and then decoding it with an URL-decoder would fail! Still, I found the need of using the escape()-function as well, and will explain this as I go.

The first function I tried uses the encodeURI()-function inside javascript directly:

function encodeUrl(url)
{
	return encodeURI(url);
}

And from my popupWindow()-function I call the encodeUrl() like this:

var newWindow = '';
function popupWindow(url, name, width, height)
{
	name = name.replace(/\/|\-|\./gi, "");
	var whitespace = new RegExp("\\s","g");
	name = name.replace(whitespace,"");
	if (!newWindow.closed && newWindow.location)
	{
		newWindow.location.href = encodeUrl(url);
	}
	else
	{
		newWindow = window.open(encodeUrl(url),name, "location=no, scrollbars=yes, resizable=yes, toolbar=no, menubar=no, width=" + width + ", height=" + height );
		if (!newWindow.opener)
			newWindow.opener = self;
	}
	if (window.focus)
	{
		newWindow.focus()
	}
}

But doing it like this the decoding seemed to fail, as I couldn’t get the proper value from the url on the receiving side (decodedValue != encodedValue). Having struggled with this one for some time, I discovered that the window.open()-function actually tries to do some sort of decoding of the url itself before it passes it on. So I created another method that encodes on parameter level, and which also uses the escape-method to “protect” the encoded parameters from the window.open()-function.

function encodeUrl(url)
{
 	if (url.indexOf("?")>0)
 	{
		encodedParams = "?";
 		parts = url.split("?");
 		params = parts[1].split("&");
 		for(i = 0; i < params.length; i++)
 		{
			if (i > 0)
	 		{
				encodedParams += "&";
			}
			if (params[i].indexOf("=")>0) //Avoid null values
			{
				p = params[i].split("=");
				encodedParams += (p[0] + "=" + escape(encodeURI(p[1])));
			}
			else
			{
				encodedParams += params[i];
			}
		}
		url = parts[0] + encodedParams;
	}
	return url;
}

One thing to mention in this function is that is uses the encodeURI()-function. This one doesn’t do anything with the reserved characters like ; , / ? : @ & = + $. If you for some reason need to encode these as well, you should use the encodeURIComponent()-function.I tried to do this function without the use of escape(), but then the parameters “arrived” wrongly encoded, and became “corrupted” after I decoded them. Wrapping the encodeURI() inside an escape() solved this problem:

encodedParams += (p[0] + "=" + escape(encodeURI(p[1])));

Finally, inside my receiving page, I managed to decode the parameters and get the values I actually submitted with a java decoding method like this:

public String urlDecode(String urlToDecode)
{
	String decodedUrl = "";
 	if(urlToDecode != null)
 	{
 		try
 		{
 			decodedUrl = URLDecoder.decode(urlToDecode,"UTF-8");
 		}
 		catch (Exception e)
 		{
 			log.error("INVALID URL: " + urlToDecode,e);
 			return "";
 		}
 	}
	return decodedUrl;
}

This way of doing it also works for special characters belonging to the ISO-8859-1 encoding, even if the encoding used is actually UTF-8. As a small reference I can list the proper UTF-8 encoding for the Scandinavian specific characters if you use those, in this way you should be able to check whether your URL is encoded correctly or not if you print it out before decoding it.

  • æ = %E6
  • ø = %F8
  • å = %E5
  • Æ = %C6
  • Ø = %D8
  • Å = %C5

You can also check out this page for a complete reference of encoded characters: http://www.w3schools.com/tags/ref_urlencode.asp

If you want to try an example of how encodeURI and encodeURIComponent works, copy and paste the following javascript and test with your own strings:


<script type="text/javascript">

                var unencodedText = "This is my text that contains whitespaces and characters like # and Ø";
                var encodedText = "";
                var decodedText = "";
                alert('unencodedText: ' + unencodedText);

                //To encode whitespaces and the 'Ø' character - use encodeURI
                encodedText = encodeURI(unencodedText);
                //We see that whitespaces and 'Ø' are encoded, but the '#' is still there:
                alert('encodedText: ' + encodedText);

                //If we decode it we should get our unencodedText back
                decodedText = decodeURI(encodedText);
                alert('decodedText: ' + decodedText);

                //To also encode the '#' we use the encodeURIComponent
                encodedText = encodeURIComponent(unencodedText);
                //Now all the characters have been encoded:
                alert('encodedText: ' + encodedText);

                //To get our unencodedText back we now need to use the decodeURIComponent
                decodedText = decodeURIComponent(encodedText);
                alert('decodedText: ' + decodedText);

            </script>

I hope that you might find this post useful, and that it might save you from some pain in the a** if you try to use the encoding capabilities of Javascript.

Advertisements

Using JSON with JSF

This post will describe what to do if you want to use JSON in your web applications together with JSF. JSON (JavaScript Object Notation) is a way of doing information interchange between your programming language and javascript. In other words you can use it to access for example java objects in the world of javascript, and this will give you the possibility of creating more advanced web applications. Some basic knowledge of javascript is recommended.

  1. First thing to do is to download the JSON RPC library from JsonRpc and put it in your WEB-INF/lib directory (or the directory you use that holds the libs and is packaged together with the war-file). Also remember to add the library to the classpath of your project in Eclipse or other software frameworks you use to develop.
  2. Then secondly you need to include the jsonrpc.js javascript in the header of your page. The script can be found within the downloaded lib inside \json-rpc-java-1.0.1\webapps\jsonrpc. You include it by writing something similar like this in your header:
    <script type="text/javascript" src="${pageContext.request.contextPath}/javascript/jsonrpc.js"></script>
  3. Then you need to add the servlet configuration to the web.xml
    <servlet>
    
    	<servlet-name>com.metaparadigm.jsonrpc.JSONRPCServlet</servlet-name>
    
    	<servlet-class>com.metaparadigm.jsonrpc.JSONRPCServlet</servlet-class>
    
    </servlet>
    
    <servlet-mapping>
    
    	<servlet-name>com.metaparadigm.jsonrpc.JSONRPCServlet</servlet-name>
    
    	<url-pattern>/JSON-RPC</url-pattern>
    
    </servlet-mapping>
  4. Then, on top of your page (outside the <f:view> tags) you write:
    <jsp:useBean id="JSONRPCBridge" scope="session"class="com.metaparadigm.jsonrpc.JSONRPCBridge" />
  5. Then you must register the handler/backing bean you want the json to access. Do this inside the <f:view> tags but within jsp code tags
    </pre>
    <pre><%
    
    MyHandler myHandler = (MyHandler )FacesContext.getCurrentInstance().getApplication ()
    
    .getVariableResolver().resolveVariable(FacesContext.getCurrentInstance(),"myHandler ");
    
    JSONRPCBridge.registerObject("myHandler ", myHandler );%>
  6. From javascript side, you can now access you handler, set properties and execute methods like this:
    function JsonTest()
    
    {
    
    	try
    
    	{
    
    		//The input-parameter to the JSONRpcClient is basically the path to
    		//the servlet you registered in web.xml
    
    		jsonrpc = new JSONRpcClient("/YOUR_APPLICATION_CONTEXT_ROOT/JSON-RPC");
    
    		// Call a Java method
    
    		var result = jsonrpc.myHandler.getTestValue();
    
    		//Do something with result.....
    
    		jsonrpc.myHandler.setTestData("SET A PROPERTY TO SOMETHING");
    
    		jsonrpc.myHandler.execute(); //Execute a method
    	}
    
    	catch(e)
    
    	{
    
    		alert(e);
    
    	}
    
    }

Good luck!

Automatic logon to a web application (JSF on jboss-4.2.2.GA) using JCIFS NTLM HTTP Authentication

The goal was to be able to make users which are logged in on a windows domain to automatically authenticate with their windows credentials when starting a web application, and also to make this solution possible both for IE and Firefox. In other words, the user doesn’t have to enter user-name/password when starting a corporate web application.

The web-app is done in JSF (myfaces 1.1.5), the application server is jboss-4.2.2.GA and the library I use to “grab” the windows user is JCIFS NTLM HTTP Authentication version 1.2.17 (http://jcifs.samba.org/). I will try to give a step-by-step explanation to what I did to make this work, and I will point out the differences you need to do compared to JAAS authentication mechanism (which is commonly used with applications running on jboss).

  1. The first ting you need to do is to download and put the jcifs library (jcifs-1.2.17.jar – or newer version) into your JBOSS_HOME/server/<NAME-OF-SERVER>/lib -folder.
  2. In your web.xml, you need to apply the following:
    <filter>
    
    	<filter-name>NtlmHttpFilter</filter-name>
    
    	<filter-class>jcifs.http.NtlmHttpFilter</filter-class>
    
    	<init-param>
    
    		<param-name>jcifs.http.domainController</param-name>
    
    		<param-value>IP ADDRESS_OF_THE_DOMAIN_CONTROLLER</param-value>
    
    	</init-param>
    
    	<init-param>
    
    		<param-name>jcifs.smb.client.domain</param-name>
    
    		<param-value>NAME_OF_DOMAIN</param-value>
    
    	</init-param>
    
    	<init-param>
    
    		<param-name>jcifs.smb.client.username</param-name>
    
    		<param-value>A_USERNAME_IN_ACTIVE_DIRECTORY</param-value>
    
    	</init-param>
    
    	<init-param>
    
    		<param-name>jcifs.smb.client.password</param-name>
    
    		<param-value>PASSWORD_FOR_THIS_USER</param-value>
    
    	</init-param>
    
    	<init-param>
    
    		<param-name>jcifs.util.loglevel</param-name>
    
    		<param-value>3</param-value>
    
    	</init-param>
    
    </filter>
    
    <filter-mapping>
    
    	<filter-name>NtlmHttpFilter</filter-name>
    
    	<url-pattern>/*</url-pattern>
    
    </filter-mapping>

    For the jcifs.http.domainController you specify the ip-address of the domainController you are authenticating against, e.g. 192.168.1.100.

    For jcifs.smb.client.domain you specify the domain that the users are logging in to. If you are uncertain about this check the logon-box in windows (XP login to domain) and write the domain name as its written here.

    In some examples on the net the jcifs.smb.client.username and jcifs.smb.client.password are not included. These are used for pre-authentication, think of it as a user that has access to the active directory so that it can retrieve information about the other users trying to log in. My experience is that if I leave out these two parameters, it works fine for the first user logging in, but for the first user only. When the second user logs in you get a 0xC0000022: jcifs.smb.SmbAuthException in the server log, and the negotiation of user-name/password fails. Including those parameters solves this issue. You could perhaps create a “dummy user” in the Active Directory just for this purpose (if you have administrator rights), if not use any other user of the AD.

    At least when trying to get this up and running I would recommend a logging-level set to 3. And of course you would set the url-pattern of the filter according to the path of the resources you want to protect, e.g. <url-pattern>/myJsps/*</url-pattern>

  3. If you are used to restricting access to your web application, you might have something like this in your web.xml:
    <security-constraint>
    
    	<web-resource-collection>
    
    		<web-resource-name>PROTECTED AREA</web-resource-name>
    
    		<description>Require users to authenticate</description>
    
    		<url-pattern>/*</url-pattern>
    
    	</web-resource-collection>
    
    	<auth-constraint>
    
    		<role-name>SOME_ROLE</role-name>
    
    	</auth-constraint>
    
    </security-constraint>
    
    <login-config>
    
    	<auth-method>BASIC</auth-method>
    
    	<realm-name>MY_NAME</realm-name>
    
    </login-config>
    
    <security-role>
    
    	<role-name>SOME_ROLE</role-name>
    
    </security-role>

    And then you would also have a security-domain set up in jboss-web.xml that points to an application-policy in login-config.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 2.3//EN"
    
    "http://www.jboss.org/j2ee/dtd/jboss-web_3_2.dtd">
    
    <jboss-web>
    
    	<security-domain>java:/jaas/MY_APPLICATION_POLICY</security-domain> -->
    
    	<context-root>/MY_CONTEXT_ROOT</context-root>
    
    </jboss-web>

    To be able to use the security filter with success, you would need to remove all of the settings related to security and JAAS. Remove the <security-constraint> (including children) , <login-config> and <security-role> tags from web.xml, and also delete the line specifying the <security-domain> in jboss-web.xml

  4. All the settings server-side are done now, what remains is a very simple configuration of the client/web browser. You need to tell your browser that the host/site you are trying to reach should be considered a trusted site. If you don’t do this, the browser would pop up the usual login-dialog and ask for username and password.
    • Enable it in IE:Go to tools->Internet Options->SecuritySelect Local Intranet icon and press “Sites”Press “Advanced”Fill in the url to the host of your application and press add. (Do not use the full application path or port settings, only the host address – for example: http://192.168.1.100)
    • Enable it in Firefox:Type about:config in the url-barLocate the key network.automatic-ntlm-auth.trusted-urisAdd the url to the host in the value-field. If there is other urls specified, separate them with a comma.
  5. If you now try to start your application, you should be automatically logged in with your windows user. The server log should say something like this if you have a successful authentication:
    [STDOUT] NtlmHttpFilter: THE_DOMAIN\user_name successfully authenticated
    
    against 0.0.0.0<00>/IP_OF_DOMAIN_CONTROLLER

I hope this overview can be helpful, but of course there are situations and special cases that I haven’t covered here. I would have to point you to the documentation and the FAQ on the homepage of the JCIFS library (see link in the start of this post). Still, I spent some time sorting out configurations, parameters and mistakes, so hopefully this will give others a shorter path to the goal.

Good luck!