Java Web Aufbau – Tag 4 von 10
Von Elyndra Valen, Senior Developer bei Java Fleet Systems Consulting

Container-Managed

📋 Deine Position im Kurs

TagThemaStatus
1Filter im Webcontainer✅ Abgeschlossen
2Listener im Webcontainer✅ Abgeschlossen
3Authentifizierung gegenüber einer Datenbank✅ Abgeschlossen
4Container-Managed Security & Jakarta EE Security API👉 DU BIST HIER!
5Custom Tags & Tag Handler (SimpleTag)🔒 Noch nicht freigeschaltet
6Custom Tag Handler mit BodyTagSupport🔒 Noch nicht freigeschaltet
7JPA vs JDBC – Konfiguration & Persistence Provider🔒 Noch nicht freigeschaltet
8Relationen (1) in der JPA – @OneToOne & @ManyToOne🔒 Noch nicht freigeschaltet
9Relationen (2) in der JPA – @OneToMany & @ManyToMany🔒 Noch nicht freigeschaltet
10JSF Überblick – JavaServer Faces🔒 Noch nicht freigeschaltet

🎯 Was du heute lernst

Progressive Security-Levels:

🟢 Level 1: Basic Authentication + File Realm (Einfachster Start)
🟡 Level 2: Form-based Authentication + File Realm (Bessere UX)
🔵 Level 3: JDBC Realm + Datenbank (Production-Ready)

Alle drei Application Server:

  • Apache Tomcat 10.x
  • GlassFish 7.x
  • Payara 6.x

🟢 LEVEL 1: Basic Authentication mit File Realm

Konzept

Basic Authentication = Browser zeigt Standard-Login-Dialog

Vorteile:

  • ✅ Extrem einfach (10 Zeilen Config)
  • ✅ Keine Login-Seite nötig
  • ✅ Perfekt für Testing

Nachteile:

  • ❌ Hässlicher Browser-Dialog
  • ❌ Kein echtes Logout
  • ❌ Nicht für Production Web-Apps

Implementation

1. Protected Servlet erstellen:

@WebServlet("/secure/dashboard")
public class SecureServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        String username = request.getUserPrincipal().getName();
        boolean isAdmin = request.isUserInRole("admin");
        
        response.getWriter().println("<h1>Welcome, " + username + "!</h1>");
        response.getWriter().println("<p>Admin: " + isAdmin + "</p>");
    }
}

2. web.xml konfigurieren:

<security-constraint>
    <web-resource-collection>
        <web-resource-name>Secure Area</web-resource-name>
        <url-pattern>/secure/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>admin</role-name>
        <role-name>user</role-name>
    </auth-constraint>
</security-constraint>

<login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>file</realm-name>
</login-config>

<security-role>
    <role-name>admin</role-name>
</security-role>
<security-role>
    <role-name>user</role-name>
</security-role>

3. File Realm konfigurieren:

Tomcat (conf/tomcat-users.xml):

<tomcat-users>
  <role rolename="admin"/>
  <role rolename="user"/>
  
  <user username="admin" password="admin123" roles="admin,user"/>
  <user username="john" password="john123" roles="user"/>
</tomcat-users>

GlassFish/Payara (Admin-Console):

http://localhost:4848
→ Configurations → Security → Realms → file → Manage Users
→ New: admin / admin123 / Groups: admin,user

Testen

http://localhost:8080/yourapp/secure/dashboard
→ Browser zeigt Login-Dialog
→ Eingabe: admin / admin123
→ Dashboard wird angezeigt!

🟡 LEVEL 2: Form-based Authentication mit File Realm

Konzept

Form-based Auth = Eigenes Login-Formular (JSP)

Vorteile gegenüber Basic Auth:

  • ✅ Eigenes Design möglich
  • ✅ Bessere UX
  • ✅ Echter Logout
  • ✅ Fehlermeldungen gestaltbar

Implementation

1. Login-Formular erstellen (login.jsp):

<!DOCTYPE html>
<html>
<head>
    <title>Login</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            display: flex;
            align-items: center;
            justify-content: center;
            min-height: 100vh;
        }
        .login-container {
            background: white;
            padding: 40px;
            border-radius: 16px;
            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
            max-width: 400px;
        }
        input {
            width: 100%;
            padding: 12px;
            margin: 8px 0;
            border: 2px solid #e0e0e0;
            border-radius: 8px;
        }
        button {
            width: 100%;
            padding: 14px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            border-radius: 8px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div class="login-container">
        <h1>🔒 Secure Login</h1>
        
        <% if (request.getParameter("error") != null) { %>
            <div style="color: red; margin-bottom: 20px;">
                ❌ Login failed! Invalid credentials.
            </div>
        <% } %>
        
        <!-- KRITISCH: Diese Namen MÜSSEN exakt so sein! -->
        <form method="POST" action="j_security_check">
            <label>Username</label>
            <input type="text" name="j_username" required autofocus>
            
            <label>Password</label>
            <input type="password" name="j_password" required>
            
            <button type="submit">Log In</button>
        </form>
        
        <div style="margin-top: 20px; font-size: 13px; color: #666;">
            <strong>Demo:</strong> admin/admin123 oder john/john123
        </div>
    </div>
</body>
</html>

WICHTIG:

  • Form Action: j_security_check (Jakarta EE Standard!)
  • Username Field: j_username (nicht ändern!)
  • Password Field: j_password (nicht ändern!)

2. web.xml anpassen:

<login-config>
    <auth-method>FORM</auth-method>
    <realm-name>file</realm-name>
    <form-login-config>
        <form-login-page>/login.jsp</form-login-page>
        <form-error-page>/login.jsp?error=true</form-error-page>
    </form-login-config>
</login-config>

3. Logout implementieren:

@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        HttpSession session = request.getSession(false);
        if (session != null) {
            session.invalidate();
        }
        
        request.logout();
        response.sendRedirect(request.getContextPath() + "/login.jsp");
    }
}

Testen

http://localhost:8080/yourapp/secure/dashboard
→ Redirect zu /login.jsp (dein Formular!)
→ Eingabe: admin / admin123
→ Automatic redirect zu /secure/dashboard
→ Logout-Link funktioniert!

🔵 LEVEL 3: JDBC Realm mit Datenbank

Konzept

JDBC Realm = User-Daten in relationaler Datenbank

Vorteile:

  • ✅ Dynamische User-Verwaltung
  • ✅ Skalierbar (Millionen User)
  • ✅ Registrierung via Web-UI möglich
  • ✅ Production-Ready

Datenbank-Setup

1. Schema erstellen:

CREATE DATABASE securitydb;
USE securitydb;

-- User-Tabelle
CREATE TABLE users (
    username VARCHAR(255) PRIMARY KEY,
    password VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Rollen-Tabelle  
CREATE TABLE user_roles (
    username VARCHAR(255) NOT NULL,
    role_name VARCHAR(50) NOT NULL,
    PRIMARY KEY (username, role_name),
    FOREIGN KEY (username) REFERENCES users(username) ON DELETE CASCADE
);

2. Test-User einfügen (mit SHA-256 Hashes):

-- admin / admin123 (Hash: 240be518fabd...)
INSERT INTO users (username, password) VALUES
('admin', '240be518fabd2724ddb6f04eeb1da5967448d7e831c08c8fa822809f74c720a9');

-- john / john123 (Hash: ecd71870d196...)
INSERT INTO users (username, password) VALUES
('john', 'ecd71870d1963316a97e3ac3408c9835ad8cf0f3c1bc703527c30265534f75ae');

-- Rollen zuweisen
INSERT INTO user_roles (username, role_name) VALUES
('admin', 'admin'),
('admin', 'user'),
('john', 'user');

Hash generieren:

# Linux/Mac
echo -n "admin123" | sha256sum

# Online-Tool
https://emn178.github.io/online-tools/sha256.html

Configuration: Apache Tomcat

1. MySQL Connector hinzufügen:

Kopiere mysql-connector-j-8.2.0.jar nach $TOMCAT_HOME/lib/

2. META-INF/context.xml erstellen:

<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <!-- Datasource -->
    <Resource name="jdbc/securityDS"
              auth="Container"
              type="javax.sql.DataSource"
              maxTotal="20"
              maxIdle="10"
              username="root"
              password="root"
              driverClassName="com.mysql.cj.jdbc.Driver"
              url="jdbc:mysql://localhost:3306/securitydb?useSSL=false&amp;serverTimezone=UTC"/>
    
    <!-- JDBC Realm -->
    <Realm className="org.apache.catalina.realm.DataSourceRealm"
           dataSourceName="jdbc/securityDS"
           userTable="users"
           userNameCol="username"
           userCredCol="password"
           userRoleTable="user_roles"
           roleNameCol="role_name"
           digest="SHA-256"/>
</Context>

Configuration: GlassFish/Payara

Admin-Console: http://localhost:4848

1. JDBC Connection Pool erstellen:

Resources → JDBC → JDBC Connection Pools → New
Name: securityPool
Driver: MySQL
Properties:
  - serverName: localhost
  - portNumber: 3306
  - databaseName: securitydb
  - user: root
  - password: root

2. JDBC Resource erstellen:

Resources → JDBC → JDBC Resources → New
JNDI Name: jdbc/securityDS
Pool: securityPool

3. JDBC Realm erstellen:

Configurations → server-config → Security → Realms → New
Name: jdbcRealm
Class: com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm
Properties:
  - datasource-jndi = jdbc/securityDS
  - user-table = users
  - user-name-column = username
  - password-column = password
  - group-table = user_roles
  - group-name-column = role_name
  - digest-algorithm = SHA-256
  - encoding = Hex

4. web.xml anpassen:

<login-config>
    <auth-method>FORM</auth-method>
    <realm-name>jdbcRealm</realm-name>
    <form-login-config>
        <form-login-page>/login.jsp</form-login-page>
        <form-error-page>/login.jsp?error=true</form-error-page>
    </form-login-config>
</login-config>

Server neu starten!

Testen

http://localhost:8080/yourapp/secure/dashboard
→ Login mit admin/admin123
→ Server fragt Datenbank!
→ SELECT password FROM users WHERE username='admin'
→ Hash-Vergleich
→ SELECT role_name FROM user_roles WHERE username='admin'
→ Login erfolgreich!

Hier sind relevante externe Ressourcen zu Container-based Security in Jakarta EE:

Offizielle Jakarta EE Dokumentation

Jakarta Security Tutorial – Umfassende Einführung in Jakarta Security mit praktischen Beispielen zu HttpAuthenticationMechanism, IdentityStore und SecurityContext

Jakarta Security Specifications – Die offizielle Spezifikation mit allen Details zu deklarativer und programmatischer Sicherheit

Security Authorization and Authentication Explained – Erläutert das Zusammenspiel von Jakarta Security, Jakarta Authentication und Jakarta Authorization

Securing Web Applications – Fokus auf Web-Container-Sicherheit mit Servlets

Securing Enterprise Applications – Container-basierte Sicherheit für Enterprise Beans

Tutorials und Praxisbeispiele

Baeldung: Jakarta EE 8 Security API – Praktische Beispiele für HttpAuthenticationMechanism, IdentityStore (Database, LDAP) mit Code-Beispielen

DZone: Jakarta EE Security – Using Identity Stores – Detailliertes Tutorial für RDBMS und LDAP Identity Stores mit Payara

Eclipse Foundation: Jakarta EE Security Specifications – Überblick über die drei Haupt-Spezifikationen und deren Zusammenspiel

Diese Ressourcen decken die wichtigsten Aspekte der containerbasierten Sicherheit ab: deklarative Security mit Annotationen (@RolesAllowed, @DeclareRoles), programmatische Security mit SecurityContext, sowie die Konfiguration von Authentication Mechanisms und Identity Stores.


💬 Real Talk: Container-Managed vs. Selbst gebaut

Java Fleet Küche, 14:30 Uhr

Nova: „Elyndra, warum sollte ich CMS nutzen? Mein Login-Servlet von Tag 3 funktioniert doch!“

Elyndra: „Drei Gründe:

  1. Security – Du baust wahrscheinlich Security-Lücken ein
  2. Wartbarkeit – 300 Zeilen Code vs. 20 Zeilen Config
  3. Standardisierung – Portable zwischen allen Servern“

Nova: „Aber ich verstehe meinen Code besser…“

Elyndra: „Verstehst du auch SHA-256 Salt-Generation? SQL-Injection-Prevention? Session-Hijacking-Abwehr? Der Container macht das richtig.“

Nova: „Okay, überzeugt. Real talk: Ich nutze CMS.“


✅ Checkpoint & Quiz

Mini-Challenge:

Implementiere ein JDBC-basiertes Login-System mit:

  • 3 Usern (admin, manager, user)
  • 3 geschützten Bereichen (/admin/, /manager/, /user/*)
  • Deployment auf allen drei Servern

Lösung: Am Anfang von Tag 5!

Quiz:

  1. Was ist der Unterschied zwischen Basic Auth und Form-based Auth?
  2. Warum MUSS das Login-Formular j_security_check als Action haben?
  3. Wie funktioniert Logout bei Basic Auth vs. Form-based Auth?
  4. Welche SQL-Tabellen braucht ein JDBC Realm mindestens?
  5. Warum hasht man Passwörter statt sie im Klartext zu speichern?
  6. Wie gibt man einem User mehrere Rollen in JDBC Realm?

❓ FAQ

F1: Kann ich BCrypt statt SHA-256 nutzen?

Ja! BCrypt ist sogar besser für Passwörter:

<!-- Tomcat -->
<Realm ... digest="bcrypt"/>

F2: Wie erzwinge ich HTTPS?

<security-constraint>
    <web-resource-collection>
        <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <user-data-constraint>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

F3: Wie implementiere ich „Remember Me“?

Nicht Teil von CMS – musst du selbst bauen:

  • Cookie mit Token erstellen
  • Token in DB speichern
  • Bei Request Token prüfen → Auto-Login

F4: Bernd meinte, Sessions sind old-school. Stimmt das?

Lowkey ja für moderne Microservices (JWT besser).

Aber für klassische Web-Apps mit JSP? Sessions sind perfekt!

Learn both – du brauchst beides in der Praxis.


📚 Quiz-Lösungen

Frage 1: Basic Auth vs. Form-based Auth

Basic AuthForm-based Auth
Browser-DialogEigenes Formular
StatelessSession-basiert
Kein LogoutEchtes Logout
Für APIs gutFür Web-Apps gut

Frage 2: Warum j_security_check?

j_security_check ist Jakarta EE Standard!

Der Container registriert automatisch ein internes Servlet unter dieser URL.

Andere Namen funktionieren NICHT!

Frage 3: Logout

Basic Auth: Kein echtes Logout (Browser cached Credentials)

Form-based Auth:

session.invalidate();
request.logout();

Frage 4: SQL-Tabellen für JDBC Realm

Minimum zwei Tabellen:

  1. users (username, password)
  2. user_roles (username, role_name)

Frage 5: Warum Passwort-Hashing?

  1. Datenbank-Leak – Hacker hat nur nutzlose Hashes
  2. Insider-Angriffe – Admins können Passwörter nicht lesen
  3. Compliance – DSGVO verlangt Hashing
  4. Reputation – Vertrauensverlust vermeiden

Frage 6: Mehrere Rollen

Mehrere Zeilen in user_roles:

INSERT INTO user_roles VALUES ('admin', 'admin');
INSERT INTO user_roles VALUES ('admin', 'user');
INSERT INTO user_roles VALUES ('admin', 'manager');

🔵 BONUS: Jakarta EE Security API

Der moderne Ansatz (Java statt XML):

@FormAuthenticationMechanismDefinition(
    loginToContinue = @LoginToContinue(
        loginPage = "/login.jsp",
        errorPage = "/login.jsp?error=true"
    )
)
@DatabaseIdentityStoreDefinition(
    dataSourceLookup = "java:app/jdbc/securityDS",
    callerQuery = "SELECT password FROM users WHERE username = ?",
    groupsQuery = "SELECT role_name FROM user_roles WHERE username = ?",
    hashAlgorithm = Pbkdf2PasswordHash.class
)
@ApplicationScoped
public class SecurityConfig {
}

CDI Security Context:

@Inject
private SecurityContext securityContext;

String username = securityContext.getCallerPrincipal().getName();
boolean isAdmin = securityContext.isCallerInRole("admin");

Vorteile:

  • ✅ Alles in Java (keine XML)
  • ✅ CDI-Integration
  • ✅ Testbar (Mock SecurityContext)
  • ✅ Modern

🎉 Tag 4 geschafft!

Slay! Du hast es geschafft! 🚀

Das hast du heute gerockt:

  • ✅ Basic Authentication implementiert
  • ✅ Form-based Authentication mit eigenem Design
  • ✅ JDBC Realm mit Datenbank konfiguriert
  • ✅ Auf drei Servern deployed (Tomcat, GlassFish, Payara)
  • ✅ Production-Ready Security erstellt

Von Browser-Dialog zu vollwertigem Datenbank-Login-System!

Main Character Energy: Unlocked!


Wie geht’s weiter?

Morgen (Tag 5): Custom Tags & Tag Handler

Erstelle wiederverwendbare JSP-Komponenten!


Troubleshooting

ClassNotFoundException: com.mysql.cj.jdbc.Driver → MySQL Connector fehlt in $TOMCAT_HOME/lib/

Login failed obwohl Credentials stimmen → Hash-Algorithmus prüfen (SHA-256?)

403 Forbidden nach Login → Rolle fehlt in Datenbank!

Realm not found → Server neu starten nach Realm-Erstellung


Bis morgen! 👋

Elyndra


Java Web Aufbau – Tag 4 von 10
© 2025 Java Fleet Systems Consulting
Website: java-developer.online

Autor

  • Elyndra Valen

    28 Jahre alt, wurde kürzlich zur Senior Entwicklerin befördert nach 4 Jahren intensiver Java-Entwicklung. Elyndra kennt die wichtigsten Frameworks und Patterns, beginnt aber gerade erst, die tieferen Zusammenhänge und Architektur-Entscheidungen zu verstehen. Sie ist die Brücke zwischen Junior- und Senior-Welt im Team.