J2EE – zabezpečení webové aplikace

Tento příspěvek popisuje, jak zabezpečit webovou aplikaci napsanou na platformě Java a běžící na aplikačním serveru GlassFish. Cílem bude zabránit přístupu na vybrané stránky těm uživatelům, kteří nemají přidělenou potřebnou roli.

1. Vytvoření databáze

  • vytvoříme databázi (v zásadě je jedno, jaký typ DB zvolíme, já použil MySQL)
  • tabulka User (Id: email, FK: Grouptable_email)
  • tabulka Grouptable (Id: email, Varchar: groupid)

2. Nastavení aplikačního serveru

  • otevřeme admin consoli
  • v menu vybereme: Configuration → Security → Realms
  • vytvoříme nový Realm:
    • name: Database
    • classname: com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm
    • další property – viz. obrázek

  • v menu vybereme: Configuration → Security a jako default realm vybereme Database
  • dále vytvoříme connection pool (Resources → JDBC → Connection Pools)
  • vytvoříme JNDI, které nazveme „CeltsMysqlDS“ (Resources → JDBC → JDBC Resources)

    3. Vytvoření a nastavení enterprise aplikace

  • např. v prostředí Netbeans IDE vytvoříme enterprise aplikaci
    • J2EE_security (.ear)
    • J2EE_security-ejb (.jar)
    • J2EE_security-war (.war)
  • nastavíme web.xml
    • nastavíme způsob autentifikace
    • nastavíme logovací a chybovou stránku:

    <login-config>
            <auth-method>FORM</auth-method>
            <form-login-config>
                <form-login-page>/login.jsf</form-login-page>
                <form-error-page>/loginError.jsf</form-error-page>
            </form-login-config>
    </login-config>
    

    • nastavíme seznam uživatelských rolí:

    <security-role>
            <description>READWRITE</description>
            <role-name>READWRITE</role-name>
    </security-role>
    <security-role>
            <description>SUPERWRITE</description>
            <role-name>SUPERWRITE</role-name>
    </security-role>
    <security-role>
            <description>ADMIN</description>
            <role-name>ADMIN</role-name>
    </security-role>
    

    • nastavíme security constraint:

    <security-constraint>
            <display-name>Editation</display-name>
            <web-resource-collection>
                <web-resource-name>edit.jsf</web-resource-name>
                <description/>
                <url-pattern>/edit.jsf</url-pattern>
                <http-method>GET</http-method>
                <http-method>POST</http-method>
                <http-method>HEAD</http-method>
                <http-method>PUT</http-method>
                <http-method>OPTIONS</http-method>
                <http-method>TRACE</http-method>
                <http-method>DELETE</http-method>
            </web-resource-collection>
            <auth-constraint>
                <description>vkládat nesmí uživatel read-only</description>
                <role-name>READWRITE</role-name>
                <role-name>SUPERWRITE</role-name>
                <role-name>ADMIN</role-name>
            </auth-constraint>
            <user-data-constraint>
                <description/>
                <transport-guarantee>NONE</transport-guarantee>
            </user-data-constraint>
    </security-constraint>
    

    • Pokud se bude chtít uživatel dostat na stránku /edit.jsf, bude muset mít jednu z definovaných rolí – tedy buď READWRITE, SUPERWRITE či ADMIN.
    • namapujeme role v konfiguračním souboru sun-web.xml (je nutné ho vytvořit ve stejném adresáři, ve kterém je web.xml):

    <sun-web-app error-url="">
      <context-root>/SecurityGUI</context-root>
      <security-role-mapping>
        <role-name>ADMIN</role-name>
        <group-name>3</group-name>
      </security-role-mapping>
      <security-role-mapping>
        <role-name>READWRITE</role-name>
        <group-name>1</group-name>
      </security-role-mapping>
      <security-role-mapping>
        <role-name>SUPERWRITE</role-name>
        <group-name>2</group-name>
      </security-role-mapping>
      <class-loader delegate="true"/>
      <jsp-config>
        <property name="keepgenerated" value="true">
          <description>Keep a copy of the generated servlet class' java code.</description>
        </property>
      </jsp-config>
    </sun-web-app>
    

    • Pokud tedy bude mít uživatel v tabulce “grouptable” nastavenou hodnotu “groupid” na “1”, přidělí mu aplikace po zalogování roli “READWRITE”.
    • Nyní jsme zprovoznili zabezpečení naší testovací aplikace. J2EE nám samozřejmě také umožní zjistit, jaký klient je právě zalogovaný. Na úrovni webové aplikace to zjišťujeme následujícím způsobem (následující funkce vrátí username zalogovaného uživatele, případně null pro nezalogovaného uživatele):

    public String getLoginName() {
      Principal principal = FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal();
      if (principal == null) {
        return null;
      } else {
        return principal.getName();
      }
    }
    

    • Na úrovni aplikační vrstvy (v EJB), postupujeme takto:
      vytvoříme referenci na context

      @Resource
      SessionContext context;
      

      v mětodě dále:

      Principal principal = context.getCallerPrincipal();
      

    • Chceme-li, aby J2EE zajistilo, že se některá funkce bude spouštět pouze v případě, že má uživatel přiřazenou některou roli, vytvoříme před metodou anotaci @RolesAllowed:

    @RolesAllowed("manager")
    public Country getCountryByIso(String iso) { ... }
    

    • Chceme-li v metodě testovat, zda má uživatel přiřazenou některou roli vytvoříme anotaci @DeclareRoles před třídou:

    @Stateless
    @DeclareRoles({"manager", "manager2"})
    public class CommonDAOBean implements CommonDAOBeanLocal { … }
    

    • poté je možné v metodě použít:

    context.isCallerInRole("manager");
    

    • Pokud bychom na začátku nedeklarovali role, nastala by výjimka.