Services – mit Spring Security, WebFlow, MVC

Sonntag, 23. März 2014 von  
unter Fachartikel Architektur

Die Motivation

Das wichtige Thema Security ist nicht nur für die Sicherheit der bei einer WebService-Anfrage/-Antwort übermittelten Nachrichten von Bedeutung, sondern hat auch bei klassischen Web-Applikationen z. B. bei der Anmeldung (Login existierender Benutzer oder Registrierung neuer User) eine hohe Relevanz für die korrekte Authentifizierung der Anwender. Hier ein klassisches Beispiel zu einer solchen JEE-Login-WebApplikation auf Basis des Servlet-API 2.4 einer login.jsp und zweier simpler Javascript-Dateien. Die folgende web.xml dürfte so mancher Leserin und manchem Leser aus den Vorbereitungen zum SCWCD- bzw. OCWCD bekannt sein:

<?xml version=“1.0″ encoding=“UTF-8″?>
<web-app version=“2.4″ xmlns=“http://java.sun.com/xml/ns/j2ee“
      xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance“
      xsi:schemaLocation=“http://java.sun.com/xml/ns/j2ee
      http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd“>
     
      <display-name>loginWebApp</display-name>
      <welcome-file-list>
        <welcome-file>pages/index.jsp</welcome-file>
      </welcome-file-list>
      <security-constraint>
        <display-name>secured Area</display-name>
        <web-resource-collection>
            <web-resource-name>Secured Area</web-resource-name>
            <url-pattern>/pages/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>application-access</role-name>
        </auth-constraint>
      </security-constraint>
      <login-config>
        <auth-method>FORM</auth-method>
        <form-login-config>
            <form-login-page>/login.jsp</form-login-page>
            <form-error-page>/login.jsp?loginfailed=true</form-error-page>
        </form-login-config>
      </login-config>
      <security-role>
          <role-name>application-access</role-name>
      </security-role>
</web-app>

Der Zugriff auf das Unterverzeichnis /pages mit der darin enthaltenen index jsp wird also auf erfolgreich eingeloggte User beschränkt, welche die Rolle (role) application-access haben. Alle anderen (die nicht erfolgreich eingeloggt sind, oder nicht die Rolle application-access haben) werden (z.B. mittels Apache WebServer-Konfiguration) per redirect auf die login.jsp – Seite zurückgeschickt.

Für solche Login- und weiterführende Security-Aspekte bietet SpringSource.com mit Spring Security in gewohnter Art eine ausgereifte Lösung an mit sehr guter Integration in JEE-WebFrameworks, wie z. B. Spring WebFlow und Spring MVC, wovon dieser Blog-Eintrag handeln soll.

SpringSecurity – die Grundlagen

Der Einsatz von SpringSecurity soll anhand der Version Spring 2.5 auf Basis der Verwendung von Spring-XML-Dateien an einem bekannten Beispiel erklärt werden und danach auch für eine neuere Version Spring 3.6 unter Verwendung vom Annotationen.

Der Aufbau des Beispiel-Projekts mit den vom Einsatz von SpringSecurity betroffenen Dateien sieht dann so aus:

/WEB-INF/lib/org.springframework.security-2.0.4.A.jar
/WEB-INF/web.xml
/WEB-INF/login.xhtml
/WEB-INF/logoutSuccess.xhtml
/WEB-INF/layouts/standard.xhtml
/WEB-INF/config/web-application-config.xml
/WEB-INF/config/webflow-config.xml
/WEB-INF/config/security-config.xml
/WEB-INF/flows/booking/booking-flow.xml

Der Einsatz von SpringSecurity erfolgt also sowohl in per web.xml als auch in der Spring-Applikations-Konfigurationsdatei web-application-config.xml, der  Spring WebFlow-Konfigurationsdatei webflow-config.xml und der Flow-Konfigurationsdatei für die Hotel-Buchung booking-flow.xml:

Die SpringSecurity-Konfiguration betrifft in der web.xml bestimmte URL-Patterns, die über einen DelegationFilterProxy mit Hilfe des, z. B. in der security-config.xml definierten Authentifizierungs-Provider auch bestimmte Login/Logout-Mechanismen bedienen. Dabei wird die security-config.xml in der web-application-config.xml importiert, die wiederum in der web.xml als Context-Parameter contextConfigLocation bekanntgemacht wird.

/WEB-INF/web.xml: Einschalten der Spring Security:

<!– The master configuration file for this Spring web application –>
      <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                  /WEB-INF/config/web-application-config.xml
            </param-value>
      </context-param>
      <!– Enables Spring Security –>
       <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
      <filter-name>springSecurityFilterChain</filter-name>
      <url-pattern>/*</url-pattern>
    </filter-mapping>

Mit Hilfe der Security Attribute in der WEB-INF/flows/booking/booking-flow.xml:

<secured attributes=„ROLE_USER“ />

Hinter <secured attributes=“ …, ….“ /> kann nun prinzipiell eine Komma-separierte Liste von Security-Attributen folgen.

Den Attributen wird von einem Spring Security Default Zugriffskontroll-Manager der Zugriff auf einen Flow erlaubt oder verboten. Dieses Default-Verhalten des User-/Rollen-basierten “Access Decision Managers“ muss überschrieben werden, wenn die Applikation keine “Authorization Roles“ (wie z. B. ROLE_USER) verwenden soll. (In der Spring Security Reference Dokumentation finden sich mehr Infos hierzu und über die “Custom Decision Manager“-Konfiguration.) Das match-Attribut wird allerdings nur ausgewertet, wenn der Default Zugriffskontroll-Manager (“Access Decision Manager“) verwendet wird.

Das Attribut match kann hierbei die Werte any oder all annehmen. Any erlaubt den Zugriff auf einen bestimmten Flow (hier: den booking-flow),wenn der User mindestens eins der erforderlichen Security Attribute hat. All erlaubt den Zugriff auf einen bestimmten Flow (hier: den booking-flow) nur, wenn der User alle der erforderlichen Security Attribute hat. Der Default-Wert für match ist “any“.

Allein das Definieren von Security Regeln für URL-Patterns in der web.xml und Secured Attributen in einer *flow.xml verhindert jedoch noch nicht die Ausführung dieses Flows. Auch muss ein SecurityFlowExecutionListener als Bean in the WebFlow-Konfiguration  definiert werden und dem Flow Executor übergeben werden, wie hier in der

/WEB-INF/config/webflow-config.xml:

<?xml version=„1.0“ encoding=„UTF-8“?>
<beans xmlns=„http://www.springframework.org/schema/beans“
       xmlns:xsi=„http://www.w3.org/2001/XMLSchema-instance“
       xmlns:flow=„http://www.springframework.org/schema/webflow-config“
       xmlns:faces=„http://www.springframework.org/schema/faces“
       xsi:schemaLocation=
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.orgHYPERLINK „http://www.springframework.org/schema/webflow-config“/schema/webflow-config
           http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd
           http://www.springframework.org/schema/faces
           http://www.springframework.org/schema/faces/spring-faces-2.0.xsd>

      <!– Executes flows: the central entry point into the Spring Web Flow system –>
      <flow:flow-executor id=flowExecutor>
            <flow:flow-execution-listeners>
                  <flow:listener ref=securityFlowExecutionListener />
            </flow:flow-execution-listeners>
      </flow:flow-executor>
[…]
      <!– Installs a listener to apply Spring Security authorities –>
      <bean id=securityFlowExecutionListener class=„org.springframework.webflow.security.SecurityFlowExecutionListener“ />
</beans>

Wenn der Zugriff zu einem Teil der Applikation verweigert wird, erfolgt die Generierung und Auslösung einer AccessDeniedException, welche von SpringSecurity gefangen und verwendet wird, um den User auf die Login-Seite umzuleiten, wobei es wichtig ist, dass diese Exception auch vorher von keinem anderen Aufrufer auf dem Call Stack gefangen und behandelt wird, damit SpringSecurity dies tun kann.

Hier noch die /WEB-INF/config/security-config.xml mit Authentication Provider-Beispiel:

<?xml version=“1.0″ encoding=“UTF-8″?>
<beans xmlns=“http://www.springframework.org/schema/beans“
       xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance“
       xmlns:security=“http://www.springframework.org/schema/security“
       xsi:schemaLocation=“
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/security
           http://www.springframework.org/schema/security/spring-security-2.0.2.xsd„>
 
      <!– Configure Spring Security –>
      <security:http auto-config=“true“>
            <security:form-login login-page=“/spring/login“
login-processing-url=“/spring/loginProcess“
default-target-url=“/spring/main“
authentication-failure-url=“/spring/login?login_error=1″
/>
      <security:logout logout-url=“/spring/logout“
     logout-success-url=“/spring/logoutSuccess“ />
      </security:http>
      <!– Define local authentication provider, could be another provider
(JDBC, LDAP, etc.)
            usernames/passwords are:
                  keith/melbourne
                  erwin/leuven
                  jeremy/atlanta
                  scott/rochester
                  chuck/bananaboat
      –>
      <security:authentication-provider>
            <security:password-encoder hash=“md5″ />
            <security:user-service>
                  <security:user name=“keith“ password=“417c7382b16c395bc25b5da1398cf076″ authorities=“ROLE_USER, ROLE_SUPERVISOR“ />
                  <security:user name=“erwin“ password=“12430911a8af075c6f41c6976af22b09″ authorities=“ROLE_USER, ROLE_SUPERVISOR“ />
                  <security:user name=“jeremy“ password=“57c6cbff0d421449be820763f03139eb“ authorities=“ROLE_USER“ />
                  <security:user name=“scott“ password=“942f2339bf50796de535a384f0d1af3e“ authorities=“ROLE_USER“ />              
                  <security:user name=“chuck“ password=“361e4ebb3c04665a16bf044bfeaf04f1″ authorities=“ROLE_USER“ />
            </security:user-service>
      </security:authentication-provider>
</beans>

Die verwendeten MD5-Hashes können mit dem folgenden MD5HashcodeService z. B. manuell erzeugt werden:

package org.springframework.webflow.samples.booking;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;

public class Md5HashcodeService {
    private static final int BUFFER_SIZE = 1024;

    private static final int S11 = 7;
    private static final int S12 = 12;
    private static final int S13 = 17;
    private static final int S14 = 22;
    private static final int S21 = 5;
    private static final int S22 = 9;
    private static final int S23 = 14;
    private static final int S24 = 20;
    private static final int S31 = 4;
    private static final int S32 = 11;
    private static final int S33 = 16;
    private static final int S34 = 23;
    private static final int S41 = 6;
    private static final int S42 = 10;
    private static final int S43 = 15;
    private static final int S44 = 21;

    private static byte padding[] = { (byte) 0x80, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0,
          (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0,
          (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0,
          (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0,
          (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0,
          (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0,
          (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0 };

    private InputStream in = null;
    private boolean stringp = false;
    private int state[] = null;
    private long count = 0;
    private byte buffer[] = null;
    private byte digest[] = null;

    private static String stringify(byte buf[]) {
      StringBuffer sb = new StringBuffer(2 * buf.length);
      for (int i = 0; i < buf.length; i++) {
          int h = (buf[i] & 0xf0) >> 4;
          int l = (buf[i] & 0x0f);
          sb.append(new Character((char) ((h > 9) ? ‚a‘ + h – 10 : ‚0‘ + h)));
          sb.append(new Character((char) ((l > 9) ? ‚a‘ + l – 10 : ‚0‘ + l)));
      }
      return sb.toString();
    }

    private final int F(int x, int y, int z) {
      return ((x & y) | ((~x) & z));
    }

    private final int G(int x, int y, int z) {
      return ((x & z) | (y & (~z)));
    }

    private final int H(int x, int y, int z) {
      return (x ^ y ^ z);
    }

    private final int I(int x, int y, int z) {
      return (y ^ (x | (~z)));
    }

    private final int rotate_left(int x, int n) {
      return ((x << n) | (x >>> (32 – n)));
    }

    private final int FF(int a, int b, int c, int d, int x, int s, int ac) {
      a += (F(b, c, d) + x + ac);
      a = rotate_left(a, s);
      a += b;
      return a;
    }

    private final int GG(int a, int b, int c, int d, int x, int s, int ac) {
      a += (G(b, c, d) + x + ac);
      a = rotate_left(a, s);
      a += b;
      return a;
    }

    private final int HH(int a, int b, int c, int d, int x, int s, int ac) {
      a += (H(b, c, d) + x + ac);
      a = rotate_left(a, s);
      a += b;
      return a;
    }

    private final int II(int a, int b, int c, int d, int x, int s, int ac) {
      a += (I(b, c, d) + x + ac);
      a = rotate_left(a, s);
      a += b;
      return a;
    }

    private final void decode(int output[], byte input[], int off, int len) {
      int i = 0;
      int j = 0;
      for (; j < len; i++, j += 4) {
          output[i] = (((int) (input[off + j] & 0xff)) | (((int) (input[off + j + 1] & 0xff)) << 8)
                | (((int) (input[off + j + 2] & 0xff)) << 16) | (((int) (input[off + j + 3] & 0xff)) << 24));
      }
    }

    private final void transform(byte block[], int offset) {
      int a = state[0];
      int b = state[1];
      int c = state[2];
      int d = state[3];
      int x[] = new int[16];

      decode(x, block, offset, 64);
      /* Round 1 */
      a = FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */
      d = FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */
      c = FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */
      b = FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */
      a = FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */
      d = FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */
      c = FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */
      b = FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */
      a = FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */
      d = FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */
      c = FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
      b = FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
      a = FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
      d = FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
      c = FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
      b = FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
      /* Round 2 */
      a = GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */
      d = GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */
      c = GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
      b = GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */
      a = GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */
      d = GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */
      c = GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
      b = GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */
      a = GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */
      d = GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
      c = GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */
      b = GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */
      a = GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
      d = GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */
      c = GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */
      b = GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
 
      /* Round 3 */
      a = HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */
      d = HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */
      c = HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
      b = HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
      a = HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */
      d = HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */
      c = HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */
      b = HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
      a = HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
      d = HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */
      c = HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */
      b = HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */
      a = HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */
      d = HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
      c = HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
      b = HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */
 
      /* Round 4 */
      a = II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */
      d = II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */
      c = II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
      b = II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */
      a = II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
      d = II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */
      c = II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
      b = II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */
      a = II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */
      d = II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
      c = II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */
      b = II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
      a = II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */
      d = II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
      c = II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */
      b = II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */

      state[0] += a;
      state[1] += b;
      state[2] += c;
      state[3] += d;
    }

    private final void update(byte input[], int len) {
      int index = ((int) (count >> 3)) & 0x3f;
      count += (len << 3);
      int partLen = 64 – index;
      int i = 0;
      if (len >= partLen) {
          System.arraycopy(input, 0, buffer, index, partLen);
          transform(buffer, 0);
          for (i = partLen; i + 63 < len; i += 64)
            transform(input, i);
          index = 0;
      } else {
          i = 0;
      }
      System.arraycopy(input, i, buffer, index, len – i);
    }

    private byte[] end() {
      byte bits[] = new byte[8];
      for (int i = 0; i < 8; i++)
          bits[i] = (byte) ((count >>> (i * 8)) & 0xff);
      int index = ((int) (count >> 3)) & 0x3f;
      int padlen = (index < 56) ? (56 – index) : (120 – index);
      update(padding, padlen);
      update(bits, 8);
      return encode(state, 16);
    }

    // Encode the content.state array into 16 bytes array
    private byte[] encode(int input[], int len) {
      byte output[] = new byte[len];
      int i = 0;
      int j = 0;
      for (; j < len; i++, j += 4) {
          output[j] = (byte) ((input[i]) & 0xff);
          output[j + 1] = (byte) ((input[i] >> 8) & 0xff);
          output[j + 2] = (byte) ((input[i] >> 16) & 0xff);
          output[j + 3] = (byte) ((input[i] >> 24) & 0xff);
      }
      return output;
    }

    /**
     * Get the digest for our input stream. This method constructs the input stream digest and returns it as a String
     * following the MD5 (rfc1321) algorithm.
     *
     * @return An instance of String, giving the message digest.
     * @exception IOException thrown if the digestifier could not read input stream.
     */
    public byte[] getDigest() throws IOException {
      byte buffer[] = new byte[BUFFER_SIZE];
      int got = -1;

      if (digest != null)
          return digest;
      while ((got = in.read(buffer)) > 0)
          update(buffer, got);
      this.digest = end();
      return digest;
    }

    /**
     * Get the digest for this string digestifier. This method does not throw any IOException since the exception
     * handling within the method checks for String class and in case of class name mismatch throws a RuntimeException.
     * Further: the used byte[] stream has been built by usage of String
     */
    public byte[] processString() {
      if (!stringp)
          throw new RuntimeException(this.getClass().getName() + „[processString]“ + “ not a string.“);
      try {
          return getDigest();
      } catch (IOException ex) {
      }
      throw new RuntimeException(this.getClass().getName() + „[processString]“ + „: implementation error.“);
    }

    /**
     * Get the digest, as a string.
     */
    public String getStringDigest() {
      if (digest == null)
          throw new RuntimeException(this.getClass().getName() + „[getStringDigest]“ + „: called before processing.“);
      return stringify(digest);
    }

    /**
     * Construct a digestifier for the given string.
     *
     * @param input The string to be digestified.
     * @param encoding the encoding name used (such as UTF8)
     */
    protected void initMd5Service(String sIn, String sEnc) {
      byte bytes[] = null;
      try {
          bytes = sIn.getBytes(sEnc);
      } catch (UnsupportedEncodingException e) {
          throw new RuntimeException(„no “ + sEnc + “ encoding!!!“);
      }
      this.stringp = true;
      this.in = new ByteArrayInputStream(bytes);
      this.state = new int[4];
      this.buffer = new byte[64];
      this.count = 0;
      state[0] = 0x67452301;
      state[1] = 0xefcdab89;
      state[2] = 0x98badcfe;
      state[3] = 0x10325476;
    }

    /**
     * Default noargs constructor
     */
    public Md5HashcodeService() {
      this(„“);
    }
    /**
     * Construct a digestifier for the given string.
     *
     * @param input The string to be digestified.
     */
    public Md5HashcodeService(String input) {
      this(input, „UTF8“);
    }
    /**
     * Construct a digestifier for the given string and encoding
     *
     * @param input The string to be digestified.
     * @param enc The string of required encoding (e.g. UTF8).
     */
    public Md5HashcodeService(String input, String enc) {
      this.initMd5Service(input, enc);
    }

    /**
     * Construct a digestifier for the given input stream.
     * @param in The input stream to be digestified.
     */
    public Md5HashcodeService(InputStream in) {
      this.stringp = false;
      this.in = in;
      this.state = new int[4];
      this.buffer = new byte[64];
      this.count = 0;
      state[0] = 0x67452301;
      state[1] = 0xefcdab89;
      state[2] = 0x98badcfe;
      state[3] = 0x10325476;
    }

    public static void main(String args[]) throws IOException {
      // String strLine = args[0];
      String strLine = „bananaboat“;
      // final String strEncode = „UTF8“;
      try {
          Md5HashcodeService mymd5 = new Md5HashcodeService();
          mymd5.initMd5Service(strLine, „UTF8“);
          mymd5.getDigest();
          strLine = mymd5.getStringDigest();
          System.out.println(strLine); // 361e4ebb3c04665a16bf044bfeaf04f1
      } catch (Exception e) {
          e.printStackTrace();
      }
    }
}

Es gibt in diesem Blog bereits einen Eintrag zur Unternehmens-Login Komponente mit dem JBoss hier, weshalb bei dieser Gelegenheit auf die von RedHat bereits vorhandenen org.jboss.*-Packages und Java-Klassen zum Hashing etc. beim Login hingewiesen werden darf.

Hier noch die vom Login betroffenen XHTML-Seiten der SpringSecurity Beispiel-Applikation:

src/main/webapp/WEB-INF/login.xhtml:

<!DOCTYPE composition PUBLIC „-//W3C//DTD XHTML 1.0 Transitional//EN“ „http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd>
<ui:composition xmlns=„http://www.w3.org/1999/xhtml“
                xmlns:ui=„http://java.sun.com/jsf/facelets“
                xmlns:c=„http://java.sun.com/jstl/core“
                template=„layouts/standard.xhtml“>
<ui:define name=„content“>
<h1>Login Required</h1>
<div class=„section“>
      <c:if test=„${not empty param.login_error}“>
            <div class=„errors“>
                Your login attempt was not successful, try again.<br />
                Reason: #sessionScope.SPRING_SECURITY_LAST_EXCEPTION.message}
            </div>
      </c:if>
      <br/>
      <p>Valid username/passwords are:</p>
      <ul>
          <li>keith/melbourne</li>
          <li>erwin/leuven</li>
          <li>jeremy/atlanta</li>
          <li>scott/rochester</li>
          <li>chuck/bananaboat</li>
      </ul>
</div>
<div class=„section“>
    <form name=„f“
      action=„${request.contextPath}/spring/loginProcess“ method=„post“>
      <fieldset>
    <div class=„field“>
    <div class=„label“>User:</div>
    <div class=„output“>
    <c:if test=„${not empty param.login_error}“>
      <c:set var=„username“   value=„${sessionScope.SPRING_SECURITY_LAST_USERNAME}“/>
    </c:if>
    <input type=„text“ name=„j_username“ value=„#{username}“/>
    </div>
    </div>
                  <div class=„field“>
                        <div class=„label“>Password:</div>
                        <div class=„output“>
                             <input type=„password“ name=„j_password“ />
                        </div>
                  </div>
                  <div class=„field“>
                        <div class=„label“>Don’t ask for my password for two weeks:</div>
                        <div class=„output“>
                             <input type=„checkbox“ name=„_spring_security_remember_me“/>
                        </div>
                  </div>
            </fieldset>
            <div class=„buttonGroup“>
                  <input name=„submit“ type=„submit“ value=„Login“ />
            </div>
      </form>
</div>
</ui:define>
</ui:composition>

src/main/webapp/WEB-INF/logoutSuccess.xhtml:

<!DOCTYPE composition PUBLIC „-//W3C//DTD XHTML 1.0 Transitional//EN“ „http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd>
<ui:composition xmlns=„http://www.w3.org/1999/xhtml“
                xmlns:ui=„http://java.sun.com/jsf/facelets“
                template=„layouts/standard.xhtml“>
<ui:define name=„content“>
      <div class=„section“>
            <h1>Logout</h1>
            <p>You have successfully logged out.</p>
            <p>
               <a href=„#{request.contextPath}/spring/main“>Continue</a>
            </p> 
      </div>
</ui:define>
</ui:composition>

Weiterführende Quellen

Soviel zum simplen Einsatz von SpringSecurity in Spring 2.5, jedoch gibt es hier noch weitere Infos zum Einsatz von SpringSecurity in Spring 3.2, welcher durch den Einsatz von Annotationen weiter vereinfacht wird:

Projects:

http://projects.spring.io/spring-security

Webinar:

http://spring.io/blog/2014/01/21/webinar-replay-spring-security-3-2

An dieser Stelle darf noch auf die sehr guten Seminare zum Thema TDD mit Java von Binaris hier hingewiesen werden.

Hier noch der DZone-Link zur RefCard von Spring Security.

Allen interessierten Leserinnen und Lesern noch eine gute und erfolgreiche Zeit.

Kommentare

Ein Kommentar zu “Services – mit Spring Security, WebFlow, MVC”

  1. Von Architekturen – mit Spring CDI, JPA, JBoss, JavaEE : binaris informatik GmbH am Dienstag, 1. Juli 2014 22:21

    […] einen Blog-Eintrag. In Spring wird auch gerne das Spring Security Framework verwendet, worüber es hier bereits einen Blog-Eintrag […]

Einen Kommentar hinzufügen...

Sie müssen registriert und angemeldet sein um einen Kommentar zu schreiben.