Canigó - Servei de Seguretat 2.3.x
SERVEI SEGURETAT
IntroduccióPropòsitEl Servei de Seguretat té com a propòsit principal gestionar l'autentificació i l'autorització dels usuaris de les nostres aplicacions. L'objectiu de l'autentificació és comprovar que l'usuari és qui diu ser, mentre que l'autorització s'encarrega de comprovar que realment té accés al recurs sol.licitat. NOTA: L'especificació JAAS (Java Authorization and Authentication) de J2EE proporciona els mecanismes necessaris de seguretat. Cada servidor d'aplicacions pot implementar l'estàndard però ho fa de diferents formes produint problemes de compatibilitat. Actualment, i a causa del seu grau de maduresa i facilitat canigo recomana l'ús de 'Acegi' com framework base i les extensions que canigo proporciona. Per a l'ús d'aquest servei i la lectura del present document es necessiten com a prerequisits els següens aspectes:
Context i Escenari d'úsEl Servei de Seguretat és un servei general de canigo.
Versions i DependènciesLes dependències descrites a la següent url son requerides per tal de compilar i fer funcionar el projecte: A qui es dirigeixAquest document es dirigeix als perfils següents:
Documents i Fonts de Referència
GlossariACLs (Access Control List) Les llistes de control d'accés o ACLs permeten gestionar les autoritzacions de cada rol o usuari a nivell de dades o instàncies de lògica de negoci. Per a cada instància es poden especificar diferents dret: lectura, escriptura, esborrat, creació. Descripció DetalladaArquitectura BàsicaEn el present apartat es mostren els diagrames de seqüència associats al tractament de peticions i cóm el servei de seguretat activarà el pas o no al recurs sol- licitat. Accés a una url no protegidaSi hi ha un accés a una url que no s'ha configurat com a protegida (veure apartat 'Configuració') es segueixen els següents pasos:
Accés a urls protegides
![]() Si s'accedeix a una url protegida, el funcionament és:
Accés a urls protegides si usuari autentificatSi el mateix usuari amb un rol 1 intenta accedir a una url restringida pel rol 2, la seqüència és:
Interfícies i Components GenèricsInterfícies PrincipalsNomés la interfície 'SecurityService' serà visible des de les classes desenvolupades per les aplicacions, les restants classes s'usaran per definir mitjançant la configuració el comportament dessitjat per la seguretat. Es pot trobar tota la documentació JavaDoc y el codi font referent aquests components a les següents urls: JavaDoc: http://canigo.ctti.gencat.net/confluence/canigodocs/site/canigo2_0/canigo-services-security/apidocs/index.html Instal.lació i ConfiguracióPrerequisit: Per al funcionament correcte d'aquest servei és important haver realitzat prèviament les configuracions especificades de la part 'Configuració Bàsica' del document 'Servei de Presentació'. Per a la instal.lació del Servei de Seguretat necessitem usar el fitxer 'canigo-services-security.jar' i les dependències indicades en l'apartat 'Versions i Dependències'. La configuració del servei de seguretat es descompon en les parts següents:
El Servei de Seguretat de canigo encapsula la complexitat de "Acegi" per mitjà d'una configuració simplificada i centralitzada en dos beans: authenticationConfiguration i authorizationConfiguration. NOTA prèvia: ![]() Exemple: <bean id="authorizationConfiguration" class="net.gencat.ctti.canigo.services.security. AuthorizationSecurityConfiguration"> ... <property name="beanNamesPatternList"> <list> <value> ?businessService</value> </list> </property> </bean> Per a més informació sobre el framework "Acegi", consultar la pàgina http://acegisecurity.sourceforge.net/suggested.html Configuració de la Base de DadesPer l'ús de la seguretat canigo utilitza, per defecte, l'esquema de base de dades mostrat a continuació: ![]() NOTA: Si la base de dades d'usuaris ja existeix en el moment d'implantació de canigo, no cal crear les taules d'autentificació 'USER_LOGIN', 'PARTY_ROLE' i 'ROLE'. Haurem d'indicar les query per a obtenir el nom d'usuari, password i rol. Taules d'Autentificació # USER_LOGIN Els camps de la taula "USER_LOGIN" són:
Taules d'Autorització Les taules d'autorització només són necessàries si necessitem gestionar les autoritzacions a nivell d'instàncies de la lògica de negoci (ACLs).
La taula ACL_PERMISSION conté els permisos que s'apliquin als usuaris o rols per a cada objecte de la lògica de negoci. Conté els camps següents:
Per a la creació de l'esquema podem fer ús del script següent per Oracle:
DROP SEQUENCE ACL_PERMISSION_SEQ; DROP SEQUENCE ACL_OBJECT_SEQ; DROP SEQUENCE PARTY_OBJECT_SEQ; DROP SEQUENCE ROLE_OBJECT_SEQ; create sequence ROLE_OBJECT_SEQ start with 1 increment by 1; create sequence ACL_OBJECT_SEQ start with 1 increment by 1; create sequence ACL_PERMISSION_SEQ start with 1 increment by 1; create sequence PARTY_OBJECT_SEQ start with 1 increment by 1; DROP TABLE ACL_PERMISSION; DROP TABLE ACL_OBJECT_IDENTITY; DROP TABLE PARTY CASCADE CONSTRAINTS; DROP TABLE GROUP_ROLE CASCADE CONSTRAINTS; DROP TABLE PARTY_GROUP CASCADE CONSTRAINTS; DROP TABLE PARTY_RELATIONSHIP CASCADE CONSTRAINTS; DROP TABLE PARTY_ROLE CASCADE CONSTRAINTS; DROP TABLE ROLE CASCADE CONSTRAINTS; DROP TABLE USER_LOGIN CASCADE CONSTRAINTS; CREATE TABLE acl_object_identity ( id NUMBER PRIMARY KEY, object_identity VARCHAR2(250) NOT NULL, parent_object INTEGER, acl_class VARCHAR2(250) NOT NULL, CONSTRAINT unique_object_identity UNIQUE(object_identity), FOREIGN KEY (parent_object) REFERENCES acl_object_identity(id) ); CREATE TABLE acl_permission ( id NUMBER PRIMARY KEY, acl_object_identity NUMBER NOT NULL, recipient VARCHAR2(100) NOT NULL, mask NUMBER NOT NULL, CONSTRAINT unique_recipient UNIQUE(acl_object_identity, recipient), FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity(id) ); CREATE TABLE PARTY ( PARTY_ID NUMBER NOT NULL, PARTY_TYPE_ID NUMBER, EXTERNAL_ID VARCHAR2(20), CREATED_DATE VARCHAR2(255), CREATED_BY_USER_LOGIN VARCHAR2(255), LAST_MODIFIED_DATE VARCHAR2(255), LAST_MODIFIED_BY_USER_LOGIN VARCHAR2(255), LAST_UPDATED_STAMP VARCHAR2(255), LAST_UPDATED_TX_STAMP VARCHAR2(255), CREATED_STAMP VARCHAR2(255), CREATED_TX_STAMP VARCHAR2(255), CONSTRAINT PK_PARTY_ID UNIQUE(PARTY_ID) ) ; CREATE TABLE PARTY_GROUP ( PARTY_ID NUMBER NOT NULL, GROUP_PARENT_ID NUMBER NULL, GROUP_NAME VARCHAR2(255) NOT NULL, CONSTRAINT PK_PARTY_GROUP UNIQUE(PARTY_ID) ) ; CREATE TABLE PARTY_RELATIONSHIP ( PARTY_ID_FROM NUMBER NOT NULL, PARTY_ID_TO NUMBER NOT NULL, TYPE NUMBER NULL, CONSTRAINT PK_PARTY_RELATIONSHIP UNIQUE(PARTY_ID_FROM, PARTY_ID_TO), FOREIGN KEY (PARTY_ID_FROM) REFERENCES PARTY(PARTY_ID), FOREIGN KEY (PARTY_ID_TO) REFERENCES PARTY(PARTY_ID) ) ; CREATE TABLE ROLE ( ROLE_ID NUMBER NOT NULL, ROLE_ID_PARENT NUMBER, ROLE_NAME VARCHAR2(50) NOT NULL, ROLE_DESCRIPTION VARCHAR2(255), CONSTRAINT PK_ROLE UNIQUE(ROLE_ID), FOREIGN KEY (ROLE_ID_PARENT) REFERENCES ROLE(ROLE_ID) ) ; CREATE TABLE USER_LOGIN ( USER_LOGIN_ID VARCHAR2(50) NOT NULL, PARTY_ID NUMBER NOT NULL, PASSWORD VARCHAR2(50) NOT NULL, CONSTRAINT PK_USER_LOGIN UNIQUE(USER_LOGIN_ID), FOREIGN KEY (PARTY_ID) REFERENCES PARTY_GROUP(PARTY_ID) ) ; CREATE TABLE PARTY_ROLE ( USER_LOGIN_ID VARCHAR2(50) NOT NULL, ROLE_ID NUMBER NOT NULL, CONSTRAINT PK_PARTY_ROLE UNIQUE(USER_LOGIN_ID, ROLE_ID), FOREIGN KEY (USER_LOGIN_ID) REFERENCES USER_LOGIN(USER_LOGIN_ID), FOREIGN KEY (ROLE_ID) REFERENCES ROLE(ROLE_ID) ) ; CREATE TABLE GROUP_ROLE ( PARTY_ID NUMBER NOT NULL, ROLE_ID NUMBER NOT NULL, CONSTRAINT PK_GROUP_ROLE UNIQUE(PARTY_ID, ROLE_ID), FOREIGN KEY (PARTY_ID) REFERENCES PARTY_GROUP(PARTY_ID), FOREIGN KEY (ROLE_ID) REFERENCES ROLE(ROLE_ID) ) ; CREATE OR REPLACE TRIGGER ACL_OBJ_TRIGGER before insert on ACL_OBJECT_IDENTITY for each row begin select ACL_OBJECT_SEQ.nextval into :new.id from dual; end; CREATE OR REPLACE TRIGGER ACL_PERMISSION_TRIGGER before insert on ACL_PERMISSION for each row begin select ACL_PERMISSION_SEQ.nextval into :new.id from dual; end; CREATE OR REPLACE TRIGGER PARTY_OBJECT_SEQ_TRIGGER before insert on PARTY_GROUP for each row WHEN (new.PARTY_ID is null) begin select PARTY_OBJECT_SEQ.nextval into :new.PARTY_ID from dual; end; CREATE OR REPLACE TRIGGER CANIGO.ROLE_OBJECT_SEQ_TRIGGER before insert on ROLE for each row WHEN (new.ROLE_ID is null) begin select ROLE_OBJECT_SEQ.nextval into :new.ROLE_ID from dual; end; Configuració dels filtres de l'Aplicació WebFitxer de configuració: web.xml Acegi usa un conjunt de filtres per a detectar aspectes de l'autorització i autentificació. Per a usar-los definirem en el fitxer 'WEB-INF/web.xml el codi següent:
<!-- Filter ACEGI, using a FilterChainProxy makes it easier to configure different Filters in the Spring application context configuration file--> <filter> <filter-name>Acegi Filter Chain Proxy</filter-name> <filter-class> net.sf.acegisecurity.util.FilterToBeanProxy </filter-class> <init-param> <param-name>targetClass</param-name> <param-value> net.sf.acegisecurity.util.FilterChainProxy </param-value> </init-param> </filter> <filter-mapping> <filter-name>Acegi Filter Chain Proxy</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> Per a més informació consultar la pàgina http://acegisecurity.sourceforge.net/docbook/acegi.html#security-filters
Configuració de la font de dades de SeguretatFitxer de configuració: canigo-services-security.xml El Servei de Seguretat necessita conèixer com connectar-se a l'esquema de base de dades que defineix les taules anteriorment comentades. Per a això, hem d'especificar un bean 'dataSource' en el que configurarem les propietats de la base de dades. Es recomana l'ús de JNDI per a definir la font de dades. Exemple amb jndi:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:comp/env/JNDISource..."/> </bean> Exemple amb jdbc:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>$\{jdbc.driverClassName\}</value> </property> <property name="url"> <value>$\{jdbc.url\}</value> </property> <property name="username"> <value>$\{jdbc.username\}</value> </property> <property name="password"> <value>$\{jdbc.password\}</value> </property> <bean> Configuració de l'AutentificacióEn la configuració de l'Autentificació tindrem en consideració:
Configuració de la Font d'Autorització per Base de Dades Fitxer de configuració: canigo-services-security.xml La configuració de l'autentificació per mitjà de base de dades es realitza per mitjà del bean de la classe 'net.gencat.ctti.canigo.services.
Exemple:
<bean id="databaseAuthenticationConfiguration1" class="net.gencat.ctti.canigo.services.security. DatabaseAuthenticationConfiguration"> <!- Propietats obligatóries -> <property name ="passwordEncoderClass" value ="net.sf.acegisecurity.providers.encoding. PlaintextPasswordEncoder" /> <!- Propietats opcionals -> <property name ="usersByUserNameQuery"> <value> SELECT \{Nombre_usuario\}, \{contraseña\} * FROM* \{usuarios\} _ WHERE \{_Nombre_usuario\} =? </value> </property> <property name ="authoritiesbyUserNameQuery"> <value> SELECT \{Nombre_usuario\}, \{rol\} * FROM* \{privilegios\} _ WHERE \{_Nombre_usuario\} =? </value> </property> </bean> Configuració de la Font d'Autorització per LDAP
Exemple:
<bean id="ldapAuthenticationConfiguration" class="net.gencat.ctti.canigo.services.security. LDAPAuthenticationConfiguration"> <!- Propietats obligatóries -> <property name ="ldapURL" value ="ldap://localhost:389/dc=mycompany,dc=com" /> <property name ="usernameFormat" value ="uid=\{0\},ou=people,dc=mycompany,dc=com" /> <!- Propietats opcionals -> <property name ="userLookupNameFormat" value ="uid=\{0\},ou=people" /> <property name ="roleAttribute" value ="title" /> </bean> Configuració de la Font d'Autorització per SACE
Exemple:
<bean id="SACEAuthenticationConfiguration" class="net.gencat.ctti.canigo.services.security.SACEAuthenticationConfiguration"> <property name ="urlSACE" value="https*://sace.prepdc.gencat.intranet/SACE/SACE_Logon.aspx?XMLIn=*"/> <property name ="userNameFormat" value="NIF"/> <property name ="i18nSecurityService" ref="securityI18nService" /> <property name="keyStore" value="classpath:keystore/saceTrust_pre.jks"/> <property name="keyStorePassPhrase" value="saceTrust_pre2007"/> </bean> ... <bean id="securityI18nService" class="net.gencat.ctti.canigo.services.i18n.impl.SpringI18nServiceImpl"> <property name="messageSource" ref="securityMessageSource"/> <property name="defaultLocale" ref="securityDefaultLocale"/> </bean> ... <bean id="securityMessageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>i18n/saceErrorMessages</value> </list> </property> </bean> <bean id="securityI18nService" class="net.gencat.ctti.canigo.services.i18n.impl.SpringI18nServiceImpl"> <property name="messageSource" ref="securityMessageSource"/> <property name="defaultLocale" ref="securityDefaultLocale"/> </bean> IMPORTANT: Cal tenir el contenidor de certificats desplegat amb l'aplicació per a poder fer la connexió correctament amb SACE. Per això és important que tingueu aquest fitxer dins el vostre projecte normalment dins "src/main/resources/keystore" i així es desplegarà dins "WEB-INF/classes/keystore". A més cal que configureu les dues propietats "keyStore" i "keyStorePassPhrase". La paraula clau del contenidor que us hemo posat disponible és "saceTrust_pre2007". Configuració de la Font d'Autorització per GICARLa utilització de l'autenticació mitjançant GICAR es basa en la configuració. Amb aquesta configuració ha de ser possible autoritzar un usuari que prèviament ha estat auntenticat en el servei de GICAR. Per aquest motiu és necessari rebre certes dades referents a aquesta autenticació ja realitzada. A la capçalera HTML podrem accedir a aquestes dades:
HTTP_GICAR=CODIINTERN=NRDRJN0001;NIF=11112222W;EMAIL=mail.admin@gencat.net;UNITAT_MAJOR=CTTI; On CODIINTERN és el codi intern, el NIF el NIF, EMAIL l'adreça de correu electrònic registrada al Director Corporatiu, UNITAT_MAJOR és l'organització i UNITAT_MENOR és la unitat. Tot seguit explicarem com integrar la autenticació de Gicar dins Canigó utilitzant aquestes dades.
La integració amb Gicar es realitza per injecció amb spring, amb la següent definició del bean dins l'arxiu xml de configuració del servei de seguretat:
<beans> ... <bean id="GICARAuthenticationConfiguration" class="net.gencat.ctti.canigo.services.security.GICARAuthenticationConfiguration"> <property name="httpGicarHeaderUsernameKey" value="NIF"/> </bean> ... </beans>
Un cop definida la configuració per Gicar, cal afegir-la a la llista de providers a la configuració de l'autenticació:
<bean id="authenticationConfiguration" class="net.gencat.ctti.canigo.services.security.AuthenticationSecurityConfiguration"> <property name="filterProcessesUrl" value="/AppJava/j_acegi_security_check"/> <!--<property name="loginFormUrlValue" value="/AppJava/pagelogin.do"/>--> <property name="loginFormUrlValue" value="/AppJava/j_acegi_security_check"/> <!--<property name="authenticationFailureUrlValue" value="/AppJava/pagelogin.do"/>--> <property name="authenticationFailureUrlValue" value="/gicar-error.html"/> <property name="authenticationProvidersConfigurationList"> <list> <!-- <ref local="SACEAuthenticationConfiguration1"/> --> <!-- <ref local="LDAPAuthenticationConfiguration2"/> --> <ref local="GICARAuthenticationConfiguration" /> <!-- <ref local="databaseAuthenticationConfiguration3"/> --> </list> </property> </bean>
Logoff de GicarPer tots els mètodes d'autentificació, el procediment de logoff consisteix en invalidar la sessió, forçant així que el servei de seguretat intervingui en la següent petició solicitant la nova identificació de l'usuari. En el cas de Gicar, però, aquesta autentificació és realitzada per un sistema extern a l'aplicació i, per tant, s'ha de comunicar a aquest sistema extern la intenció de fer el logoff. El mecanisme previst per fer-ho consisteix en una URL de Gicar que, al ser invocada, realitza el logoff. Aquest enllaç de logout és depenent de l'agent de SiteMinder que l'aplicació fa servir per a comunicar-se amb el Policy Server.
Configuració del Formulari d'Autentificació i la Seqüència d'Autentificació
<bean id="SACEAuthenticationConfiguration1" ... </bean> <bean id="ldapAuthenticationConfiguration2" ... </bean> <bean id="databaseAuthenticationConfiguration3" ... </bean> <bean id="authenticationConfiguration" class="net.gencat.ctti.canigo.services.security.AuthenticationSecurityConfiguration"> <property name="loginFormUrlValue" value="/login.jsp" /> <property name="authenticationFailureUrlValue" value="/login.jsp" /> <property name="filterProcessesUrl" value="/AppJava/acegiLogon" /> <property name="authenticationProvidersConfigurationList"> <list> <ref local="SACEAuthenticationConfiguration1" /> <ref local="ldapAuthenticationConfiguration2" /> <ref local="databaseAuthenticationConfiguration3" /> </list> </property> </bean> Configuració de l'Autorització
Configuració de les Autoritzacions d'urls per rol d'usuari
<bean id="authorizationConfiguration" class="net.gencat.ctti.canigo.services.security.AuthorizationSecurityConfiguration"> <!-- Propietat opcional per les urls autoritzades --> <property name ="secureUrls"> <value> read = ROLE_GESTOR, ROLE_USUARIO delete = ROLE_GESTOR secure = ROLE_GESTOR </value> </property> </bean>
Configuració de les Autoritzacions a nivell de classe i mètodePer mitjà de proxies dinàmics lligats a aspectes (programació orientada a aspectes) podem configurar que determinats rols no tinguin accés a determinats mètodes de determinades classes. La configuració de les autoritzacions a nivell de classe i mètode s'han de realitzar per mitjà de les següents propietats del bean de canigo "authorizationConfiguration":
Exemple:
<bean id="authorizationConfiguration" class="net.gencat.ctti.canigo.services.security.AuthorizationSecurityConfiguration"> <!-- Propietat opcional pels beans als que es verificaran les regles de mètodes --> <property name="beanNamesPatternList"> <list> <value>usersManager</value> <value>categoryManager</value> </list> </property> <!-- Propietat opcional per les classes i mètodes autoritzats --> <property name="secureBusinessObjects"> <value> com.business.UsersManager = ROLE_GESTOR com.business.CategoryManager.delete = ROLE_GESTOR com.business.CategoryManager.read = ROLE_GESTOR, ROLE_USUARIO </value> </property> </bean>
Configuració de les Autoritzacions amb ACLS
Exemple:
<bean id="authorizationConfiguration" class="net.gencat.ctti.canigo.services.security. AuthorizationSecurityConfiguration"> <!- Propietat pels getters dels identificadors -> <property name ="domainObjectsIdGetters"> <value>com.business.ComplexDomainObject = getDomainObjectId</value> </property> </bean> Configuració del Servei d'Accés a Informació GeneralEn casos concrets, des del codi voldrem obtenir informació relacionada amb l'usuari connectat. Per aquest objectiu s'ofereix una interfície 'SecurityService'. En l'actualitat s'ofereix una implementació basada en 'Acegi' que podrem utilitzar introduint la següetn configuració:
<bean id="securityService" class="net.gencat.ctti.canigo.services.security.acegi.
SecurityServiceAcegiImpl"/>
En aquest moment, segons la configuració d'injecció de beans (tal i com s'exposa al document de Servei de Configuració) podrem usar el servei des de qualsevol classe de l'aplicació.
Configuració d'HTTP i HTTPSExisteix la possibilitat de configurar un aplicatiu final basat en Canigó diferenciant una part pública (accessible via HTTP i sense que calgui seguretat) i una part privada (accessible via HTTPS). Fitxer de configuració: canigo-services-https.xml Per defecte, tota URL del nostre aplicatiu anirà per HTTP. Caldrà explicitar la propietat httpsURL del bean 'httpsConfigBean', per indicar quins AntPaths (patrons d'URLs) aniràn per protocol HTTPS. A continuació, detallem totes i cadascunes de les propietats del bean 'httpsConfigBean' (fitxer canigo-services-https.xml):
Exemple:
<bean id="httpsConfigBean"class="net.gencat.ctti.canigo.services.web.filter.HttpsConfigBean"> <property name ="httpsPortIn" value ="8444"> <property name ="httpsPortOut" value ="443"> <property name ="httpPortIn" value ="8080"> <property name ="httpPortOut" value ="80"> <property name="httpsURLs"> <list> <value>/**/Admin*.do*</value> <value>/**/edit*.do*</value> </list> </property> A partir de la versió 2.3.9 es soluciona un bug en la configuració HTTPS per a diferents entorns. A continuació es mostra un exemple de la configuració per a cadascun dels modes: https no, exclusiu i mixte: Configuració del fitxer canigo-services-https.xml <property name="httpsPortIn" value="${https.portIn}"/> <property name="httpsPortOut" value="${https.portOut}"/> <property name="httpPortIn" value="${http.portIn}"/> <property name="httpPortOut" value="${http.portOut}"/> <property name="httpsMode" value="${https.Mode}"/> <property name="httpsURLs"> <list> <value>${https.URLs.pattern}</value> <value>${https.URLs.pattern2}</value> </list> </property> Configuració del fitxer https.properties per a cadascun dels modes * Mode "no" https.portIn=0 https.portOut=0 http.portIn=0 http.portOut=0 https.Mode=no https.URLs.pattern= https.URLs.pattern2= és important deixar els ports in/out tant d'http com https a 0 i les urls a buit. Ha d'estar així ja que el servei https no permet informar ports ni URLs. * Mode "exclusiu" https.portIn=0 https.portOut=port http.portIn=0 http.portOut=0 https.Mode=exclusiu https.URLs.pattern= https.URLs.pattern2= és important deixar els ports (excepte el port de sortida https) a 0 i les urls a buit. * Mode "Mixte" https.portIn=8080 https.portOut=80 http.portIn=8443 http.portOut=443 https.Mode=mixte https.URLs.pattern=/*peticionsHttps*/* https.URLs.pattern2= Indicant aquesta informació, per totes aquelles URLs que acompleixen el AntPath indicat sota httpsURLs i que són vàlides (el port d'entrada de la petició que ha d'anar per HTTPS, es correspon amb httpsPortIn), es reescriu la URL canviant el protocol d'http a https, i canviant el port d'entrada pel port de sortida (httpsPortOut). La reescriptura de la URLm, no obstant, només es farà quan es faci un redirect dintre d'alguna JSP a alguna acció d'struts que acompleixi el patró de les URLs definides a httpsURLs (per exemple, per les httpsURLs anteriors: <c:redirect url="/editAccount.do"/>).
Una altra consideració a tenir en compte és que la URL d'entrada ens ve per un port determinat (per exemple: 80 ò 443). En funció d'aquest port podrem saber si es tracta realment d'una petició que ha d'anar per HTTP ò per HTTPS. Direm que una URL és invàl- lida quan, per exemple, s'ha definit a la propietat httpsURLs, però no ve per un port d'entrada segur, httpsPortIn (en aquests casos es llença una excepció del tipus: HttpsSecurityException).
<filter> <filter-name>Https Filter</filter-name> <filter-class> net.gencat.ctti.canigo.services.web.filter.HttpRequestWrapperFilter </filter-class> </filter> <!-- Filter mappings --> <filter-mapping> <filter-name>Https Filter</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping> Utilització del ServeiAutentificació per FormulariHi ha diverses possibilitats d'autentificació des de Web. En aquest apartat ens centrarem en l'autentificació amb formulari (per a més informació consultar la secció '1.10' del tutorial de 'Acegi'). És necessari crear una pàgina en què s'introdueixi l'usuari i el password. Aquesta pàgina ha de complir els requisits següents:
Podem adaptar el codi font següent en les nostres pàgines de Login. El text en negreta i marcat és codi reutilitzable:
<%@ taglib prefix='c' uri='http://java.sun.com/jstl/core'%> <%@ page import="net.sf.acegisecurity.ui.AbstractProcessingFilter"%> <%@ page import="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter"%> <%@ page import="net.sf.acegisecurity.AuthenticationException"%> <html> <head> <title>Login</title> </head> <body> <h1>Login</h1> <p><%-- this form-login-page form is also used as the form-error-page to ask for a login again. --%> <c:if test="${not empty param.login_error}"> <font color="red"> Your login attempt was not successful, try again.<BR> <BR> Reason: <%=((AuthenticationException) session .getAttribute(AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY)) .getMessage()%> </font> </c:if> <form action="<c:url value='j_acegi_security_check'/>" method="POST"> <table> <tr> <td>User:</td> <td> <input type='text' name='j_username' <c:if test="${not empty param.login_error}"> value='<%=session.getAttribute(AuthenticationProcessingFilter. ACEGI_SECURITY_LAST_USERNAME_KEY) %>' </c:if>> </td> </tr> <tr> <td>Password:</td> <td> <input type='password' name='j_password'> </td> </tr> <tr> <td colspan='2'><input name="submit" type="submit"></td> </tr> </table> </form> </body> </html> Utilització de l'Autorització en les nostres pàgines JSPEn cas de voler presentar determinat contingut depenent dels rols de l'usuari podem fer ús dels tags proporcionats per Acegi. Per a això, seguirem els passos següents:
Des de la versió JSP 1.2 no és necessari configurar en el fitxer 'web.xml' la referència a les llibreries. Podem referenciar directament per mitjà d'una url el jar, tal com es mostra a continuació:
<%@ taglib prefix="authz" uri="http://acegisecurity.sf.net/authz" %>
Una vegada definida la referència a la llibreria i el seu prefix 'authz', podem incorporar el tag a la pàgina JSP per mitjà de '<authz:authorize>', en el que podrem definir els atributs següents:
Exemple:
<authz:authorize ifAllGranted="ROLE_ADMIN"> <td> <A href="del.htm?id=" 123">Borrar</A> </td> </authz:authorize> Per a més informació sobre el tag "authorize", consultar la pàgina http://acegisecurity.sourceforge.net/docbook/acegi.html#N10C28 Obtenció d'Informació de Seguretat amb APIEl servei de seguretat, definit amb el bean 'SecurityService' (tal i com s'explica en l'apartat de Configuració) conté una signatura d'operacions Consultar en el JavaDoc la informació sobre la interfície SecurityService: Gestió dels ACLs mitjançant la APIPer a cada instància de la lògica de negoci podem afegir a la base de dades els permisos per mitjà de l'ús de la interfície 'ACLsDAO': Consultar en el JavaDoc la informació sobre la interfície ACLsDAO: Eines de SuportServidor LDAP de proves: openLDAPEls diferents passos per a instal- lar openLDAP i importar un directori LDAP d'exemple són:
Si tot ha funcionat bé, hauríem de veure una sortida com la següent:
![]()
La contrasenya per defecte és "secret".
JxplorerComprovarem que la importació de dades ha funcionat amb Jxplorer, un client LDAP Java i opensource.
La contrasenya per defecte és "secret". La pantalla següent mostra els valors de la diferents paràmetres:
![]()
ExemplesExemple de configuració d'autentificació amb BBDD, SACE i LDAPEn aquesta part es descriuen 2 dels exemples més comuns d'autentificació:
Exemple de configuració amb autentificació SACE<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="${dataSource.jndiName}"/> </bean> <bean id="SACEAuthenticationConfiguration" class="net.gencat.ctti.canigo.services.security.SACEAuthenticationConfiguration"> <property name ="urlSACE" value="https*://sace.prepdc.gencat.intranet/SACE/SACE_Logon.aspx?XMLIn=*"/> <property name ="userNameFormat" value="NIF"/> <property name ="i18nSecurityService" ref="securityI18nService" /> <property name="keyStore" value="classpath:keystore/saceTrust_pre.jks"/> <property name="keyStorePassPhrase" value="saceTrust_pre2007"/> </bean> ... <bean id="securityMessageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>i18n/saceErrorMessages</value> </list> </property> </bean> <bean id="securityDefaultLocale" class="java.util.Locale"> <constructor-arg type="java.lang.String"><value>es</value></constructor-arg> </bean> <bean id="securityI18nService" class="net.gencat.ctti.canigo.services.i18n.impl.SpringI18nServiceImpl"> <property name="messageSource" ref="securityMessageSource"/> <property name="defaultLocale" ref="securityDefaultLocale"/> </bean> <bean id="authenticationConfiguration" class="net.gencat.ctti.canigo.services.security.AuthenticationSecurityConfiguration"> <property name="loginFormUrlValue" value="/login.jsp" /> <property name="authenticationFailureUrlValue" value="/login.jsp" /> <property name="filterProcessesUrl" value="/AppJava/acegiLogin" /> <property name="authenticationProvidersConfigurationList"> <list> <ref local="SACEAuthenticationConfiguration" /> </list> </property> </bean> <bean id="authorizationConfiguration" class="net.gencat.ctti.canigo.services.security.AuthorizationSecurityConfiguration"> <property name="rolesList"> <list> <value>ROLE_ADMIN</value> <value>ROLE_USER</value> </list> </property> <property name="secureUrls"> <value> /**/*.do* = ROLE_USER,ROLE_ADMIN </value> </property> </bean> <import resource="classpath:/spring/acegi-beans.xml" /> </beans> Els beans canigo que usen "Acegi" estan continguts en el jar del servei de seguretat, per la qual cosa haurem d'importar aquest recurs en el nostre "application context" de Spring amb la línia següent:
<import resource="classpath:spring/acegi-beans.xml" /> Autentificació per BBDD amb una BBDD ja existent
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="${dataSource.jndiName}"/> </bean> <bean id="existingDatabaseAuthenticationConfiguration" class="net.gencat.ctti.canigo.services.security.DatabaseAuthenticationConfiguration"> <property name="passwordEncoderClass" value="net.sf.acegisecurity.providers.encoding.PlaintextPasswordEncoder" /> <property name="usersByUserNameQuery" value="SELECT username,password FROM users WHERE username = ?" /> <property name="authoritiesbyUserNameQuery" value="SELECT username, role FROM user_roles WHERE username =?" /> </bean> <bean id="authenticationConfiguration" class="net.gencat.ctti.canigo.services.security.AuthenticationSecurityConfiguration"> <property name="loginFormUrlValue" value="/login.jsp" /> <property name="authenticationFailureUrlValue" value="/login.jsp" /> <property name="filterProcessesUrl" value="/AppJava/acegiLogin" /> <property name="authenticationProvidersConfigurationList"> <list> <ref local=" existingDatabaseAuthenticationConfiguration" /> </list> </property> </bean> <bean id="authorizationConfiguration" class="net.gencat.ctti.canigo.services.security.AuthorizationSecurityConfiguration"> <property name="rolesList"> <list> <value>ROLE_ADMIN</value> <value>ROLE_USER</value> </list> </property> <property name="secureUrls"> <value> /**/*.do* = ROLE_USER,ROLE_ADMIN </value> </property> </bean> <import resource="classpath:/spring/acegi-beans.xml" /> </beans> Exemple de formulari de login amb JSTL i Struts
<%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean" %> <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <%@ page import="net.sf.acegisecurity.ui.AbstractProcessingFilter"%> <%@ page import="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter"%> <%@ page import="net.sf.acegisecurity.AuthenticationException"%> <style type="text/css"> <%-- this form-login-page form is also used as the form-error-page to ask for a login again. --%> <c:if test="${not empty param.login_error}"> <bean:message key="jsp.login.error" /> </c:if> <bean:message key="jsp.login.message" /> <form action="<c:url value='/AppJava/acegiLogin'/>" method="post"> <table width="100%" border="0"> <tr> <td><bean:message key="jsp.login.user" />:</td> <td><input type='text' name='j_username' <c:if test="${not empty param_error}"> value='<%= session.getAttribute(AuthenticationProcessingFilter. ACEGI_SECURITY_LAST_USERNAME_KEY)%>' </c:if>></td> </tr> <tr> <td><bean:message key="jsp.login.password" />:</td> <td><input type='password' name='j_password'></td> </tr> <tr> <td colspan="2" align="right"><input type="submit" value="<bean:message key="jsp. includes.submit"/>"/></td> </tr> </table> </form> Exemple de configuració d'ACLsVolem donar els permisos "llegir" i "escriure" (READ +WRITE) a un usuari "usuari1" per a la instància de la lògica de negoci "Categoria Papagais". Primer, recollim el DAO de seguretat de canigo:
// Get the DAO from the Spring context ACLsDAO securityDAO = (ACLsDAO) applicationContext.getBean("securityDao"); Segon, recollim la instància de la lògica de negoci "Categoria Papagais" , per mitjà d'un DAO:
// Get the business object using a DAO Category papagayo = CategoryDAO.read("papagayo"); Finalment podem afegir els permisos per a aquesta instància per l'usuari "usuari1":
// Add permissions for user "usuario1" for the papagayo securityDAO.addReadPermission("usuario1", papagayo); securityDAO.addWritePermission("usuario1", papagayo); |