[ajug-members] intercept / resume site flow
Ben McEwen
bmcewen at fleetcor.com
Mon Feb 21 11:49:41 EST 2005
I use a Spring HandlerInterceptorAdapter couple with a SimpleFormController
to handle logins for a couple of small apps I have developed. It has to tie
into a convoluted legacy [in]security framework, so container-based security
wasn't an easy fit. The default success view is the index page, but I
encode the original destination view in the URL so the login will take the
user directly to whatever protected page they were trying to reach. There
are other (and certainly better) examples in the Spring documentation and
sample applications.
Here's the Spring configuration:
<!-- default handler mapping for all protected controllers -->
<bean id="defaultHandlerMapping"
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<ref bean="loginInterceptor"/>
</property>
</bean>
<!-- Configure Spring Controllers as usual -->
<!-- Login Stuff -->
<bean id="loginInterceptor"
class="fleetcor.web.controller.FleetnetLoginInterceptor"/>
<!-- Handler mapping for the login controller -->
<bean id="loginHandlerMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<map>
<entry key="/login.form">
<ref local="loginForm"/>
</entry>
</map>
</property>
</bean>
<bean id="loginForm"
class="fleetcor.web.controller.FleetnetLoginController">
<property name="bindOnNewForm">
<value>false</value>
</property>
<property name="validateOnBinding">
<value>true</value>
</property>
<property name="validator">
<bean class="fleetcor.validation.FleetnetLoginFormValidator">
<property name="fleetnetSecurity">
<ref bean="fleetnetSecurity"/>
</property>
</bean>
</property>
<property name="formView">
<value>loginform</value>
</property>
<property name="successView">
<value>index</value>
</property>
<property name="commandName">
<value>fleetnetUser</value>
</property>
<property name="commandClass">
<value>fleetcor.beans.FleetnetUser</value>
</property>
</bean>
Here's the code for the login Interceptor:
public class FleetnetLoginInterceptor extends
org.springframework.web.servlet.handler.HandlerInterceptorAdapter {
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
UserSession userSession =
(UserSession)WebUtils.getSessionAttribute(request, "userSession");
if(userSession == null){
String url = request.getRequestURI();
String query = request.getQueryString();
ModelAndView mv = new ModelAndView("redirect:/login.form");
if(query != null){
url = url + "?" + query;
}
mv.addObject("forwardAction", url );
throw new ModelAndViewDefiningException(mv);
}
return true;
}
}
Here's the code for the login controller:
public class FleetnetLoginController extends SimpleFormController {
protected ModelAndView onSubmit(HttpServletRequest request,
HttpServletResponse
response,
Object command,
BindException errors) throws
Exception {
ModelAndView mv;
mv = super.onSubmit(request, response, command, errors);
if(errors.getErrorCount() <= 0){
FleetnetUser user = (FleetnetUser)command;
UserSession userSession = new UserSession(user);
request.getSession().setAttribute("userSession", userSession);
String forwardAction = request.getParameter("forwardAction");
if(forwardAction != null){
response.sendRedirect(forwardAction);
mv = null;
}
}
return mv;
}
protected Object formBackingObject(HttpServletRequest request)
throws Exception {
FleetnetUser user = new FleetnetUser();
user.setUser(request.getParameter("user"));
user.setPassword(request.getParameter("password"));
return user;
}
}
And here's the form validator that actually checks the user's credentials
against the security DAO (FleetnetSecurity):
public class FleetnetLoginFormValidator implements Validator{
public boolean supports(Class clazz) {
return clazz.equals(FleetnetUser.class);
}
public void validate(Object obj, Errors errors) {
FleetnetUser form = (FleetnetUser)obj;
log.debug("Validating login form: " + form.toString());
if(form.getPassword() == null ||
form.getPassword().trim().equalsIgnoreCase("")){
errors.rejectValue("password", "error.required", " Password is
required ");
}
if(form.getUser() == null ||
form.getUser().trim().equalsIgnoreCase("")){
errors.rejectValue("user", "error.required", "User is
required");
}
if(!errors.hasErrors){
Map<String, String> map =
this.getFleetnetSecurity().validatePassword(form.getUser(),
form.getPassword());
if(!map.get("isValid").equalsIgnoreCase("true")){
errors.reject("error.login", "Invalid login.");
errors.reject("error.fleetnet", "Fleetnet Message: " +
map.get("message"));
}
}
}
public FleetnetSecurity getFleetnetSecurity() {
return fleetnetSecurity;
}
public void setFleetnetSecurity(FleetnetSecurity login) {
this.fleetnetSecurity = login;
}
}
-----Original Message-----
From: ajug-members-bounces at ajug.org [mailto:ajug-members-bounces at ajug.org]
On Behalf Of Rob Kischuk
Sent: Friday, February 18, 2005 11:10 AM
To: General AJUG membership forum (100-200 messages/month)
Subject: Re: [ajug-members] intercept / resume site flow
A good solution to this problem is built into the servlet container -
container managed security. You declare in your web.xml what resources
you want to require a login for, configure the login method (FORM is
best for most apps), and tell your app server where to find the
user/password/role information.
When the user tries to access a protected resource, the container steps
in, requires a login, and upon successful login sends them to whatever
resource they were trying to access. I have a sample app and
presentation slides here: http://kischuk.com/devcon/
If your security needs are complex and you are using Spring, I have
heard good things about Acegi Security:
http://acegisecurity.sourceforge.net/, but in this particular case, it
sounds like that would be overkill.
-Rob
Akilas Yemane wrote:
>Hello fellow members,
>I was hoping if you know a pattern or framework that addresses the
>problem below. I apologize in advance for the verbose scenario.
>
>Scenario is simple 'petstore' web-app using Springframework
>1. User click add product to cart and proceeds to checkout.
>2. Upon clicking [checkout], an interceptor checks user login status,
>and redirects to login page.
>3. User enters user/pass, and clicks submit. The login controller
>completes its task, then forwards to the 'success view' which is the
>user home/profile page.
>
>And that's a problem...
>under normal login this works fine. u want the user to go to home page.
>but in this case, the user was in the middle of a checkout....and was
>intercepted & redirected. So they ought to be able to resume their
>checkout after the login.
>
>Frameworks like Struts, Springs and Webworks enable u to make site
>flow decision by defining a 'success view' or 'failure view' for each
>form processor/controller.
>but these frameworks, don't have a ready made was of making siteflow
>descision based on where the user came from.
>
>My solution was this:
>When the interceptor intercepts the request, it adds a 'nextAction'
>parameter, to it. Then forwards to the login page, the the login page,
>after completing its job, would forward, to this nextAction page
>parameter (if found)
>
>So far this works, but now I'm seeing more and more scenarios that
>require the need to intercept a request, and resume again.
>
>Are there any frameworks, patterns that address this issue?
>
>Thank you,
>-ay
>
>
>_______________________________________________
>ajug-members mailing list
>ajug-members at ajug.org
>http://www.ajug.org/mailman/listinfo/ajug-members
>
>
_______________________________________________
ajug-members mailing list
ajug-members at ajug.org
http://www.ajug.org/mailman/listinfo/ajug-members
More information about the ajug-members
mailing list