Saturday, March 15, 2014

Using Apache Shiro for JSF 2.0 Web Application Session Management


  • Configure Shiro filter as the first filter in web.xml:
        
  
  webapp.CdiEnvironmentLoaderListener
 
 
  ShiroFilter
  org.apache.shiro.web.servlet.ShiroFilter
 

 
  ShiroFilter
  /*
  REQUEST
  FORWARD
  INCLUDE
  ERROR
 
public class CustomRealm extends AuthenticatingRealm {

  private CredentialsMatcher credentialsMatcher;

  public String getName() {
    return "myRealm";
  }

  public boolean supports(AuthenticationToken token) {
    return true;
  }

  public CredentialsMatcher getCredentialsMatcher() {
    return credentialsMatcher;
  }

  public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
    this.credentialsMatcher = credentialsMatcher;
  }

  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    // we can safely cast to a UsernamePasswordToken here, because this class
    // 'supports' UsernamePasswordToken
    // objects. See the Realm.supports() method if your application will use a
    // different type of token.
    UsernamePasswordToken upToken = (UsernamePasswordToken) token;
    return new SimpleAuthenticationInfo(upToken.getUsername(), upToken.getPassword(), getName());
  }
}
public class CustomAuthenticator extends AbstractAuthenticator {
  @Override
  protected AuthenticationInfo doAuthenticate(AuthenticationToken token) throws AuthenticationException {
    // perform custom authentication - lookup DB or invoke session EJB for auth
  }
}
import java.io.IOException;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.web.filter.authc.UserFilter;

public class FacesAjaxAwareUserFilter extends UserFilter {

    private static final String FACES_REDIRECT_XML = ""
            + "";

    @Override
    protected void redirectToLogin(ServletRequest req, ServletResponse res) throws IOException {
        HttpServletRequest request = (HttpServletRequest) req;

        if ("partial/ajax".equals(request.getHeader("Faces-Request"))) {
            res.setContentType("text/xml");
            res.setCharacterEncoding("UTF-8");
            res.getWriter().printf(FACES_REDIRECT_XML, request.getContextPath() + getLoginUrl());
        }
        else {
            super.redirectToLogin(req, res);
        }
    }

}
public class CdiEnvironmentLoaderListener extends EnvironmentLoaderListener {

  CustomRealm customRealm = null;

  @Override
  protected WebEnvironment createEnvironment(ServletContext context) {
    WebEnvironment environment = super.createEnvironment(context);
    customRealm = new CustomRealm();

    RealmSecurityManager rsm = (RealmSecurityManager) environment.getSecurityManager();

    /*-HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
    matcher.setHashAlgorithmName(Sha512Hash.ALGORITHM_NAME);*/
    AllowAllCredentialsMatcher matcher = new AllowAllCredentialsMatcher();
    
    customRealm.setCredentialsMatcher(matcher);

    rsm.setRealm(customRealm);

    ((DefaultWebEnvironment) environment).setSecurityManager(rsm);

    return environment;
  }
}
@Named
@SessionScoped
public class LoginBean implements Serializable {
 private String username;
 private String password;
private static Factory factory = null;

  @PostConstruct
  public void init() {
    if (factory == null) {
      factory = new IniSecurityManagerFactory("classpath:shiro.ini");
      SecurityManager securityManager = factory.getInstance();
      SecurityUtils.setSecurityManager(securityManager);
    }
  }

 public String login() {
  UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    token.setRememberMe(true);

    // Submit the principals and credentials
    Subject currentUser = SecurityUtils.getSubject();

   try {
     FacesContext fc = FacesContext.getCurrentInstance();
      ExternalContext externalContext = fc.getExternalContext();
      HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
      /*
       * Enable the session creation only during login.
       */
      request.setAttribute(DefaultSubjectContext.SESSION_CREATION_ENABLED, Boolean.TRUE);

      currentUser.login(token);
      return "SUCCESS";
    } catch (AuthenticationException | IOException e) {
      return "FAILED";
    }
   }

  public void logout() {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
    try {

      // Invalidate HTTP session.
      Subject currentUser = SecurityUtils.getSubject();
      if (currentUser != null) {
          try {
          currentUser.logout();
        } catch (Exception e) {
          
        }
        externalContext.invalidateSession();
      }
    } catch (Exception ex) {
      
    } finally {
      try {
        externalContext.redirect("/login.xhtml");
      } catch (IOException e) {
        
      }
    }
  }
 }
}

Login

Lastly, the shiro.ini:
[main]
user = webapp.FacesAjaxAwareUserFilter
user.loginUrl = /login.xhtml

# Configure The EhCacheManager
cacheManager = org.apache.shiro.cache.ehcache.EhCacheManager
cacheManager.cacheManagerConfigFile = classpath:ehcache.xml

# Configure the above CacheManager on Shiro's SecurityManager
# to use it for all of Shiro's caching needs:
securityManager.cacheManager = $cacheManager

# Auth
myRealm = webapp.CustomRealm
myRealmCredentialsMatcher = org.apache.shiro.authc.credential.AllowAllCredentialsMatcher
myRealm.credentialsMatcher = $myRealmCredentialsMatcher
securityManager.realms = $myRealm

authenticator = webapp.CustomAuthenticator
securityManager.authenticator = $authenticator

#Remember Me
rememberMe = org.apache.shiro.web.mgt.CookieRememberMeManager
securityManager.rememberMeManager = $rememberMe

[urls]
/ = user
/login.xhtml = user

/javax.faces.resource/** = noSessionCreation, anon
/images/** = noSessionCreation, anon
/js/**= noSessionCreation, anon
/css/** = noSessionCreation, anon
/** =  user

No comments: