Java tutorial
/* Copyright (C) 2007 Flix Garca Borrego (borrego at gmail.com) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ package org.viafirma.cliente.openid; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.openid4java.OpenIDException; import org.openid4java.consumer.ConsumerException; import org.openid4java.consumer.ConsumerManager; import org.openid4java.consumer.InMemoryNonceVerifier; import org.openid4java.consumer.VerificationResult; import org.openid4java.discovery.DiscoveryException; import org.openid4java.discovery.DiscoveryInformation; import org.openid4java.discovery.Identifier; import org.openid4java.message.AuthRequest; import org.openid4java.message.AuthSuccess; import org.openid4java.message.MessageException; import org.openid4java.message.ParameterList; import org.openid4java.message.ax.AxMessage; import org.openid4java.message.ax.FetchRequest; import org.openid4java.message.ax.FetchResponse; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.viafirma.cliente.exception.CodigoError; import org.viafirma.cliente.exception.InternalException; import org.viafirma.cliente.util.TypeRequest; /** * Contiene mtodos de utilidad. Nota: Este Cdigo esta basado en el cdigo del * proyecto Openid4Java: * http://openid4java.googlecode.com/svn/trunk/src/net/openid/consumer/SampleConsumer * * @author Felix Garcia Borrego (borrego at gmail.com) */ public class OpenIdHandler { /** * Manejador del protocolo OpenId */ ConsumerManager manager; private Log log = LogFactory.getLog(OpenIdHandler.class); /** * Inicia el proceso de autenticacin utilizando el protocolo OpenID. * * @param urlId * @param requestProperties * @param request * @param response * @param uri, * path relativo donde se recupera el resultado de la * autenticacin. * @throws InternalException */ public void autenticar(String urlId, Set<String> requestProperties, HttpServletRequest request, HttpServletResponse response) throws InternalException { String urlRetorno = generarUrlRetorno(request, null); // Iniciamos el proceso de autenticacin OpenId iniciarProtocoloOpenId(urlId, requestProperties, request, response, urlRetorno); } /** * Inicia el proceso de firmar utilizando el protocolo OpenID. * * @param urlId * @param requestProperties * @param request * @param response * @param uriRetorno * Url de retorno, cuando el proceso termine. * @throws InternalException */ public void firmar(String urlId, Set<String> requestProperties, HttpServletRequest request, HttpServletResponse response, String uriRetorno) throws InternalException { String urlRetorno = generarUrlRetorno(request, uriRetorno); iniciarProtocoloOpenId(urlId, requestProperties, request, response, urlRetorno); } /** * Inicia el proceso de autenticacin utilizando el protocolo OpenID. * * @param urlId * url OpenId( utilizada) * @param requestProperties * Conjunto de propiedades que se solicitaran * @param request * @param response * @param urlReturn * url de retorno cuando el proceso termine. * @throws InternalException * No se puede iniciar la autenticacin */ @SuppressWarnings("unchecked") public void iniciarProtocoloOpenId(String urlId, Set<String> requestProperties, HttpServletRequest request, HttpServletResponse response, String returUrl) throws InternalException { String userSuppliedString = urlId; try { // descubrimos el punto de entrada para autenticar al usuario // esto se conecta a la url indicada, parsea el html retornado // en busca de la cabecera estandar OpenId // ej:<link rel="openid.server" // href="http://localhost:8080/viafirma/conectorOpenID" /> List discoveries = manager.discover(userSuppliedString); // invoca a la url descubierta para asociarla a esta peticin // para realizar la autenticacin DiscoveryInformation discovered = manager.associate(discoveries); // almacena el resultado en sessin para recuperarlo en la fase de // validacin request.getSession().setAttribute(OPEN_ID_DISC, discovered); // Desactivamos la validacin RP. ( Necesaria para que funcione en // entornos no OpenID2 ) manager.getRealmVerifier().setEnforceRpId(false); // obtengo el resultado de la autenticacin, esto redireccionaria el // navegador al usuario. // obtengo AuthRequest message y lo envia OpenID provider // indica la url de retorno a utilizar cuando el proceso de // autenticacin termine AuthRequest authReq = manager.authenticate(discovered, returUrl); // Aadimos los datos que deseamos solicitar( ej: email, nombre, // etc) addRequestRequired(requestProperties, authReq); if (!discovered.isVersion2()) { // Opcion 1: redireccionamos (http-redirect) al proveedor de // servicios OpenId // Es el nico mtodo soportado en OpenID 1.x limitado a ~2048 // bytes // Redireccin String url = authReq.getDestinationUrl(true); response.sendRedirect(url); } else { // Option 2: HTML FORM Redirection (Allows payloads >2048 bytes) // RequestDispatcher dispatcher = // getServletContext().getRequestDispatcher("formredirection.jsp"); // httpReq.setAttribute("prameterMap", // response.getParameterMap()); // httpReq.setAttribute("destinationUrl", // response.getDestinationUrl(false)); // dispatcher.forward(request, response); throw new UnsupportedOperationException( "Operacin no soportada.Option 2: HTML FORM Redirection (Allows payloads >2048 bytes)"); } } catch (DiscoveryException e) { log.warn( "El servidor de autenticacin no esta disponible en este momento. No se ha podido localizar un servicio OpenId en la url solicitada " + urlId + ". " + e.getMessage()); throw new InternalException(CodigoError.ERROR_AUTENTICACION_NO_DISPONIBLE, "Url Solicitada " + urlId, e); } catch (MessageException e) { log.warn("El mensaje contiene errores. " + e.getMessage()); throw new InternalException(CodigoError.ERROR_PROTOCOLO_AUTENTICACION, "Url Solicitada " + urlId, e); } catch (ConsumerException e) { log.warn("No se puede enviar la redireccin al usuario. " + e.getMessage()); throw new InternalException(CodigoError.ERROR_PROTOCOLO_AUTENTICACION_REDIRECCION, "Url Solicitada " + urlId, e); } catch (IOException e) { log.debug("No se puede enviar la redireccin al usuario. " + e.getMessage()); throw new InternalException(CodigoError.ERROR_PROTOCOLO_AUTENTICACION_REDIRECCION, "Url Solicitada " + urlId, e); } } /** * Procesa el resultado de la autenticacin para extraer los datos del * usuario. * * @param httpReq * @param httpRes * @throws InternalException * No se pueden recuperar los datos resultado de la * autenticacin. */ @SuppressWarnings("unchecked") public Map<String, String> processResponseAuthentication(HttpServletRequest httpReq, HttpServletResponse httpRes) throws InternalException { try { // Obtengo todos los parametros de la peticin ParameterList response = new ParameterList(httpReq.getParameterMap()); // recupero los parametros almacenados en sessin DiscoveryInformation discovered = (DiscoveryInformation) httpReq.getSession() .getAttribute(OPEN_ID_DISC); // Recuperamos la url de retorno esperada String urlRetorno = (String) httpReq.getSession().getAttribute(VARIABLE_URL_RETORNO_OPENID); log.debug("Url de retorno: " + urlRetorno); if (urlRetorno == null) { log.warn("No hay url de retorno. Probablemente problema con sesiones distintas en en navegador. " + urlRetorno); throw new org.viafirma.cliente.exception.InternalException( CodigoError.ERROR_PROTOCOLO_AUTENTICACION_REDIRECCION, "No hay url de retorno. Probablemente problema con sesiones distintas en en navegador. "); } // recupero la uri utilizada desde sessin. // extract the receiving URL from the HTTP request // para asegurarnos de que funcione correctamente detras de un // mod-proxy apache, modificamos la url del tipo: StringBuilder receivingURL = new StringBuilder(urlRetorno);// httpReq.getRequestURL(); String queryString = httpReq.getQueryString(); if (receivingURL.indexOf("?") != -1) { receivingURL.delete(receivingURL.indexOf("?"), receivingURL.length()); } if (queryString != null && queryString.length() > 0) { receivingURL.append("?").append(httpReq.getQueryString()); } // verify the response; ConsumerManager needs to be the same // (static) instance used to place the authentication request VerificationResult verification = manager.verify(receivingURL.toString(), response, discovered); // examine the verification result and extract the verified // identifier Identifier verified = verification.getVerifiedId(); if (verified != null) { AuthSuccess authSuccess = (AuthSuccess) verification.getAuthResponse(); if (authSuccess.hasExtension(AxMessage.OPENID_NS_AX)) { FetchResponse fetchResp = (FetchResponse) authSuccess.getExtension(AxMessage.OPENID_NS_AX); // retorno todos los atributos recueperados return fetchResp.getAttributes(); } else { // no tiene las extensiones requeridas. return Collections.EMPTY_MAP; } } else { log.warn("No se puede verificar la autenticacin. " + receivingURL); throw new org.viafirma.cliente.exception.InternalException( CodigoError.ERROR_AUTENTICACION_VERIFICACION, receivingURL.toString()); } } catch (OpenIDException e) { throw new org.viafirma.cliente.exception.InternalException(CodigoError.ERROR_AUTENTICACION_VERIFICACION, e.getMessage(), e); } } // *************************************** // Mtodos de utilidad // *************************************** /** * Aade una solicitud para cada propiedad solicitada. * * Este mtodo determina que propiedades seran solicitadas a viafirma. * * @param propiedadesSolicitadas * @param authReq * @throws MessageException */ public void addRequestRequired(Set<String> propiedadesSolicitadas, AuthRequest authReq) throws MessageException { // nueva solicitud FetchRequest fetch = FetchRequest.createFetchRequest(); // iteramos sobre los typos de datos disponibles for (TypeRequest tipo : TypeRequest.values()) { // si el tipo es solicitado if (propiedadesSolicitadas.contains(tipo.getAlias())) { fetch.addAttribute(tipo.getAlias(), tipo.getTypeUri(), true); log.debug("Aadida solicitud de " + tipo.getAlias()); } } // aado a la peticin authReq.addExtension(fetch); } /** * Indica si la peticin contiene datos de retorno OpenID * * @param request * @return */ public boolean isResponseAuthentication(HttpServletRequest request) { if (request.getParameter("openid.ext1.mode") != null) { // hay datos retornados por viafirma return true; } else { return false; } } /** * Indica si la peticin es un retorno de autenticacin cancelada por el * usuario. * * @param request * @return */ public boolean isResponseCancel(HttpServletRequest request) { if (request.getParameter("openid.rpsig") != null && !isResponseAuthentication(request)) { // hay datos retornados por viafirma return true; } else { return false; } } /** * Inicializa un manejador OpenId. * * @param urlAplicacin * url pblica de la aplicacin. * @param urlFirmaReturnOpenId * @throws ConsumerException */ OpenIdHandler(String urlAplicacion) throws ConsumerException { // creamos una instancia del manejador OpenId manager = new ConsumerManager(); // A diferencia de la versin por defecto, damos 5 minutos al usuario // para que complete la autenticacin. // Por defecto son 60 segundos. manager.setNonceVerifier(new InMemoryNonceVerifier(60 * 5)); this.urlAplicacion = urlAplicacion; } /** * Genera la url de retorno. Soluciona problemas causados por mod_proxys. * * @param uriRetorno Si no se indica se genera utilizando el uri actual. * @param request * @return */ private String generarUrlRetorno(HttpServletRequest request, String uriRetorno) { // Generamos la uri de la solicitud if (uriRetorno == null) { // antes de enviar a afirma cacheamos la url destino a la que se // dirigia el usuario para redireccioanrlo posteriormente uriRetorno = StringUtils.substringAfterLast(request.getRequestURI(), request.getContextPath()); // le aadimos los parametros si los tiene. uriRetorno += request.getQueryString() == null ? "" : ("?" + request.getQueryString()); } // si la url de retorno ha sido indicada en sessin, la utilizamos. String urlRetorno = urlAplicacion + uriRetorno; log.debug("Url de retorno: " + urlRetorno); // almacenamos en sessin la uri de retorno para recordarla al finalizar // la autenticacin request.getSession().setAttribute(VARIABLE_URL_RETORNO_OPENID, urlRetorno); return urlRetorno; } // *************************************** // Constantes relacionadas con OpenID // *************************************** // almacena el resultado en sessin para recuperarlo en la fase de // validacin public static String OPEN_ID_DISC = "openid-disc"; /** * Url que pblica desde la que es visible la aplicacin. */ private String urlAplicacion; /** * Url a la que retornar cuando el proceso de firma OpenId termine. El valor * de esta propiedad se recupera del parametro PARAM * URL_FIRMA_RETURN_OPEN_ID al inicializar el cliente. * * private String urlFirmaReturnOpenId; */ /** * Nombre de la variable de sessin en la que se almacena la url de retorno. */ private String VARIABLE_URL_RETORNO_OPENID = "urlRetornoOpenID"; }