Tuesday, February 1, 2011

@Value!; How do I inject you, you little $#%@!!



Spring 3 introduces a new annotation, @Value, for injecting values into properties. This is especially useful if you're using the <component-scan/> of spring's context namespace.

I recently need to access tomcat's "webapp.root" variable in a @Controller stereotype class, which was conveniently managed via Spring.

Our webapp's log files are stored in the root directory, and I needed to access this property in order to read the data in the file. The simplest way to get that value injected into the controller's property would be to inject it using this new value.

After some digging, bashing, and cursing, I finally got it to work...(drumroll)...

@Value("#{systemProperties['webapp.root']}/bookin-synchronizer.log}")

I certainly wish that this was more properly documented in Spring's documentation, as it would have saved me some time...oh well, can't have everything.

Friday, January 28, 2011

Expired Password??? No problem with Spring Security!


I've got a user story which requires a feature that lets a user change their expired password. We've already got the views defined/written, and they'll work pretty much right away, so I'd like to leverage Spring Security's authentication framework to detect when a user's password has expired, and redirect them to the view that will allow them to change their password.

Spending some time on the forum, didn't yield a whole lot of answers that were directly relevant to 3.0.x, but after digging through the API's I found that I could swap the <form-login >'s authentication-failure-handler with Spring Security's own org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler, which will let me provide separate views for each type of exception that is thrown during authentication. In my particular case, I'm concerned with org.springframework.security.authentication.AccountExpiredException.

So, I need to change the <form-login > namespace a bit:
   

Next, I configure the authenticationFailureHandler bean above, and provide it with the appropriate mappings:


  
  
   
    /login.jsp?login_error=1
    
    /expiredPassword.htm
    
    /expiredPassword.htm
    
   
  
 

Before I can load the view, I need to populate the backing bean (or form) for the view, which requires that I populate it with a valid Users' object...I can find a user based on their username, and luckily, Spring puts the username into the requests' session scope, so I can handle the request like so
 @RequestMapping(value="/expiredPassword.htm", method=RequestMethod.GET)
 public ModelAndView goToChangePassword(HttpServletRequest request,
   HttpServletResponse response) throws Exception {
     String username = (String) request.getSession().getAttribute(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY);
     
     
     Users user = userService.getUserByUsername(username);
     if (user == null){
      return new ModelAndView( new RedirectView("/home.htm"));
     }
     
     PasswordForm form = new PasswordForm(user);
  ModelAndView mav = new ModelAndView(CHANGE_PASSWORD);
  mav.addObject(form);
  return mav;
 }

I can do whatever I want in the case I can't load the username, but for now, I'll just redirect the user to the login page (ambiguously through the home.htm mapping).

Everything else behaves the way it did before; other exceptions get mapped to the default view (login.jsp?login_error=1); I can change this in the future, if I need to handle any other special cases. Thank you Spring Security team for a really easy implementation...

Tuesday, September 21, 2010

Spring Security 3 Ajaxified Login with jQuery (1.4.x)

On a recent project at work, I was tasked with porting a web app (or at least certain views of the UI) to a mobile device friendly UI. For whatever reason, the mandate was to make it "slick" on an iPhone. Since the project already uses jQuery extensively, it naturally made sense to use a framework that is compatible with jQuery, and mobile device. I chose (for a prototype) jQtouch, as jQuery mobile isn't slated for release until end of year.

Too many pros to talk about this framework it kicks ass. Unfortunately, looks like everything runs on a single (index.*) page, with separate divs which forms the panels.

My first hiccup using jQtouch was the authentication process. I'd hoped to be able to re-use ALL of our customized authentication code. We use Spring Security, along with some Roll-Your-Own UserdDetailsService. After some research, I determined that I could run multiple Authentication Processing filters on Spring Security's filter chain. Here's how I made the whole thing work using an ajaxified login form with Spring's UsernamePasswordAuthenticationFilter, and LogoutFilter.

first the application-context beans required:


 
     
     
  
   
       
      
  
     
      
       
      
     
 
 
 
 
  
   
  
  
   
    
    
   
  
     
 


Then, I added these beans to the existing Spring Security Configuration's filter chain (pay specific to the last 2 lines);

     
     
        
        
        
        
        
        
        
    

Don't forget to update the web.xml with the new filters;


    mobileAuthenticationProcessingFilter
    org.springframework.web.filter.DelegatingFilterProxy
  
  
    mobileAuthenticationProcessingFilter
    /*
  
  
  
    mobileAuthenticationInvalidationFilter
    org.springframework.web.filter.DelegatingFilterProxy
  
  
    mobileAuthenticationInvalidationFilter
    /*
  

Since I don't want to redirect after login, I need to override the default behaviour in the MobileAuthenticationProcessingFilter:

public class MobileAuthenticationProcessingFilter extends UsernamePasswordAuthenticationFilter {
 
 private static Logger log = Logger.getLogger(MobileAuthenticationProcessingFilter.class);

 @Override
 protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
   Authentication authResult) throws IOException, ServletException {

  super.successfulAuthentication(request, response, authResult);
  HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper(response);
  responseWrapper.setContentType("text/json");
  String json = "{authenticated:true, navTag : \"" + request.getRequestURI() + "#Crew \"}";
  log.debug("Successful Authentication, writing JSON success Response: " + json );

  renderResponse(responseWrapper, json);
 }

 @Override
 protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
   AuthenticationException failed) throws IOException, ServletException {
  /*
   * Hacky, but works.  What's happening is that the normal "onAuthenticationFailure" method
   * is sending a redirect, which is screwing up the ajax call to authenticate.  Inb this case, 
   * we'll let the AbstractAuthenticationFilter populate the session scope with the EXCEPTION_KEY,
   * but then gracefully STFU.
   */
  super.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler() {
   @Override
   public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
     AuthenticationException exception) throws IOException, ServletException {
    // do nothing
   }
  });
  
  super.unsuccessfulAuthentication(request, response, failed);
  
  HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper(response);
  String failureReason = request.getSession().getAttribute(SPRING_SECURITY_LAST_EXCEPTION_KEY) != null ?  
    ((Exception)request.getSession().getAttribute(SPRING_SECURITY_LAST_EXCEPTION_KEY)).getMessage() :
     "Invalid login attempt, check your authentication credentials.";
  responseWrapper.setContentType("text/json");
  String json = "{ authenticated: false,\"errors\": { \"reason\": \" " + failureReason + " \"} }";
  log.debug("Failed Authentication, writing JSON failure Response: " + json);
  renderResponse(responseWrapper, json);

 }

 private void renderResponse(HttpServletResponseWrapper responseWrapper, String json) throws IOException {
  Writer out = responseWrapper.getWriter();
  out.write(json);
  out.close();
 }
}

Additionally, if you noticed in the application-context bean we provided the com.denlab.web.filter.MobileLogoutSuccessHandler class as a constructor arg, which also overrides the behaviour of the default:

public class MobileLogoutSuccessHandler implements LogoutSuccessHandler {

 @Override
 public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
   Authentication authentication) throws IOException, ServletException {
  
  //Do Nothing, we'll write a response later.
  Logger.getLogger(this.getClass()).debug("Logging out");
  
 }

}

Now that we've configured spring, implemented our classes, all that's left is to provide the login form, inside the index.jsp, with Spring's default params:




And a logout button:


   Logout
  

Use jQuery to intercept the form submission, and fire the ajax event:
 

When the principal authenticates, the MobileAuthenticationProcessingFilter will prevent the response redirect and instead write a json object directly to the response.