Tip of the Day - Login Magic by Overriding Struts Actions

Liferay offers extensive set of features regarding user accounts and signing in to your portal out of the box. Many of the features are made configurable through portal properties, enabling the developer to concentrate more on the core features clients want from their portals.
 
These options include, among others, the “remember me” feature, login by openid and whether you want your users to authenticate using their email address or screen name. And this is just the tip of the iceberg, there are numerous possibilities to configure the login process.
 
Yet sometimes what Liferay offers as a default might not be enough. Luckily there’s more. In a blog post from the past, Mika Koivisto describes how to override and add struts actions from hook plugins. This feature is really neat and makes it possible to do some Liferay Magic™.
 
Earlier on I mentioned that it was possible to choose the authentication method used by your portal by changing a property. What if you wanted to enable both logging in by screen name and by email address? You could create two different pages with login portlet and, as the authentication method is configurable also on portlet-by-portlet basis, change the method used by the other portlet. But what if I told you that you could use Struts portlet actions to enable both, or even create some custom authentication methods?
 
We’ll achieve this by creating new hook, called login-hook. Within the hook, we’ll override the default struts action for login. In our own custom action we can wrap the original ActionRequest with a custom LoginParamWrapper and forward the processing of the login to the original action given as a parameter to your custom action.
 
Override the default login struts action with your custom action
Override the default login action with a custom class which extends BaseStrutsPortletAction
 
The magic happens in the LoginParamWrapper, where we override the getParameter method. When ever a request parameter is queried, we’ll check if the login name was requested. If that is the case and user has supplied us with an email address, we’ll secretly fetch the screen name of the user and, if found, return it instead. And voilà, user has logged in with their email even though it was login by screen name that was enabled.
 
Our custom LoginParamWrapperMagic
LoginParameterWrapper class extends ActionRequestWrapper, the derived class applies a bit of magic when the login parameter is queried.
 
Of course, now we’ve left with some minor hooking of the login page itself to reflect on this change and tell the user they can login by screen name or email, which ever they prefer. And to make the feature complete, don’t forget to override the forgot password action to create similar feature when users have forgotten their passwords.
 
Override the default login page to reflect these changes and you're done
Override login.jsp to reflect the changes made.
 
Some things to note, however. You’re extending the BaseStrutsPortletAction class and not the LoginAction. This is why you need to override the render and serveResource methods as well. It is a bit of a nuisance, but you can just forward the handling to the original action which is included as a parameter on your methods.
 
And to tell you that this is not just a show-off, this is a pattern we’ve successfully used in production environments. We at Ambientia have done some magic similar to this for instance in The Finnish Business School Graduates Liferay Portal to enable customised methods of logging in.
 
So there you have it, overriding Liferay's default struts actions is a powerful tool. Use it with care.
9
Blogs
Very impressive magic and happy that you really made it work. We did the similar thing using an Ext plugin for one of our major implementations. But this looks much simpler and trendier. Also had a look into the site that you have mentioned. You guys have done a great job. Keep up the good work and keep sharing the knowledge with rest of the community members. Thanks.

Ahamed Hasan
Author, Liferay Cookbook
The BaseStrutsPortletAction does not have getParameter() method. Where to get getParameter() method in the hook ?
Sorry Manish, I realised that my screenshots missed one crucial part. In your custom StrutsPortletAction, you have to create a request wrapper object which extends ActionRequestWrapper. It's the getParameter-method in this wrapper class you need to override. So in your StrutsPortletAction you just do something like this:

originalStrutsPortletAction.processAction(originalStrutsPortletAction, portletConfig, new LoginParamWrapper(actionRequest),
actionResponse);
could you suggest where to look for LoginNameResolver and LoginParamKeys class. I could not find in Liferay 6.2.
Are they custom classes ?
Yes Manish, the LoginNameResolver is a custom class which basically translates the email address used to screen name (or vice versa). This is done simply by fetching the user with the said screen name or email and returning the requested attribute. LoginParamKeys is also a custom class, it was created just to avoid using the String "login" inline in the LoginParamWrapper.
Thanks for the article, I have been searching for the above scenario, Luckily I got this link.

could u please elaborate on below ,

new LoginParamWrapper(actionRequest),LoginNameResolver and LoginParamKeys .


when I put new LoginParamWrapper(actionRequest) in struct actions as mentioned u i'm getting error.

And plz tell me how to use LoginNameResolver and LoginParamKeys, because i'm new to these struct actions . It making some confusion.

Please help.
Hello Ramu. You’ll need to create a LoginParamWrapper class, which extends ActionRequestWrapper. Then in the class defined as the struts-action-impl, you’ll just wrap the original actionRequest with it. What is the error you’re getting? LoginNameResolver is a custom (dummy) class which does nothing but hides the logic of resolving the needed login name from email or screen name. The LoginParamKeys is just a class containing the static parameter names used while logging in, the string “login” for instance.
Thanks for the quick reply Laur

I resolved the problem by seeing the comments of urs, thanks a lot for the article, it saved a lot of time.

Excellent article...