Há algum tempo, dois de meus clientes solicitaram segurança customizada para aplicativos da web. Em um novo projeto, o cliente era principalmente uma loja do IBM i e queria que os usuários pudessem acessar o aplicativo por meio de logon no IBM i. O outro cliente queria um aprimoramento para um aplicativo existente no qual determinados usuários seriam validados a partir de um banco de dados seguro, enquanto usuários "padrão" seriam validados por meio de entradas existentes do protocolo Lightweight Directory Access Protocol (LDAP).
Ambos eram aplicativos de produtividade corporativa— em vez de páginas da web voltadas ao público — executados localmente ou em uma rede privada virtual. Em geral, s segurança gerenciada por contêiner funcionou bem nessas circunstâncias, liberando os aplicativos do fardo de filtros ou de outros códigos para realizar a verificação das credenciais em cada página. O problema com os novos requisitos é que a segurança gerenciada por contêiner é normalmente conduzida por domínios — mecanismos de origem de dados do usuário fornecidos por um contêiner como Apache Tomcat, IBM WebSphere ou GlassFish. Domínios e implementações de domínio variam e tendem ser específicos a um determinado contêiner. Esses aplicativos seriam executados no GlassFish, que não define domínios para acesso por logon do IBM i ou para diversos métodos de autenticação e autorização em um único domínio.
Minha meta era poder autenticar e autorizar usuários de uma maneira customizada a princípio e, em seguida, entregar as tarefas de verificação de segurança à segurança gerenciada por contêiner. Neste artigo, apresentarei minha jornada pelo Java Authentication and Authorization Service (JAAS) e JSR 196 até uma solução usando o AuthenticRoast, um projeto licenciado sob o GNU Lesser General Public License. O artigo inclui um WAR demo executado sob o GlassFish (consulte Download). Primeiro, apresentarei uma breve revisão da segurança gerenciada por contêiner, com um foco na autenticação baseada em formulário.
Conceitos da segurança gerenciada por contêiner
Segurança gerenciada por contêiner é o termo comum para os recursos de segurança declarativa descritos na especificação do servlet Java (consulte Recursos). Quando os recursos de segurança gerenciada por contêiner se adéquam às necessidades de um aplicativo, eles liberam o programador de uma grande quantidade de codificação maçante e com tendência a erros. Eles também fornecem consistência para autenticação e autorização em diversos aplicativos.
A ideia por trás da segurança gerenciada por contêiner é que os usuários e funções são definidos fora do aplicativo e um método padrão para adquirir credenciais é fornecido. As páginas da web que deve ser protegidas também são definidas externamente e podem ser acessadas somente por usuários ou funções autorizadas. A segurança gerenciada por contêiner controla automaticamente o acesso usando funções declaradas, mapeadas para páginas ou grupos de páginas.
A especificação de servlet define quatro tipos de autenticação:
- Autenticação básica HTTP
- Autenticação de compilação HTTP
- Autenticação de cliente HTTPS
- Autenticação baseada em formulário
A especificação também inclui uma forte recomendação para que os servidores de aplicativo implementem o Servlet Container Profile do Java Authentication SPI for Containers, conforme especificado no JSR 196 (consulte Recursos).
Assim que um usuário é autenticado, é possível usar os seguintes métodos HttpServletRequest para obter um controle com mais baixa granularidade de elementos de página específicos:
-
getRemoteUser() -
getUserPrincipal() -
isUserInRole(java.lang.String role)
A segurança gerenciada por contêiner padrão exige a configuração de restrições de segurança, funções de segurança, login e, como opção, mapeamento de função. A configuração de login define e descreve um entre os quatro tipos de autenticação listados acima. Mais adiante, você verá que o AuthenticRoast dispensa a configuração de login, em vez de depender da classe usada como um
Autenticador para invocar o tipo de autenticação apropriado.
Embora as versões mais recentes da especificação Servlet apresentem anotações e outros métodos programáticos para definir restrições de segurança, eu uso declarações no arquivo web.xml— pois são familiares, estão apenas em um local e são acessíveis aos implementadores e administradores de aplicativo. Para obter elementos adicionais, subelementos e detalhes não usados neste artigo, consulte Recursos.
O elemento <security-constraint> do arquivo web.xml declara as páginas que serão protegidas e as funções que recebem permissão de acesso a essas páginas. A Listagem 1 mostra um exemplo:
Listagem 1.
<security-constraint> , elemento
<security-constraint>
<web-resource-collection>
<web-resource-name>Protected Pages</web-resource-name>
<url-pattern>/protected/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>boss</role-name>
<role-name>mgr</role-name>
<role-name>user</role-name>
</auth-constraint>
</security-constraint>
|
O subelemento <web-resource-collection> nomeia a coleção e define um ou mais elementos <url-pattern> a serem protegidos. No exemplo da Listagem 1, a coleção é chamada Protected Pages. Todas as páginas no diretório protegido estão sujeitas ao subelemento <auth-constraint> , que indica os nomes de função autorizados. No código demonstração, essas funções são boss, mgr e user. Observe que todos os tipos de método HTTP — GET, POST e assim por diante — também estão sujeitos à restrição por padrão; é possível especificar a inclusão ou omissão de tipos específicos, se for necessário.
Os elementos <security-role> n a Listagem 2 fornecem definições de função para as funções listadas na Listagem 1, no subelemento <auth-constraint> :
Listagem 2. elementos <security-role>
<security-role>
<description>boss</description>
<role-name>boss</role-name>
</security-role>
<security-role>
<description>mgr</description>
<role-name>mgr</role-name>
</security-role>
<security-role>
<description>user</description>
<role-name>user</role-name>
</security-role>
|
Autenticação baseada em formulário
A autenticação baseada em formulário permite que você customize a interface com o usuário para a segurança gerenciada por contêiner. Como mostra a Listagem 3, o elemento <form /> precisa usar o método POST e uma ação de j_security_check. O formulário também precisa incluir elementos <input /> chamados j_username e j_password para a autenticação do ID do usuário e da senha.
Listagem 3. Requisitos mínimos de página da web para autenticação baseada em formulário
<form method="POST" action="j_security_check"> <input type="text" name="j_username"> <input type="password" name="j_password"> </form> |
Assim que sua página da web atender aos requisitos mínimos, você poderá adicionar o que for necessário para corresponder às normas de seu aplicativo. A Figura 1, por exemplo, mostra a página de login do código de demonstração. Ela inclui um cabeçalho com uma imagem, rótulos e instruções.
Figura 1.
dwAuthenticRoastDemo , página de login
O logout é normalmente realizado invalidando a sessão. Também é possível usar o método HttpServletRequest logout() no lugar ou além da invalidação, a fim de redefinir a identidade do responsável pela chamada.
A segurança gerenciada por contêiner padrão também exige uma entrada <login-config /> em web.xml, como mostra a Listagem 4:
Listagem 4. Exemplo de entrada do web.xml,
<login-config />
<login-config>
<auth-method>FORM</auth-method>
<realm-name>SomeRealmName</realm-name>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/login-error.jsp</form-error-page>
</form-login-config>
</login-config>
|
A entrada <login-config /> define o método de autenticação e o domínio. Como esse exemplo usa o método baseado em formulário, um elemento <form-login-config /> é necessário para definir as páginas usadas para login e erros.
Se os tipos de domínio suportados pelo servidor de aplicativos atenderem às suas necessidades, essa configuração funcionará perfeitamente. Caso contrário, a dependência da segurança gerenciada por contêiner padrão de domínios definidos se tornará um problema real. Foi esse impasse que deu início à minha procura por uma solução.
JAAS é uma API de nível inferior — integrada ao Java SE na versão 1.4 — para desenvolvimento de módulos de autenticação e autorização (consulte Recursos). JAAS foi a primeira solução que pensei para meus projetos; ele é flexível, permite módulos conectáveis e é uma parte padrão do Java SE.
Infelizmente, JAAS foi especificado antes de os mecanismos de segurança JEE existirem. O JAAS funciona bem para aplicativos independentes, mas não tem integração ou ligação padrão com servlets ou aplicativos JEE. Ele também baseia as funções de autoridade na classe javax.security.auth.Subject , que contém diversos principais, enquanto a segurança gerenciada por contêiner funciona com a classe java.security.Principal .
Na ausência de uma norma de integração, qualquer suporte para JAAS pelo servlet ou por fornecedores de contêiner JEE é proprietário. De alguma forma, os domínios têm o mesmo problema, mas eu decidi tentar o JAAS proprietário, ou meu próprio hack de JAAS, somente se não houvesse disponível uma solução melhor e mais padronizada.
O Java Authentication Service Provider Interface for Containers às vezes é chamado de JASPI ou JASPIC, mas é mais conhecido como JSR 196. Seu objetivo é especificar uma norma que permita a configuração de mecanismos de autenticação customizados que um contêiner compatível usará para aplicar a segurança declarativa. O JSR define os perfis para diversos contextos, incluindo servlets.
Se eu tivesse ouvido falar em JSR 196 lá atrás em 2004, mas ele ficou esperando no Java Community Process até o rascunho final em 2007. Ao mesmo tempo, GlassFish, como a implementação de referência JEE, suportou a parte do Servlet Container Profile do JSR desde, pelo menos, a versão 2.1. JSR 196 está incluído na especificação JEE 6 (consulte Recursos).
Além do jargão de segurança normal e do conceito claramente relevante do server authentication module (SAM), o JSR 196 usa muitas frases às vezes tediosas como message authentication modules, message processing runtimes e Java Authorization Contract for Containers (JACC ou Java ACC). Mesmo assim, eu estava disposto a passar pelo processo até que cheguei a dois problemas principais:
- Cada SAM precisa ser configurado e integrado efetivamente ao contêiner, em vez de servir apenas para um aplicativo.
- Seguir todas as etapas fornecidas nos exemplos — como "Adding Authentication Mechanisms to the GlassFish Servlet Container" pelo líder técnico do JSR 196 em Enterprise Tech Tip (consulte Recursos) — é muito trabalho.
Apesar de eu querer usar os métodos JSR 196, se possível, eu precisava de uma maneira de minimizar o impacto e o esforço envolvido na implementação de SAMs. Após outra rodada de muita pesquisa, descobri um projeto de software livre cujo objetivo era exatamente esse.
O projeto AuthenticRoast (consulte Recursos) está na versão 0.3.3 desde que este texto foi escrito, mas tem sido usado em produção há mais de três anos. Meus dois projetos utilizaram-no durante dois anos e um ano, respectivamente.
O AuthenticRoast é composto por três JARs primários com uma divisão de trabalho em duas partes:
-
AuthenticRoast-API-ver.jar e AuthenticRoast-Impl-ver.jar: a integração com o contêiner não pode ser evitada, mas esses JARs:
- Permitem a configuração única do contêiner.
- Agem como um intermediário entre o contêiner e os módulos de segurança de seu aplicativo (autenticadores).
-
AuthenticRoast-Extras-ver.jar: apesar do nome, você usará esse JAR na maioria dos aplicativos (a menos que goste de escrever códigos customizados), pois ele contém as seguintes classes abstratas para diversos tipos de autenticação, que podem ser usadas com a segurança gerenciada por contêiner:
-
BasicAuthenticator -
CompositeAuthenticator -
FormAuthenticator -
SSLClientAuthenticator -
TicketAuthenticator
-
No restante deste artigo e no código de demonstração, mostrarei o uso da classe FormAuthenticator com o GlassFish 3.1.1.
Há quatro áreas envolvidas no processo de desenvolvimento de autenticação do AuthenticRoast:
- Configuração do contêiner
- Configuração do aplicativo
- Registro do autenticador
- Código do autenticador
Eu as apresentarei em ordem.
A configuração para o contêiner (GlassFish nesse caso) é, felizmente, uma tarefa única com o AuthenticRoast. A página InstallationForGlassfish no wiki do projeto (consulte Recursos) é bem direta, e — como não deverá haver mudanças no futuro — eu o direciono até lá para as etapas de configuração. No entanto, essas direções são para o GlassFish 2.x, portanto, as Figuras 2, 3, 4 e 5 apontam as diferenças para o GlassFish 3.1.1. Antes de você iniciar o processo, certifique-se de que copiou o AuthenticRoast-API-ver.jar e o AuthenticRoast-Impl-ver.jar à pasta lib da instalação do GlassFish e de que reiniciou o servidor.
Como mostra a Figura 2, quando você clica em Message Security, é possível ver que a camada de autenticação HttpServlet já existe. Clique em HttpServlet.
Figura 2. Página Message Security Configurations do GlassFish 3.1.1
A página Edit Message Security Configuration exibida na Figura 3 é exibida:
Figura 3. Página Edit Message Security Configuration do GlassFish 3.1.1
Clique na guia Providers .
A Figura 4 mostra porque a camada de autenticação HttpServlet já existe: o próprio console administrativo do GlassFish agora usa um provedor para segurança. A imagem também mostra que eu já criei o provedor roast . Adicione-o clicando no botão New .
Figura 4. Página Provider Configurations do GlassFish 3.1.1
A Figura 5 mostra as entradas para o provedor roast . Elas são as mesmas listadas na página wiki do InstallationForGlassfish do AuthenticRoast. Deixe os campos de Response Policy, mais abaixo na página (não exibido na Figura 5), em branco, sem seleções.
Figura 5. Página Edit Provider Configuration do GlassFish 3.1.1
Lembre-se de salvar a configuração e reiniciar o servidor. Depois desse processo, é possível usar o AuthenticRoast em seus aplicativos.
O arquivo web.xml para um aplicativo que usa o AuthenticRoast, segue a norma para segurança gerenciada por contêiner, como mostra a Listagem 1 e a Listagem 2. Perceba uma exceção, no entanto: AuthenticRoast não exige ou usa o elemento <form-login-config /> da Listagem 4.
Como você verá na próxima seção, o AuthenticRoast exige que seu autenticador se registre com o contêiner, uma tarefa normalmente realizada por um listener de aplicativo. A Listagem 5 mostra a entrada para definir o listener do aplicativo de demonstração:
Listagem 5. Entrada de definição do listener do aplicativo no web.xml
<listener> <listener-class>com.dw.ARDAppInit</listener-class> </listener> |
Na última parte da configuração necessária, o aplicativo informa ao contêiner que usa um provedor de segurança httpservlet . O atributo httpservlet-security-provider do elemento <glassfish-web-app /> no arquivo glassfish-web.xml específico ao contêiner (chamado de sun-web.xml antes do GlassFish 3.x) executa essa tarefa com uma entrada (roast) correspondendo a um ID de provedor definido:
<glassfish-web-app httpservlet-security-provider="roast" error-url=""> |
O mapeamento de função é um recurso do JEE opcional, mas útil. Ele permite que os implementadores mapeiem grupos ou funções diferentes para aquelas usadas em um aplicativo. Infelizmente, as entradas e locais são específicos do contêiner; o WebSphere, por exemplo, usa ibm-application-bnd.xml, enquanto o GlassFish novamente usa o glassfish-web.xml. A Listagem 6 mostra entradas para mapear elementos <role-name /> usados no aplicativo para implementação específica de site <group-name />:
Listagem 6. Mapeamento de função no
glassfish-web.xml
<security-role-mapping>
<role-name>boss</role-name>
<group-name>ARDBoss</group-name>
</security-role-mapping>
<security-role-mapping>
<role-name>mgr</role-name>
<group-name>ARDMgr</group-name>
</security-role-mapping>
<security-role-mapping>
<role-name>user</role-name>
<group-name>ARDUser</group-name>
</security-role-mapping>
|
Como resultado dessas entradas, quando o aplicativo de demonstração procura por boss, por exemplo, o contêiner se certifica de que a função ARDBoss se qualifica.
ARDAppInit é o listener de aplicativo do exemplo de demonstração definido na Listagem 5. Como você pode ver na Listagem 7, ele usa o método contextInitialized() para registrar uma instância da classe autenticadora no tempo de inicialização do aplicativo:
Listagem 7. Registro do autenticador do código de demonstração
package com.dw;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import name.aikesommer.authenticator.Registry;
...
public class ARDAppInit implements ServletContextListener
{
...
public void contextInitialized( ServletContextEvent sce )
{
ServletContext sc = null;
sc = sce.getServletContext();
// register AuthenticRoast authenticator
Registry.forContext( sc ).register( new ARDFormAuthenticator() );
System.out.println( "[ARDAppInit Executed]" );
} // end contextInitialized
} // end class ARDAppInit
|
Esse código é essencialmente o mesmo em todos os aplicativos; normalmente, as únicas mudanças são no nome do pacote e configuração de sua instância do autenticador.
Com base na discussão, configuração e instalação anterior, deve ser uma surpresa agradável que para ativar a autenticação baseada em formulário com seu próprio código customizado normalmente é necessário apenas:
- Estender a classe
FormAuthenticator - Substituir, no máximo, quatro — normalmente somente dois — dos métodos de
FormAuthenticator - Conhecer um pouco sobre a classe
SimplePrincipal
A classe FormAuthenticator (em AuthenticRoast-Extras-ver.jar) estende PluggableAuthenticator, que, junto com SimplePrincipal, reside no AuthenticRoast-API-ver.jar. O contêiner, via AuthenticRoast, faz as chamadas de método nas instâncias de classe.
Se você estiver disposto a usar os nomes login.jsp para sua página de login e login-error.jsp para sua página de erro de login, será necessário lidar com dois métodos FormAuthenticator . Caso contrário, substitua String getErrorPage() e String getLoginPage() para retornar aos seus nomes preferidos para essas páginas.
Os dois métodos que você sempre precisa substituir são:
-
boolean checkCredentials(AuthenticationManager manager, AuthenticationRequest request, String username, String password)O método
checkCredentials()é responsável por validar as credenciais passadas — ou seja, o nome de usuário e a senha. Retornatruese as credenciais forem aprovadas; caso contrário, retornafalse. -
SimplePrincipal loadPrincipal(AuthenticationManager manager, AuthenticationRequest request, String username)O método
loadPrincipal()deve ser chamado somente secheckCredentials()retornartruepara indicar um usuário validado. O método retorna umSimplePrincipal, que é, essencialmente, um nome de usuário e umSetde nomes de grupo. O construtor usa um nome de usuário e um arrayStringcontendo os nomes dos grupos ou funções associados a esse usuário. O contêiner procura os nomes de grupo para obter uma correspondência com as funções listadas no elemento<auth-constraint>de web.xml (consulte Listagem 1) ou mapeado (consulte Listagem 6). A Listagem 8 mostra um exemplo do carregamento de umSimplePrincipal:
Listagem 8. Carregando umSimplePrincipalusando um array estáticoString[] asBossGroups = { "ABCBoss", "ARDBoss", "MVBoss", "RSBoss" }; SimplePrincipal sp = new SimplePrincipal( "bossLady", asBossGroups );
Observe que, conforme foram enviados, esses dois métodos são stateless e não são relacionados, portanto, o acesso aos dados do usuário e do grupo frequentemente termina sendo repetido em cada método. É possível alterar isso com um código adicional, é claro, mas é algo do que você precisa estar ciente.
E é basicamente isso para a estrutura do código do autenticador. Acredito que você concordará que o projeto dificilmente será mais simplificado do que (normalmente) trabalhar com apenas dois métodos.
Sobre o aplicativo demonstração
O aplicativo demonstração associado a esse artigo (consulte Download) fornece um exemplo concreto do uso do AuthenticRoast. Além das páginas usadas para validação de login, há apenas uma página no diretório protegido, que mostra somente os resultados das chamadas para getRemoteUser(), getUserPrincipal() e isUserInRole(java.lang.String role), como mostra a Figura 6:
Figura 6. Página de resultados do login
Três conjuntos de usuário/senha são aceitos pelo método checkCredentials() :
-
Usuário
stevie, senhauser1 -
Usuário
ray, senhamgr1 -
Usuário
vaughan, senhaboss1
Se as credenciais passadas forem validadas, o nome de usuário e o array de nomes de grupo associados retornarão do método loadPrincipal() em um objeto SimplePrincipal :
-
asUserGroupspara o usuáriostevie -
asMgrGroupspara o usuárioray -
asBossGroupspara o usuáriovaughan
A Listagem 9 mostra o conteúdo do array:
Listagem 9. Arrays de grupo usados por
ARDFormAuthenticator
private static String[] asBossGroups =
{ "ABCBoss", "ARDBoss", "MVBoss", "RSBoss" };
private static String[] asMgrGroups =
{ "ABCMgr", "ARDMgr", "MVMgr", "RSMgr" };
private static String[] asUserGroups =
{ "ABCUser", "ARDUser", "MVUser", "RSUser" };
|
Quase todo o código e configuração restantes, incluindo o mapeamento de função, são exibidos nas Listagens 1 a 7. A exceção, conforme explicado acima, é que o elemento <login-config /> exibido na Listagem 4 não é necessário e não é usado no AuthenticRoast.
Os módulos customizados para segurança gerenciada por contêiner são necessários apenas ocasionalmente, mas a necessidade pode ser crítica nesses momentos. O JSR 196 alcançou seu objetivo de integração para segurança declarativa customizada, mas com o preço de, às vezes, ser trabalhoso e complicado. O projeto AuthenticRoast simplifica esse processo.
Lembre-se de que a principal vantagem do AuthenticRoast — você no controle completo — também é sua grande desvantagem: você tem que fazer tudo. Por exemplo, quando eu usei o domínio LDAP de GlassFish anteriormente para acessar o Microsoft Active Directory a fim de obter dados de senha e de grupo, obtive êxito depois de preparar o encanto para a cadeia de caractere de procura. Quando usei o AuthenticRoast para adicionar outro processo de validação, se as credenciais LDAP falhassem, eu tinha que escrever o código de acesso LDAP por conta própria.
Essa desvantagem, no entanto, também se aplica ao trabalho direto com o JSR 196, ou praticamente com qualquer outra coisa, a fim de criar um módulo de segurança customizado. No final, o AuthenticRoast é uma solução de software livre que obtém êxito em minimizar o impacto de configuração em contêineres JEE e reduz bastante o esforço de codificação para seus requisitos de segurança customizados.
| Descrição | Nome | Tamanho | Método de download |
|---|---|---|---|
| Demonstration WAR for this article1 | j-authenticroast.zip | 86KB | HTTP |
Informações sobre métodos de download
Nota
- Os códigos-fonte Java estão incluídos no arquivo WAR. Para obter instruções sobre como usar o arquivo WAR, consulte o arquivo readme.txt incluído com o download.
Aprender
-
AuthenticRoast: a página inicial do projeto apresenta links para o wiki e para downloads.
-
InstallationForGlassfish: página wiki do AuthenticRoast com etapas de configuração para o GlassFish.
-
JSR 315: Especificação Java Servlet 3.0: o JSR 315 inclui as normas para segurança gerenciada por contêiner.
-
"Java authorization internals" (Abhijit Belapurkar, developerWorks, maio de 2004): este artigo apresenta uma introdução detalhada ao JAAS.
-
Java Authentication Service Provider Interface for Containers: confira o JSR 196.
-
Java EE 6 Technologies: saiba mais sobre as tecnologias que compõem a plataforma Java EE 6.
-
Securing Web Applications: um guia para segurança gerenciada por contêiner do The Java EE 6 Tutorial.
-
Adding Authentication Mechanisms to the GlassFish Servlet Container: um exemplo de como trabalhar diretamente com os mecanismos JSR 196 fornecido pelo líder técnico da especificação.
-
WebSphere 8 support for JASPI: documentação que descreve o suporte e a configuração no WebSphere para JSR 196.
-
WebSphere 8: Implementing a custom authentication provider using JASPI: documentação do WebSphere que discute a configuração e mapeamento para provedores de autenticação.
-
Navegue na
livraria de tecnologia para ver livros sobre este e outros tópicos técnicos.
-
Zona de tecnologia Java do developerWorks: Encontre centenas de artigos sobre quase todos os aspectos da programação Java.
Obter produtos e tecnologias
-
AuthenticRoast: faça o download do AuthenticRoast.
-
GlassFish: encontre informações e downloads para o servidor de aplicativos GlassFish.
-
WebSphere Application Server for Developers: faça o download de uma oferta gratuita do WebSphere idêntica ao ambiente de tempo de execução de produção no qual seu aplicativo será executado.
-
Avalie produtos
IBM da maneira que for melhor para você: faça download da versão de teste de um produto, avalie um produto on-line, use-o em um ambiente de nuvem ou passe algumas horas na SOA Sandbox aprendendo como implementar Arquitetura Orientada a Serviços de forma eficiente.
Discutir
- Participe da comunidade do developerWorks. Entre em contato com outros usuários do developerWorks, enquanto explora os blogs, fóruns, grupos e wikis orientados ao desenvolvedor.

Joe Sam Shirah é diretor e desenvolvedor na conceptGO. Enquanto tentava manter os clientes felizes, ele criou diversos tutoriais para o developerWorks e para o site Oracle Java Developer e é ganhador do prêmio Java Community Award. Também foi moderador do fórum de filtro Java do developerWorks e gerenciou FAQs de JDBC, I18N e Java400 do jGuru.