Showing posts with label Remember-Me. Show all posts
Showing posts with label Remember-Me. Show all posts

Thursday, July 18, 2013

JSF 2, Spring, Spring Security, Hibernate/JPA, Jasypt, application sample available at Github

If you have followed my previous articles series about JSF 2 and different frameworks integration, then you can find the complete application source code on Github here.
Please feel free to post your comments, suggestions and ameliorations

Monday, July 8, 2013

JSF 2, Spring Security integration with Remember-Me (Contd.)

In last part, we said that we are going to use a database to store Remember-Me cookie's related data. This data consists of the username used to login, a series identifier and a token and a timestamp to maintain the last login to the app. For more details about the principles behind stored data, please refer to this article. Fortunately, for us, Spring Security will handle this stuff. We just need to give him the right configs.

1) Configuring database

First thing to do, is to prepare our database. In this series, I will be using MySQL as a DB. 
So let's create our table to store mentioned data:
create table persistent_logins (username varchar(64) not null,
      series varchar(64) primary key,
      token varchar(64) not null,
      last_used timestamp not null)
Next, we configure a datasource to be used, not only by Spring Security, but also by future persistence frameworks (Hibernate, Spring Data etc...). In applicationContext.xml add following:




First line, is just telling Spring about a properties file contained in classpath of our application and named "application.properties". This file, will contain config params that will help system administrators to deploy our application without any need to recompile whole application when a server password is changed:
db.driverClass=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/db_name_here
db.username=dbUserName
db.password=dbUserPassword
db.showSql=true

In next lines, we are just defining a Spring bean that will be our used JDBC datasource implementation. We need to pass it params like db url, username, driver class etc... Don't forget to add dependencies of your DB driver in POM file, for MySQL, just add following:


        mysql
 mysql-connector-java
 5.1.6

2) Remember-Me stuff

In previous part, we added a remember-me element to http config element. That will tell Spring Security to try to fetch user from our system whenever his browser send a valid cookie value.
Now we need to update that remember-me element so it references a new defined Spring bean:




     
    
    
    
    

The remember-me services bean must define an implementation of a remember-me strategy. Here I am going to use one of the two implementations Spring Security offers. For the PersistentTokenBasedRememberMeServices to work, we must give it: a tokenRepository which is a bean we will define and that will store tokens in previously created table, a userDetailsService (and we already defined one in previous part), a key that will be used to encrypt/decrypt sent cookie to client browser.  The two other properties are optional. And their names are just meaningful.
Now we define the tokenRepository bean:


 
     
     

We just give it the name of already configured datasource. The other property is just to prevent it to drop/create the table on each server start.

We need also to define an authentication provider related to the Remember-Me concept and register it:

 
   
   

And we need to register it in the authentication-manager already defined, here is the entire declaration:

    
     
    
    

And the last config we need is to define a Remember-Me filter that should intercept HTTP requests and validate cookies whenever needed:

     
     

Notice how we gave it references to our defined rememberMeServices and authenticationManager.

3) JSF 2 Remember-Me part

Now that everything is configured as expected, let's implement the JSF part of the app that should tell Spring Security when to remember a user, and when not.
First thing to do, is to inject the rememberMeServices service in the UserManagedBean managed bean:
@Inject
@Named("rememberMeServices")
private RememberMeServices rememberMeServices;
Next, in the login method in UserManagedBean class, we just add the following code:

HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();  
HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request) {
    @Override 
    public String getParameter(String name) { return "true"; }            
};  
rememberMeServices.loginSuccess(wrapper, response, authentication);
So what does this code do ? From the Spring Security docs, the RememberMeServices#loginSuccess method must be called "whenever an interactive authentication attempt is successful". And since we are doing login manually, then, we should invoke it manually. As for its role, this class "examines the incoming request and checks for the presence of the configured "remember me" parameter. If it's present, or if alwaysRemember is set to true, calls onLoginSucces". The onLoginSucces method by itself is responsible for creating a new persistent login token with a new series number. Then it stores the data in the persistent token repository and adds the corresponding cookie to the response. The loginSuccess method must be given a HttpServletRequest to check if a remember-me param is present and if it's equal to "true", if not it will just return. Second param is a HttpServletResponse, in which generated cookie is written, and finally a org.springframework.security.core.Authentication that will contain username of loggedin user. Now it should be clear why I overrided the getParameter method in the HttpServletRequestWrapper passed to loginSuccess. The added code by now, will activate the Remember-Me whenver called. This should not be the case, since not all users will wish their connections be maintained (they may connect from a public PC for example). This been said, we need to test if connecting user wants to be remembered:
//In user managed bean (where login method is defined)
private Boolean rememberMe = false;
and in the login page, we add a checkbox to let user decide:


Now we just test on the rememberMe value to decide whether to call RememberMeServices#loginSuccess or not. Et voilà!! We just finished with the Remember-Me feature in JSF 2 with Spring Security.
You can test your code by setting a session timeout value of 5 minutes for example, login to the application and wait for session to timeout and then re-call home page, you should access it without being obliged to re-login.

4) Instantiate JSF Managed beans

Now suppose, that with normal login, if user is successfully authenticated, you have a session managed bean that will handle some informations to be used by other managed beans in the application. You may say that we already have the logged in user's username in Spring Security' SecurityContextHolder. That's true, but also, the stored object has a few data, and you will soon be obliged to fetch more data for the User object from DB. That will result in extra DB access. 
So let's assume you have a managed bean like this: 
package com.raissi.managedbeans;

import java.io.Serializable;
import javax.inject.Named;
import org.springframework.context.annotation.Scope;
import com.raissi.model.User;

@Named("loggedInUser")
@Scope("session")
public class LoggedInUser implements Serializable{
 private static final long serialVersionUID = -1033377115353626379L;
 private User user;

 public User getUser() {
  return user;
 }

 public void setUser(User user) {
  this.user = user;
 }
}


We inject this bean in the UserManagedBean (view managed bean responsible for the login/logout process), and whenever a successful login takes place, we set user in LoggedInUser with the returned object from DB. And in all managed beans that need informations about user logged to the app, we just inject this managed bean.
Now if you try to access the application after session timeout, Spring Security will create a new session for you and authenticate you automatically. But in this newly created session, you won't have JSF session managed bean LoggedInUser. And if you try to access its User variable in a homeManagedBean, you will have a NullPointerException.
To initialize this bean, in early defined CustomUserDetailsService add the following code after setting authentication in SecurityContextHolder:

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
LoggedInUser loggedInUser = (LoggedInUser)request.getSession().getAttribute("loggedInUser");
 if(loggedInUser == null){
         loggedInUser = new LoggedInUser();
  request.getSession().setAttribute("loggedInUser", loggedInUser);
 }
 if(loggedInUser.getUser() == null){
  loggedInUser.setUser(user);
 }
Thanks to Arjin comment below, this code will only work if the LoggedInUser objects are simple POJOs with no injected beans. But when we want to use other services inside it (as for example: injecting the RememberMeServices bean to centralize the updating process of SecurityContext, which I did indeed) then, things will go bad and complicated. And all of that if because of LoggedInUser object being manually instantiated.
The solution I opted for in this situation is to inject a scoped proxy of LoggedInUser inside CustomUserDetailsService. You would say, "but CustomUserDetailsService is a singleton service that will be created only once a time, however LoggedInUser is a session scoped bean, that will be created on every new session". And that's why I am using a scoped proxy of LoggedInUser. By that we will be "injecting a proxy object that exposes the same public interface as the scoped object but that can also retrieve the real, target object from the relevant scope" (more details here). So how to do it ?
First let's create an interface that LoggedInUser will implement:
package com.raissi.managedbeans;

import java.io.Serializable;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.raissi.domain.User;

public interface ILoggedInUser extends Serializable{

 public User getUser();
 public void setUser(User user);
 /**
  * Update security context
  * This method should be called whenever setUser is called.
  * It's defined here to be conform with the DRY principle.
  * @param request
  * @param response
  */
 public void updateSecurityContext(HttpServletRequest request, HttpServletResponse response);
}

After that let's change our LoggedInUser class:
package com.raissi.managedbeans;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.stereotype.Component;

import com.raissi.domain.User;
import com.raissi.security.Authority;
import com.raissi.security.CustomUserDetails;
import com.raissi.util.UserRole;

@Component("loggedInUser")
@Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
public class LoggedInUser  implements ILoggedInUser{
 private static final long serialVersionUID = -1033377115353626379L;
 
 @Inject
 @Named("rememberMeServices")
 private RememberMeServices rememberMeServices;
 
 private User user;

 public User getUser() {
  return user;
 }
 @PostConstruct
 public void init(){
 }
 public void setUser(User user) {
  this.user = user;
 }
 
 
 public void updateSecurityContext(HttpServletRequest request, HttpServletResponse response){
  List auths = new ArrayList();
  if(user.hasRole(UserRole.ADMIN.toString())){
   auths.add(new Authority("ROLE_ADMIN")); //Role here, like "admin"
   auths.add(new Authority("ROLE_USER"));
  }else {
   auths.add(new Authority(user.getRole())); //Role here, like "admin"
  }
  
  Authentication authentication =  new UsernamePasswordAuthenticationToken(new CustomUserDetails(user), null, auths);
  SecurityContextHolder.getContext().setAuthentication(authentication);
  
  HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request) {
      @Override public String getParameter(String name) { return "true"; }            
  };
  rememberMeServices.loginSuccess(wrapper, response, authentication);
 }

}
Two things are to be noticed here:
a) we are using "proxyMode = ScopedProxyMode.INTERFACES" to tell Spring that we are exposing this bean as a proxy
b) we moved the logic to set authentication in Spring Security context from view managed beans (ex: in UserManagedBean) into the LoggedInUser.
This been done, you just update managed beans containing references to LoggedInUser to reference the new defined ILoggedInUser instead, example in UserManagedBean:
@Inject
private @Named("loggedInUser") ILoggedInUser loggedInUser;
And in login method:
//Get User object based on provided login and password:
User user = userService.loginUser(userLogin, password);
//Set user in LoggedInUser
loggedInUser.setUser(user);
//Update Security context:
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
loggedInUser.updateSecurityContext(request, response);

By now everything should work fine, and you completely have Spring Security working with your JSF 2 application and also with the Remember-Me feature activated

Thursday, July 4, 2013

JSF 2, Spring security integration with Remember-Me

Spring Security is a very powerful security framework. You can use it to manage application access both by defining Authentication and Authorization policies at both the web request level and at method invocation level. At the first level (request) Spring Security uses servlet filters. At the latter level (method), Spring Security uses Spring AOP-proxying objects and applying advice that ensures a particular user has the good rights to invoke a method.

1) Spring security Maven dependencies

To use the Spring Security framework at request level, we need to declare the "springSecurityFilterChain" at our web.xml. But first here are minimal dependencies to add in pom.xml file:


 org.springframework.security
 spring-security-core
 3.1.4.RELEASE



 org.springframework.security
 spring-security-web
 3.1.4.RELEASE



 org.springframework.security
 spring-security-config
 3.1.4.RELEASE

2) Spring security config

Now in web.xml:

 springSecurityFilterChain
 org.springframework.web.filter.DelegatingFilterProxy



 springSecurityFilterChain
 /*

The DelegatingFilterProxy filter by itself doesn't do much work. Instead, and as its name says, it delegates work to a defined bean in application context that will handle requests. For a matter of simplicity, I will separate my Spring files, into general application context beans and security ones. So in web.xml file, declare a context-param like this one:

 contextConfigLocation
 
 /WEB-INF/spring-security.xml
 /WEB-INF/applicationContext.xml
 

If defined, Spring will use this param to read config files and load defined beans. Now in spring-security.xml fil, we will define the specialized filter that will handle our security stuff:


 
  
   
  
 
 
  
 


Unlike in applicationContext file, here we are using beans:bean elements, this is because I chose the Spring Security namespace to be the default one. Notice that in the first declared bean, we are using the same bean name as the filter-name in web.xml for the DelegatingFilterProxy filter. This is very important. In fact, when finding a DelegatingFilterProxy filter, Spring security will automatically create a filter bean whose ID is: springSecurityFilterChain. This is the bean that will be responsible for security chaining. Once we defined which beans should be used for our security, we need to specify which URLs must be secured and which ones can be with public access:



 
 
 
 
 
 
 
 
 
 

Here for example, we decided that only users that have "ROLE_ADMIN" as role to access all "/admin/blah" pages. We also, excluded all paths containing "/javax.faces.resource/" from security checks. In fact, we don't want Spring Security to filter CSS or Javascript files. Now in the "form-login" element, we defined "/login" as login-page. Spring security will redirect non logged-in users to this page. remember-me and access-denied-handler will be discussed later, just ignore them by now.

3) Spring security authentication manager with custom user details

When designed, Spring Security was aimed to fetch by itself user credentials from DB or other data sources (LDAP etc...). To do that, you have to follow forms naming. Personnally, I prefer to manage Spring Security Context manually. This means, that it's up to me to tell Spring Security that a user has logged in. But even, in this way, you still need to define an authentication-manager:

    
    

Now you may notice that I gave to the authentication provider bean a reference to user-service. Well this is a " reference to a user-service (or UserDetailsService bean) Id". The custom user details service bean, must implement the UserDetailsService interface, which declares a single method to be implemented:
UserDetails loadUserByUsername(String username)
                               throws UsernameNotFoundException
So here is our CustomUserDetailsService class:
package com.raissi.security;

import javax.inject.Inject;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.raissi.model.User;
import com.raissi.service.UserService;

@Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService{

 @Inject
 private UserService userService;
 
 @Override
 public UserDetails loadUserByUsername(String login)
   throws UsernameNotFoundException {
  System.out.println("Trying to fetch user with login: "+login);
  final User user = userService.findUserByLoginOrEmail(login);
  if(user == null){
   throw new UsernameNotFoundException("User not found");
  }
  UserDetails details = new CustomUserDetails(user);
  Authentication authentication =  new UsernamePasswordAuthenticationToken(details, null, details.getAuthorities());
  SecurityContextHolder.getContext().setAuthentication(authentication);
  return details;
 }

}
The CustomUserDetails class by itself is implementing the UserDetails interface:
package com.raissi.security;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.raissi.model.User;
import com.raissi.util.UserRole;

public class CustomUserDetails implements UserDetails{
 private static final long serialVersionUID = 2270217112782820892L;
 private User user;
 
 
 public CustomUserDetails(User user) {
  super();
  this.user = user;
 }
 @Override
 public boolean isEnabled() {
  return true;
 }
 @Override
 public boolean isCredentialsNonExpired() {
  return true;
 }
 @Override
 public boolean isAccountNonLocked() {
  return true;
 }
 @Override
 public boolean isAccountNonExpired() {
  return true;
 }
 @Override
 public String getUsername() {
  return user.getLogin();
 }
 
 @Override
 public String getPassword() {
  return user.getPassword();
 }
 @Override
 public Collection getAuthorities() {
  List auths = new ArrayList();
  if(user.hasRole(UserRole.ADMIN.toString())){
   auths.add(new Authority("ROLE_ADMIN")); //Role here, like "admin"
   auths.add(new Authority("ROLE_USER"));
  }else {
   auths.add(new Authority("ROLE_USER")); //Role here, like "admin"
  }
  return auths;
 }
}
Next thing to do, is to define our GrantedAuthority objects. In fact, these objects will be used by Spring Security to know which role does user have(Remember in first step, we defined roles for users to access particular pages). So here are our very simple Authority implementation:
package com.raissi.security;

import org.springframework.security.core.GrantedAuthority;

public class Authority implements GrantedAuthority{
  private static final long serialVersionUID = 9170140593525051237L;

  private String authority;

  public Authority(String authority) {
    super();
    this.authority = authority;
  }

  @Override
  public String getAuthority() {
    return authority;
  }
  @Override
  public String toString() {
    return "Authority [authority=" + authority + "]";
  }

}

Until now, we are just doing Spring Security in the standard way. The only difference we will make, is how setting the security context will go. In second part of this series, we defined a ManagedBean (Named UserManagedBean) that handles user login via "login()" method. Now, in that method, just add following lines, before returning home page:
List auths = new ArrayList();
  if(user.hasRole(UserRole.ADMIN.toString())){
   auths.add(new Authority("ROLE_ADMIN")); //Role here, like "admin"
   auths.add(new Authority("ROLE_USER"));
  }else {
   auths.add(new Authority("ROLE_USER")); //Role here, like "admin"
  }
  
  Authentication authentication =  new UsernamePasswordAuthenticationToken(new CustomUserDetails(user), null, auths);
  SecurityContextHolder.getContext().setAuthentication(authentication);  
Et voilà! Now if you start server, and try to access any page except the login page, you will be redirected to login page.

4) Spring security custom access denied page

For now, if you login as simple user with "ROLE_USER" to the application, and then try to access an admin page, you will get an ugly Tomcat 403 page, which mentions that you are not authorized to see this page. A good point would be to customize this page. Remember from part 2 in this article that we used a "access-denied-handler" balise in our config. Well, this refers to a custom bean that we are defining to handle non authorized access: 
package com.raissi.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

/**
 * Custom Access denied handler, called in the spring-security config
 *
 */
@Component("customAccessDeniedHandler")
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
 
 public CustomAccessDeniedHandler() {
 }
 
 @Override
 public void handle(HttpServletRequest request, HttpServletResponse response,
      AccessDeniedException accessDeniedException) throws IOException, ServletException {
    response.sendRedirect(request.getContextPath()+"/accessdenied");
    request.getSession().setAttribute("message",
  "You do not have permission to access this page!");
 
 }
 
}
As you can see, here we are just implementing the AccessDeniedHandler interface which defines a single method. This method will be called by Spring Security every time a non authorized user is trying to access some forbidden places.
In this method, we are redirecting user to a path named "accessdenied" under root path. So using Prettyfaces we will define this path. If you are not using Prettyfaces, you are free to define the path you want to use.

    
    

For the accessdenied.xhtml page, it will just contain a simple message indicating that user is not authorized to see this content. You can use the message we set in session attribute "message".

5) Spring Security Remember-Me with JSF 2

Almost every login based application in the world offers to user the possibility to stay logged in the system when connecting from the same machine and same browser. This is as you may guessed, the "Remember-Me' feature, and happily it's supported by Spring Security. 
To do this, we will use a DB (yes, a DB, for the first time in these series) to store data related to user being logged in and his remember me cookie.
Please follow next part to continue with remember me implementation.