{"id":55001196,"date":"2019-08-01T00:00:00","date_gmt":"2020-05-13T20:55:30","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1196"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Tabellen_vor_unerlaubtem_Zugriff_schuetzen","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\/","title":{"rendered":"Tabellen vor unerlaubtem Zugriff sch&uuml;tzen"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg06.met.vgwort.de\/na\/e9b83bbf73484a7caaeb6f514825c512\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>In Ausgabe 3\/2019 haben wir einige Elemente vorgestellt, die den Zugriff auf die Daten einer Datenbank einschr&auml;nken sollen &#8211; zum Beispiel eine einfache Benutzerverwaltung und die M&ouml;glichkeit, den Zugriff auf Tabellen per Datenmakro einzuschr&auml;nken. Im vorliegenden Beitrag wollen wir zeigen, wie Sie diese Techniken abrunden und den direkten Zugriff des Benutzers auf die Tabellen der Datenbank endg&uuml;ltig verhindern. Dazu exportieren wir diese in eine Backend-Datenbank, auf die wir diesmal nicht wie gewohnt per Verkn&uuml;pfung zugreifen &#8211; sondern ausschlie&szlig;lich per VBA. Das hat den Vorteil, dass wir das Backend mit einem Kennwort versehen k&ouml;nnen, das nur im Code des Frontends zum Einsatz kommt &#8211; und diesen k&ouml;nnen wir durch Umwandlung in eine .mde- beziehungsweise .accde-Datenbank vor den Augen des Benutzers verbergen.<\/b><\/p>\n<h2>Voraussetzungen<\/h2>\n<p>Die Beispieldatenbank dieses Beitrags haben wir in einigen anderen Beitr&auml;gen vorbereitet. Der Beitrag <b>Benutzerverwaltung mit verschl&uuml;sselten Kennw&ouml;rtern <\/b>(<b>www.access-im-unternehmen.de\/1190<\/b>) zeigt, wie die Benutzerverwaltung programmiert wird.<\/p>\n<p>Unter dem Titel <b>Zugriffsrechte mit Datenmakros <\/b>(<b>www.access-im-unternehmen.de\/1193<\/b>) zeigen wir, wie Sie Tabellen so sch&uuml;tzen, dass Sie nur noch nach der Anmeldung unter einem bestimmten Benutzerkonto auf die enthaltenen Daten zugreifen k&ouml;nnen.<\/p>\n<p>Die Beispieldatenbank enth&auml;lt also einen Anmeldedialog namens <b>frmAnmeldung<\/b>, mit dem der Benutzer die Zugangsdaten eingeben kann. Hat er keine Zugangsdaten eingegeben, ist durch die Definition entsprechender Datenmakros kein schreibender Zugriff auf die Tabellen der Datenbank m&ouml;glich. Den lesenden Zugriff schr&auml;nken wir noch durch die Pr&uuml;fung der Berechtigungen beim &Ouml;ffnen der jeweiligen Formulare ein.<\/p>\n<h2>Geplante &Auml;nderungen<\/h2>\n<p>Die erste &Auml;nderung, die wir durchf&uuml;hren, ist das Exportieren der Tabellen der Anwendung in eine Backend-Datenbank. Danach sch&uuml;tzen wir diese Datenbank durch ein Datenbankkennwort. Schlie&szlig;lich m&uuml;ssen wir &uuml;berall, wo wir in der Frontend-Datenbank auf die Daten der Tabellen oder Abfrage zugegriffen haben, entsprechenden VBA-Code f&uuml;r den Zugriff auf die Daten unter Ber&uuml;cksichtigung des Datenbankkennworts des Backends hinterlegen.<\/p>\n<p>Nat&uuml;rlich ist das je nach der Komplexit&auml;t der Anwendung eine mehr oder weniger gro&szlig;e Aufgabe, aber wenn Sie eine Access-Anwendung ohne Einsatz des SQL Servers oder eines &auml;hnlichen Systems unter maximaler Datensicherheit erstellen wollen, m&uuml;ssen Sie dies in Kauf nehmen. Schlie&szlig;lich wandeln Sie die Frontend-Datenbank in eine <b>.mde<\/b>&#8211; beziehungsweise <b>.accde<\/b>-Datenbank um, deren Code der Benutzer nicht mehr einsehen kann. Dieser Schritt ist wichtig, da wir das Kennwort f&uuml;r den Zugriff auf die Tabellen des Backends im Code hinterlegen.<\/p>\n<h2>Exportieren der Tabellen in eine Backend-Datenbank<\/h2>\n<p>Das Aufteilen der Datenbank erledigen Sie am einfachsten durch den Aufruf des Ribbon-Befehls <b>Datenbanktools|Daten verschieben|Access-Datenbank<\/b> (siehe Bild 1). Nach dem Aufruf w&auml;hlen Sie im Dialog <b>Assistent zur Datenbankaufteilung <\/b>den Befehl <b>Datenbank aufteilen<\/b>. Anschlie&szlig;end brauchen Sie nur noch den Pfad der zu erstellenden Backend-Datenbank anzugeben. Wir nennen das Backend <b>Daten.accdb <\/b>und speichern es im gleichen Verzeichnis wie die Frontend-Datenbank. Danach zeigt die Frontend-Datenbank nicht mehr die Tabellen selbst im Bereich <b>Tabellen <\/b>des Navigationsbereich an, sondern nur noch die Verkn&uuml;pfungen auf die Tabellen (siehe Bild 2). Diese befinden sich nun in der neu erstellten Backend-Datenbank.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_04\/pic_1196_001.png\" alt=\"Aufruf des Befehls zum Aufteilen einer Datenbank\" width=\"700\" height=\"240,0221\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Aufruf des Befehls zum Aufteilen einer Datenbank<\/span><\/b><\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_04\/pic_1196_002.png\" alt=\"Verkn&uuml;pfungen zu den Tabellen des Backends\" width=\"499,6607\" height=\"626,3075\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Verkn&uuml;pfungen zu den Tabellen des Backends<\/span><\/b><\/p>\n<p>Solange sich diese Verkn&uuml;pfungen in der Frontend-Datenbank befinden, kann der Benutzer der Datenbank zumindest lesend auf die Daten der Tabellen zugreifen, da er diese &uuml;ber einen Doppelklick auf die Verkn&uuml;pfungen &ouml;ffnen kann. Um das zu verhindern, greifen wir zu einer harten Ma&szlig;nahme: Wir l&ouml;schen die Verkn&uuml;pfungen schlicht und einfach. Damit funktionieren die Abfragen und Formulare, die an die Tabellen gebunden sind, nun nicht mehr, da die Datenquelle nicht mehr verf&uuml;gbar ist. Und auch die Formulare wie etwa <b>frmBerechtigungenVerwalten<\/b>, die per VBA auf die Tabellen zugreifen, um die Daten in einer HTML-Tabelle anzuzeigen, versagen ihren Dienst.<\/p>\n<h2>Sch&uuml;tzen der Backend-Datenbank durch ein Kennwort<\/h2>\n<p>Bevor wir die notwendigen &Auml;nderungen am Frontend vornehmen, um wieder auf die Daten der Tabellen zuzugreifen, legen wir ein Datenbankkennwort f&uuml;r die Backend-Datenbank fest. Dazu &ouml;ffnen Sie diese direkt in einer weiteren Access-Instanz. Dazu gen&uuml;gt ein Doppelklick auf die Datei <b>Daten.accdb<\/b>.<\/p>\n<p>Anschlie&szlig;end &ouml;ffnen Sie mit einem Klick auf den Ribbon-Reiter <b>Datei <\/b>den Backstage-Bereich. Unter Informationen finden Sie hier die Schaltfl&auml;che <b>Mit Kennwort verschl&uuml;sseln <\/b>(siehe Bild 3). Wenn Sie die Datenbank per Doppelklick ge&ouml;ffnet haben, ist diese noch nicht im Exklusiv-Modus ge&ouml;ffnet. Dies k&ouml;nnen Sie erledigen, indem Sie die Datenbank mit dem Ribbon-Befehl <b>Datei|Schlie&szlig;en <\/b>schlie&szlig;en. Danach bet&auml;tigen Sie den Ribbon-Befehl <b>Datei|&Ouml;ffnen<\/b>. Hier w&auml;hlen Sie den Befehl <b>Durchsuchen <\/b>aus. Im nun erscheinenden <b>&Ouml;ffnen<\/b>-Dialog w&auml;hlen Sie die gew&uuml;nschte Datei aus, in diesem Fall <b>Daten.accdb<\/b>, und w&auml;hlen den Untereintrag <b>Exklusiv &ouml;ffnen <\/b>der Schaltfl&auml;che <b>&Ouml;ffnen <\/b>aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_04\/pic_1196_003.png\" alt=\"Zuweisen eines Datenbankkennworts zur Backend-Datenbank\" width=\"649,559\" height=\"420,7892\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Zuweisen eines Datenbankkennworts zur Backend-Datenbank<\/span><\/b><\/p>\n<p>Danach k&ouml;nnen Sie erneut den Ribbon-Reiter <b>Datei <\/b>anklicken und im Bereich <b>Information <\/b>die Schaltfl&auml;che <b>Mit Kennwort verschl&uuml;sseln <\/b>bet&auml;tigen. Nun erscheint der Dialog <b>Datenbankkennwort festlegen<\/b>, in dem Sie das Kennwort zwei Mal eingeben und dieses dann durch einen Klick auf die Schaltfl&auml;che <b>OK <\/b>best&auml;tigen (siehe Bild 4). Zu Beispielzwecken haben wir hier das Kennwort <b>kennwort <\/b>eingetragen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_04\/pic_1196_004.png\" alt=\"Festlegen des Datenbankkennworts\" width=\"549,6265\" height=\"353,7491\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Festlegen des Datenbankkennworts<\/span><\/b><\/p>\n<p>Wenn Sie die Backend-Datenbank nun schlie&szlig;en und erneut &ouml;ffnen, erscheint der Dialog <b>Kennwort erforderlich <\/b>und verlangt nach der Eingabe des Kennworts.<\/p>\n<h2>Ersetzen der Bindung durch Zuweisen von Recordsets<\/h2>\n<p>Damit haben wir den Zugriff auf die Daten vom Frontend aus noch l&auml;ngst nicht wiederhergestellt &#8211; im Gegenteil, durch Vergabe des Kennworts haben wir diesen sogar noch erschwert. Doch nun werden wir Schritt f&uuml;r Schritt den Zugriff auf die Tabellen des Backends wieder hinzuf&uuml;gen.<\/p>\n<p>Wenn wir die Frontend-Datenbank &ouml;ffnen, sollte nach dem Best&auml;tigen des Intro-Formulars der Dialog <b>frmAnmelden <\/b>erscheinen. Stattdessen erhalten wir die Fehlermeldung aus Bild 5. Als fehlerhafte Zeile wird die folgende Zeile markiert:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_04\/pic_1196_005.png\" alt=\"Fehlermeldung beim Versuch, auf die Tabelle tblBenutzer zuzugreifen\" width=\"499,6607\" height=\"283,6212\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Fehlermeldung beim Versuch, auf die Tabelle tblBenutzer zuzugreifen<\/span><\/b><\/p>\n<pre><span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT Benutzername FROM tblBenutzer\", dbOpenDynaset)<\/pre>\n<p>Hier ben&ouml;tigen wir nur eine kleine Anpassung, die wie folgt aussieht:<\/p>\n<pre><span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT Benutzername FROM [;PWD=kennwort;DATABASE=\" & CurrentProject.Path _\r\n     & \"Daten.accdb].tblBenutzer\", dbOpenDynaset)<\/pre>\n<p>Wir f&uuml;gen also lediglich vor dem Namen der Tabelle einen Ausdruck in eckigen Klammern hinzu, der das Kennwort sowie den Pfad zur Backend-Datenbank enth&auml;lt. Wenn wir das Formular <b>frmAnmeldung <\/b>nun &ouml;ffnen, erscheint dieses ohne Fehler, denn es ist auch nicht an eine der Tabellen gebunden.<\/p>\n<p>W&auml;hrend der Aktualisierung des Benutzernamens bei der Eingabe der Anmeldedaten wird jedoch in der Prozedur <b>txtBenutzername_Change <\/b>ein weiteres Recordset ge&ouml;ffnet, das die Tabelle <b>tblBenutzer <\/b>als Datenquelle verwendet. Hier passen wir die Anweisung wie folgt an:<\/p>\n<pre><span style=\"color:blue;\">Set<\/span> rstBenutzer = db.OpenRecordset(\"SELECT * FROM [;PWD=kennwort;DATABASE=\" & CurrentProject.Path _\r\n     & \"Daten.accdb].tblBenutzer WHERE Benutzername = ''\" & strBenutzername & \"''\", dbOpenDynaset)<\/pre>\n<p>An dieser Stelle wird klar: Wir ben&ouml;tigen den Ausdruck <b>[;PWD=kennwort;DATABASE=&#8220; &#038; CurrentProject.Path &#038; &#8222;Daten.accdb] <\/b>an mehr als einer Stelle. Wir k&ouml;nnen diesen Ausdruck an jeder Stelle einf&uuml;gen, an der es erforderlich ist, allerdings wartet dann eine Menge Arbeit auf uns, wenn sich der Pfad der Backend-Datenbank &auml;ndert &#8211; oder auch das Kennwort dieser Datenbank. Also erstellen wir dazu zun&auml;chst eine einfache Funktion, welche uns die gew&uuml;nschte Zeichenkette zusammenstellt:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>BackendPathPassword()<span style=\"color:blue;\"> As String<\/span>\r\n     BackendPathPassword = \"[;PWD=kennwort;DATABASE=\" _\r\n         & CurrentProject.Path & \"Daten.accdb].\"\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Der Zugriff auf diese Funktion sieht in der Anweisung aus dem ersten Beispiel wie folgt aus:<\/p>\n<pre><span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT Benutzername FROM \" & BackendPathPassword & \"tblBenutzer\", dbOpenDynaset)<\/pre>\n<p>Und f&uuml;r das zweite Beispiel sieht der Aufruf so aus:<\/p>\n<pre><span style=\"color:blue;\">Set<\/span> rstBenutzer = db.OpenRecordset(\"SELECT * FROM \" _\r\n              & BackendPathPassword & \"tblBenutzer WHERE  Benutzername = ''\" & strBenutzername & \"''\",  dbOpenDynaset)<\/pre>\n<p>Der n&auml;chste Fehler lauert in der Funktion <b>AnmeldungPruefen <\/b>im Modul <b>mdlBenutzerverwaltung <\/b>auf uns. Hier tritt der Fehler wieder in einem Aufruf der <b>OpenRecordset<\/b>-Methode auf. Wir machen kurzen Prozess und passen alle Aufrufe der <b>OpenRecordset<\/b>-Methode wie oben beschrieben an.<\/p>\n<h2>Kennwortgesch&uuml;tzter Zugriff per DLookup<\/h2>\n<p>Wenn wir die Schaltfl&auml;che <b>cmdAnmelden <\/b>im Formular <b>frmAnmeldung <\/b>bet&auml;tigen, finden wir den n&auml;chsten Fehler in der folgenden <b>DLookup<\/b>-Anweisung:<\/p>\n<pre>lngBenutzerID = DLookup(\"BenutzerID\", \"tblBenutzer\",  \"Benutzername = ''\" & strBenutzername & \"''\")<\/pre>\n<p>Welche M&ouml;glichkeit haben wir hier, um den Zugriff auf die kennwortgesch&uuml;tzte Backend-Datenbank zu erm&ouml;glichen Wir probieren es mit der folgenden Variante aus:<\/p>\n<pre>lngBenutzerID = DLookup(\"BenutzerID\", BackendPathPassword  & \"tblBenutzer\", _\r\n     \"Benutzername = ''\" & strBenutzername  & \"''\")<\/pre>\n<p>Wir f&uuml;gen also einfach vorn an den zweiten Parameter den Ausdruck mit dem Kennwort und dem Pfad in eckigen Klammern an. Das Ergebnis ist allerdings ern&uuml;chternd und es sieht wie in Bild 6 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_04\/pic_1196_006.png\" alt=\"Fehler beim Versuch, das Kennwort in die DLookup-Abfrage zu integrieren\" width=\"549,6265\" height=\"311,9832\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Fehler beim Versuch, das Kennwort in die DLookup-Abfrage zu integrieren<\/span><\/b><\/p>\n<h2>PLookup statt DLookup<\/h2>\n<p>Statt der gewohnten Funktion <b>DLookup <\/b>verwenden wir eine benutzerdefinierte Version dieser Funktion, die wir <b>PLookup <\/b>nennen (f&uuml;r Password-Lookup). Zun&auml;chst schauen wir uns die kleine &Auml;nderung an, die den Aufruf betrifft:<\/p>\n<pre>lngBenutzerID = PLookup(\"BenutzerID\", \"tblBenutzer\",  \"Benutzername = ''\" & strBenutzername & \"''\")<\/pre>\n<p>Die Funktion <b>PLookup <\/b>definieren wir im Modul <b>mdlBackend <\/b>wie folgt:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>PLookup(strField<span style=\"color:blue;\"> As String<\/span>,  strTable<span style=\"color:blue;\"> As String<\/span>, strCriteria<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As Variant<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT \" & strField  & \" FROM \" & BackendPathPassword & strTable _\r\n          & \" WHERE \" & strCriteria, dbOpenDynaset)\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> rst.EOF<span style=\"color:blue;\"> Then<\/span>\r\n         PLookup = rst.Fields(0)\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Die Funktion <b>PLookup <\/b>erwartet die gleichen Parameter wie die eingebaute <b>DLookup<\/b>-Funktion. Sie erstellt ein neues Recordset, bei dem der Abfrageausdruck aus den zu einer <b>SELECT<\/b>-Anweisung zusammengesetzten Parametern der Funktion resultiert.<\/p>\n<p>Dabei verwendet die Funktion beim Zusammenstellen der <b>SELECT<\/b>-Anweisung wieder die Funktion <b>BackendPath-Password<\/b>, welche den Ausdruck mit dem Kennwort und dem Backend-Pfad in eckigen Klammern und einen abschlie&szlig;enden Punkt liefert.<\/p>\n<p>Sie k&ouml;nnen nun, &auml;hnlich wie bei der Anpassung der Recordsets, auch alle Aufrufe der <b>DLookup<\/b>-Funktion anpassen, indem Sie <b>DLookup <\/b>durch <b>PLookup <\/b>ersetzen.<\/p>\n<h2>Abfragen anpassen<\/h2>\n<p>Die Funktion <b>BenutzerBerechtigungen <\/b>aus dem Modul <b>mdlBenutzerverwaltung <\/b>verwendet eine <b>SELECT<\/b>-Anweisung mit einer Abfrage als Datenquelle als Parameter einer <b>OpenRecordset<\/b>-Methode:<\/p>\n<pre><span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT * FROM  qryBenutzerBerechtigungen WHERE BenutzerID = \" _\r\n      & lngBenutzerID & \" AND Tabelle = ''\" & strTabelle  & \"''\", dbOpenDynaset)<\/pre>\n<p>Die Abfrage <b>qryBenutzerBerechtigungen <\/b>referenziert logischerweise die Tabellen im Frontend beziehungsweise w&uuml;rde auch noch funktionieren, wenn die Verkn&uuml;pfungen auf die Tabellen im Backend noch vorhanden w&auml;ren.<\/p>\n<p>Es gibt aber weder Tabellen noch Verkn&uuml;pfungen im Frontend, weshalb die Abfrage nicht mehr funktionieren kann. Genau genommen k&ouml;nnen wir noch nicht einmal mehr den Entwurf ansehen &#8211; dies resultiert in der Fehlermeldung aus Bild 7.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_04\/pic_1196_007.png\" alt=\"Fehler beim Anzeigen der Entwurfsansicht einer Abfrage\" width=\"700\" height=\"99,61279\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 7: Fehler beim Anzeigen der Entwurfsansicht einer Abfrage<\/span><\/b><\/p>\n<p>Nach dem Schlie&szlig;en der Fehlermeldung erscheint immerhin noch die SQL-Ansicht der Abfrage (siehe Bild 8). Wir haben aber keine M&ouml;glichkeit, etwa &uuml;ber eine Eigenschaft festzulegen, dass die Abfrage auf die in der Backend-Datenbank befindlichen Tabellen zugreifen soll.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_04\/pic_1196_008.png\" alt=\"SQL-Ansicht der Abfrage\" width=\"649,559\" height=\"220,3049\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 8: SQL-Ansicht der Abfrage<\/span><\/b><\/p>\n<p>Wir wollen nun f&uuml;r jede Abfrage eine Funktion im Modul <b>mdlBackend<\/b> hinterlegen, welche den SQL-Code der Abfrage enth&auml;lt und die Klausel mit dem Pfad und dem Kennwort zur Angabe der Quelltabellen hinzuf&uuml;gt. Dazu vereinfachen wir die Abfrage zun&auml;chst, indem wir Alias-Bezeichnungen f&uuml;r die Felder im <b>FROM<\/b>-Teil angeben und diese in der Feldliste und in den &uuml;brigen Bereichen wie den Kriterien und den Sortierungen nutzen. Vorher sieht die Abfrage noch wie folgt aus:<\/p>\n<pre>SELECT tblBenutzer.BenutzerID, tblBenutzer.Benutzername, tblTabellen.Tabelle, tblBerechtigungen.Berechtigung, tblBerechtigungszuordnungen.BerechtigungID FROM tblTabellen INNER JOIN (tblBenutzer INNER JOIN ((tblBerechtigungen INNER JOIN (tblBenutzergruppen INNER JOIN tblBerechtigungszuordnungen ON tblBenutzergruppen.BenutzergruppeID = tblBerechtigungszuordnungen.BenutzergruppeID) ON tblBerechtigungen.BerechtigungID = tblBerechtigungszuordnungen.BerechtigungID) INNER JOIN tblGruppenzuordnungen ON tblBenutzergruppen.BenutzergruppeID = tblGruppenzuordnungen.BenutzergruppeID) ON tblBenutzer.BenutzerID = tblGruppenzuordnungen.BenutzerID) ON tblTabellen.TabelleID = tblBerechtigungszuordnungen.TabelleID;<\/pre>\n<p>Nach den &Auml;nderungen erscheint diese schon etwas aufger&auml;umter:<\/p>\n<pre>SELECT t2.BenutzerID, t2.Benutzername, t1.Tabelle, t3.Berechtigung, t5.BerechtigungID\r\nFROM tblTabellen AS t1 \r\nINNER JOIN (tblBenutzer AS t2 INNER JOIN ((tblBerechtigungen AS t3 INNER JOIN (tblBenutzergruppen AS t4 INNER JOIN tblBerechtigungszuordnungen AS t5 ON t4.BenutzergruppeID = t5.BenutzergruppeID) ON t2.BerechtigungID = t5.BerechtigungID) INNER JOIN tblGruppenzuordnungen AS t6 ON t4.BenutzergruppeID = t6.BenutzergruppeID) ON t2.BenutzerID = t6.BenutzerID) ON t1.TabelleID = t5.TabelleID;<\/pre>\n<p><!--30percent--><\/p>\n<p>Hier f&uuml;gen wir nun innerhalb der wie folgt zu definierenden Funktion noch dynamisch den Inhalt der Funktion <b>BackendPathPassword <\/b>ein (siehe Listing 1). Und das ist auch der Grund, warum wir mit dem Schl&uuml;sselwort <b>AS <\/b>angegebene Alias-Bezeichnungen f&uuml;r die Tabellen arbeiten. Wenn wir immer die vollen Tabellennamen verwenden w&uuml;rden, m&uuml;ssten wir vor jedem Tabellennamen den Inhalt der Funktion <b>BackendPathPassword <\/b>einf&uuml;gen. Wichtig ist noch, dass der SQL-String nicht mit einem Semikolon abschlie&szlig;t &#8211; gegebenenfalls wollen wir ja noch eine &#8211;<b>WHERE<\/b>&#8211; oder <b>ORDER BY<\/b>-Klausel anh&auml;ngen.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>qryBenutzerBerechtigungen()<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strSQL<span style=\"color:blue;\"> As String<\/span>\r\n     strSQL = \"SELECT t2.BenutzerID, t2.Benutzername, t1.Tabelle, t3.Berechtigung, t5.BerechtigungID \"\r\n     strSQL = strSQL & \"FROM \" & BackendPathPassword & \"tblTabellen AS t1 \"\r\n     strSQL = strSQL & \"INNER JOIN (\" & BackendPathPassword & \"tblBerechtigungen AS t3 \"\r\n     strSQL = strSQL & \"INNER JOIN (\" & BackendPathPassword & \"tblBenutzer AS t2 \"\r\n     strSQL = strSQL & \"INNER JOIN ((\" & BackendPathPassword & \"tblBenutzergruppen AS t4 \"\r\n     strSQL = strSQL & \"INNER JOIN \" & BackendPathPassword & \"tblGruppenzuordnungen AS t6 \"\r\n     strSQL = strSQL & \"ON t4.BenutzergruppeID = t6.BenutzergruppeID) \"\r\n     strSQL = strSQL & \"INNER JOIN \" & BackendPathPassword & \"tblBerechtigungszuordnungen AS t5 \"\r\n     strSQL = strSQL & \"ON t4.BenutzergruppeID = t5.BenutzergruppeID) \"\r\n     strSQL = strSQL & \"ON t2.BenutzerID = t6.BenutzerID) \"\r\n     strSQL = strSQL & \"ON t3.BerechtigungID = t5.BerechtigungID) \"\r\n     strSQL = strSQL & \"ON t1.TabelleID = t5.TabelleID\"\r\n     qryBenutzerBerechtigungen = strSQL\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Funktion zum Zusammenstellen der Abfrage qryBenutzerBerechtigungen<\/span><\/b><\/p>\n<p>Das Ergebnis der Funktion sieht wie in Listing 2 aus.<\/p>\n<pre>SELECT t2.BenutzerID, t2.Benutzername, t1.Tabelle, t3.Berechtigung, t5.BerechtigungID \r\nFROM [;PWD=kennwort;DATABASE=C:...Daten.accdb].tblTabellen AS t1 \r\nINNER JOIN ([;PWD=kennwort;DATABASE=C:...Daten.accdb].tblBerechtigungen AS t3 \r\nINNER JOIN ([;PWD=kennwort;DATABASE=C:...Daten.accdb].tblBenutzer AS t2 \r\nINNER JOIN (([;PWD=kennwort;DATABASE=C:...Daten.accdb].tblBenutzergruppen AS t4 \r\nINNER JOIN [;PWD=kennwort;DATABASE=C:...Daten.accdb].tblGruppenzuordnungen AS t6 \r\nON t4.BenutzergruppeID = t6.BenutzergruppeID) \r\nINNER JOIN [;PWD=kennwort;DATABASE=C:...Daten.accdb].tblBerechtigungszuordnungen AS t5 \r\nON t4.BenutzergruppeID = t5.BenutzergruppeID) \r\nON t2.BenutzerID = t6.BenutzerID) \r\nON t3.BerechtigungID = t5.BerechtigungID) \r\nON t1.TabelleID = t5.TabelleID<\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Die fertige Abfrage qryBenutzerBerechtigungen<\/span><\/b><\/p>\n<p>In der Funktion <b>BenutzerBerechtigungen <\/b>f&uuml;gen wir die Funktion <b>qryBenutzerBerechtigungen <\/b>nun wie folgt in die <b>OpenRecordset<\/b>-Methode ein:<\/p>\n<pre><span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(qryBenutzerBerechtigungen  & \" WHERE t2.BenutzerID = \" & lngBenutzerID _\r\n     & \" AND  t1.Tabelle = ''\" & strTabelle & \"''\", dbOpenDynaset)<\/pre>\n<p>Hier m&uuml;ssen wir auch noch den Tabellen-Alias f&uuml;r die beiden Parameter eintragen, also <b>t2.BenutzerID <\/b>statt <b>BenutzerID <\/b>und <b>t1.Tabelle <\/b>statt <b>Tabelle<\/b>. Sie sehen: So richtig komfortabel und einfach ist es nicht, es sind schon einige Anpassungen n&ouml;tig.<\/p>\n<h2>Gebundene Formulare<\/h2>\n<p>Bei den gebundenen Formularen, also solchen Formularen, deren Eigenschaft <b>Datensatzquelle <\/b>die Tabelle oder Abfrage angibt, deren Daten das Formular anzeigen soll, verlieren wir den Komfort, den die Datenbindung bietet. So m&uuml;ssen Sie die Eigenschaft <b>Datensatzquelle <\/b>leeren und die Bindung an die Tabelle oder Abfrage &uuml;ber die Zuweisung eines <b>Recordset<\/b>-Objekts an die VBA-Eigenschaft <b>Recordset <\/b>erledigen.<\/p>\n<p>Dadurch entf&auml;llt auch der Komfort, die Felder der Datensatzquelle einfach aus der Feldliste in das Formular zu ziehen.<\/p>\n<p>Andererseits k&ouml;nnen Sie die Formulare auch zuerst bei aktivierter Datenbindung entwerfen und erst nach der Fertigstellung die Datenbindung entfernen.<\/p>\n<p>Dazu m&uuml;ssten Sie nat&uuml;rlich tempor&auml;r auch wieder die Tabellen in das Frontend holen &#8211; entweder als lokale Tabelle oder als Verkn&uuml;pfung zur Tabelle im Backend.<\/p>\n<p>Wir gehen an dieser Stelle davon aus, dass etwa das Formular <b>frmKunden <\/b>aktuell an die Tabelle <b>tblKunden <\/b>gebunden ist.<\/p>\n<p>In diesem Fall f&uuml;hrt das &Ouml;ffnen des Formulars nach dem Entfernen der Tabellen und Verkn&uuml;pfungen aus dem Frontend zu der Fehlermeldung <b>Die auf diesem Formular oder in diesem Bericht angegebene Datensatzquelle &#8220;tblKunden&#8220; ist nicht vorhanden<\/b>. <\/p>\n<p>Danach wird das Formular in der Entwurfsansicht angezeigt, wo auch gleich alle gebundenen Felder markiert sind. Ein Klick auf die Schaltfl&auml;che mit dem Ausrufezeichen aus Bild 9 zeigt den Grund f&uuml;r die Markierung &#8211; die f&uuml;r die Eigenschaft <b>Steuerelementinhalt <\/b>angegebenen Felder sind nicht in der Feldliste vorhanden.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_04\/pic_1196_009.png\" alt=\"Meldung bei Steuerelementen mit fehlerhaftem Steuerelementinhalt\" width=\"549,6265\" height=\"491,303\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 9: Meldung bei Steuerelementen mit fehlerhaftem Steuerelementinhalt<\/span><\/b><\/p>\n<h2>Datensatzquelle ersetzen<\/h2>\n<p>Damit das Formular die Daten der Tabelle <b>tblKunden <\/b>aus dem Backend anzeigt, sind zwei Schritte n&ouml;tig: das Entfernen der Tabelle <b>tblKunden <\/b>aus der Eigenschaft <b>Datensatzquelle<\/b> und das Erstellen und Zuweisen eines geeigneten Recordsets.<\/p>\n<p>Nach dem Leeren der Eigenschaft <b>Datensatzquelle <\/b>verschwinden interessanterweise die Markierungen der gebundenen Steuer-elemente im Formular.<\/p>\n<p>Offensichtlich pr&uuml;ft Access also nur bei Vorhandensein einer Tabelle oder Abfrage, ob die f&uuml;r die Eigenschaft <b>Steuerelementinhalt <\/b>angegebenen Felder in der Feldliste enthalten sind. <\/p>\n<p>Damit das Formular nun die Daten der Tabelle <b>tblKunden <\/b>anzeigt, hinterlegen wir f&uuml;r die Ereigniseigenschaft <b>Beim Laden <\/b>die Prozedur aus Listing 3. Diese f&uuml;llt ein <b>Database<\/b>-Objekt namens <b>db<\/b>, das die aktuelle Datenbank referenziert.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT * FROM \" & BackendPathPassword & \"tblKunden\", dbOpenDynaset)\r\n     <span style=\"color:blue;\">Set<\/span> Me.Recordset = rst\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Zuweisen eines Recordsets beim Laden des Formulars<\/span><\/b><\/p>\n<p>Mit der <b>OpenRecordset<\/b>-Methode &ouml;ffnet die Prozedur dann ein Recordset auf Basis der Tabelle <b>tblKunden<\/b>, wobei wieder die Funktion <b>BackendPathPassword <\/b>verwendet wird, um Pfad und Kennwort der Datenbank in eckigen Klammern anzugeben.<\/p>\n<p>Damit landen dann die gew&uuml;nschten Daten in den Steuerelementen des Formulars und &uuml;ber die Navigationsleiste k&ouml;nnen Sie auch durch die Datens&auml;tze bl&auml;ttern (siehe Bild 10).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_04\/pic_1196_010.png\" alt=\"Per Recordset zugewiesene Datensatzquelle im Formular frmKunden\" width=\"499,6607\" height=\"382,4922\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 10: Per Recordset zugewiesene Datensatzquelle im Formular frmKunden<\/span><\/b><\/p>\n<h2>Formular mit Listenfeldern<\/h2>\n<p>Als N&auml;chstes schauen wir uns das Formular <b>frmBenutzerVerwalten <\/b>an, das nicht nur an die Tabelle <b>tblBenutzer <\/b>gebunden ist, sondern in zwei Listenfeldern noch die &uuml;ber die Tabelle <b>tblGruppenzuordnungen <\/b>zugewiesenen und nicht zugewiesenen Benutzergruppen aus der Tabelle <b>tblBenutzergruppen <\/b>anzeigen soll. Das Formular sieht im Entwurf wie in Bild 11 aus. Beim Anzeigen in der Formularansicht erscheint auch hier zun&auml;chst eine Fehlermeldung, die darauf hinweist, dass die als Datensatzquelle angegebene Tabelle <b>tblBenutzer <\/b>nicht vorhanden ist.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_04\/pic_1196_011.png\" alt=\"Das Formular frmBenutzerVerwalten mit zwei Listenfeldern\" width=\"549,6265\" height=\"352,7316\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 11: Das Formular frmBenutzerVerwalten mit zwei Listenfeldern<\/span><\/b><\/p>\n<p>Auch hier leeren wir die Eigenschaft <b>Datensatzquelle <\/b>und hinterlegen eine entsprechende Ereignisprozedur f&uuml;r das Ereignis <b>Beim Laden <\/b>des Formulars. Diesmal weisen wir der Eigenschaft <b>Recordset <\/b>die Daten der Tabelle <b>tblBenutzer <\/b>zu. Die entscheidende Anweisung sieht dann wie folgt aus:<\/p>\n<pre><span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT * FROM \"  & BackendPathPassword & \"tblBenutzer\", dbOpenDynaset)<\/pre>\n<p>Der n&auml;chste Versuch, das Formular in der Formularansicht anzuzeigen, liefert mehrere Fehler wegen der fehlenden Datensatzherk&uuml;nfte der beiden Listenfelder <b>lstZugewiesen <\/b>und <b>lstNichtZugewiesen<\/b>. Das Listenfeld <b>lstZugewiesen <\/b>verwendet etwa die folgende Abfrage als Datensatzherkunft:<\/p>\n<pre>SELECT tblBenutzergruppen.BenutzergruppeID, tblBenutzergruppen.Benutzergruppe, tblGruppenzuordnungen.BenutzerID FROM tblBenutzergruppen INNER JOIN tblGruppenzuordnungen ON tblBenutzergruppen.BenutzergruppeID = tblGruppenzuordnungen.BenutzergruppeID WHERE (((tblGruppenzuordnungen.BenutzerID)=[Forms]![frmBenutzerVerwalten]![BenutzerID]));<\/pre>\n<p>F&uuml;r das Listenfeld <b>lstNichtZugewiesen <\/b>sieht die Datensatzherkunft noch etwas umfangreicher aus:<\/p>\n<pre>SELECT tblBenutzergruppen.BenutzergruppeID, tblBenutzergruppen.Benutzergruppe FROM tblBenutzergruppen WHERE (((tblBenutzergruppen.BenutzergruppeID) <span style=\"color:blue;\">Not<\/span> In (SELECT tblBenutzergruppen.BenutzergruppeID FROM tblBenutzergruppen INNER JOIN tblGruppenzuordnungen ON tblBenutzergruppen.BenutzergruppeID = tblGruppenzuordnungen.BenutzergruppeID WHERE (((tblGruppenzuordnungen.BenutzerID)=[Forms]![frmBenutzerVerwalten]![BenutzerID])))));<\/pre>\n<p>Diese Datensatzherk&uuml;nfte entfernen wir aus den jeweiligen Eigenschaften der Listenfeld-Steuerelemente und erweitern stattdessen die Ereignisprozedur <b>Form_Load <\/b>wie in Listing 4. Hier haben wir auch wieder <b>AS<\/b>-Bezeichnungen f&uuml;r alle verwendeten Tabellen vergeben (<b>t1<\/b>, <b>t2<\/b>, &#8230;) und an den relevanten Stellen die Funktion <b>BackendPathPassword <\/b>eingef&uuml;gt.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>rstLstZugewiesen<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>rstLstNichtZugewiesen<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT * FROM \" & BackendPathPassword & \"tblBenutzer\", dbOpenDynaset)\r\n     <span style=\"color:blue;\">Set<\/span> Me.Recordset = rst\r\n     <span style=\"color:blue;\">Set<\/span> rstLstZugewiesen = db.OpenRecordset(\"SELECT t1.BenutzergruppeID, t1.Benutzergruppe, t2.BenutzerID FROM \" _\r\n         & BackendPathPassword & \"tblBenutzergruppen AS t1 INNER JOIN \" & BackendPathPassword _\r\n         & \"tblGruppenzuordnungen AS t2 ON t1.BenutzergruppeID = t2.BenutzergruppeID WHERE t2.BenutzerID = \" _\r\n         & Me!BenutzerID, dbOpenDynaset)\r\n     <span style=\"color:blue;\">Set<\/span> Me!lstZugewiesen.Recordset = rstLstZugewiesen\r\n     <span style=\"color:blue;\">Set<\/span> rstLstNichtZugewiesen = db.OpenRecordset(\"SELECT t1.BenutzergruppeID, t1.Benutzergruppe FROM \" _\r\n         & BackendPathPassword & \"tblBenutzergruppen AS t1 WHERE t1.BenutzergruppeID <span style=\"color:blue;\">Not<\/span> In (SELECT t2.\" _\r\n         & \"BenutzergruppeID FROM \" & BackendPathPassword & \"tblBenutzergruppen AS t2 INNER JOIN \" _\r\n         & BackendPathPassword & \"tblGruppenzuordnungen AS t3 ON t2.BenutzergruppeID = t3.BenutzergruppeID \" _\r\n         & \"WHERE t3.BenutzerID = \" & Me!BenutzerID & \")\", dbOpenDynaset)\r\n     <span style=\"color:blue;\">Set<\/span> Me!lstNichtZugewiesen.Recordset = rstLstNichtZugewiesen\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Zuweisen der Recordsets der beiden Listenfelder<\/span><\/b><\/p>\n<p>Wenn wir das Formular nun in der Formularansicht &ouml;ffnen, zeigt dieses bereits alle Daten aus den Tabellen des Backends an (siehe Bild 12).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_04\/pic_1196_012.png\" alt=\"Anzeige aller gew&uuml;nschten Daten in Steuerelementen und Listenfeldern\" width=\"499,6607\" height=\"331,9424\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 12: Anzeige aller gew&uuml;nschten Daten in Steuerelementen und Listenfeldern<\/span><\/b><\/p>\n<p>Wenn wir dann allerdings etwa auf einen der Eintr&auml;ge im rechten Listenfeld klicken, erscheint die Fehlermeldung aus Bild 13.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_04\/pic_1196_013.png\" alt=\"Fehler bei Ausf&uuml;hren einer Aktionsabfrage\" width=\"499,6607\" height=\"283,6212\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 13: Fehler bei Ausf&uuml;hren einer Aktionsabfrage<\/span><\/b><\/p>\n<p>Diesen verhindern wir, indem wir wie in Listing 5 die Funktion <b>BackendPathPassword<\/b> in die Prozedur integrieren, die durch den Doppelklick auf einen der Eintr&auml;ge des Listenfeldes <b>lstNichtZugewiesen <\/b>ausgel&ouml;st wird. Das Gleiche erledigen wir auch f&uuml;r die Aktionsabfrage, die wir mit der <b>Execute<\/b>-Methode in der Prozedur <b>lstZugewiesen_DblClick <\/b>aufrufen.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>lstNichtZugewiesen_DblClick(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> IsNull(Me!BenutzerID) And <span style=\"color:blue;\">Not<\/span> IsNull(Me!lstNichtZugewiesen)<span style=\"color:blue;\"> Then<\/span>\r\n         db.Execute \"INSERT INTO \" & BackendPathPassword & \"tblGruppenzuordnungen(BenutzerID, BenutzergruppeID) \" _\r\n             & \"VALUES(\" & Me!BenutzerID & \", \" & Me!lstNichtZugewiesen & \")\", dbFailOnError\r\n         Me!lstZugewiesen.Requery\r\n         Me!lstNichtZugewiesen.Requery\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 5: Anpassen der Prozedur lstNichtZugewiesen_DblClick<\/span><\/b><\/p>\n<h2>Listenfelder aktualisieren<\/h2>\n<p>Nun erscheint keine Fehlermeldung mehr, wenn wir doppelt auf einen der Eintr&auml;ge der beiden Listenfelder klicken.  Allerdings werden die Inhalte der Listenfelder allerdings auch trotz Aufruf der <b>Requery<\/b>-Methode nicht aktualisiert. Also gliedern wir die Anweisungen, mit denen wir die Listenfelder durch Zuweisen der Recordsets zur Recordset-Eigenschaft gef&uuml;llt haben, in eine eigene Prozedur aus. Diese sieht wie in Listing 6 aus.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>ListenfelderAktualisieren()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>rstLstZugewiesen<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>rstLstNichtZugewiesen<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> rstLstZugewiesen = db.OpenRecordset(\"SELECT t1.BenutzergruppeID, t1.Benutzergruppe, t2.BenutzerID FROM \" _\r\n         & BackendPathPassword & \"tblBenutzergruppen AS t1 INNER JOIN \" & BackendPathPassword _\r\n         & \"tblGruppenzuordnungen AS t2 ON t1.BenutzergruppeID = t2.BenutzergruppeID WHERE t2.BenutzerID = \" _\r\n         & Me!BenutzerID, dbOpenDynaset)\r\n     <span style=\"color:blue;\">Set<\/span> Me!lstZugewiesen.Recordset = rstLstZugewiesen\r\n     <span style=\"color:blue;\">Set<\/span> rstLstNichtZugewiesen = db.OpenRecordset(\"SELECT t1.BenutzergruppeID, t1.Benutzergruppe FROM \" _\r\n         & BackendPathPassword & \"tblBenutzergruppen AS t1 WHERE t1.BenutzergruppeID <span style=\"color:blue;\">Not<\/span> In (\" _\r\n         & \"SELECT t2.BenutzergruppeID FROM \" & BackendPathPassword & \"tblBenutzergruppen AS t2 INNER JOIN \" _\r\n         & BackendPathPassword & \"tblGruppenzuordnungen AS t3 ON t2.BenutzergruppeID = t3.BenutzergruppeID \" _\r\n         & \"WHERE t3.BenutzerID = \" & Me!BenutzerID & \")\", dbOpenDynaset)\r\n     <span style=\"color:blue;\">Set<\/span> Me!lstNichtZugewiesen.Recordset = rstLstNichtZugewiesen\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 6: Die Prozedur zum Aktualisieren der Listenfelder<\/span><\/b><\/p>\n<p>Den Aufruf platzieren wir beispielsweise wie folgt in der Prozedur <b>lstNichtZugewiesen_DblClick<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>lstNichtZugewiesen_DblClick(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> IsNull(Me!BenutzerID)  And <span style=\"color:blue;\">Not<\/span> IsNull(Me!lstNichtZugewiesen)<span style=\"color:blue;\"> Then<\/span>\r\n         db.Execute \"...\", dbFailOnError\r\n         ListenfelderAktualisieren\r\n''        Me!lstZugewiesen.Requery\r\n''        Me!lstNichtZugewiesen.Requery\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Au&szlig;erdem ersetzen wir die <b>Requery<\/b>-Methoden in der Prozedur <b>lstZugewiesen_DblClick <\/b>durch den Aufruf der Prozedur <b>ListenfelderAktualisieren<\/b>.<\/p>\n<p>Und auch die Prozedur <b>Form_Load<\/b>, die bisher die Anweisung der Prozedur <b>ListenfelderAktualisieren <\/b>enthielt, k&ouml;nnen wir merklich verk&uuml;rzen, indem wir den Aufruf der Prozedur <b>ListenfelderAktualisieren <\/b>statt der darin enthaltenen Anweisungen einf&uuml;gen:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT * FROM \" &  BackendPathPassword & \"tblBenutzer\", dbOpenDynaset)\r\n     <span style=\"color:blue;\">Set<\/span> Me.Recordset = rst\r\n     ListenfelderAktualisieren\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Damit k&ouml;nnen wir nun Listenfeldeintr&auml;ge per Doppelklick in das jeweils andere Listenfeld verschieben.<\/p>\n<p>Was noch nicht gelingt, ist das Aktualisieren des Inhalts der Listenfelder beim Wechseln des Datensatzes im Formular <b>frmBenutzerVerwalten<\/b>.<\/p>\n<p>Aber auch das erreichen wir leicht, indem wir die Prozedur <b>Form_Current<\/b>, die durch das Wechseln des Datensatzes ausgel&ouml;st wird, wie folgt anpassen:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Current()\r\n     ListenfelderAktualisieren\r\n''    Me!lstNichtZugewiesen.Requery\r\n''    Me!lstZugewiesen.Requery\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Damit k&ouml;nnen wir auch den Aufruf der Prozedur <b>ListenfelderAktualisieren <\/b>aus der Ereignisprozedur <b>Form_Load <\/b>entfernen, denn <b>Form_Current <\/b>wird auch gleich nach dem Laden des Formulars einmal ausgef&uuml;hrt. Damit haben wir das Formular <b>frmBenutzerVerwalten <\/b>ebenfalls komplett auf den ungebundenen Betrieb umgestellt.<\/p>\n<h2>Formular frmBerechtigungenVerwalten<\/h2>\n<p>Dieses Formular enth&auml;lt nur eine Bindung &uuml;ber das Kombinationsfeld <b>cboBenutzergruppeID<\/b>.<\/p>\n<p>Diese &auml;ndern wir, indem wir die Eigenschaft <b>Datensatzherkunft <\/b>des Kombinationsfeldes leeren und eine neue Prozedur f&uuml;r das Ereignis <b>Beim Laden <\/b>anlegen, welche den Code aus Listing 7 enth&auml;lt.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT t1.BenutzergruppeID, t1.Benutzergruppe FROM \" & BackendPathPassword _\r\n         & \"tblBenutzergruppen AS t1 ORDER BY t1.Benutzergruppe\", dbOpenDynaset)\r\n     <span style=\"color:blue;\">Set<\/span> Me!cboBenutzergruppeID.Recordset = rst\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 7: Prozedur zum F&uuml;llen des Kombinationsfeldes cboBenutzergruppeID<\/span><\/b><\/p>\n<p>Au&szlig;erdem m&uuml;ssen Sie noch die drei <b>OpenRecordset<\/b>-Aufrufe in der Prozedur <b>TabelleErstellen <\/b>um die Funktion <b>BackendPathPassword <\/b>erg&auml;nzen (siehe Listing 8) sowie die Datenbankzugriffe in den beiden Klassen <b>clsCellHeaderWrapper <\/b>und <b>clsCellWrapper <\/b>anpassen.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>TabelleErstellen(lngBenutzergruppeID<span style=\"color:blue;\"> As Long<\/span>)\r\n     ...\r\n     <span style=\"color:blue;\">Set<\/span> rstSpaltenkoepfe = db.OpenRecordset(\"SELECT BerechtigungID, Berechtigung FROM \" _\r\n         & BackendPathPassword _\r\n         & \"tblBerechtigungen\", dbOpenDynaset)\r\n     ...\r\n     <span style=\"color:blue;\">Set<\/span> rstZeilen = db.OpenRecordset(\"SELECT * FROM \" & BackendPathPassword & \"tblTabellen\", dbOpenDynaset)\r\n     ...\r\n         <span style=\"color:blue;\">Set<\/span> rstWerte = db.OpenRecordset(\"SELECT BerechtigungszuordnungID, BenutzergruppeID, TabelleID, \" _\r\n             & \"BerechtigungID FROM \" & BackendPathPassword & \"tblBerechtigungszuordnungen WHERE BenutzergruppeID = \" _\r\n             & lngBenutzergruppeID & \" AND TabelleID = \" & rstZeilen!TabelleID, dbOpenDynaset)\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 8: Anpassungen an der Prozedur TabelleErstellen<\/span><\/b><\/p>\n<h2>Formular mit Unterformular<\/h2>\n<p>Um zu zeigen, wie Sie die Recordset-Eigenschaft nutzen k&ouml;nnen, um ungebundene Kombinationen aus Haupt- und Unterformular zu programmieren, haben wir der Anwendung die beiden Formulare <b>frmBenutzerBenutzergruppe <\/b>und <b>sfmBenutzerBenutzergruppe <\/b>hinzugef&uuml;gt.<\/p>\n<p>Die erste verwendet die Tabelle <b>tblBenutzergruppen <\/b>als Datensatzquelle und die zweite die Tabelle <b>tblGruppenzuordnungen<\/b>. In den Eigenschaften <b>Verkn&uuml;pfen von <\/b>und <b>Verkn&uuml;pfen nach <\/b>des Unterformular-Steuerelements ist jeweils das Feld <b>BenutzergruppeID <\/b>der Datensatzquellen von Haupt- und Unterformular angegeben (siehe Bild 14).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_04\/pic_1196_014.png\" alt=\"Haupt- und Unterformular mit Verkn&uuml;pfungseigenschaften\" width=\"649,559\" height=\"328,3001\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 14: Haupt- und Unterformular mit Verkn&uuml;pfungseigenschaften<\/span><\/b><\/p>\n<p>Wenn Sie nachtr&auml;glich ein solches Formular zu einer bereits aufgeteilten Datenbank ohne Verkn&uuml;pfungen im Frontend hinzuf&uuml;gen und dabei die Vorz&uuml;ge der Datenbindung nutzen wollen, k&ouml;nnen Sie die Formulare einfach im Backend anlegen, wo die Tabellen noch vorhanden sind, und diese dann in das Frontend verschieben und dort anpassen.<\/p>\n<p>Wenn Sie das etwa mit dem Formular <b>frmBenutzerBenutzergruppen <\/b>und dem enthaltenen Unterformular erledigen, erhalten Sie beim ersten &Ouml;ffnen zun&auml;chst wieder die Fehlermeldung, dass die Tabelle <b>tblBenutzergruppen <\/b>nicht vorhanden ist.<\/p>\n<p>Diese Tabelle entfernen Sie nun aus der Eigenschaft Datensatzquelle des Hauptformulars und f&uuml;gen diesem die folgende Ereignisprozedur hinzu:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT * FROM \" _\r\n         & strBackendPassword & \"tblKunden\", dbOpenDynaset)\r\n     <span style=\"color:blue;\">Set<\/span> Me.Recordset = rst\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Der n&auml;chste Fehler tritt dann auf, weil die Datensatzquelle des Unterformulars fehlt. Hier hilft es allerdings nichts, einfach nur die Eigenschaft <b>Datensatzquelle <\/b>zu leeren und diese in der <b>Form_Load<\/b>-Prozedur zur Eigenschaft <b>Recordset <\/b>hinzuzuf&uuml;gen. Die beiden Eigenschaften <b>Verkn&uuml;pfen von <\/b>und <b>Verkn&uuml;pfen nach <\/b>des Unterformular-Steuerelements werden dann n&auml;mlich nicht mehr ber&uuml;cksichtigt.<\/p>\n<p>Also f&uuml;gen wir die entsprechenden Zeilen direkt zum Ereignis <b>Beim Anzeigen <\/b>des Hauptformulars hinzu, wo wir dann auch direkt nach dem im Hauptformular ausgew&auml;hlten Datensatz der Tabelle <b>tblBenutzergruppen <\/b>filtern (siehe Listing 9).<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Current()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT tblGruppenzuordnungen.BenutzerID, tblGruppenzuordnungen.BenutzergruppeID FROM \" _\r\n         & BackendPathPassword & \"tblGruppenzuordnungen WHERE BenutzergruppeID = \" _\r\n         & Me!BenutzergruppeID, dbOpenDynaset)\r\n     <span style=\"color:blue;\">Set<\/span> Me!sfmBenutzerBenutzergruppen.Form.Recordset = rst\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 9: Prozedur zum F&uuml;llen des Unterformulars sfmBenutzerBenutzergruppen<\/span><\/b><\/p>\n<p>Au&szlig;erdem weisen wir das so erzeugte Recordset nicht der Eigenschaft <b>Recordset <\/b>des Hauptformulars, sondern der des Unterformulars zu, dass wir mit <b>Me!sfmBenutzerBenutzergruppen.Form.Recordset <\/b>referenzieren.<\/p>\n<p>Aber auch das reicht noch nicht, wie Bild 15 zeigt. Das Unterformular zeigt zwar zwei Datens&auml;tze an, aber das Kombinationsfeld, welches eigentlich die Namen der verkn&uuml;pften Benutzer anzeigen sollte, enth&auml;lt keine Datens&auml;tze.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_04\/pic_1196_015.png\" alt=\"Fehlende Daten im Kombinationsfeld des Unterformulars\" width=\"424,7115\" height=\"251,9476\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 15: Fehlende Daten im Kombinationsfeld des Unterformulars<\/span><\/b><\/p>\n<p>Das ist auch logisch, denn die Datensatzherkunft verweist mit folgendem Ausdruck auf eine lokale Tabelle:<\/p>\n<pre>SELECT [tblBenutzer].[BenutzerID], tblBenutzer.[Benutzername] FROM tblBenutzer;<\/pre>\n<p>Also m&uuml;ssen wir auch noch die Datensatzherkunft &uuml;ber ein geeignetes Recordset f&uuml;llen. Dazu erg&auml;nzen wir die Prozedur <b>Form_Load <\/b>des Hauptformulars wie folgt:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     ...\r\n     <span style=\"color:blue;\">Dim <\/span>rstBenutzer<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     ...\r\n     <span style=\"color:blue;\">Set<\/span> rstBenutzer = db.OpenRecordset(\"SELECT  BenutzerID, Benutzername FROM \" _\r\n           & BackendPathPassword & \"tblBenutzer\", dbOpenDynaset)\r\n     <span style=\"color:blue;\">Set<\/span> Me!sfmBenutzerBenutzergruppen.Form!BenutzerID. Recordset = rstBenutzer\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Damit erscheinen dann auch die Datens&auml;tze der Tabelle <b>tblKunden <\/b>zur Auswahl im Kombinationsfeld des Unterformulars.<\/p>\n<h2>Umwandlung des Frontends in eine .mde-\/.accde-Datenbank<\/h2>\n<p>Es fehlt nun nur noch ein Schritt, um die Datenbank abzusichern. Dieser besteht darin, dem Benutzer den Blick in den Quellcode zu verwehren, damit er nicht das Kennwort f&uuml;r den Zugriff auf die Backend-Datenbank ermitteln und so direkt auf die darin enthaltenen Daten zugreifen kann.<\/p>\n<p>Eine Voraussetzung daf&uuml;r, dass die Erstellung einer <b>.mde-\/.accde<\/b>-Datenbank gelingt, ist das fehlerfreie Debuggen des Quellcodes. Damit Sie nicht versuchen, die <b>.mde-\/.accde<\/b>-Datenbank zu erstellen und dann nicht erkennen k&ouml;nnen, warum es nicht gelingt, testen wir vorher einmal das fehlerfreie Debuggen. Dazu &ouml;ffnen Sie den VBA-Editor und w&auml;hlen dort den Befehl <b>Debuggen|Kompilieren von &#8230; <\/b>aus.<\/p>\n<p>Gelingt das ohne Fehlermeldungen, steht dem Kompilieren in eine <b>.mde-\/.accde<\/b>-Datei nichts mehr im Wege. Dann wechseln Sie wieder zur Access-Anwendung und w&auml;hlen den Bereich <b>Datei|Speichern unter <\/b>aus. Hier aktivieren Sie die Option <b>ACCDE erstellen <\/b>und klicken auf <b>Speichern unter <\/b>oder Sie klicken direkt doppelt auf <b>ACCDE erstellen<\/b> (siehe Bild 16).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_04\/pic_1196_016.png\" alt=\"Datenbank als ACCDE speichern\" width=\"700\" height=\"335,8524\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 16: Datenbank als ACCDE speichern<\/span><\/b><\/p>\n<p>Im nun erscheinenden Dialog <b>Speichern unter <\/b>geben Sie den Pfad der zu erstellenden Datenbank-Datei an.<\/p>\n<h2>Test der Funktionen<\/h2>\n<p>Nun &ouml;ffnen wir die <b>.accde<\/b>-Datei und probieren das Formular <b>frmKunden <\/b>aus. Hier taucht noch ein Fehler auf, der durch die Verwendung der Datenmakros bedingt ist, die wir im Beitrag <b>Zugriffsrechte mit Datenmakros <\/b>(<b>www.access-im-unternehmen.de\/1193<\/b>) beschreiben. Hier verwenden wir Datenmakros, um zu pr&uuml;fen, ob der aktuell angemeldete Benutzer die Berechtigung zum Hinzuf&uuml;gen, &Auml;ndern oder L&ouml;schen von Datens&auml;tzen hat. Ist das nicht der Fall, soll die Bearbeitung unterbunden werden. In den Makros greifen wir unter anderem auf eine Abfrage namens <b>qryTabellenberechtigungen <\/b>zu. Diese haben wir zu diesem Zeitpunkt aber noch nicht in das Backend kopiert, also befindet sich die Abfrage noch im Frontend. Wir versuchen zun&auml;chst, diese Abfrage einfach in das Backend zu kopieren und stellen fest, dass das Makro nun funktioniert. Abfragen, die in Datenmakros zum Einsatz kommen, m&uuml;ssen sich also in der gleichen Datenbank befinden wie die Tabellen mit den Datenmakros.<\/p>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Es gibt sicher noch weitere Konstellationen, die durch geeignete Zuweisung von Recordsets angepasst werden m&uuml;ssen. Grunds&auml;tzlich sollten sich jedoch alle F&auml;lle, in denen Daten aus den Tabellen der per Kennwort gesch&uuml;tzten Backend-Datenbank angezeigt werden sollen, abbilden lassen.<\/p>\n<p>Mit der hier verwendeten Kombination aus einem Front-end mit kompiliertem Quellcode und einem kennwortgesch&uuml;tzten Backend ist der unerw&uuml;nschte Zugriff auf die Daten quasi nicht m&ouml;glich. In der Beispieldatenbank befinden sich die Formulare f&uuml;r das Einstellen der Berechtigungen noch im Frontend. So kann der Benutzer nat&uuml;rlich noch die Berechtigungen vergeben, die er zum Manipulieren der Tabellen ben&ouml;tigt. Im Produktiveinsatz m&uuml;ssten Sie diese Elemente also noch aus der Datenbank entfernen.<\/p>\n<h3>Downloads zu diesem Beitrag<\/h3>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>Daten.accdb<\/p>\n<p>ZugriffsrechteMitDatenmakros.accdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/25070AB8-4C70-4035-BF27-0EA4A87A668E\/aiu_1196.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In Ausgabe 3\/2019 haben wir einige Elemente vorgestellt, die den Zugriff auf die Daten einer Datenbank einschr&auml;nken sollen &#8211; zum Beispiel eine einfache Benutzerverwaltung und die M&ouml;glichkeit, den Zugriff auf Tabellen per Datenmakro einzuschr&auml;nken. Im vorliegenden Beitrag wollen wir zeigen, wie Sie diese Techniken abrunden und den direkten Zugriff des Benutzers auf die Tabellen der Datenbank endg&uuml;ltig verhindern. Dazu exportieren wir diese in eine Backend-Datenbank, auf die wir diesmal nicht wie gewohnt per Verkn&uuml;pfung zugreifen &#8211; sondern ausschlie&szlig;lich per VBA. Das hat den Vorteil, dass wir das Backend mit einem Kennwort versehen k&ouml;nnen, das nur im Code des Frontends zum Einsatz kommt &#8211; und diesen k&ouml;nnen wir durch Umwandlung in eine .mde- beziehungsweise .accde-Datenbank vor den Augen des Benutzers verbergen.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[662019,66042019,44000038],"tags":[],"class_list":["post-55001196","post","type-post","status-publish","format-standard","hentry","category-662019","category-66042019","category-Sicherheit"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.9 (Yoast SEO v27.3) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Tabellen vor unerlaubtem Zugriff sch&uuml;tzen - Access im Unternehmen<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/access-im-unternehmen.de\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Tabellen vor unerlaubtem Zugriff sch&uuml;tzen\" \/>\n<meta property=\"og:description\" content=\"In Ausgabe 3\/2019 haben wir einige Elemente vorgestellt, die den Zugriff auf die Daten einer Datenbank einschr&auml;nken sollen - zum Beispiel eine einfache Benutzerverwaltung und die M&ouml;glichkeit, den Zugriff auf Tabellen per Datenmakro einzuschr&auml;nken. Im vorliegenden Beitrag wollen wir zeigen, wie Sie diese Techniken abrunden und den direkten Zugriff des Benutzers auf die Tabellen der Datenbank endg&uuml;ltig verhindern. Dazu exportieren wir diese in eine Backend-Datenbank, auf die wir diesmal nicht wie gewohnt per Verkn&uuml;pfung zugreifen - sondern ausschlie&szlig;lich per VBA. Das hat den Vorteil, dass wir das Backend mit einem Kennwort versehen k&ouml;nnen, das nur im Code des Frontends zum Einsatz kommt - und diesen k&ouml;nnen wir durch Umwandlung in eine .mde- beziehungsweise .accde-Datenbank vor den Augen des Benutzers verbergen.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2020-05-13T20:55:30+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg06.met.vgwort.de\/na\/e9b83bbf73484a7caaeb6f514825c512\" \/>\n<meta name=\"author\" content=\"Andr\u00e9 Minhorst\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Andr\u00e9 Minhorst\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"26\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Tabellen vor unerlaubtem Zugriff sch&uuml;tzen\",\"datePublished\":\"2020-05-13T20:55:30+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\\\/\"},\"wordCount\":4030,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg06.met.vgwort.de\\\/na\\\/e9b83bbf73484a7caaeb6f514825c512\",\"articleSection\":[\"2019\",\"4\\\/2019\",\"Sicherheit\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\\\/\",\"name\":\"Tabellen vor unerlaubtem Zugriff sch&uuml;tzen - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg06.met.vgwort.de\\\/na\\\/e9b83bbf73484a7caaeb6f514825c512\",\"datePublished\":\"2020-05-13T20:55:30+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg06.met.vgwort.de\\\/na\\\/e9b83bbf73484a7caaeb6f514825c512\",\"contentUrl\":\"http:\\\/\\\/vg06.met.vgwort.de\\\/na\\\/e9b83bbf73484a7caaeb6f514825c512\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Tabellen vor unerlaubtem Zugriff sch&uuml;tzen\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"name\":\"Access im Unternehmen\",\"description\":\"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access\",\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/access-im-unternehmen.de\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\",\"name\":\"Andr\u00e9 Minhorst Verlag\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"contentUrl\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"width\":370,\"height\":111,\"caption\":\"Andr\u00e9 Minhorst Verlag\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\",\"name\":\"Andr\u00e9 Minhorst\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"caption\":\"Andr\u00e9 Minhorst\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Tabellen vor unerlaubtem Zugriff sch&uuml;tzen - Access im Unternehmen","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/access-im-unternehmen.de\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\/","og_locale":"de_DE","og_type":"article","og_title":"Tabellen vor unerlaubtem Zugriff sch&uuml;tzen","og_description":"In Ausgabe 3\/2019 haben wir einige Elemente vorgestellt, die den Zugriff auf die Daten einer Datenbank einschr&auml;nken sollen - zum Beispiel eine einfache Benutzerverwaltung und die M&ouml;glichkeit, den Zugriff auf Tabellen per Datenmakro einzuschr&auml;nken. Im vorliegenden Beitrag wollen wir zeigen, wie Sie diese Techniken abrunden und den direkten Zugriff des Benutzers auf die Tabellen der Datenbank endg&uuml;ltig verhindern. Dazu exportieren wir diese in eine Backend-Datenbank, auf die wir diesmal nicht wie gewohnt per Verkn&uuml;pfung zugreifen - sondern ausschlie&szlig;lich per VBA. Das hat den Vorteil, dass wir das Backend mit einem Kennwort versehen k&ouml;nnen, das nur im Code des Frontends zum Einsatz kommt - und diesen k&ouml;nnen wir durch Umwandlung in eine .mde- beziehungsweise .accde-Datenbank vor den Augen des Benutzers verbergen.","og_url":"https:\/\/access-im-unternehmen.de\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\/","og_site_name":"Access im Unternehmen","article_published_time":"2020-05-13T20:55:30+00:00","og_image":[{"url":"http:\/\/vg06.met.vgwort.de\/na\/e9b83bbf73484a7caaeb6f514825c512","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"26\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Tabellen vor unerlaubtem Zugriff sch&uuml;tzen","datePublished":"2020-05-13T20:55:30+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\/"},"wordCount":4030,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\/#primaryimage"},"thumbnailUrl":"http:\/\/vg06.met.vgwort.de\/na\/e9b83bbf73484a7caaeb6f514825c512","articleSection":["2019","4\/2019","Sicherheit"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\/","url":"https:\/\/access-im-unternehmen.de\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\/","name":"Tabellen vor unerlaubtem Zugriff sch&uuml;tzen - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\/#primaryimage"},"thumbnailUrl":"http:\/\/vg06.met.vgwort.de\/na\/e9b83bbf73484a7caaeb6f514825c512","datePublished":"2020-05-13T20:55:30+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\/#primaryimage","url":"http:\/\/vg06.met.vgwort.de\/na\/e9b83bbf73484a7caaeb6f514825c512","contentUrl":"http:\/\/vg06.met.vgwort.de\/na\/e9b83bbf73484a7caaeb6f514825c512"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Tabellen_vor_unerlaubtem_Zugriff_schuetzen\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Tabellen vor unerlaubtem Zugriff sch&uuml;tzen"}]},{"@type":"WebSite","@id":"https:\/\/access-im-unternehmen.de\/#website","url":"https:\/\/access-im-unternehmen.de\/","name":"Access im Unternehmen","description":"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access","publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/access-im-unternehmen.de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/access-im-unternehmen.de\/#organization","name":"Andr\u00e9 Minhorst Verlag","url":"https:\/\/access-im-unternehmen.de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/","url":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","contentUrl":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","width":370,"height":111,"caption":"Andr\u00e9 Minhorst Verlag"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f","name":"Andr\u00e9 Minhorst","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","caption":"Andr\u00e9 Minhorst"}}]}},"_links":{"self":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001196","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/comments?post=55001196"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001196\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001196"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001196"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001196"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}