Sicherheit über JAAS mit einer MySQL Datenbank

Ich gebe zu, ich habe es lang vor mir her geschoben:

Neben all der schönen REST-fähigen Implementierung, ist Sicherheit ein Bereich, der bisher ein wenig zu kurz kam. Zwar habe ich schon eine schöne Möglichkeit gefunden, wie man mit YUI eine Anmeldung über HTTP AUTH hin bekommt, aber letztendlich fehlt(e) bisher die komplette Serverseite.

Ich habe jetzt einiger Zeit gesucht und bin zu dem Ergebnis gekommen, dass mir der Java Application Server (sei es Tomcat, Glassfish o.a.) bereits mehrere JAAS basierte Module zur Verfügung stellt.
Diese Entscheidung traf ich (da es hierbei sicherlich auch Einschränkungen einzugehen gilt) u.a. nach dem Lesen dieses Artikels.

Darin wird einem abgeraten, eine Logion/Session Verwaltung in Eigenregie zu erstellen, da hierdurch teilweise ein Sicherheitsrisiko eingegangen wird.

Nach weitere Suche kam ich zu einem interessanten Artikel von Shing Wai Chan, in dem er die Einrichtung eines JDBCRealm auf einem GlassFish Server beschreibt. Bis auf das die Standard digest-Algorithmen auf den verschiedenen Application Servern unterschiedlich sind, sollte dies unter den verschiedenen Systemen lauffähig sein.

Aufgrund der Übersichtlichkeit und da ich ungern Systeme in meinen Datentabellen herumfuhrwerken lasse, habe ich mir für die entsprechend benötigten Tabellen 2 Views angelegt:

CREATE ALGORITHM = TEMPTABLE VIEW usertable AS
SELECT u.email AS userid, u.password
FROM Users u
CREATE  ALGORITHM = TEMPTABLE VIEW grouptable AS
SELECT u.email AS userid, g.name
FROM Groups g, Users u, UsersGroups us
WHERE us.user_id = u.user_id
AND g.group_id = us.group_id

Damit kann ich weiterhin sicher sein, dass der Server auch tatsächlich auf die Standard-Variablen zugreifen kann. Natürlich bin ich damit den Nachteil eingegangen, dass ein Benutzer hier nur in einer Gruppe sein kann.
Ich denke aber, dass dies zu verkraften ist, da es sich hierbei nur um den Login handelt und hierbei eigentlich nur interessant ist, ob der Benutzer als Admin oder als User angemeldet wird.
Als Standardgruppen sind in der Datenbank admins und users gesetzt.
So habe ich in meiner Config-Datei (sun-web.xml) folgende Zeilen eingefügt:

<security-role-mapping>
    <role-name>cotodo-admin-login</role-name>
    <principal-name class-name="Users">admin</principal-name>
    <group-name>admins</group-name>
</security-role-mapping>
<security-role-mapping>
    <role-name>cotodo-user-login</role-name>
    <principal-name class-name="Users">admin</principal-name>
    <group-name>users</group-name>
</security-role-mapping>

Also Session Methode werde ich mich dann wohl für eine Cookie basierte Session entscheiden, weil eine URL-encodierte Session die schönen REST-URIs verschandeln würde und dadurch auch den Austausch mit YUI erschweren mag.
Meine beiden letzten Punkte auf der Agenda (die ich diese Jahr noch durch bekommen will (!!!), sind folgende:

  1. Wie gehe ich sicher dass ein User umgeleitet wird, wenn er eine ungültige Session hat und sich einen XML-Resource Stream besorgt? (letztendlich wird es da sicherlich entsprechende Methoden geben, welche vor allen anderen Aufrufen prüfen können, ob Autorisierung und Authentisierung korrekt sind.
  2. HTTPS. Es war erstaunlich einfach, dem Server klar zu machen, dass ich gerne HTTPS benutzen möchte (einfach andere URL :-)) .

Allerdings sollte man nun sicher sein dass:

  1. Alle Anfragen auf HTTP geblockt werden (oder noch besser: auf eine Login-Seite umgeleitet werden)
  2. Sicher gestellt wird, dass sich ein User nur über eine SSL Verbindung anmelden kann (sicherlich auch durch eine Weiterleitung möglich)

simple Step by Step JPA-JAXP-REST

Da ich mir ja langsam mal Gedanken machen sollte, wie ich meine Arbeit strukturiere. Habe ich mal angefangen, beispielhaft, die Schritte zu sammeln, welche notwendig sind, um eine DB Table mit Hilfe der Java Möglichkeiten REST fähig zu machen.
Ich gedenke dies in 3 Teile zu tun:

  1. JPA Entity: Wie wird aus einer Datenbank Tabelle ein Java Objekt
  2. JAXP: Wie bekomme ich von meiner JPA Entity möglichst einfach eine Repräsentation in XML (JSON geht auch, aber erstmal reicht mir XML).
  3. JSR311/Jersey: Wie gelingt, es die Ressourcen an bestimmte URIs zu binden?

Hier dann als mal Teil 1:

Als allererstes möchte ich die folgende Datenbank Tabelle als ein einfaches POJO (Plain Old Java Object) abbilden und die Annotations machen, welche notwendig sind, damit JPA auch weiß, wie es mit der Klasse umzugehen hat.
Folgende Tabelle:

TABLE Users
===================================================
[ user_id | email | forename | surname | password ]
===================================================

Die nachfolgende Klasse ist dann das passende POJO dazu:

/*
ne ganze menge imports:
*/
import de.hausswolff.cotodo.tools.Tools;
import java.io.Serializable;
import java.util.Collection;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
/*
Leider scheint der JPA Layer verschiedene Schlüsselwörter für sicher selbst in Anspruch zu nehmen. 
"User" gehört scheinbar auch dazu.
Ich musste also diese Klasse hier dann Users nennen und blieb bei den Entitäten nun bei dem Plural.
*/
@Entity
@Table(name = "Users")
public class Users implements Serializable {
    @Id
    @Column(name = "user_id", nullable = false)
    private Integer userId;

    @Column(name = "forename", nullable = false)
    private String forename;

    @Column(name = "surname", nullable = false)
    private String surname;

    @Column(name = "email", nullable = false)
    private String email;

    @Column(name = "password", nullable = false)
    private String password;

    /*
    Hier kommt dann noch eine Reihe von den Beziehungen der Entitäten untereinander. 
    Das lasse ich hier jetzt auch mal weg, weil es zum Thema keine Rolle spielt. 
    Als Beispiel mal eine Beziehung zu der Table Tasks über folgende Tabelle:
    TABLE UsersTasks
    ===============================
    [ task_id | user_id | send_to ]
    ===============================
    Hierbei ist "task_id" die ID des Tasks ;-). user_id ist die ID des Users, welcher den Tasks erstellt hat. 
    send_to enthält die ID des Users, an den der Task als Auftrag geschickt worden ist. 
    Da es n Empfänger seien sollen, muss das ganze natürlich in eine externe Tabelle ausgelagert werden.
    */

    @ManyToMany
    @JoinTable(name="UsersTasks",
    joinColumns=@JoinColumn(name="user_id"),
    inverseJoinColumns=@JoinColumn(name="task_id"))
    private Collection SendTaskCollection;

    @ManyToMany
    @JoinTable(name="UsersTasks",
    joinColumns=@JoinColumn(name="send_to"),
    inverseJoinColumns=@JoinColumn(name="task_id"))
    private Collection ReceivedTaskCollection;

    /*
    als (quasi virtuelle) Attribute erhält jede Users Klasse nun zwei Collections, 
    welche halt nicht direkt in der Users Tabelle gespeichert werden, 
    aber durch die Relationen in der UsersTasks Tabelle abgelegt sind.
    */
    public Users() {

    }
    public Users(Integer userId) {
        this.userId = userId;
    }

    public Users(Integer userId, String forename, String surname, String email, String password) {
        this.userId = userId;
        this.forename = forename;
        this.surname = surname;
        this.email = email;
        this.password = Tools.md5(password);
    }
    /*
    Es folgt nun die übliche Anzahl an gettern/settern. Hierbei habe ich mich entschlossen, 
    nicht stumpfsinnig alle Attribute per set zu öffnen, sondern wirklich nur jene, bei denen es notwendig ist. 
    Die beiden Listen aus den obigen relationen sollten nur lesbar sein. Der Empfänger/Ersteller werden im Task direkt angegeben.
    Als Beispiel hier mal setter/getter für das Passwort:
    */

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = Tools.md5(password);
    }

...

}

Ich habe vieles aus der Klasse weg gelassen, was nicht unbedingt für die Funktion von JPA notwendig ist, aber durchaus der besseren Lesbarkeit dient. Wichtig ist, dass es einen Standard-Konstruktor gibt.
Weiter geht es mit JAXP. Also der Repräsentation von obiger Klasse in XML.