Apache Shiro on AppEngine

Apache Shiro

Apache Shiro is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management.

  • Authentication: Support logins across one or more pluggable data sources (LDAP, JDBC, Kerberos, ActiveDirectory, etc).
  • Authorization: Perform access control based on roles or fine-grained permissions, also using pluggable data sources.

Apache Shiro Authentication on AppEngine

We use AppEngine’s User Service to authenticate users. To integrate Apache Shiro with AppEngine User Service, we implement our own AuthenticatingFilter to control the login process

/**
 * An {@link AuthenticatingFilter} which uses UserService
 * to control the authentication process
 */
public class GaeAuthenticatingFilter extends AuthenticatingFilter{
  public GaeAuthenticatingFilter() {
    setLoginUrl(null);
  }

  @Override
  protected AuthenticationToken createToken(
      ServletRequest request, ServletResponse response) {
    UserService userService = UserServiceFactory.getUserService();
    User user = userService.getCurrentUser();
    return createToken(user.getUserId(), null, request, response);
  }

  @Override
  protected boolean isRememberMe(ServletRequest request) {
    return true;
  }

  @Override
  protected boolean onAccessDenied(ServletRequest request,
                                   ServletResponse response) throws Exception{
    UserService userService = UserServiceFactory.getUserService();
    User user = userService.getCurrentUser();

    if (user == null){
      saveRequest(request);

      String requestURI = WebUtils.getRequestUri(WebUtils.toHttp(request));

      String loginUrl = this.getLoginUrl();
      if (loginUrl == null){
        loginUrl = userService.createLoginURL(requestURI);
        WebUtils.issueRedirect(request, response, loginUrl);
      }else{
        request.setAttribute("requestURI", requestURI);
        request.getRequestDispatcher(loginUrl).forward(request, response);
      }

      return false;
    }else{
      // Perform the internal login process
      boolean result = executeLogin(request, response);
      return result;
    }
  }
}

Apache Shiro Security Realms

Apache Shiro provides several standard Realms (JndiLdapRealm, JdbcRealm, etc) which translates this application-specific data into Shiro internal format.

AppEngine Authenticating Realm

For AppEngine our realm as below:

/**
 * A {@link Realm} which uses {@link UserService} to authenicate users
 */
public class GaeAuthenticatingRealm extends AuthorizingRealm{
  public GaeAuthenticatingRealm() {
  }

  @Override
  protected void onInit() {
    // We use UserService to authenticate users,
    // thus dont have any credentials
    setCredentialsMatcher(new AllowAllCredentialsMatcher());
  }

  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
    UserService userService = UserServiceFactory.getUserService();
    User user = userService.getCurrentUser();
    if (user == null){
      throw new AccountException("Not authenticated.");
    }

    return new SimpleAuthenticationInfo(user, null, getName());
  }

  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    // We perform Authentication only, thus return null
    return null;
  }
}

Put it all in web.xml

<web-app>
  ...
  <filter>
    <filter-name>ShiroFilter</filter-name>
    <filter-class>
      org.apache.shiro.web.servlet.IniShiroFilter
    </filter-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>
        [main]
        authc = com.gdevelop.security.GaeAuthenticatingFilter
        # Optional, used for OpenId provider selection form.
        # authc.loginUrl=/WEB-INF/openIdLogin.jsp

        authcRealm = com.gdevelop.security.GaeAuthenticatingRealm

        [urls]
        /account/* = authc
      </param-value>
    </init-param>
  </filter>

  ...
  <filter-mapping>
    <filter-name>ShiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

Build-in Authorizing Realm

For demo purpose, we use the build-in IniRealm. Just configure it in web.xml file.

<web-app>
  ...
  <filter>
    <filter-name>ShiroFilter</filter-name>
    <filter-class>
      org.apache.shiro.web.servlet.IniShiroFilter
    </filter-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>
        [main]
        authc = com.gdevelop.security.GaeAuthenticatingFilter
        # Optional, used for OpenId provider selection form.
        # authc.loginUrl=/WEB-INF/openIdLogin.jsp

        authcRealm = com.gdevelop.security.GaeAuthenticatingRealm

        [users]
        # format: username = password, role1, role2, ..., roleN
        test@example.com  = ,admin
        test1@example.com = ,president
        test2@example.com = ,darklord,schwartz
        test3@example.com = ,goodguy,schwartz

        [urls]
        /account/* = authc
      </param-value>
    </init-param>
  </filter>

  ...
  <filter-mapping>
    <filter-name>ShiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

Custom Authorizing Realm

You can implement a custom Authorizing Realm. The JdbcRealm is a good starting point to do so. See also the DatastoreRealm of the http://code.google.com/p/shiro-gae project.

<web-app>
  ...
  <filter>
    <filter-name>ShiroFilter</filter-name>
    <filter-class>
      org.apache.shiro.web.servlet.IniShiroFilter
    </filter-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>
        [main]
        authc = com.gdevelop.security.GaeAuthenticatingFilter
        # Optional, used for OpenId provider selection form.
        # authc.loginUrl=/WEB-INF/openIdLogin.jsp

        authcRealm = com.gdevelop.security.GaeAuthenticatingRealm

        authzRealm = <full-qualified class name>

        [urls]
        /account/* = authc
      </param-value>
    </init-param>
  </filter>

  ...
  <filter-mapping>
    <filter-name>ShiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

Secure our application

Now we are ready to perform authorization. For example, in your servlet class, we can check user permission, role as below:

Subject currentUser = SecurityUtils.getSubject();
if (currentUser.hasRole("manager")){
  ...
}
if (currentUser.isPermitted("reports:generate")){
  ..
}

Resources

Sample code base on the Apache Shiro Quickstart

Google App Engine
Apache Shiro

Posted in AppEngine | Tagged , , , | 2 Comments

Visual GUI Designer in web browser

I spent some my free time to develop a Visual GUI Designer application using GWT and GEF.

Visual GUI Designer

Currently, we have almost all basic editing features:

  • Tools Palette
  • Property Sheet
  • Copy, Cut, Paste, Undo and Redo
  • Keyboard shortcuts

The demo is running at demo.gdevelop.com

The application is currently in early stage, comments and suggestions are appreciated.

Posted in GWT | Tagged , , | Leave a comment