it.scoppelletti.programmerpower.web.security.SsoRememberMeServices.java Source code

Java tutorial

Introduction

Here is the source code for it.scoppelletti.programmerpower.web.security.SsoRememberMeServices.java

Source

/*
 * Copyright (C) 2011 Dario Scoppelletti, <http://www.scoppelletti.it/>.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package it.scoppelletti.programmerpower.web.security;

import javax.servlet.http.*;
import org.slf4j.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.security.core.*;
import org.springframework.security.core.userdetails.*;
import org.springframework.security.web.authentication.rememberme.*;
import it.scoppelletti.programmerpower.*;
import it.scoppelletti.programmerpower.security.*;
import it.scoppelletti.programmerpower.reflect.*;
import it.scoppelletti.programmerpower.types.*;

/**
 * Servizi per l&rsquo;autenticazione persistente in ambito
 * <ACRONYM TITLE="Single Sign-On">SSO</ACRONYM>.
 * 
 * <P>La classe {@code SsoRememberMeServices} estende le funzionalit&agrave;
 * della classe Spring Security {@code PersistentTokenBasedRememberMeServices}
 * con la possibilit&agrave; di configurare il
 * {@linkplain #setCookieDomain dominio} e il
 * {@linkplain #setCookiePath percorso} di visibilit&agrave; del cookie
 * utilizzato per l&rsquo;autenticazione persistente; la classe base infatti
 * utilizza un cookie visibile alla sola applicazione che lo ha registrato e che
 * quindi non &egrave; adatto per un&rsquo;autenticazione SSO.</P>
 * 
 * <P>Parallelamente all&rsquo;autenticazione persistente di un utente per un 
 * client, il componente {@code SsoRememberMeServices} esegue anche
 * un&rsquo;autenticazione SSO <I>fittizia</I> che mantiene la collezione delle
 * sessioni autenticate in modo che funzioni correttamente anche il Single
 * Sign-Out.<BR>
 * Poich&egrave; la caratteristica di un&rsquo;autenticazione persistente 
 * &egrave; quella appunto di non richiedere le credenziali di autenticazione
 * per l&rsquo;utente, la corrispondente autenticazione SSO pu&ograve; essere
 * eseguita solo con un {@linkplain #setUserName utente} dedicato del quale il
 * componente {@code SsoRememberMeServices} conosca la
 * {@linkplain #setPassword password}.</P>
 * 
 * @see   it.scoppelletti.programmerpower.web.security.SingleSignOutFilter
 * @see   it.scoppelletti.programmerpower.web.security.SingleSignOutSessionListener
 * @see   <A HREF="{@docRoot}/../reference/wui/init.html#idRememberme"
 *        TARGET="_top">Autenticazione persistente</A> 
 * @since 1.0.0
 */
@Final
public class SsoRememberMeServices extends PersistentTokenBasedRememberMeServices {

    /**
     * Valore di default del percorso di visibilit&agrave; del cookie per 
     * l&rsquo;autenticazione persistente. Il valore della costante &egrave;
     * <CODE>{@value}</CODE>.  
     * 
     * @see #setCookiePath
     */
    public static final String DEF_COOKIEPATH = "/";

    /**
     * Suffisso applicato ai ticket di autenticazione per identificarli come
     * ticket di autenticatione per l&rsquo;autenticazione persistente. Il
     * valore della costante &egrave; <CODE>{@value}</CODE>.
     */
    public static final String TICKET_SUFFIX = "-RM";

    /**
     * Nome della propriet&agrave; di ambiente sulla quale pu&ograve; essere
     * impostato il nome del cookie nel quale &egrave; memorizzato il token per
     * l&rsquo;autenticazione persistente. Il valore della costante &egrave;
     * <CODE>{@value}</CODE>.
     * 
     * @it.scoppelletti.tag.default {@code it.scoppelletti.RMC}
     * @see <A HREF="{@docRoot}/../reference/setup/envprops.html"
     *      TARGET="_top">Propriet&agrave; di ambiente</A> 
     */
    public static final String PROP_COOKIE = "it.scoppelletti.programmerpower.web.security.SsoRememberMeServices.cookie";

    /**
     * Nome della propriet&agrave; di ambiente sulla quale pu&ograve; essere
     * impostato il periodo di validit&agrave; (in secondi) del token per
     * l&rsquo;autenticazione persistente. Il valore della costante &egrave;
     * <CODE>{@value}</CODE>.
     * 
     * @it.scoppelletti.tag.default 3 mesi (= 7889231 secondi).
     * @see <A HREF="{@docRoot}/../reference/setup/envprops.html"
     *      TARGET="_top">Propriet&agrave; di ambiente</A> 
     */
    public static final String PROP_TIMEOUT = "it.scoppelletti.programmerpower.web.security.SsoRememberMeServices.timeout";

    /**
     * Nome della propriet&agrave; di ambiente sulla quale deve essere impostata
     * la chiave di controllo contro la manomissione del token del servizio di
     * autenticazione persistente. Il valore della costante &egrave;
     * <CODE>{@value}</CODE>.
     * 
     * @it.scoppelletti.tag.default {@code changeit}
     * @see                         #SsoRememberMeServices
     * @see <A HREF="{@docRoot}/../reference/setup/envprops.html"
     *      TARGET="_top">Propriet&agrave; di ambiente</A> 
     */
    public static final String PROP_KEY = "it.scoppelletti.programmerpower.web.security.SsoRememberMeServices.key";

    /**
     * Nome della propriet&agrave; di ambiente sulla quale pu&ograve; essere
     * impostato il nome dell&rsquo;utente che rappresenta
     * l&rsquo;autenticazione persistente. Il valore della costante &egrave;
     * <CODE>{@value}</CODE>.
     * 
     * @it.scoppelletti.tag.default {@code rememberme}
     * @see                         #setUserName
     * @see <A HREF="{@docRoot}/../reference/setup/envprops.html"
     *      TARGET="_top">Propriet&agrave; di ambiente</A> 
     */
    public static final String PROP_USERNAME = "it.scoppelletti.programmerpower.web.security.SsoRememberMeServices.user";

    /**
     * Nome della propriet&agrave; di ambiente sulla quale deve essere impostata
     * la password dell&rsquo;utente che rappresenta l&rsquo;autenticazione
     * persistente. Il valore della costante &egrave;
     * <CODE>{@value}</CODE>.
     * 
     * @it.scoppelletti.tag.default {@code changeit}
     * @see                         #setPassword
     * @see <A HREF="{@docRoot}/../reference/setup/envprops.html"
     *      TARGET="_top">Propriet&agrave; di ambiente</A> 
     */
    public static final String PROP_PASSWORD = "it.scoppelletti.programmerpower.web.security.SsoRememberMeServices.pwd";

    private static final Logger myLogger = LoggerFactory.getLogger(SsoRememberMeServices.class);

    private String myCookieDomain;
    private String myCookiePath;
    private boolean mySecureCookie;
    private String myUserName;
    private String myPwd;
    private CasClient myCasClient;

    /**
     * Costruttore.
     * 
     * @param key                Chiave di controllo contro la manomissione del
     *                           token del servizio di autenticazione
     *                           persistente.
     * @param userDetailsService Servizio di lettura dei dati degli utenti.
     * @param tokenRepository    Repository dei token del servizio di
     *                           autenticazione persistente.
     */
    public SsoRememberMeServices(String key, UserDetailsService userDetailsService,
            PersistentTokenRepository tokenRepository) {
        super(key, userDetailsService, tokenRepository);
        myCookiePath = SsoRememberMeServices.DEF_COOKIEPATH;
    }

    /**
     * Imposta il dominio di visibilit&agrave; dei cookie.
     * 
     * @param value Valore (es. {@code .scoppelletti.it}).
     */
    public void setCookieDomain(String value) {
        myCookieDomain = value;
    }

    /**
     * Imposta il percorso di visibilit&agrave; dei cookie.
     * 
     * @param                       value Valore.
     * @it.scoppelletti.tag.default       {@code "/"}
     */
    public void setCookiePath(String value) {
        if (Strings.isNullOrEmpty(value)) {
            throw new ArgumentNullException("value");
        }

        myCookiePath = value;
    }

    /**
     * Imposta l&rsquo;indicatore di trasmissione dei cookie solo attraverso
     * connessioni sicure.
     * 
     * @param value Valore.
     */
    @Override
    public void setUseSecureCookie(boolean value) {
        // La classe base non pubblica il metodo accessore di lettura:
        // Posso solo implementare una versione prevalente del metodo accessore
        // di scrittura per memorizzare il valore anche in un campo privato
        // locale.
        super.setUseSecureCookie(value);
        mySecureCookie = value;
    }

    /**
     * Imposta il nome dell&rsquo;utente che rappresenta l&rsquo;autenticazione
     * persistente.
     * 
     * @param value Valore.
     */
    @Required
    public void setUserName(String value) {
        myUserName = value;
    }

    /**
     * Imposta la password dell&rsquo;utente che rappresenta
     * l&rsquo;autenticazione persistente.
     * 
     * @param value Valore.
     */
    @Required
    public void setPassword(String value) {
        myPwd = value;
    }

    /**
     * Imposta il client CAS.
     * 
     * @param obj Oggetto.
     */
    @Required
    public void setCasClient(CasClient obj) {
        myCasClient = obj;
    }

    /**
     * Valida l&rsquo;autenticazione persistente.
     * 
     * @param  cookieTokens Componenti del cookie per l&rsquo;autenticazione
     *                      persistente.
     * @param  req          Richiesta.
     * @param  resp         Risposta.
     * @return              Utente autenticato.
     */
    @Override
    protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest req,
            HttpServletResponse resp) {
        boolean newTGT;
        String tgt, ticket;
        UserDetails user;
        HttpSession session;
        AuthenticationException authEx;

        if (Strings.isNullOrEmpty(myUserName)) {
            throw new PropertyNotSetException(toString(), "userName");
        }
        if (Strings.isNullOrEmpty(myPwd)) {
            throw new PropertyNotSetException(toString(), "password");
        }
        if (myCasClient == null) {
            throw new PropertyNotSetException(toString(), "casClient");
        }

        user = super.processAutoLoginCookie(cookieTokens, req, resp);

        tgt = getTicketGrantingTicket(req, resp);
        newTGT = Strings.isNullOrEmpty(tgt);

        try {
            if (newTGT) {
                tgt = myCasClient.newTicketGrantingTicket(myUserName, new SecureString(myPwd));
            }

            ticket = myCasClient.newServiceTicket(tgt);
        } catch (Exception ex) {
            authEx = new RememberMeAuthenticationException(ApplicationException.toString(ex));
            authEx.initCause(ex);
            throw authEx;
        }

        session = req.getSession(true);
        myLogger.debug("New ticket {} for session {}.", ticket, session.getId());

        if (newTGT) {
            tgt = tgt.concat(SsoRememberMeServices.TICKET_SUFFIX);
            myCasClient.addTicketGrantingTicket(req, resp, tgt);
        }

        myCasClient.addAuthenticatedSession(ticket, session);

        return user;
    }

    /**
     * Registra il cookie per l&rsquo;autenticazione persistente.
     *
     * @param tokens Token codificati nel cookie.
     * @param maxAge Scadenza.
     * @param req    Richiesta.
     * @param resp   Risposta.
     */
    @Override
    protected void setCookie(String[] tokens, int maxAge, HttpServletRequest req, HttpServletResponse resp) {
        String value;
        Cookie cookie;

        value = encodeCookie(tokens);
        cookie = buildCookie(value, maxAge);
        cookie.setSecure(mySecureCookie);
        resp.addCookie(cookie);
    }

    /**
     * Rimuove il cookie per l&rsquo;autenticazione persistente.
     *
     * @param req  Richiesta.
     * @param resp Risposta.
     */
    @Override
    protected void cancelCookie(HttpServletRequest req, HttpServletResponse resp) {
        Cookie cookie;

        cookie = buildCookie(null, 0);
        resp.addCookie(cookie);
    }

    /**
     * Inizializza il cookie per l&rsquo;autenticazione persistente.
     * 
     * @param  value  Valore.
     * @param  maxAge Scadenza.
     * @return        Oggetto.
     */
    private Cookie buildCookie(String value, int maxAge) {
        Cookie cookie;

        cookie = new Cookie(getCookieName(), value);
        if (myCookieDomain != null) {
            cookie.setDomain(myCookieDomain);
        }
        cookie.setPath(myCookiePath);
        cookie.setMaxAge(maxAge);

        return cookie;
    }

    /**
     * Gestore del logout.
     * 
     * @param req            Richiesta.
     * @param resp           Risposta.
     * @param authentication Token autenticato.
     */
    @Override
    public void logout(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) {
        String tgt;

        try {
            super.logout(req, resp, authentication);
        } catch (Exception ex) {
            myLogger.error(ApplicationException.toString(ex), ex);
        }

        tgt = getTicketGrantingTicket(req, resp);
        if (Strings.isNullOrEmpty(tgt)) {
            return;
        }

        myCasClient.removeTicketGrantingTicket(req, resp);
        try {
            myCasClient.destroyTicketGrantingTicket(req, resp, tgt);
        } catch (Exception ex) {
            myLogger.error(ApplicationException.toString(ex), ex);
        }
    }

    /**
     * Restituisce il ticket di autenticazione registrato come cookie.
     * 
     * @param  req  Richiesta.
     * @param  resp Risposta.
     * @return      Valore. Se il ticket non &egrave; stato registrato,
     *              restituisce {@code null}.
     */
    private String getTicketGrantingTicket(HttpServletRequest req, HttpServletResponse resp) {
        String value;

        value = myCasClient.getTicketGrantingTicket(req, resp);
        if (Strings.isNullOrEmpty(value) || value.length() <= SsoRememberMeServices.TICKET_SUFFIX.length()
                || !value.endsWith(SsoRememberMeServices.TICKET_SUFFIX)) {
            myLogger.debug("RememberMe TGT not stored in cookies.");
            return null;
        }

        value = value.substring(0, value.length() - SsoRememberMeServices.TICKET_SUFFIX.length());

        return value;
    }
}