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

📋 Deine Position im Kurs
| Tag | Thema | Status |
|---|---|---|
| 1 | Filter im Webcontainer | ✅ Abgeschlossen |
| 2 | Listener im Webcontainer | ✅ Abgeschlossen |
| 3 | Authentifizierung gegenüber einer Datenbank | ✅ Abgeschlossen |
| → 4 | Container-Managed Security & Jakarta EE Security API | 👉 DU BIST HIER! |
| 5 | Custom Tags & Tag Handler (SimpleTag) | 🔒 Noch nicht freigeschaltet |
| 6 | Custom Tag Handler mit BodyTagSupport | 🔒 Noch nicht freigeschaltet |
| 7 | JPA vs JDBC – Konfiguration & Persistence Provider | 🔒 Noch nicht freigeschaltet |
| 8 | Relationen (1) in der JPA – @OneToOne & @ManyToOne | 🔒 Noch nicht freigeschaltet |
| 9 | Relationen (2) in der JPA – @OneToMany & @ManyToMany | 🔒 Noch nicht freigeschaltet |
| 10 | JSF Ü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&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:
- Security – Du baust wahrscheinlich Security-Lücken ein
- Wartbarkeit – 300 Zeilen Code vs. 20 Zeilen Config
- 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:
- Was ist der Unterschied zwischen Basic Auth und Form-based Auth?
- Warum MUSS das Login-Formular
j_security_checkals Action haben? - Wie funktioniert Logout bei Basic Auth vs. Form-based Auth?
- Welche SQL-Tabellen braucht ein JDBC Realm mindestens?
- Warum hasht man Passwörter statt sie im Klartext zu speichern?
- 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 Auth | Form-based Auth |
|---|---|
| Browser-Dialog | Eigenes Formular |
| Stateless | Session-basiert |
| Kein Logout | Echtes Logout |
| Für APIs gut | Fü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:
- users (username, password)
- user_roles (username, role_name)
Frage 5: Warum Passwort-Hashing?
- Datenbank-Leak – Hacker hat nur nutzlose Hashes
- Insider-Angriffe – Admins können Passwörter nicht lesen
- Compliance – DSGVO verlangt Hashing
- 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

