Only for the record. Here it is a link of a post in the Jboss community about a bug I've found. This cause me some headache. ;)
https://community.jboss.org/message/588983
Here it is the bug filled: https://issues.jboss.org/browse/EJBTHREE-2238
corecanarias
CTO and lead developer. Focused on cloud computing and renderfarm. Involved development methodologies and platforms includes TDD (CI integration using Git, Jenkins and Sonar); Java JEE (JBoss AS, Hibernate ORM, JMS); remoting (REST, RMI); Javascript (deep knowledge of Sencha platform)
Monday, July 15, 2013
Friday, March 8, 2013
JAAS Persistent sessions with JBoss 6
A common issue you can found when your web application is becoming more popular is how to update it without disturbing your users.
Redeploying a standard application could take arround 5-10 secs in a JBoss6, so we could assume that down-time for a monthly basis update.
But, what happens with logged-in users when you update your application? Well, they will loose their session and will need to login again. We could assume that for a couple of redeploys, but at the end you will notice that you are very reticent to update your application because your users will suffer it.
If you look at google for jboss persistent sessions, you will found that it is so easy solve this problem. In most tutorials you will be encouraged to save the session data to disk, so when you redeploy your application, the session will not be lost. Configuring jboss for that is very simple: In server/default/deploy/jbossweb.sar/context.xml you will see a comment saying that session persistence is disabled and what you need to enable it is uncomment the following line:
<Manager pathname="SESSIONS.ser" />
Now, the session will be serialized to that file, you should take in account that your session data must be serializable, but in most cases it is. You could even change the path of the SESSIONS.ser since it is saved on a temporary directory and it will be deleted on jboss restarts. To avoid this, simply set an absolute path:
<Manager pathname="/opt/app/data/SESSIONS.ser" />
That is all, so easy isn't it? Well, but... This doesn't work if you are using JAAS to authenticate your users. The reason is because the session is stored correctly but the JAAS principal is not, and JAAS will discard the session for users without a principal. So at this point, you have two options:
auto eth0:2
Redeploying a standard application could take arround 5-10 secs in a JBoss6, so we could assume that down-time for a monthly basis update.
But, what happens with logged-in users when you update your application? Well, they will loose their session and will need to login again. We could assume that for a couple of redeploys, but at the end you will notice that you are very reticent to update your application because your users will suffer it.
If you look at google for jboss persistent sessions, you will found that it is so easy solve this problem. In most tutorials you will be encouraged to save the session data to disk, so when you redeploy your application, the session will not be lost. Configuring jboss for that is very simple: In server/default/deploy/jbossweb.sar/context.xml you will see a comment saying that session persistence is disabled and what you need to enable it is uncomment the following line:
<Manager pathname="SESSIONS.ser" />
Now, the session will be serialized to that file, you should take in account that your session data must be serializable, but in most cases it is. You could even change the path of the SESSIONS.ser since it is saved on a temporary directory and it will be deleted on jboss restarts. To avoid this, simply set an absolute path:
<Manager pathname="/opt/app/data/SESSIONS.ser" />
That is all, so easy isn't it? Well, but... This doesn't work if you are using JAAS to authenticate your users. The reason is because the session is stored correctly but the JAAS principal is not, and JAAS will discard the session for users without a principal. So at this point, you have two options:
- Not use JAAS as authentication framework.
- Assume that your users will loose their session when you redeploy your application.
- Or...
Create a jboss cluster. I haven't evaluate this option before because seemed I was complicating a lot the infrastructure for a simple web app. But I was really frustrated by the fact I was unable to update the application without disturbing my users. So I decided to give it go.
I starting reading a couple of tutorial on how to setup a cluster and seemed to be very easy.
Since my intention with the cluster was only maintain the users logged in when redeploy my application it's required only two nodes on the cluster. If I use two ethernet interfaces I could maintain the two nodes in the same server, simplifying the overhead of this configuration.
This is the recipe I've used to configure two nodes of a JBoss Cluster to avoid JAAS principal loss:
Changes in the application
We don't need to change any code on our application, simple add this line to the web.xml to inform to jboss that the application is clusterizable:
<distributable />
In most cases you need to do nothing else in your application.
Changes in the server
Simply configure a virtual ethernet interface assigning an additional IP:
For ubuntu/debian, in /etc/network/interfaces
auto eth0
iface eth0 inet static
address 172.26.1.1
netmask 255.255.255.0
iface eth0:2 inet static
address 172.26.1.2
netmask 255.255.255.0
Configuring the JBoss
There is not so much to configure, create two separate installations of jboss, for example:
/opt/jboss-node1
/opt/jboss-node2
And use the following run scripts for each one:
/opt/jboss-node1/bin/run.sh -c all -b eth0 -g <cluster_name> -Djboss.messaging.ServerPeerID=1 -Djboss.jvmRoute=<node1_name>
/opt/jboss-node2/bin/run.sh -c all -b eth0:2 -g <cluster_name> -Djboss.messaging.ServerPeerID=2 -Djboss.jvmRoute=<node2_name>
/opt/jboss-node2/bin/run.sh -c all -b eth0:2 -g <cluster_name> -Djboss.messaging.ServerPeerID=2 -Djboss.jvmRoute=<node2_name>
You must replace the values between <> with your specific values.
Cluster_name Is a name you want to give to your cluster. Since the cluster will autodiscover all nodes in the network with the same cluster name, you will want to have a special name to separate dev and production clusters in the same network.
The ServerPeerID and node_name are unique for each node, you can select whatever you want.
The jvmRoute name allows us to associate all request from a specific user to the same server. In server/all/deploy/jbossweb.sar/server.xml locate the line:
<Engine name="jboss.web" defaultHost="localhost">
and change it as:
<Engine name="jboss.web" defaultHost="localhost" jvmRoute="${jboss.jvmRoute}">
The balancer I'm using is mod_jk, you can install it on debian/ubuntu with:
apt-get install libapache2-mod-jk
Then, proceed as usual to enable the module on your apache-httpd.
Configuring the mod_jk to register the nodes on the cluster and how will be reached:
workers.properties:
worker.<node1_name>.type=ajp13
worker.<node1_name>.host=172.26.1.1
worker.<node1_name>.port=8009
# worker.<node1_name>.fail_on_status=404
worker.<node2_name>.type=ajp13
worker.<node2_name>.host=172.26.1.2
worker.<node2_name>.port=8009
# worker.<node2_name>.fail_on_status=404
worker.mycluster.type=lb
worker.mycluster.balance_workers=<node1_name>,<node2_name>
worker.mycluster.sticky_session=1
Now, we can mount mycluster to a specific url on our apache-httpd with the following configuration. You can put it in your virtual server configuration:
JkMount /yourapp mycluster
JkMount /yourapp/* mycluster
As you can see, we have made available our cluster in the http://apache_url/yourapp url. If you access to that url, mod_jk will redirect you to any of the two server nodes. If that node fails it will redirect to the other node.
The advantage here is the jboss cluster will maintain http session and JAAS principals in all cluster nodes, so if one node goes down, the other node can maintain the users authenticated.
I've used this configuration for a while and works really well, and doesn't requires more maintenance or attention that a simple jboss.
The jvmRoute name allows us to associate all request from a specific user to the same server. In server/all/deploy/jbossweb.sar/server.xml locate the line:
<Engine name="jboss.web" defaultHost="localhost">
and change it as:
<Engine name="jboss.web" defaultHost="localhost" jvmRoute="${jboss.jvmRoute}">
Finally, you need to enable the ClusteredSingleSignOn valve in jboss.web to activate the JAAS principal propagation. Go to server/all/deploy/jbossweb.sar/server.xml and uncomment the line:
<Valve className="org.jboss.web.tomcat.service.sso.ClusteredSingleSignOn" />
Now, you can deploy your application on server/all/deploy, this should work as usual.
Finally, you need to enable the ClusteredSingleSignOn valve in jboss.web to activate the JAAS principal propagation. Go to server/all/deploy/jbossweb.sar/server.xml and uncomment the line:
<Valve className="org.jboss.web.tomcat.service.sso.ClusteredSingleSignOn" />
Now, you can deploy your application on server/all/deploy, this should work as usual.
Configuring the balancer
If you have deployed your application on both servers you will be able to reach your application by pointing your browser to 172.26.1.1 or 172.26.1.2 On that point, you only need to balance the incoming users to each node, and of course if one of the nodes goes down the users on that node will be moved to the other node. And the most important: The users won't notice the change.The balancer I'm using is mod_jk, you can install it on debian/ubuntu with:
apt-get install libapache2-mod-jk
Then, proceed as usual to enable the module on your apache-httpd.
Configuring the mod_jk to register the nodes on the cluster and how will be reached:
workers.properties:
worker.<node1_name>.type=ajp13
worker.<node1_name>.host=172.26.1.1
worker.<node1_name>.port=8009
# worker.<node1_name>.fail_on_status=404
worker.<node2_name>.type=ajp13
worker.<node2_name>.host=172.26.1.2
worker.<node2_name>.port=8009
# worker.<node2_name>.fail_on_status=404
worker.mycluster.type=lb
worker.mycluster.balance_workers=<node1_name>,<node2_name>
worker.mycluster.sticky_session=1
As you can see, we have defined our two jboss nodes, and a cluster called mycluster that will balance the incoming requests alternatively to both nodes node1 and node2. If a node is unreachable it will be marked as down and won't receive requests anymore until becomes active again. We can also use the fail_on_status to force a node status down when the node returns the specified error codes. (can be a comma separated list).
The sticky_session will maintain the users in the same server.Now, we can mount mycluster to a specific url on our apache-httpd with the following configuration. You can put it in your virtual server configuration:
JkMount /yourapp mycluster
JkMount /yourapp/* mycluster
As you can see, we have made available our cluster in the http://apache_url/yourapp url. If you access to that url, mod_jk will redirect you to any of the two server nodes. If that node fails it will redirect to the other node.
The advantage here is the jboss cluster will maintain http session and JAAS principals in all cluster nodes, so if one node goes down, the other node can maintain the users authenticated.
I've used this configuration for a while and works really well, and doesn't requires more maintenance or attention that a simple jboss.
Thursday, November 8, 2012
Jasig CAS
In most cases the login code of your application does not require thinking a lot. Simply use the standard module available on your framework, that is all you need.
But sometimes, you need to integrate third-party applications with your own. For example a cms, forums, ticketing systems, etc. Your users can stand a couple of months (in the best case) entering two times the user/password. But you know it is something you need to change.
At the beginning someone could be tempted to implement your own solution because seems it is very simple. Simply read one cookie from here and put there, and then redirect to there and back to... Well, at the end you will see yourself hacking every third-party application, and learning new concepts as man-in-the-middle, spoofing, etc. That is good, of course. But could be even more good if you can learn it with a bit less pressure because you have been so optimistic when you schedule this feature.
What I use in that cases is a SSO server. This allows me to leverage of SSO functionality on my application in a fraction of time if I'd try to implement by myself.
Jasig CAS (Central Authentication System) lets you to integrate it easily on your java web applications, but also in other platforms as PHP, .NET an others. The protocol is well documented and can be implemented in any platform if it is not available. On the other side, you have a lots of CASified applications which is how they name the clients that are been integrated into CAS.
Here we will see how can we use CAS on our JBoss6 applications. But, first of all you should start reading https://wiki.jasig.org/display/CASUM/Demo and try to become familiar with the CAS use.
Then, when you feel prepared to try it out with your JBoss/JAAS application follow the next steps. Remember you need to have a CAS server already up and running and reachable from http://yourcasserver/cas so, lets go:
But sometimes, you need to integrate third-party applications with your own. For example a cms, forums, ticketing systems, etc. Your users can stand a couple of months (in the best case) entering two times the user/password. But you know it is something you need to change.
At the beginning someone could be tempted to implement your own solution because seems it is very simple. Simply read one cookie from here and put there, and then redirect to there and back to... Well, at the end you will see yourself hacking every third-party application, and learning new concepts as man-in-the-middle, spoofing, etc. That is good, of course. But could be even more good if you can learn it with a bit less pressure because you have been so optimistic when you schedule this feature.
What I use in that cases is a SSO server. This allows me to leverage of SSO functionality on my application in a fraction of time if I'd try to implement by myself.
Jasig CAS (Central Authentication System) lets you to integrate it easily on your java web applications, but also in other platforms as PHP, .NET an others. The protocol is well documented and can be implemented in any platform if it is not available. On the other side, you have a lots of CASified applications which is how they name the clients that are been integrated into CAS.
Here we will see how can we use CAS on our JBoss6 applications. But, first of all you should start reading https://wiki.jasig.org/display/CASUM/Demo and try to become familiar with the CAS use.
Then, when you feel prepared to try it out with your JBoss/JAAS application follow the next steps. Remember you need to have a CAS server already up and running and reachable from http://yourcasserver/cas so, lets go:
- Copy the cas-client-core-<version>.jar and cas-client-integration-jboss-<version>.jar to the server/default/lib dir.
- Configure de JAAS login-config module as follows:
<application-policy name="cas"> <authentication> <login-module code="org.jasig.cas.client.jaas.CasLoginModule" flag="required"> <module-option name="ticketValidatorClass"> org.jasig.cas.client.validation.Cas20ServiceTicketValidator </module-option> <module-option name="casServerUrlPrefix"> http://yourcasserver/cas </module-option> <module-option name="tolerance">20000</module-option> <module-option name="defaultRoles">admin,user</module-option> <module-option name="roleAttributeNames">role,list</module-option> <module-option name="principalGroupName">CallerPrincipal</module-option> <module-option name="roleGroupName">Roles</module-option> <module-option name="cacheAssertions">true</module-option> <module-option name="cacheTimeout">480</module-option> </login-module> </authentication> </application-policy>
- Modify the deploy/jbossweb.sar/server.xml and uncomment:
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
- Finally in the web.xml of your xml you need to configure the servlet filters:<!-- Facilitates CAS single sign-out --> <listener> <listener-class> org.jasig.cas.client.session.SingleSignOutHttpSessionListener </listener-class> </listener> <!-- Following is needed only if CAS single-sign out is desired --> <filter> <filter-name>CAS Single Sign Out Filter</filter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class> </filter> <!-- Only 2 CAS filters are required for JAAS support --> <filter> <filter-name>CASWebAuthenticationFilter</filter-name> <filter-class>org.jasig.cas.client.jboss.authentication.WebAuthenticationFilter</filter-class> </filter> <filter> <filter-name>CASAuthenticationFilter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>https://cas.example.com/cas/login</param-value> </init-param> </filter> <!-- Other filters as needed --> <!-- CAS client filter mappings --> <!-- The order of the following filters is vitally important --> <filter-mapping> <filter-name>CAS Single Sign Out Filter</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CASWebAuthenticationFilter</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping> <filter-mapping> <filter-mapping> <filter-name>CASAuthenticationFilter</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping>In your WEB-INF/jboss-web.xml select the appropriate security domain:
<jboss-web><security-domain>java:/jaas/cas</security-domain> </jboss-web>
- The first time, may be you could want to activate the trace logging to inspect any error you could get in your JBoss app. In server/default/deploy/jboss-logging.xml do it by adding:
<logger category="org.jasig"> <level name="TRACE" /> </logger>
That is all. Start the server and make some tests. You have all of this more detailed in:
Subscribe to:
Posts (Atom)