Manteniment d'estat, autenticació i autorització amb 'servlets' i EJB

En aquest apartat s’explicaran les diferents maneres que té el programador Java Web per mantenir l’estat de l’aplicació. Moltes vegades ens interessa reconèixer un usuari que ha entrat anteriorment al programa i ha seleccionat les seves preferències a l’hora d’interactuar amb l’aplicació.

Existeixen diferents maneres d’emmagatzemar aquest tipus de dades, des de tècniques molt avançades fins a algunes que us podeu inventar per tal de guardar la informació on volem. Però, en general, existeixen dues maneres d’aconseguir-ho.

La primera manera consisteix en el fet que la informació l’ha d’emmagatzemar el mateix usuari. Així, cada vegada que es comuniqui amb el servidor automàticament enviarà tota la informació que s’havia emmagatzemat durant l’última visita al programa. Les diferents tècniques que s’utilitzen per emmagatzemar la informació en el costat client corresponen a la utilització de galetes, formularis amb camps ocults i reescriptura d’adreces web (URL) amb paràmetres.

La segona manera consisteix a guardar la informació en el costat del servidor. El client només s’ha d’identificar per tal d’obtenir les dades que s’havien emmagatzemat. La tècnica que consisteix a guardar la informació en el costat del servidor s’anomena sessió. És una tècnica molt útil que, junt amb les tècniques anteriors, es pot utilitzar per fer aplicacions interactives i amb una experiència molt satisfactòria per part de l’usuari.

A més a més, també s’explicarà com autenticar un usuari que està intentant accedir a un recurs del sistema. Normalment, l’autenticació del client es fa mitjançant un usuari i una contrasenya. Una vegada autenticat, depenent del rol de l’usuari, aquest podrà accedir a unes funcions o a unes altres.

Podeu descarregar-vos el codi Java utilitzat en aquest apartat des de l’enllaç que trobareu a l’apartat d’annexos de la unitat. Recordeu que podeu utilitzar la funció d’importar del Netbeans per accedir a aquest contingut.

Per a aquesta última part de la unitat es recomana haver realitzat l’apartat anomenat “Formularis amb servlets i EJB”, on s’expliquen els diferent elements que intervenen en l’arquitectura EJB.

L'usuari, en una galeta

En aquest apartat aprendrem a emmagatzemar informació utilitzant galetes. El navegador guarda la informació en forma d’arxiu de text al disc dur del visitant de la pàgina web per tal que certes informacions puguin ser recuperades en posteriors visites.

Comencem creant un nou projecte amb Maven, i posteriorment crearem un formulari en una pàgina web que enviï les dades d’un usuari a un servlet per tal que l’emmagatzemi en una galeta (cookie). La pàgina web l’anomenarem galetes.html, i el seu contingut és el següent:

  1. <!DOCTYPE html>
  2. <head>
  3. <title>Galetes</title>
  4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  5. </head>
  6. <body>
  7. <h1>Emmagatzemar l'usuari a una galeta.</h1>
  8. <h2>Introdueix el teu nom:</h2>
  9. <form action="galetesServlet" method="post">
  10. <label for="nom"> Nom:</label>
  11. <input id="nom" type="text" name="userName"/> <br/>
  12. <input type="submit" value="enviar"/>
  13. </form>
  14. </body>
  15. </html>

Com podeu veure a la figura, la pàgina web envia el nom d’usuari al servlet anomenat galetesServlet. Heu de crear aquest servlet, que rebrà el nom d’usuari i l’emmagatzemarà en una galeta.

Figura Enviar dades al ‘servlet’ per crear una galeta
Enviar dades al 'servlet' per crear una galeta

Recordeu que per crear el servlet amb NetBeans heu d’accedir a File / New File / Web / Servlet . No cal que afegiu la configuració del servlet al fitxer web.xml. En aquest cas, utilitzarem les anotacions per configurar-lo. El seu nom serà galetesServlet, i la implementació del seu codi és la següent:

  1. @WebServlet(name = "galetesServlet", urlPatterns = {"/galetesServlet"})
  2. public class GaletesServlet extends HttpServlet {
  3.  
  4. /**
  5.   * Processes requests for both HTTP GET and POST
  6.   * methods.
  7.   *
  8.   * @param request servlet request
  9.   * @param response servlet response
  10.   * @throws ServletException if a servlet-specific error occurs
  11.   * @throws IOException if an I/O error occurs
  12.   */
  13. protected void processRequest(HttpServletRequest request, HttpServletResponse response)
  14. throws ServletException, IOException {
  15. response.setContentType("text/html;charset=UTF-8");
  16. try (PrintWriter out = response.getWriter()) {
  17.  
  18. out.println("<!DOCTYPE html>");
  19. out.println("<html>");
  20. out.println("<head>");
  21. out.println("<title>Servlet galetes1</title>");
  22. out.println("</head>");
  23. out.println("<body>");
  24. out.println("<h1>Ara guardem la galeta en el teu navegador</h1>");
  25.  
  26. String n = request.getParameter("userName");
  27. out.print("Benvingut " + n);
  28.  
  29. Cookie ck = new Cookie("usuari", n);
  30. response.addCookie(ck);
  31.  
  32. out.print("<form action='galetesServlet2'>");
  33. out.print("<input type='submit' value='anar'>");
  34. out.print("</form>");
  35.  
  36. out.println("</body>");
  37. out.println("</html>");
  38. }
  39. }
  40.  
  41. // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
  42. /**
  43.   * Handles the HTTP GET method.
  44.   *
  45.   * @param request servlet request
  46.   * @param response servlet response
  47.   * @throws ServletException if a servlet-specific error occurs
  48.   * @throws IOException if an I/O error occurs
  49.   */
  50. @Override
  51. protected void doGet(HttpServletRequest request, HttpServletResponse response)
  52. throws ServletException, IOException {
  53. processRequest(request, response);
  54. }
  55.  
  56. /**
  57.   * Handles the HTTP POST method.
  58.   *
  59.   * @param request servlet request
  60.   * @param response servlet response
  61.   * @throws ServletException if a servlet-specific error occurs
  62.   * @throws IOException if an I/O error occurs
  63.   */
  64. @Override
  65. protected void doPost(HttpServletRequest request, HttpServletResponse response)
  66. throws ServletException, IOException {
  67. processRequest(request, response);
  68. }
  69.  
  70. /**
  71.   * Returns a short description of the servlet.
  72.   *
  73.   * @return a String containing servlet description
  74.   */
  75. @Override
  76. public String getServletInfo() {
  77. return "Short description";
  78. }// </editor-fold>
  79.  
  80. }

El servlet galetesServlet rebrà el nom d’usuari que el navegador web li envia mitjançant un formulari, crearà la galeta i emmagatzemarà el nom d’usuari dins d’aquesta. A més a més, crearà un altre formulari amb un botó.

Una galeta (més coneguda per la seva denominació anglesa, cookie) és un fragment d’informació enviat des d’un servidor de pàgines web a un navegador que pot ésser retornada pel navegador en posteriors accessos a aquest servidor.

Tipus de galetes

Existeixen molts tipus de galetes. Sempre es generen de la mateixa manera, però es diferencien en el seu ús. Per exemple, existeixen galetes de sessió, galetes persistents, galetes segures (https), galetes només http, i inclús galetes zombie. Aquestes últimes s’emmagatzemen en diversos llocs de l’aplicació, fins i tot en objectes flash, perquè si s’esborren es tornin a generar.

Però anem pas a pas. Primer de tot heu de recuperar l’usuari enviat pel navegador web. Aquest usuari s’emmagatzema a l’objecte request, i amb la funció getParameter(nom_parametre) podeu recuperar-lo:

  1. String n = request.getParameter("userName");

Una vegada teniu la informació que voleu emmagatzemar a la galeta, heu de crear un objecte de tipus cookie, i els seus paràmetres de creació són els següents:

  • name: correspon al nom de la galeta.
  • value: correspon a la informació que es vol emmagatzemar.
  1. Cookie ck = new Cookie("usuari", n);

Ja teniu creada la galeta, que pot ser de dos tipus:

  • Persistent (persistent cookie): no s’esborra quan l’usuari tanca el navegador, sinó que la galeta té un temps màxim de vida i s’esborra quan arriba el moment.
  • No persistent (non-persistent cookie): la galeta s’esborra quan l’usuari tanca el navegador.

Per defecte, la galeta es crea del tipus no persistent. Si voleu crear una galeta de l’altre tipus s’ha d’utilitzar el mètode de l’objecte cookie anomenat setMaxAge(temps_en_millisegons).

Per exemple, per emmagatzemar una galeta al navegador durant 24 hores escriuríem:

  1. ck.setMaxAge(24 * 60 * 60); // 24 hores * 60 minuts * 60 segons

Si volguéssim esborrar una galeta en aquest mateix moment, sense haver d’esperar que l’usuari tanqui el navegador o que li arribi el temps establert, podeu posar-li un temps igual a zero, i la galeta s’esborra en zero mil·lisegons.

  1. ck.setMaxAge(0); //esborra la galeta

Una vegada s’ha creat la galeta, heu d’enviar-la al navegador perquè aquest sigui qui l’emmagatzemi dins del disc dur client.

  1. response.addCookie(ck);

Com podeu veure, la utilització de galetes és una tècnica molt senzilla per guardar informació a l’ordinador. S’utilitza bàsicament per mantenir l’estat de l’aplicació en el costat client.

El desavantatge que us podeu trobar en fer servir les galetes és que el navegador té una opció per evitar que les pàgines web les emmagatzemin. Per exemple, al navegador web Chrome podeu anar a Configuració / Mostrar configuració avançada / Privacitat / Configuració del contingut per modificar el seu comportament quan un servidor vulgui enviar-li una galeta (vegeu la figura).

Figura Configuració de les galetes del navegador Chrome

Com heu pogut comprovar, el funcionament de les galetes és senzill:

  1. L’usuari demana la pàgina al servidor web.
  2. El servidor web li envia la pàgina amb una galeta.
  3. L’usuari envia la galeta cada vegada que es comunica amb el servidor web.

Arribats a aquest punt, la galeta està emmagatzemada al navegador client. Ara veurem com recuperar-la i com mostrar el seu contingut. Ho farem modificant el servlet GaletesServlet afegint un botó que enviï a l’usuari a un altre servlet, que serà qui recuperi el valor de la galeta creada.

El codi s’afegeix després que s’hagi creat la galeta i s’hagi enviat a l’usuari (vegeu la figura):

  1. out.print("<form action='galetesServlet2'>");
  2. out.print("<input type='submit' value='anar'>");
  3. out.print("</form>");

Figura Galeta emmagatzemada al navegador

Aquest botó enviarà l’usuari al servlet galetesServlet2, que recuperà la galeta i mostrarà el seu contingut en una pàgina web.

Cal crear el servlet GaletesServlet2 de la mateixa manera que heu creat anteriorment el servlet GaletesServlet. La implementació del seu codi és la següent:

  1. @WebServlet(name = "galetesServlet2", urlPatterns = {"/galetesServlet2"})
  2. public class GaletesServlet2 extends HttpServlet {
  3.  
  4. /**
  5.   * Processes requests for both HTTP GET and POST
  6.   * methods.
  7.   *
  8.   * @param request servlet request
  9.   * @param response servlet response
  10.   * @throws ServletException if a servlet-specific error occurs
  11.   * @throws IOException if an I/O error occurs
  12.   */
  13. protected void processRequest(HttpServletRequest request, HttpServletResponse response)
  14. throws ServletException, IOException {
  15. response.setContentType("text/html;charset=UTF-8");
  16. try (PrintWriter out = response.getWriter()) {
  17. /* TODO output your page here. You may use following sample code. */
  18. out.println("<!DOCTYPE html>");
  19. out.println("<html>");
  20. out.println("<head>");
  21. out.println("<title>Servlet galetes2</title>");
  22. out.println("</head>");
  23. out.println("<body>");
  24. out.println("<h1>Accedim a la galeta per veure el nom d'usuari.</h1>");
  25. Cookie ck[]=request.getCookies();
  26. out.print("Hola "+ck[0].getValue());
  27. out.println("</body>");
  28. out.println("</html>");
  29. }
  30. }
  31.  
  32. // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
  33. /**
  34.   * Handles the HTTP GET method.
  35.   *
  36.   * @param request servlet request
  37.   * @param response servlet response
  38.   * @throws ServletException if a servlet-specific error occurs
  39.   * @throws IOException if an I/O error occurs
  40.   */
  41. @Override
  42. protected void doGet(HttpServletRequest request, HttpServletResponse response)
  43. throws ServletException, IOException {
  44. processRequest(request, response);
  45. }
  46.  
  47. /**
  48.   * Handles the HTTP POST method.
  49.   *
  50.   * @param request servlet request
  51.   * @param response servlet response
  52.   * @throws ServletException if a servlet-specific error occurs
  53.   * @throws IOException if an I/O error occurs
  54.   */
  55. @Override
  56. protected void doPost(HttpServletRequest request, HttpServletResponse response)
  57. throws ServletException, IOException {
  58. processRequest(request, response);
  59. }
  60.  
  61. /**
  62.   * Returns a short description of the servlet.
  63.   *
  64.   * @return a String containing servlet description
  65.   */
  66. @Override
  67. public String getServletInfo() {
  68. return "Short description";
  69. }// </editor-fold>

Una vegada que està creat, podeu recuperar la galeta de la següent manera:

  1. Cookie ck[]=request.getCookies();

Adoneu-vos que el mètode getCookies() retorna totes les galetes que té el navegador de la nostra pàgina web. Així, si teniu més d’una galeta heu de fer un bucle per trobar la que us interessi. En el nostre cas no cal, només n’hem emmagatzemat una.

Tots els navegadors proporcionen una manera de veure les galetes que ha emmagatzemat una pàgina web. En concret, a Google Chrome hi podem accedir clicant el botó dret a la pàgina i seleccionant l’element de menú anomenat ‘inspecciona’. S’obrirà una finestra on haurem de clicar la pestanya ‘recursos’ per poder veure tota la informació que ens proporciona la pàgina, incloent-hi les galetes.

Així, podeu obtenir el valor de la galeta emmagatzemada fent ús del mètode getValue().

  1. out.print("Hola "+ck[0].getValue());

El resultat de l’execució del codi anterior el podeu veure a la figura.

Figura Mostrar el contingut d’una galeta

Altres maneres d'enviar informació al client

Us podeu trobar que el navegador web no accepti galetes. En aquest apartat veureu unes tècniques no tan sofisticades com les galetes que us poden ajudar a emmagatzemar informació en el costat client.

Utilitzant un camp ocult

Aquesta tècnica empra un camp ocult d’un formulari per enviar informació a un altre servlet.

Utilitzant el mateix projecte Maven, crearem una pàgina web anomenada textOcult.html (File / New File / HTML5 / HTML File). Aquesta pàgina és idèntica a la pàgina galetes.html, únicament heu de canviar el contingut de la propietat action de l’etiqueta formulari:

  1. <form action="TextOcultServlet" method="post">

Vegeu que el servlet que rep la petició de la pàgina web ha canviat. Llavors heu de crear aquest servlet seguint els mateixos passos que vam utilitzar en crear el servlet GaletesServlet.

Una vegada teniu el servlet creat, recolliu l’usuari que ens envien per paràmetre:

  1. String n = request.getParameter("userName");

Seguidament, creem un formulari amb un camp de text ocult i emmagatzemem la informació dins d’aquest camp. L’usuari rebrà aquesta informació, però no la veurà.

  1. out.print("<form action='TextOcultServlet2'>");
  2. out.print("<input type='hidden' name='nom' value='" + n + "'>");
  3. out.print("<input type='submit' value='anar'>");
  4. out.print("</form>");

L’usuari només veurà un botó que li permet anar a una altra pàgina web del programa (vegeu la figura). Aquesta pàgina web serà un altre servlet, anomenat TextOcultServlet2, que rebrà el paràmetre ocult.

Figura Pàgina web amb un text ocult

Finalment, només falta crear el servlet TextOcultServlet2 per rebre la informació que envia el navegador client sense que aquest ho sàpiga. Crearem el servlet de la mateixa manera que vam crear els servlets anteriors i modifiquem la funció processRequest perquè recuperi el paràmetre ocult del formulari i el mostri per pantalla.

  1. out.println("<h1>Recuperem el valor ocult enviat amb el formulari.</h1>");
  2. String n = request.getParameter("nom");
  3. out.print("Hola " + n);

A la figura podeu veure la pàgina que envia el servlet TextOcultServlet2.

Figura Pàgina que mostra un text ocult

Creant un URL amb paràmetres

Aquesta tècnica empra un URL amb paràmetres per enviar informació a un altre servlet.

Utilitzant el mateix projecte Maven, crearem una pàgina web anomenada reescripturaURL.html (File / New File / HTML5 / HTML File). Aquesta pàgina és idèntica a la pàgina galetes.html, únicament heu de canviar el contingut de la propietat action de l’etiqueta formulari:

  1. <form action="ReescripturaURLServlet" method="post">

Vegeu que el servlet que rep la petició de la pàgina web ha canviat. Llavors heu de crear aquest servlet seguint els mateixos passos que en la creació del servlet GaletesServlet.

Una vegada teniu el servlet creat, recolliu l’usuari que ens envien per paràmetre:

  1. String n = request.getParameter("userName");

Seguidament, creem un URL i li afegim un paràmetre de tipus GET. La pàgina de l’usuari rebrà aquesta informació, i quan faci clic enviarà sense saber-ho el paràmetre a l’URL destí.

  1. String n = request.getParameter("userName");
  2. out.print("<br><a href='ReescripturaURLServlet2?nom="+n+"'>anar</a>");

L’usurari només veurà un enllaç que li permet anar a una altra pàgina web del programa (vegeu la figura). El destí d’aquest enllaç serà un altre servlet, anomenat ReescripturaURLServlet2, que rebrà el paràmetre.

Figura Pàgina web amb un paràmetre a l’enllaç

Finalment, només ens falta crear el servlet ReescripturaURLServlet2 per rebre la informació que ens envia el navegador client mitjançant l’URL. Crearem el servlet de la mateixa manera que heu creat els servlets anteriors i modifiquem la funció processRequest perquè recuperi el paràmetre de l’URL i el mostri per pantalla.

  1. out.println("<h1>Recuperem el valor enviat amb la URL.</h1>");
  2. String n = request.getParameter("nom");
  3. out.print("Hola " + n);

A la figura podeu veure la pàgina que envia el servlet ReescripturaURLServlet2.

Figura Pàgina que mostra el paràmetre enviat per l’URL

L'usuari a la sessió

En aquest apartat s’explicarà com guardar i recuperar informació de sessió. L’usuari emplenarà un formulari web. Les dades d’aquest formulari s’enviaran a un servlet que les emmagatzemarà a sessió. Un altre servlet consultarà la sessió i mostrarà les dades emmagatzemades.

Utilitzarem el mateix projecte Maven. Creeu una nova pàgina HTML, amb el programa NetBeans, anomenada sessio.html (File / New File / HTML5 / HTML File), on afegirem les dades d’un formulari web HTML. El codi de la pàgina pot ser semblant a aquest:

  1. <!DOCTYPE html>
  2. <head>
  3. <title>Sessió</title>
  4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  5. </head>
  6. <body>
  7. <h1>Emmagatzemar l'usuari a la sessió.</h1>
  8. <h2>Introdueix el teu nom:</h2>
  9. <form action="SessioServlet" method="post">
  10. <label for="nom"> Nom:</label>
  11. <input id="nom" type="text" name="userName"/> <br/>
  12. <input type="submit" value="enviar"/>
  13. </form>
  14. </body>
  15. </html>

Com podeu veure, el formulari demana el seu nom a l’usuari de la pàgina web. Aquest nom serà enviat al servlet SessioServlet, que el recollirà i l’emmagatzemarà a sessió.

Una sessió serveix per emmagatzemar informació entre diferents peticions HTTP.

En moltes ocasions ens trobarem amb el problema de compartir l’estat (dades d’usuari) entre un conjunt ampli de pàgines de la nostra aplicació. La classe HttpSession, que s’encarrega d’administrar la sessió, té una estructura de diccionari (HashMap) i permet emmagatzemar qualsevol tipus d’objecte de tal manera que pugui ser compartit per les diferents pàgines de l’aplicació.

El servlet SessioServlet és l’encarregat d’utilitzar la classe HttpSession per emmagatzemar el nom d’usuari dins d’ella. Així, crearem el servlet, tal com heu creat els anteriors, i modificarem la funció processRequest per tal d’afegir aquesta funcionalitat.

HttpSession session=request.getSession();  
String n = request.getParameter("userName");
session.setAttribute( "nom",n);
 
out.print("<br><a href='SessioServlet2'>anar</a>");  

Primer de tot, obteniu la sessió mitjançant la funció getSession() de l’objecte Request. Aquesta funció crea una nova sessió si no existeix una sessió creada prèviament. Si ja hi ha una sessió creada, s’utilitzarà la mateixa per poder manipular, si es vol, les dades que contingui.

Una vegada teniu la sessió, afegiu el nom de l’usuari que s’ha recuperat de la petició. Per afegir el nom de l’usuari s’utilitza la funció setAttribute. Aquest mètode rep dos paràmetres: el primer correspon a l’identificador (nom) amb el qual podrem obtenir les dades emmagatzemades. El segon paràmetre correspon a l’objecte (valor) que es vol emmagatzemar.

Altres mètodes interessants de l’objecte sessió:

  • getCreationTime(): retorna quan va ser creada la sessió, mesurada en mil·lisegons, des del dia 1 de gener de 1970.
  • invalidate(): invalida la sessió i desvincula tots els objectes emmagatzemats. Recordeu que el procés Garbage collection recull tots els objectes que no estan vinculats a cap variable i els elimina.
  • getId(): retorna l’identificador de la sessió.

Però on es guarda la sessió? A diferència de les galetes, la sessió es guarda en el costat del servidor. Una vegada s’ha creat la sessió s’envia al navegador de l’usuari una galeta que serveix per identificar-li i associar-li el HashMap que s’acaba de construir perquè pugui emmagatzemar-hi informació (vegeu la figura). A aquest HashMap s’hi pot accedir des de qualsevol altra pàgina, i ens permet compartir informació.

Figura Galeta de sessió
Galeta de sessió

Normalment s’utilitzen tècniques criptogràfiques per emmagatzemar les dades en el servidor en forma de sessió. Així, es garanteixen la confidencialitat, la integritat i la autenticitat de les dades emmagatzemades.

La sessió és individual de cada usuari que es connecta a la nostra aplicació, i la informació no és compartida entre ells. Així doncs, cada usuari disposarà del seu propi HashMap on emmagatzemar la informació que resulti útil entre pàgines.

No s’ha d’abusar de l’emmagatzematge d’objectes a sessió, ja que si teniu molts usuaris concurrents estarem obligant el servidor a utilitzar molta memòria per emmagatzemar-los.

Ja heu vist com emmagatzemar els objectes a sessió, ara veurem com recuperar les dades. Crearem el servlet SessioServlet2 i afegirem un enllaç a la resposta del servlet anterior per tal que puguem accedir a aquest.

  1. out.print("<br><a href='SessioServlet2'>anar</a>");

Modificarem la funció processRequest del servlet SessióServlet2 per accedir a la sessió i recuperar el nom d’usuari que s’ha emmagatzemat.

  1. out.println("<h1>Recuperem el nom d'usuari de la sessió.</h1>");
  2.  
  3. HttpSession session = request.getSession();
  4. String n = (String) session.getAttribute("nom");
  5. out.print("Hola " + n);

Vegeu que per obtenir l’objecte de sessió utilitzem la mateixa funció que vam utilitzar en crear-la. Com que aquest client ja va crear una sessió el mètode getSession agafa la cookie amb la informació de la sessió i pot accedir al HashMap que es va crear amb els valors emmagatzemats.

Per obtenir un valor s’utilitza el mètode getAttribute i s’empra l’identificador del valor que es vol obtenir per recuperar-lo. Una vegada obteniu el valor ja el podrem usar, com en aquest cas, que el mostrem per pantalla. Vegeu la figura per veure’n el resultat final:

Figura Resultat d’obtenir una dada de sessió
Resultat d'obtenir una dada de sessió

Un formulari d'autenticació amb EJB

Es vol crear una aplicació que autentiqui l’usuari mitjançant un nom i una contrasenya. Una vegada s’ha autenticat, hi haurà una sèrie de mètodes d’un objecte EJB als quals tindrà permisos per accedir i alguns altres per als quals no tindrà permisos.

En aquest apartat s’explicaran l’autenticació i l’autorització utilitzant servlets i Enterprise Java Beans (EJB). Es recomana haver fet l’apartat anomenat “Formularis amb servlets i EJB”, on s’explica el funcionament d’un EJB sense estat.

Creeu un nou servlet amb el programa NetBeans anomenat BibliotecaServlet.java (File / New File / Web / Servlet). No cal que afegiu la configuració del servlet al fitxer web.xml. En aquest cas, utilitzarem les anotacions per configurar-lo.

Crearem també un Enterprise Java Beans sense estat anomenat Biblioteca amb una interfície local associada anomenada BibliotecaLocal. Podeu crear les dues classes a la vegada utilitzant el programa Netbeans i accedint a File / New File / Enterprise JavaBeans / SessionBean.

La interfície BibliotecaLocal tindrà tres mètodes:

  • catalogar(String llibre)
  • veureDisponibilitat(String llibre)
  • demanarPrestec(String llibre)

Aquests mètodes els sobreescriurà l’EJB sense estat Biblioteca. El mètode catalogar retornarà una cadena de text dient que s’ha catalogat el llibre, i el mètode veureDisponibilitat compararà si és un llibre Java. En el cas que sigui així, retornarà una cadena de text on s’informarà que està disponible, i en cas contrari s’informarà que no en queden còpies disponibles. Finalment, el mètode demanarPrestec retorna sempre false.

Un exemple de la seva codificació és la següent:

  1. @Stateless
  2. public class Biblioteca implements BibliotecaLocal {
  3.  
  4. @Override
  5. public String catalogar(String llibre){
  6. return "Llibre " + llibre + " catalogat.";
  7. }
  8.  
  9. @Override
  10. public String veureDisponibilitat(String llibre){
  11. if(llibre.equals("Java")){
  12. return "Llibre " + llibre + " disponible.";
  13. }
  14. return "Llibre " + llibre + " no disponible.";
  15. }
  16.  
  17.  
  18. @Override
  19. public Boolean demanarPrestec(String llibre){
  20. return false;
  21. }
  22. }

Volem fer que el mètode catalogar només sigui accessible pel bibliotecari, que el mètode veureDisponibilitat estigui disponible per a tothom i que el mètode demanarPrestec no el pugui utilitzar ningú (ja que encara no s’ha implementat).

L’autenticació és el procés mitjançant el qual el sistema s’assegura que un usuari és qui diu que és.

Per poder autoritzar l’accés als mètodes de l’EJB a un usuari heu de comprovar quin usuari està utilitzant l’aplicació. Podeu emprar els usuaris de GlassFish per portar a terme l’autenticació. Aquests usuaris els podeu configurar mitjançant la consola d’administració (web) del servidor (vegeu la figura figura).

Figura Pàgina d’administració del servidor Glassfish

Per accedir a la consola podeu fer-ho posant a l’URL del navegador l’adreça localhost:4848/common/index.jsf, o bé amb el programa NetBeans accedint al menú desplegat en fer clic amb el botó dret del ratolí damunt del servidor Glassfish i clicant a l’opció View Domain Admin Console.

Una vegada heu accedit a la consola, aneu al menú de l’esquerra i cliqueu a l’opció Configurations / Servlet Config / Security / Realms / File (vegeu la figura).

Figura Menú de la consola d’administració per afegir-hi usuaris

Normalment els usuaris d’una aplicació es troben emmagatzemats en una base de dades, segurament, de la pròpia aplicació. No hem d’oblidar, però, que els servidors d’aplicacions ens proporcionen un mecanisme d’autenticació per a aplicacions petites.

Si us fixeu, el menú Realm té tres opcions:

  • admin-realm: és un fitxer on s’emmagatzemen les credencials dels usuaris administradors. Aquest fitxer s’anomena admin-keyfile.
  • certificate: el servidor emmagatzema les credencials d’usuari en una base de dades de certificats. Quan s’utilitza aquesta opció, el servidor utilitza certificats amb HTTPS per autenticar els clients web. Per verificar la identitat d’un usuari en el domini certificat, el servei d’autenticació verifica un certificat X.509.
  • file: el servidor emmagatzema les credencials d’usuari local en un arxiu amb el nom de keyfile. El servei d’autenticació del servidor verifica la identitat de l’usuari mitjançant la comprovació d’aquest fitxer, que s’utilitza per a l’autenticació de tots els clients excepte per als clients de l’explorador web que utilitzen HTTPS i certificats.

Un àmbit o domini (realm) és una política de seguretat definida per a un servidor web o aplicació. Conté una col·lecció d’usuaris, que poden o no poden ser assignats a un grup.

Vosaltres emmagatzemareu tots els usuaris dintre d’un fitxer al nostre servidor web, i llavors accedireu a la opció de menú file.

Tot seguit, procedireu a la creació d’usuaris clicant al botó Manage users. Una vegada heu accedit al llistat d’usuaris, el programa us permet crear de nous clicant al botó new. Si ho feu, apareixerà una pantalla com la que podeu veure a la figura.

Figura Creació d’un usuari nou

Hi afegireu dos usuaris. El primer l’anomenareu alex, o amb qualsevol altre nom, i pertanyerà al grup bibliotecari. En canvi, el segon usuari pertanyerà al grup d’user, i el seu nom serà user (vegeu la figura).

Figura Llistat d’usuaris del servidor Glassfish

Una vegada definits els usuaris i els grups al servidor Glassfish heu de definir els rols dintre del fitxer de desplegament web.xml. Si no teniu aquest fitxer podeu crear-lo amb el programa NetBeans. Aneu a File / New File / Web / Standard Deployment Descriptor (web.xml) i creeu-lo dins de la carpeta WEB-INF.

Aneu a l’apartat de seguretat del fitxer web.xml i afegiu-hi els rols, tal com podeu veure a la figura):

Figura Configuració dels rols d’usuari al fitxer web.xml

A continuació cal especificar quin servlet de l’aplicació necessita autenticació. Per accedir a aquests servlets se’ls demanarà un usuari i una contrasenya. Per definir-los cal crear una restricció (constraint) nova en el fitxer web.xml.

A més a més, en la restricció s’ha d’especificar quins rols podran utilitzar aquest servlet. Cal seleccionar, mitjançant el botó Edit de l’opció Enable autentication Constraint, els rols creats anteriorment (vegeu la figura).

Figura Configuració de l’autenticació

Finalment, haureu de tenir un fitxer web.xml com aquest:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
  3. <session-config>
  4. <session-timeout>
  5. 30
  6. </session-timeout>
  7. </session-config>
  8. <security-constraint>
  9. <display-name>restriccion1</display-name>
  10. <web-resource-collection>
  11. <web-resource-name>login</web-resource-name>
  12. <description/>
  13. <url-pattern>/bibliotecaServlet</url-pattern>
  14. </web-resource-collection>
  15. <auth-constraint>
  16. <description/>
  17. <role-name>user</role-name>
  18. <role-name>bibliotecari</role-name>
  19. </auth-constraint>
  20. </security-constraint>
  21. <login-config>
  22. <auth-method>BASIC</auth-method>
  23. </login-config>
  24. <security-role>
  25. <description/>
  26. <role-name>user</role-name>
  27. </security-role>
  28. <security-role>
  29. <description/>
  30. <role-name>bibliotecari</role-name>
  31. </security-role>
  32. </web-app>

Per tal que funcioni, al servidor Glassfish s’ha d’activar el rol principal. Podeu anar a la web d’administració del servidor Glassfish i al menu Server-config / Security activar l’opció (vegeu la figura).

Figura Activació del rol principal al servidor Glassfish

Arribats a aquest punt, ja heu acabat de configurar l’autenticació per al servlet BibliotecaServlet. A partir d’ara, cada vegada que accediu al servlet us demanarà que proporcioneu un usuari i una contrasenya vàlids.

Seguidament, definireu els rols dels usuaris que poden executar els diferents mètodes de l’EJB biblioteca. Aquest rols han de coincidir amb els rols definits a l’arxiu web.xml anterior.

L’autorització defineix quin usuari o grups d’usuari poden executar un recurs determinat. Tot i que tinguin permís d’accés a un recurs, potser no poden utilitzar tot el recurs, sinó només una part.

És essencial identificar el sistema o els usuaris que accedeixen a les aplicacions i proporcionar l’accés o la negació dels recursos dins de l’aplicació basada en alguns criteris. No tots els usuaris han de tenir els drets d’accés a dades sensibles i cal que hi hagi algun mecanisme d’identificació per restringir-ho.

En el vostre cas, el recurs EJB anomenat biblioteca poden utilitzar-lo, mitjançant el servlet BibliotecaServlet, els usuaris que pertanyin als rols bibliotecari o users. Però, en concret, voleu que el mètode catalogar només sigui accessible per al bibliotecari, que el mètode veureDisponibilitat estigui disponible per a tothom i que el mètode demanarPrestec no el pugui utilitzar ningú (ja que encara no s’ha implementat).

L’autorització s’ha de fer a nivell d’EJB. Heu d’afegir, mitjançant anotacions, quins mètodes poden executar els diferents rols del sistema. Les anotacions que podeu fer servir són les següents:

  • DeclareRoles: indica que l’EJB acceptarà els rols definits amb aquesta anotació.
  • RolesAllowed: indica quin mètode pot ser accessible per un rol determinat.
  • PermitAll: indica que aquest mètode és accessible per a tothom.
  • DenyAll: indica que aquest mètode no és accessible per cap rol.

A continuació, modificareu la classe EJB Biblioteca per afegir-hi la informació anterior:

  1. @DeclareRoles({"bibliotecari"})
  2. @Stateless
  3. public class Biblioteca implements BibliotecaLocal {
  4.  
  5. @RolesAllowed({"bibliotecari"})
  6. @Override
  7. public String catalogar(String llibre){
  8. return "Llibre " + llibre + " catalogat.";
  9. }
  10.  
  11. @PermitAll
  12. @Override
  13. public String veureDisponibilitat(String llibre){
  14. if(llibre.equals("Java")){
  15. return "Llibre " + llibre + " disponible.";
  16. }
  17. return "Llibre " + llibre + " no disponible.";
  18. }
  19.  
  20. @DenyAll
  21. @Override
  22. public Boolean demanarPrestec(String llibre){
  23. return false;
  24. }
  25.  
  26. }

D’aquesta manera, la funció catalogar només la podran executar els usuaris que pertanyin al rol bibliotecari, la funció veureDisponibilitat podrà executar-la tothom que tingui accés a l’EJB i la funció demanarPrestec no podrà executar-la ningú.

Les versions anteriors d’EJB 3.0 no podien utilitzar anotacions i empraven un fitxer XML addicional per configurar el comportament. El fitxer s’anomena ejb-jar.xml i s’ha de crear a la mateixa carpeta que el fitxer de desplegament web.xml. Un exemple de declaració de rols EJB (que han de coincidir amb els declarats al fitxer web.xml) pot ser:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE sun-ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 EJB 3.0//EN" "http://www.sun.com/software/appserver/dtds/sun-ejb-jar_3_0-0.dtd">
  3. <sun-ejb-jar>
  4. <security-role-mapping>
  5. <role-name>user</role-name>
  6. <group-name>user-group</group-name>
  7. </security-role-mapping>
  8. <security-role-mapping>
  9. <role-name>bibliotecari</role-name>
  10. <group-name>bibliotecari-group</group-name>
  11. </security-role-mapping>
  12. <enterprise-beans/>
  13. </sun-ejb-jar>

Ja heu acabat de donar l’autorització demanada als rols dels usuaris perquè puguin executar els mètodes apropiats de l’objecte EJB. A continuació implementareu el servlet BibliotecaServlet perquè executi uns mètodes o uns altres segons el rol de l’usuari.

Primer injectareu l’objecte EJB dintre del servlet per poder-lo utilitzar.

  1. @EJB
  2. private BibliotecaLocal b;

A continuació, modificareu la funció processRequest perquè mostri per pantalla el nom de l’usuari que ha accedit al servlet i que comprovi si l’usuari és bibliotecari.

  1. out.println("<p>Usuari: " + request.getUserPrincipal() + "</p>");
  2. if(request.isUserInRole("bibliotecari")){
  3. out.println("<p>" + b.catalogar("java") + "</p>");
  4. out.println("<p>" + b.veureDisponibilitat("php") + "</p>");
  5. }

Amb la funció getUserPrincipal() teniu accés al nom de l’usuari que s’ha registrat a l’aplicació, i la funció isUserInRole(nomRol) comprova si l’usuari té el rol enviat com a paràmetre de la funció. En el cas que l’usuari sigui bibliotecari s’executaran les funcions del component EJB catalogar i veureDisponibilitat.

En canvi, si l’usuari que s’ha registrat té el rol user, llavors no hauria d’accedir a catalogar, ja que no hi està autoritzat, però sí que podria accedir a veureDisponibilitat. El codi seria el següent:

  1. if(request.isUserInRole("user")){
  2. out.println("<p>" + b.veureDisponibilitat("java") + "</p>");
  3. }

Finalment, es mostra el que passa si qualsevol dels dos usuaris intenta accedir a la funció demanarPrestec. Aquesta funció està configurada de tal manera que cap dels dos usuaris hauria de poder-la executar. Si ho fan, el component EJB llença una excepció que s’ha de recollir i tractar.

  1. if(b.demanarPrestec("java")){
  2. out.println("<p> Mai es veurà aquest text.</p>");
  3. }

En executar el servlet BibliotecaServlet us demana un usuari i una contrasenya (vegeu la figura).

Figura Finestra d’autenticació per accedir al ‘servlet’ BibliotecaServlet

En utilitzar l’usuari amb rol bibliotecari, la informació que mostra es pot veure a la figura.

Figura Execució dels mètodes autoritzats al rol ‘bibliotecari’

En canvi, si s’utilitza l’usuari amb el rol user, la informació que mostra es pot veure a la figura.

Figura Execució dels mètodes autoritzats al rol ‘usuari’

Si us n’heu adonat, tant en l’execució del programa amb el rol bibliotecari com en l’execució del programa amb el rol usuari, el servlet ha mostrat per pantalla que no teniu permisos per accedir a un recurs. Això és degut al fet que intenteu accedir, tant en un cas com en l’altre, a la funció demanarPrestec(), i aquesta funció no es pot utilitzar. De fet, el servidor llança la següent excepció (podeu veure el log d’execució).

Información:   JACC Policy Provider: Failed Permission Check, context(U2-A3-Servlets/U2-A3-Servlets_internal)- permission(("javax.security.jacc.EJBMethodPermission" "Biblioteca" "demanarPrestec,Local,java.lang.String"))
Advertencia:   A system exception occurred during an invocation on EJB Biblioteca, method: public java.lang.Boolean cat.ioc.m7.u2.a3.servlets.Biblioteca.demanarPrestec(java.lang.String)
Advertencia:   javax.ejb.AccessLocalException: Client not authorized for this invocation

Què s'ha après?

Al acabar aquest apartat s’ha après a utilitzar correctament les diferents tècniques per guardar les preferències de l’usuari de l’aplicació. Les tècniques estudiades són les següents:

  • Cookies
  • Formularis amb informació oculta
  • Reescriptura d’adreces web (URL)
  • Sessions

A més a més, s’ha utilitzat els components de seguretat EJB per realitzar l’autenticació i l’autorització dels usuaris. Així, tot i que els usuaris poden accedir a l’aplicació mitjançant unes credencials pot ser, no tenen els permisos suficients per accedir a totes les parts del sistema.

En acabar aquest apartat, els estudiants ja estan preparats per començar les activitats proposades en aquest apartat i poden continuar amb el seu aprenentatge del llenguatge Java Web.

Anar a la pàgina anterior:
Annexos
Anar a la pàgina següent:
Activitats