Java tutorial
/* * 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 java.net.*; import java.util.regex.*; import javax.servlet.http.*; import javax.servlet.http.Cookie; import org.restlet.*; import org.restlet.data.*; import org.jasig.cas.client.session.*; import org.slf4j.*; import org.springframework.beans.factory.annotation.*; import org.springframework.security.authentication.*; import org.springframework.security.cas.*; import org.springframework.web.util.*; import org.springframework.web.util.WebUtils; import it.scoppelletti.programmerpower.*; import it.scoppelletti.programmerpower.reflect.*; import it.scoppelletti.programmerpower.security.*; import it.scoppelletti.programmerpower.types.*; import it.scoppelletti.programmerpower.web.*; /** * Client delle <ACRONYM TITLE="Application Programming Interface">API</ACRONYM> * <ACRONYM TITLE="REpresentional State Transfer">REST</ACRONYM> del server * <ACRONYM TITLE="Central Authentication Service">CAS</ACRONYM>. * * @since 1.0.0 */ @Final public class CasClient { /** * Nome della proprietà di ambiente sulla quale deve essere impostato * l’indirizzo del server CAS (ad esempio, * {@code https://localhost:8443/cas}). Il valore della costante è * <CODE>{@value}</CODE>. * * @see <A HREF="{@docRoot}/../reference/setup/envprops.html" * TARGET="_top">Proprietà di ambiente</A> */ public static final String PROP_URL = "it.scoppelletti.programmerpower.web.security.CasClient.url"; /** * Nome della proprietà di ambiente sulla quale può essere * impostato il nome del cookie nel quale è memorizzato il ticket di * autenticazione. Il valore della costante è <CODE>{@value}</CODE>. * * @it.scoppelletti.tag.default {@code it.scoppelletti.TGC} * @see <A HREF="{@docRoot}/../reference/setup/envprops.html" * TARGET="_top">Proprietà di ambiente</A> */ public static final String PROP_COOKIE = "it.scoppelletti.programmerpower.web.security.CasClient.cookie"; private static final Pattern myTGTFormat = Pattern.compile(".*action=\".*/(.*?)\".*"); private static final Logger myLogger = LoggerFactory.getLogger(CasClient.class); private String myServerUrl; private ServiceProperties myServiceProps; private CookieGenerator myTGTCookieGenerator; private SessionMappingStorage mySessionStorage; /** * Costruttore. */ public CasClient() { } /** * Imposta l’URL delle API RESTful del server CAS. * * @param value Valore. */ @Required public void setServerUrl(String value) { myServerUrl = value; } /** * Imposta le proprietà del servizio CAS corrispondente * all’applicazione. * * @param obj Oggetto. */ @Required public void setServiceProperties(ServiceProperties obj) { myServiceProps = obj; } /** * Imposta il generatore del cookie del ticket di autenticazione. * * @param obj Oggetto. */ @Required public void setTicketGrantingTicketCookieGenerator(CookieGenerator obj) { myTGTCookieGenerator = obj; } /** * Imposta la collezione delle sessioni autenticate. * * @param obj Oggetto. */ @Required public void setSessionMappingStorage(SessionMappingStorage obj) { mySessionStorage = obj; } /** * Richiede un ticket di autenticazione. * * @param userName Nome dell’utente. * @param pwd Password. * @return Ticket di autenticazione. */ public String newTicketGrantingTicket(String userName, SecureString pwd) throws ProtocolException { String text; Matcher matcher; Client client; Request req; Response resp; Form form; Status status; WebResources res = new WebResources(); SecurityResources secRes = new SecurityResources(); if (Strings.isNullOrEmpty(userName)) { throw new ArgumentNullException("userName"); } if (Values.isNullOrEmpty(pwd)) { throw new ArgumentNullException("pwd"); } if (Strings.isNullOrEmpty(myServerUrl)) { throw new PropertyNotSetException(toString(), "serverUrl"); } if (myServiceProps == null) { throw new PropertyNotSetException(toString(), "serviceProperties"); } form = new Form(); form.add("username", userName); form.add("password", pwd.toString()); req = new Request(Method.POST, myServerUrl); req.setEntity(form.getWebRepresentation(CharacterSet.UTF_8)); client = new Client(Protocol.HTTPS); resp = client.handle(req); status = resp.getStatus(); if (status.equals(Status.CLIENT_ERROR_BAD_REQUEST)) { throw new BadCredentialsException(secRes.getFailedLoginException()); } if (status.equals(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE)) { throw new ProtocolException(res.getUnsupportedMediaTypeException()); } if (!status.equals(Status.SUCCESS_CREATED)) { throw new ProtocolException( res.getUnexpectedStatusCodeException(status.getCode(), status.getDescription())); } text = resp.getEntityAsText(); if (Strings.isNullOrEmpty(text)) { throw new ProtocolException(res.getEmptyResponseException()); } matcher = myTGTFormat.matcher(text); if (!matcher.matches()) { throw new ProtocolException(res.getInvalidResponseException(text)); } return matcher.group(1); } /** * Richiede un ticket di servizio. * * @param ticketGrantingTicket Ticket di autenticazione. * @return Ticket di servizio. */ public String newServiceTicket(String ticketGrantingTicket) throws ProtocolException { String text, url; Client client; Request req; Response resp; Form form; Status status; WebResources res = new WebResources(); SecurityResources secRes = new SecurityResources(); if (Strings.isNullOrEmpty(ticketGrantingTicket)) { throw new ArgumentNullException("ticketGrantingTicket"); } if (Strings.isNullOrEmpty(myServerUrl)) { throw new PropertyNotSetException(toString(), "serverUrl"); } if (myServiceProps == null) { throw new PropertyNotSetException(toString(), "serviceProperties"); } if (myServerUrl.endsWith("/")) { url = myServerUrl; } else { url = myServerUrl.concat("/"); } url = url.concat(ticketGrantingTicket); form = new Form(); form.add(myServiceProps.getServiceParameter(), myServiceProps.getService()); req = new Request(Method.POST, url); req.setEntity(form.getWebRepresentation(CharacterSet.UTF_8)); client = new Client(Protocol.HTTPS); resp = client.handle(req); status = resp.getStatus(); if (status.equals(Status.CLIENT_ERROR_BAD_REQUEST)) { throw new BadCredentialsException(secRes.getFailedLoginException()); } if (status.equals(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE)) { throw new ProtocolException(res.getUnsupportedMediaTypeException()); } if (!status.equals(Status.SUCCESS_OK)) { throw new ProtocolException( res.getUnexpectedStatusCodeException(status.getCode(), status.getDescription())); } text = resp.getEntityAsText(); if (Strings.isNullOrEmpty(text)) { throw new ProtocolException(res.getEmptyResponseException()); } return text; } /** * Rimuove tutte le autenticazioni eseguite con lo stesso ticket di * autenticazione registrato come cookie. * * @param req Richiesta. * @param resp Risposta. * @param ticket Ticket di autenticazione. */ public void destroyTicketGrantingTicket(HttpServletRequest req, HttpServletResponse resp, String ticket) { String url; Client client; Request restReq; Response restResp; Status status; if (req == null) { throw new ArgumentNullException("req"); } if (resp == null) { throw new ArgumentNullException("resp"); } if (Strings.isNullOrEmpty(ticket)) { throw new ArgumentNullException("ticket"); } if (Strings.isNullOrEmpty(myServerUrl)) { throw new PropertyNotSetException(toString(), "serverUrl"); } if (myServerUrl.endsWith("/")) { url = myServerUrl; } else { url = myServerUrl.concat("/"); } url = url.concat(ticket); try { restReq = new Request(Method.DELETE, url); client = new Client(Protocol.HTTPS); restResp = client.handle(restReq); status = restResp.getStatus(); if (!status.isSuccess()) { // CAS non documenta i possibili codici di risposta myLogger.error("destroyTicketGrantingTicket status: {} {}", status.getCode(), status.getDescription()); } } catch (Exception ex) { throw new AuthenticationServiceException(ApplicationException.toString(ex), ex); } } /** * Restituisce il ticket di autenticazione registrato come cookie. * * @param req Richiesta. * @param resp Risposta. * @return Valore. Se il ticket non è stato registrato, * restituisce {@code null}. */ public String getTicketGrantingTicket(HttpServletRequest req, HttpServletResponse resp) { Cookie cookie; if (req == null) { throw new ArgumentNullException("req"); } if (myTGTCookieGenerator == null) { throw new PropertyNotSetException(toString(), "ticketGrantingTicketCookieGenerator"); } cookie = WebUtils.getCookie(req, myTGTCookieGenerator.getCookieName()); if (cookie == null) { return null; } return cookie.getValue(); } /** * Registra un ticket di autenticazione come cookie. * * @param req Richiesta. * @param resp Risposta. * @param ticket Ticket di autenticazione. */ public void addTicketGrantingTicket(HttpServletRequest req, HttpServletResponse resp, String ticket) { if (resp == null) { throw new ArgumentNullException("resp"); } if (Strings.isNullOrEmpty(ticket)) { throw new ArgumentNullException("ticket"); } if (myTGTCookieGenerator == null) { throw new PropertyNotSetException(toString(), "ticketGrantingTicketCookieGenerator"); } try { myTGTCookieGenerator.addCookie(resp, ticket); } catch (Exception ex) { myLogger.error("Failed to store TGT cookie.", ex); } } /** * Rimuove il ticket di autenticazione registrato come cookie. * * @param req Richiesta. * @param resp Risposta. */ public void removeTicketGrantingTicket(HttpServletRequest req, HttpServletResponse resp) { if (resp == null) { throw new ArgumentNullException("resp"); } if (myTGTCookieGenerator == null) { throw new PropertyNotSetException(toString(), "ticketGrantingTicketCookieGenerator"); } try { myTGTCookieGenerator.removeCookie(resp); } catch (Exception ex) { myLogger.error("Failed to remove TGT cookie.", ex); } } /** * Colleziona una sessione autenticata. * * @param serviceTicket Ticket di servizio. * @param session Sessione. */ public void addAuthenticatedSession(String serviceTicket, HttpSession session) { if (Strings.isNullOrEmpty(serviceTicket)) { throw new ArgumentNullException("serviceTicket"); } if (session == null) { throw new ArgumentNullException("session"); } if (mySessionStorage == null) { throw new PropertyNotSetException(toString(), "sessionMappingStorage"); } myLogger.debug("Storing authenticated session {}.", session.getId()); try { mySessionStorage.removeBySessionById(session.getId()); } catch (Exception ex) { // NOP } mySessionStorage.addSessionById(serviceTicket, session); } /** * Rimuove una sessione autenticata. * * @param serviceTicket Ticket di servizio. */ public void removeAuthenticatedSession(String serviceTicket) { HttpSession session; if (Strings.isNullOrEmpty(serviceTicket)) { throw new ArgumentNullException("serviceTicket"); } if (mySessionStorage == null) { throw new PropertyNotSetException(toString(), "sessionMappingStorage"); } myLogger.debug("Removing service ticket {}.", serviceTicket); session = mySessionStorage.removeSessionByMappingId(serviceTicket); if (session == null) { return; } myLogger.debug("Invalidating session {}.", session.getId()); try { session.invalidate(); } catch (Exception ex) { myLogger.error("Failed to invalidate session.", ex); } } }