BigBastis Blog About Me & my Digital Lifestyle

30Jan/140

Spring Security with custom AuthoritiesPopulator over LDAP

springlogo

Spring offers you a lot of possibilities when it comes to configuration. But sometimes this billions of possibilities can be a real pita! Reacently i had to connect a Spring MVC application to a LDAP authentication server and since the webapp allready run on spring security i decided to keep it that way an use the LdapAuthenticationProvider offered by spring.

When your project is simple enough so you can use the default configurations offered by spring the complete working LDAP configuration can be as simple as this:


<security:authentication-manager>
	<security:ldap-authentication-provider 
		user-search-filter="(uid={0})"
		user-search-base="ou=users"
		group-search-filter="(uniqueMember={0})"
		group-search-base="ou=groups"
		group-role-attribute="cn"
		role-prefix="ROLE_">
	</security:ldap-authentication-provider>
 </security:authentication-manager>
 
 <security:ldap-server url="ldap://localhost:10389/o=mojo" 
	 manager-dn="uid=admin,ou=system" 
	 manager-password="secret" />

It's as simple as that. Unfortunately our webapp doesn't get the roles from the LDAP, it's only used to authenticate the user, the roles are stored in a seperate database. So now we have to configure a seperate custom AuthoritiesPopulator for our users to get roles.

After a few minutes on google and stackoverflow a still haven't found a suitable solution for my little problem, which is very strange since google AND stackoverflow combine all the human knowledge, right? ;-)

I tried several combinations of different configurations and after a few hours i still haven't found a good and complete example of a configuration i needed. I even started to think that my solution was to unusual to find some good examples.

But eventually after A LOT trial and error i managed to get it working. Looking back it's not even complicated in any way, which makes me wonder why i haven't found a complete example on the internet.

So just to document my solution for the next somebody who's searching fo a similar way to implement LDAP in spring security, here's my configuration:

spring-configuration:


<?xml version="1.0" encoding="windows-1252"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd


http://www.springframework.org/schema/security

        http://www.springframework.org/schema/security/spring-security-3.1.xsd">

    <http use-expressions="true">
	    [...]
    </http>

	<authentication-manager>
		<authentication-provider ref='ldapAuthProvider'/>
	</authentication-manager>
	
	<beans:bean id="contextSource" 
		class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">

	    <beans:constructor-arg value="${ldap.url}" />
	    <beans:property name="userDn" value="${ldap.username}" />
	    <beans:property name="password" value="${ldap.password}" />
	</beans:bean>
	
	<beans:bean id="userSearch" 
		class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">

		<beans:constructor-arg index="0" value="" />
		<beans:constructor-arg index="1" value="(uid={0})" />
		<beans:constructor-arg index="2" ref="contextSource" />
		<beans:property name="searchSubtree" value="true" />
	</beans:bean>
	
   <beans:bean id="ldapAuthProvider" 
   		class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
     <beans:constructor-arg>
       <beans:bean 
       		class="org.springframework.security.ldap.authentication.BindAuthenticator">
			<beans:constructor-arg ref="contextSource" />
			<beans:property name="userDnPatterns">
				<beans:list>
					<beans:value>uid={0}</beans:value>
				</beans:list>
			</beans:property>
			<beans:property name="userSearch" ref="userSearch"/>
       </beans:bean>
     </beans:constructor-arg>
     <beans:constructor-arg>
       <beans:bean class="my.company.ldap.DatabaseAuthoritiesPopulator">
           <beans:constructor-arg ref="contextSource"/>
           <beans:constructor-arg value=""/>
           <beans:property name="groupRoleAttribute" value="cn"/>
       </beans:bean>
     </beans:constructor-arg>
   </beans:bean>
</beans:beans> 

DatabaseAuthoritiesPopulator.java


import java.util.HashSet;
import java.util.Set;

import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;

public class DatabaseAuthoritiesPopulator extends DefaultLdapAuthoritiesPopulator{

	public DatabaseAuthoritiesPopulator(ContextSource contextSource,String groupSearchBase) {
		super(contextSource, groupSearchBase);
	}
	
    public Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, String username) { 
    	//Get roles from a datasource of your choice
    	Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
        authorities.add((new SimpleGrantedAuthority("ROLE_USER")));
        return authorities;
    }
}

The tricky part is to set the "userSearch" property on line 50. This property is optional and it's easy to overlook. If this property is missing no LDAP search is performed and the LdapAuthenticationProvider tries to bind the user directly based on the given base("" in this case).

If you are having trouble testing the LDAP connection try activating DEBUG output for LdapAuthenticationProvider. Just put this logback.xml somewhere on your classpath:


<configuration>
	<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
		<!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder 
			by default -->
		<encoder>
			<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
			</pattern>
		</encoder>
	</appender>

	<logger level="DEBUG" additivity="false" name="org.springframework.security">
		<appender-ref ref="STDOUT" />
	</logger>
</configuration>

Now you have all the important information on your STDOUT.

8Okt/122

ASP.NET MVC: Aktuellen Menülink hervorheben

Dieses "Problem" müsste eigentlich jeder kennen der schon mein eine Webseite mit mehreren Bereichen erstellt hat. Wie kann ich den Menüpunkt der aktuell angezeigten Seite hervorheben?

Hier gibt es viele Ansätze. Ich hatte schon Seiten, die die CSS Klassen ins HTML hardcoden, irgendwelche anderen Schandtaten betreiben oder nicht wirklich schöne If-Abfragen um die Menüpunkte legen.


            <li @if (activeMenu.Equals("Home"))
                {
                    <text>class="active"</text>
                }>@Html.ActionLink("Home", "index", "home")</li>

Hier muss man natürlich noch bedenken, dass ein entsprechender ViewBag-Eintrag namens "activeMenu" in jeder Action gesetzt werden muss. urgs

Ich möchte diese ganze Logik nicht in den Views haben. Daher habe ich eine kleine ExtensionMethot für den HtmlHelper geschrieben.

Diese Extension generiert für mich die Menülinks und prüft beim Generieren des Links ob das Ziel des Links mit dem aktuellen Controller und der aktuellen View übereinstimmt. Wenn das der Fall ist wird dieser Link als aktiv markiert. So sieht das Ganze aus:


public static MvcHtmlString NavigationActionLink(
    this HtmlHelper htmlHelper, string linkText,
    string actionName, string controllerName, object routeValues = null,
    object htmlAttributes = null, 
    string wrapperElement = "li", string flag = "active")
{
    var generatedLink = HtmlHelper.GenerateLink(
                    htmlHelper.ViewContext.RequestContext,
                    htmlHelper.RouteCollection, linkText,
                    (string)null, actionName, controllerName,
                    new RouteValueDictionary(routeValues), 
                    (IDictionary<string, object>)HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));

    var wrappedElement = WrapGeneratedLink(
                    htmlHelper, actionName, 
                    controllerName, wrapperElement, flag, generatedLink);

    return MvcHtmlString.Create(wrappedElement.ToString(TagRenderMode.Normal));
}

private static TagBuilder WrapGeneratedLink(
    HtmlHelper htmlHelper, string actionName, string controllerName,
    string wrapper, string flag, string generatedLink)
{
    var wrapperElement = new TagBuilder(wrapper)
                                {
                                    InnerHtml = generatedLink
                                };

    if (CurrentRouteMatchesGeneratedUrl(actionName, controllerName, htmlHelper))
    {
        wrapperElement.AddCssClass(flag);
    }
    return wrapperElement;
}

private static bool CurrentRouteMatchesGeneratedUrl(
    string actionName, string controllerName, HtmlHelper htmlHelper)
{
    var currentAction = (string)htmlHelper.ViewContext.RouteData.Values["action"];
    var currentController = (string)htmlHelper.ViewContext.RouteData.Values["controller"];

    return string.Equals(currentAction, actionName, 
                        StringComparison.CurrentCultureIgnoreCase) &amp;&amp;
            string.Equals(currentController, controllerName, 
                        StringComparison.CurrentCultureIgnoreCase);
}

Hier der Code nochmal etwas schöner formatiert.

Dieser Helper orientiert sich an dem gewöhnlichen ActionLink und hat die gleichen Parameter bis auf die letzten zwei, mit denen man bestimmen kann wie der Link hervorgehoben werden soll.

So könnte man nun die Menülinks verwenden am Beispiel Twitter Bootstrap:


<div class="navbar navbar-inverse navbar-fixed-top">
    <div class="navbar-inner">
        <div class="container">
            <div class="nav-collapse">
                <ul class="nav">
                @Html.NavigationActionLink("meinlink", "index", "home")
                @Html.NavigationActionLink("meinlink", "index", "account", null, null, "div", "selected")
                </ul>
            </div>
        </div>
    </div>
</div>    

So würde der erste Link folgendes generieren:


<li class="active"><a href="/">meinlink</a></li>

Und der zweite Link das hier:


<div class="selected"><a href="/">meinlink</a></div>

Auf diese Weise kann mann etwas Einfluss darauf nehmen was da generiert wird und mit welcher Klasse das erzeugte Feld markiert wird.

Der zweite Link macht natürlich keinen Sinn in dem Twitter Bootstrap Beispiel und wurde nur zu Demonstrationszwecken genutzt :)

So entfernt man den hässlichen Code aus den Views und muss sich darum keinen Kopf mehr machen.

Übrigens: In dem meisten Fällen reicht es wenn man nur nach dem Controller prüft, da häufig mehrere Actions zu einem Menüpunkt gehören und man dann einen Menüpunkt mit einem Controller abdeckt.

Wie macht ihr das?

Wie ist eure Methode um diese kleine Problematik zu lösen? Habt ihr vielleicht einen schöneren/einfacheren Ansatz? Dann würde ich mich sehr freuen wenn ihr mich belehren würdet :)

7Aug/120

Log4Net in einem ASP.NET MVC Projekt nutzen

Nutzt ihr eigentlich Logging Systeme? Ja? Wirklich? Jaja, das hätte ich jetzt auch gesagt ;-) Wenn ihr noch kein Logging in euren Web Projekten habt solltet ihr das schleunigst nachholen, denn es ist oft einfachste und schnellste Weg genaue Informationen darüber zu erhalten was in der Applikation los war als es zu einem Fehler oder Absturz kam.

Eine der einfachsten und schnellsten Möglichkeiten Logging zu implementieren ist es die fertige und sehr mächtige Library Log4Net zu nutzen. Java Entwickler werden diese als Log4J kennen und schätzen.

Am schnellsten geht es wenn ihr Log4Net über NuGet installiert:

Nun habt ihr schon mal die Assembly in eurem Projekt. Als nächstes müssen wir Log4Net konfigurieren. Das kann man zum Beispiel in der web.config machen:


    <configSections>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
    </configSections>

    <log4net>
        <root>
            <level value="ALL" />
            <appender-ref ref="RollingFile" />
        </root>
        <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
            <file value="app.log" />
            <appendToFile value="true" />
            <maximumFileSize value="1000KB" />
            <maxSizeRollBackups value="2" />
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%date [%thread] %-5p %c - %message%newline" />
            </layout>
        </appender>
    </log4net>

Interessant ist hier eigentlich der Appender-Teil, denn hier definiert ihr wie geloggt werden soll. In diesem Beispiel werden alle Ausgaben in eine Datei namens app.log geloggt, weiterhin werden Informationen wie Datum, Zeit, Thread, Log-Level, Klasse und Lognachricht angezeigt. Hier gibt es unendlich viele Kombinationen.

Auf der Homepage von Log4Net findet ihr noch viele andere Beispiele für Appender-Configs, mit denen ihr direkt in eine Datenbank loggen könnt, oder die Nachrichten an einen SMTP Server senden könnt, die dann per Mail versendet werden. Natürlich kann auch auf die Konsole geloggt werden, sucht euch einfach die Appender aus die ihr benötigt und tragt diese ein, ihr könnt auch mehrere Appender kombinieren.

Bevor ihr nun losloggen könnt müsst ihr den Logger noch laden. Das macht ihr in der Application_Start-Methode in der Global.asax.cs. Hierzu reicht eine einfach Zeile:


log4net.Config.XmlConfigurator.Configure();

Wenn ihr die Konfiguration von Log4Net in eine Extra Datei auslagern wollt, könnt ihr hier auch den Pfad zu dieser Konfiguration angeben.


log4net.Config.XmlConfigurator.Configure(new System.IO.FileInfo(filename));

Nun wechseln wir mal in einen Controller und loggen mal etwas:


    public ActionResult Index()
    {
        ILog logger = LogManager.GetLogger(typeof(HomeController));

        logger.Info("Index Action wurde aufgerufen.....");

        try
        {
            logger.Debug("Don't mess with math dude...");
            int zero = 0;
            int endOfWorld = 12 / zero;
        }
        catch (Exception ex)
        {
            logger.Error("Ohh noez, you're dead now!", ex);
        }

        return View();
    }

Wie man sieht funktioniert das Ganze ziemlich einfach und effektiv. Der Logger lässt euch zwischen verschiedenen Log-Leveln auswählen. In der XML-Konfiguration definiert ihr den Loglevel, dann werden nur bestimmte Dinge geloggt. So könnt ihr für Diagnosezwecke den DEBUG-Level setzen um alle wichtigen Infos zu erhalten, und später wenn alles erledigt ist den Level wieder auf INFO senken.

So sieht übrigens die Ausgabe zu dem oberen Code aus:

Im Grunde ziemlich simpel, aber sehr effektiv. Alternativ kann man auch ELMAH verwenden.

Ein Demo-Projekt findet ihr hier.

5Apr/122

ASP.NET MVC Security Teil 2: Unnoetige HTTP Response-Header entfernen

In dieser Reihe von Posts möchte ich Wege zum Absichern einer MVC Anwendung zeigen, die man gerne mal vergisst oder als Neueinsteiger nicht gleich auf dem Schirm hat.

Teil 2: Unnötige HTTP Response-Header entfernen

Wenn wir im Internet surfen werden oft Informationen über uns ohne unser Zutun bei jedem Request an den Server übertragen. Dies geschieht über die HTTP-Header. So wird z.B. die Adresse der Seite auf der wir einen Link geklickt haben in dem Referrer-Header an den Server übertragen.

Aber auch die Server selbst sind in dieser Sache oft sehr gesprächig und verraten mehr über sich als wirklich nötig ist.

Eine Typische ASP.NET Serverantwort

In dem Bild sieht man (rot eingerahmt) Informationen die der Server über unsere Anwendung nach außen gibt, die eigentlich lieber geheim bleiben sollten.

Warum ist das gefährlich?

Die Informationen, die hier angezeigt werden verraten die Version des Servers, der Laufzeitumgebung und die Version des Frameworks, das wir benutzen. In diesem Fall ASP.NET MVC 3.

Falls nun bekannt wird, dass der Server oder ASP.NET in dieser Version eine Sicherheitslücke aufweist, sieht ein potentieller Angreifer sofort, dass unsere Seite für eine Attacke anfällig ist.

Es gibt sogar extra Tools, die das Web nach Webseiten durchsuchen, die eine Serverversion aufweisen, die als unsicher eingestuft wird oder für die es bekannte Lücken gibt. Diese Tools nutzen unter Anderem dann auch diese Informationen um zu prüfen ob es sich lohnt bei dieser Seite einen Angriff zu starten.

Was nun?

Im Grunde ist die Aufgabe sehr einfach, wir müssen diese unnötigen Serverheader ausblenden. Wenn die Webseite dann keine anderen Frameworkspezifischen Merkmale aufweist (wie z.B. einen Viewstate) ist es für einen Besucher praktisch unmöglich herauszufinden, welcher Server und welches Framework im Backend laufen.

Die Umsetzung

Leider ist ASP.NET in dieser Hinsicht noch nicht so richtig benutzerfreundlich, denn um all diese Header zu entfernen müssen wir an einigen verschiedenen Stellen Änderungen vornehmen. Gehen wir mal Schritt für Schritt alles durch.

1. ASP.NET &  MVC Header

Diesen Header können wir recht einfach entfernen, indem wir in der global.asax die nötige Property setzen:


protected void Application_Start()
{
    MvcHandler.DisableMvcResponseHeader = true; //<- Hier

    AreaRegistration.RegisterAllAreas();

    Database.SetInitializer(new F1DBInitializer());

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
}

Dies hat schon eine Gute Wirkung auf unsere Response Header:

Verbliebene Response Header

Die Version von ASP.NET und des MVC Frameworks wird nun nicht mehr angezeigt. Nun zu den anderen.

2. X-Powered-By Header

Um diesen Header zu entfernen müssen wir unsere web.config ergänzen (Quelle):


<?xml version="1.0"?>
<configuration>

  <system.web>
    <httpRuntime enableVersionHeader="false" />
    ...
  </system.web>

  <system.webServer>
    ...
    <httpProtocol>
      <customHeaders>
        <remove name="X-Powered-By" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>

 ...
</configuration>

Dann verabschiedet sich auch dieser Header.

Und auch X-Powered-By ist nun weg

3. Einer geht noch: Der Server-Header

Dies geht leider nicht so einfach durch das Setzen eines Configparameters, hier müssen wir selbst Hand anlegen und diesen Header aus der Response löschen. Dazu schreiben wir uns ein eigenes HttpModul (Quelle):


public class RemoveServerHeaderModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.PreSendRequestHeaders += OnPreSendRequestHeaders;
    }

    public void Dispose()
    { }

    void OnPreSendRequestHeaders(object sender, EventArgs e)
    {
        HttpContext.Current.Response.Headers.Remove("Server");
    }
}

Anschließend müssen wir das neue Modul über die web.config registrieren (Quelle):


<?xml version="1.0"?>
<configuration>
  ...
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>
    <modules runAllManagedModulesForAllRequests="true">
      <add name="RemoveServerHeaderModule" type="SecurityTipp.RemoveServerHeaderModule"/>
    </modules>
	...
  </system.webServer>

</configuration>

Das Ergebnis:

Alle unnötigen Header sind raus

Nun haben wir unser Ziel erreicht und alle Plaudertaschen aus der Response entfernt :)

Weitergedacht

Hier könnte man nun ansetzen und sogar versuchen den potentiellen Angreifer in die Irre zu führen, indem man einen modifizierten Server-Header zurückgibt der auf eine Ganz andere Serversoftware verweist, Apache oder sowas ;)

Das Schöne hierbei ist weiterhin, dass wir alle diese Änderungen innerhalb unserer App machen können und nicht im IIS rumfummeln müssen. Einerseits weil man da viel kaputtkonfigurieren kann, andererseits weil man nicht immer Zugriff auf den IIS hat, wenn man z.B. nur ein Webspace hat.

Diese Schritte sollten bei jeder Anwendung durchgeführt werden, egal ob MVC, ASP.NET oder PHP, gebt den Besuchern nicht mehr Informationen als sie unbedingt benötigen!

kick it on dotnet-kicks.de

4Apr/120

ASP.NET MVC Security Teil 1: Das AntiForgeryToken nutzen

In dieser Reihe von Posts möchte ich Wege zum Absichern einer MVC Anwendung zeigen, die man gerne mal vergisst oder als Neueinsteiger nicht gleich auf dem Schirm hat.

Teil 1: Nutzen des AntiForgeryToken bei POST Requests

POST Requests sind gefährlich, da diese im Normalfall dafür verwendet werden dem Server Informationen mitzuteilen. Die Informationen sollte man natürlich prüfen, auf Form und Inhalt, aber noch wichtiger ist es, zu wissen wer uns diese Daten sendet. (Cross-Site-Attacken verhindern)

Wie funktioniert das?

Mit dem AntiForgeryToken können wir an dieser Stelle sichergehen, dass nur Formulare am Server angenommen werden, die der Server selbst erzeugt hat.
Dabei erzeugt der Server beim Generieren des Formulars einen Token und speichert diesen in einem versteckten Input-Feld (__RequestVerificationToken), des Weiteren wird auch ein Cookie namens "__RequestVerificationToken" mit dem gleichen Token angelegt, sodass beim Absenden des Formulars sowohl der Cookie als auch das versteckte Feld wieder zurück an den Server übertragen wird.

Wenn der Server diesen Request empfängt prüft er diesen Token aus dem versteckten Feld gegen das Token aus dem Cookie. Gibt es hier keine Übereinstimmung wird der Request abgewiesen.

Wie wird das implementiert?

Als Erstes muss das Token in das Formular gesetzt werden und das Cookie erzeugt werden. Diese Aufgabe übernimmt für und der kleine Html-Helper aus MVC.


<div class="myForm">
    @using (Html.BeginForm())
    {
        @Html.AntiForgeryToken()

        <!-- REST DES FORMULARS -->
    }
</div>

Das ging ja einfach, nun müssen wir dafür sorgen, dass dieses Token beim Verarbeiten des Requests auch geprüft wird.


[ValidateAntiForgeryToken]
public ActionResult UpdateStuff()
{
...
}

Mehr ist das nicht, ein einfaches Attribut genügt, und unsere MVC Anwendung ist mit nur zwei Zeilen sicherer geworden.

Wenn Ihr nun einen Blick in euer gerendertes Formular werft, werdet ihr etwas in der Art finden:


<input name="__RequestVerificationToken" type="hidden" value="4PhvBw7EO5I3O4zw2cdYkaEfSJycy1Ugi8AOT6glW6IqZNnVKXTzL52LgQGNMviOhYTxAOQH3iZRtehUoHKRprcHXa8YNel6RzVmKjGAEi0W0duRpnwJu9EpR7ybAoZX9yqeiP3Pyng/ma1t/k6QhM2bbcoBgkou5y0KXEj8Ut8=" />

Gibt es Einschränkungen?

  • Da hier ein Cookie gesetzt wird müssen natürlich Cookies aktiviert sein da der Server sonst alle Requests abweisen wird.
  • Das Ganze funktioniert nur mit POST Requests, was auch logisch ist, da GET Requests nur zum Abfragen von Informationen genutzt werden sollten.

Alternativen?

Wenn man keine Cookies nutzen will kann man ein ähnliches Verhalten hervorrufen, indem man prüft ob der eingehende Request ein Referrer-Header besitzt der auf unsere eigene Domain + URL verweist.
Dies ist natürlich nicht wirklich Sicher, da es leicht zu fälschen ist und außerdem haben manche Browser Anonymisierungsfunktionen die diese Header-Felder nicht mitsenden, also ist hier Vorsicht geboten.

Zusammenfassung

Das AntiForgeryToken bietet einen recht guten Schutz vor Cross-Site-Attacken bei sehr sehr einfacher und schneller Implementierung.

kick it on dotnet-kicks.de

15Mrz/120

Chart helper in ASP.NET MVC nutzen

Vor einer Weile habe ich ein kleines Tutorial geschrieben wie ihr das kostenlose MSChart Control benutzen könnt um euch hübsche Grafen zeichnen zu lassen.

ASP.NET MVC 3 bietet euch einen Helper der eine sehr ähnliche Funktionalität bietet und sehr einfach in eure MVC Anwendung zu integrieren ist.

Das Schöne ist, dass wenn ihr das aktuelle Visual Studio Tools update installiert habt, ihr diesen Helper bereits out of the box nutzen. Falls nicht könnt ihr es über das NuGet Paket bequem nachinstallieren.

Hier ein kleines Beispiel wie eine Action aussehen kann, die ein Chart erzeugen soll:

Beispiel 1


    public ActionResult CreateChart()
    {
        Chart bytes = new Chart(width: 400, height: 200)
            .AddSeries(
                chartType: "column",
                xValue: new[] { "Wert1", "Wert2", "Wert3", "Wert4" },
                yValues: new[] { "12", "3", "23", "11" })
            .AddSeries(
                chartType: "column",
                xValue: new[] { "Wert1", "Wert2", "Wert3", "Wert4" },
                yValues: new[] { "13", "32", "23", "5" });

            return File(bytes.GetBytes("png"), "image/png");
    }

Wie man sieht wird hier nachdem das Chart erstellt wurde eine Grafik daraus gemacht und diese dann an die View zur Anzeige gesendet. Der Aufruf in der View ist dementsprechend simpel:


<img src="@Url.Action("CreateChart")" alt="Beschreibung" />

Was vielleicht nicht auf den ersten Blick ersichtlich ist, ist wie man eine legende zu dem Chart anzeigen lassen kann. Dazu muss jede "Serie" einen namen erhalten und zum Schluss kann man dann die "AddLegend()" Methode ohne Parameter aufrufen:

Beispiel 2


    public ActionResult CreateChart()
    {
        Chart chart = new Chart(width: 400, height: 200)
            .AddSeries(
                chartType: "column",
                xValue: new[] { "Wert1", "Wert2", "Wert3", "Wert4" },
                name: "Serie 1",
                yValues: new[] { "12", "3", "23", "11" })
            .AddSeries(
                chartType: "column",
                xValue: new[] { "Wert1", "Wert2", "Wert3", "Wert4" },
                name: "Serie 2",
                yValues: new[] { "13", "32", "23", "5" });

        chart.AddLegend();

        return File(chart.GetBytes("png"), "image/png");
    }

Noch etwas umständlicher wird es, wenn man nun die legende an eine andere Stelle setzen will, denn hier muss ein eigenes Theme her. Dieses sollte dann auch nur die Felder enthalten die man ändern will. Wenn man z.B. die legende unten angezeigt haben möchte kann man so vorgehen:

Beispiel 3


        public ActionResult CreateChart()
        {
            string theme = @"<Chart BackColor=""WhiteSmoke"" >
                                    <ChartAreas>
                                        <ChartArea Name=""Default"" BackColor=""White"">
                                    </ChartAreas>
                                    <Legends>
                                        <Legend _Template_=""All"" BackColor=""Transparent"" Font=""Trebuchet MS, 9pt"" IsTextAutoFit=""False"" Docking=""Bottom"" /> 
                                    </Legends>
                                </Chart>";

            Chart chart = new Chart(width: 400, height: 200, theme: theme)
                .AddSeries(
                    chartType: "column",
                    xValue: new[] { "Wert1", "Wert2", "Wert3", "Wert4" },
                    name: "Serie 1",
                    yValues: new[] { "12", "3", "23", "11" })
                .AddSeries(
                    chartType: "column",
                    xValue: new[] { "Wert1", "Wert2", "Wert3", "Wert4" },
                    name: "Serie 2",
                    yValues: new[] { "13", "32", "23", "5" });

            chart.AddLegend();

            return File(chart.GetBytes("png"), "image/png");
        }

Man beachte hier dass dem Chart-Konstruktor ein weiterer Parameter "theme" übergeben wird.

Im Grunde wars das auch schon. An der Handhabung ändert sich nicht viel. Falls ihr euch irgendwo vertippt wird übrigens einfach kein Bild erzeugt, also vorsicht beim Tippen ;)

20Feb/122

ASP.NET: Verbindungsname LocalSqlServer wurde in der Anwendungskonfiguration nicht gefunden

Am Wochenende hatte ich einen interessanten Fehler in meiner ASP.NET MVC Anwendung. Dieser tauchte auf nachdem die Anwendung schon einige Wochen Problemlos lief. Es handelte sich um diesen Fehler:

Konfigurationsfehler

Beschreibung: Fehler beim Verarbeiten einer Konfigurationsdatei, die für diese Anforderung erforderlich ist. Überprüfen Sie die unten angegebenen Fehlerinformationen, und ändern Sie die Konfigurationsdatei entsprechend.

Parserfehlermeldung: Der Verbindungsname LocalSqlServer wurde in der Anwendungskonfiguration nicht gefunden, oder die Verbindungszeichenfolge war leer.

Das Problem ist hier, dass das ASP.NET Membership per Default in der root web.config so konfiguriert ist, dass es den Connectionstring "LocalSqlServer" nutzen soll. Dieser Connectionstring zeigt dann (meistens) auf eine SQL Datenbank die auf dem Hostingsystem (noch) nicht verfügbar ist. An dieser Stelle knallt es dann.

Die einfachste Lösung für dieses Problem ist es den Connectionstring neu zusetzen. Dazu entfernt man erst den Defaulteintrag und setzt den neuen dann auf eine gültige Datenbankverbindung:


	<remove name="LocalSqlServer" />
	<add name="LocalSqlServer"
		connectionString="[EUER CONNECTIONSTRING]"
		providerName="System.Data.SqlClient" />

Dies hat das Problem behoben, sodass die Anwendung wieder wie gewohnt lief.

Was mir noch ein wenig Kopfschmerzen bereitet, ist dass ich das ASP.NET Membership gar nicht nutze und auch keinen "LocalSqlServer" Connectionstring habe. Des Weiteren lief die Anwendung erst wochenlang und plötzlich kam dann dieser Fehler.

Wenn ihr diesen Fehler vielleicht schon kennt oder euch noch was dazu einfällt würde ich mich über einen Kommentar mit einem Tipp freuen.

25Jan/126

ASP.NET MVC: HTML5 Elemente mit jQuery Fallback nutzen

Soo, nachdem ich die Überschrift mit all den hippen Begriffen vollgepackt habe die es in Sachen Web momentan so gibt möchte ich euch kurz erklären worum es in diesem Artikel gehen soll.

Ihr werdet sicher mitbekommen haben, dass HTML5 auf dem Vormarsch ist und auch wenn es immer noch nicht offiziell fertig ist findet man immer häufiger den HTML5-Header <!DOCTYPE html> auf diversen Seiten.

Eins der meiner Meinung nach nützlichsten Features die wir mit HTML5 erhalten sind die neuen Input-Typen. Denn bisher hatten wir keine Wahl und mussten type="text" benutzen!

Die Eingabefelder vom Typ Text konnten natürlich alles enthalten, sind aber nicht gerade Benutzerfreundlich wenn man etwas komplexere Daten eingeben soll wie zum Beispiel ein Datum.

In HTML5 wurden deswegen spezifische Typen für solche immer wieder einkehrende Eingaben eingeführt. Eine von ihnen ist &quot;Date&quot;:


Birthday: <input type="date" name="bday" />

Der große Vorteil hier ist, dass die Browser nun eine eigene Implementierung für diesen Typ von Eingabefeldern machen können. Falls der Browser nun also diesen neuen Eingabe Typ unterstützt blendet er automatisch einem (mehr oder weniger) schönen Date-Picker ein in dem man bequem das Datum wählen kann:

Ansicht des neuen Typen in verschiedenen Browsern

Ein weiterer großer Vorteil ist, dass auch die mobilen Browser wie z.B. auf dem iPhone diese neuen Elemente ebenfalls unterstützen und einen passenden Picker einblenden:

Date-Picker auf dem iPhone

Wie ihr in dem ersten Screenshot sehen könnt unterstützt der FireFox (zumindest zum Zeitpunkt des Screenshots) den neuen Typ nicht. In diesem Fall blendet er ein gewöhnliches Textfeld ein wo wir nun gezwungen sind das Datum auf die gewohnte (unbequeme) Weise einzutragen.

Übrigens, ich glaube ich muss euch nicht sagen warum der Internet Explorer hier nicht mit aufgeführt ist oder? ;-)

Und genau das ist das Problem um das es in diesem Blogpost geht. Wenn wir die tollen neuen Elemente nutzen gehen wir das Risiko ein, dass Nutzer die einen älteren Browser nutzen nicht in den Genuss einer bequemen Eingabe kommen.

jQuery Date-Picker

Wir könnten auch komplett auf das HTML5 Element verzichten und direkt alles mit jQuery machen, welches auch einen Date-Picker bietet, aber das ist auch nicht das gelbe vom Ei, denn dann wären die Mobilen Nutzer in Nachteil, da dort die Browser diese Elemente für gewöhnlich unterstützen und eine gewohnte Oberfläche für die Eingabe bieten.

Die Optimale Lösung ist offensichtlich eine Mischung beider Welten. Wenn der Benutzer die Seite mit einem kompatiblen Browser aufruft soll der Date-Picker des Browsers genutzt werden, wenn der Browser aber veraltet ist soll stattdessen der jQuery Fallback greifen und der JavaScript Date-Picker (siehe links) genutzt werden.

Zu unserem Glück macht uns ASP.NET MVC die Umsetzung dieses Plans sehr leicht, da es an den nötigen Stellen sehr einfach erweitert werden kann.

Schauen wir uns mal ein kleines Beispiel an. Wir haben eine Klasse Person, und wollen auch dessen Geburtstag speichern, hier wollen wir die tolle neue Funktionalität nutzen. Implementieren wir das Ganze jedoch erstmal wie gewohnt:


    public class Person
    {
        public int ID { get; set; }
        public String Name { get; set; }
        public DateTime Geburtstag { get; set; }
    }

Wenn wir die Model-Klasse haben können wir das MVC Scaffolding nutzen um für uns eine View zum Erstellen neuer Personen anzulegen:

MVC Scaffolding kommt uns zu Hilfe

Schauen wir uns nun doch mal an was da für uns tolles generiert wurde:


    <div class="editor-field">
        @Html.EditorFor(model => model.Geburtstag)
        @Html.ValidationMessageFor(model => model.Geburtstag)
    </div>

Auffällig hierbei ist, dass hier @Html.EditorFor genutzt wird und nicht etwa @Html.TextBoxFor! Der Hintergrund ist der, dass MVC bei der Generierung versuchen wird ein passendes Element für den angegebenen Datentyp (DateTime) zu bestimmen, aber da MVC standardmäßig nur den Typ Text kennt wird hier eigentlich immer ein Input-Element vom Typ text generiert.

Hier kommt uns die Erweiterbarkeit von MVC zu gute, denn wir können ganz einfach NuGet nutzen um uns die nötige Funktionalität zu verschaffen. Wir installieren das Packet MvcHtml5Templates.

Installation über NuGet

Neue Templates

Nachdem NuGet alles erledigt hat werdet ihr feststellen, dass ihr ein paar neue Dateien in eurem Views/Shared Verzeichnis habt (siehe Bild links). Und wie ihr schon an dem Namen erkennen könnt sind das die neuen Input-Typen aus HTML5.

Diese Templates machen im Grunde nichts anderes als die MVC-Eigenen zu überschreiben und versehen die mit dem passendem Type-Attribut.

Hier sind euch keine Grenzen gesetzt ihr könnt natürlich auch eire eigenen Templates definieren mit euren eigenen Typen. (Auch wenn man das eher selten benötigt)

Das Tolle: mehr müssen wir nicht machen. MVC wird nun zur Laufzeit statt einem Text ein Date Input-Element für uns anlegen. Beachtet, dass das nur geht wenn wir in der View die allgemeine Funktion @Html.EditorFor nutzen und keinen Spezifischen Typ angeben.

Schauen wir mal in den Sourcecode der zur Laufzeit für das Geburtstagsfeld generiert wird:


<input class="text-box single-line"
data-val="true"
data-val-required="Das Feld 'Geburtstag' ist erforderlich"
id="Geburtstag"
name="Geburtstag"
type="datetime"
value=""; />

Wie ihr seht wird nun der Korrekte Typ, nämlich datetime verwendet. Wenn wir die Seite nun also mit einem kompatiblen Browser aufrufen können wir ganz bequem das Datum wählen.

Leider ist der Opera Browser momentan wohl der einzige Desktop Browser der uns hier einen Benutzerfreundlichen Dialog einblendet (siehe erstes Bild oben), somit müssen wir dafür sorgen, dass die Benutzer mit anderen oder alten Browsern nicht benachteiligt werden.

JavaScript Magie

Auch für diese Problematik bringt MVC bereits alles mit was nötig ist sie zu lösen. Werfen wir doch mal einen Blick in unseren Scripts Ordner finden wir alle nötigen jQuery und jQuery UI Skripte, dazu kommt noch die modernizr Bibliothek die uns auch zu Gute kommen wird.

Die jQuery Bibliotheken liefern und die nötige Funktionalität die wir benötigen um diesen hübschen Date-Picker einzublenden.

Die modernizr Bibliothek dagegen hilft uns herauszufinden ob der Browser, den der Benutzer momentan verwendet die gewünschten HTML5 Features unterstützt.

Nun müssen wir also bei unseren Formularen prüfen, ob der Browser die nötigen Funktionen kennt und bei Bedarf die jQuery Klassen einbinden. Das Ganze ist dank der Einfachheit von Modernizr und jQuery ziemlich simpel.

Wechseln wir zu unserer View und fügen die Referenzen auf die nötigen Skripte und CSS Dateien ein:


<script src="@Url.Content("~/Scripts/jquery-1.5.1.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery-ui-1.8.11.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/modernizr-1.7.js")" type="text/javascript"></script>
<link href="@Url.Content("~/Content/themes/base/jquery.ui.all.css")" rel="stylesheet" type="text/css" />

Unten in der View fügen wir nun eine Modernizr Abfrage ein und prüfen, ob der Browser das DateTime-Feld unterstützt, und wenn nicht lassen wir jQuery das Ganze für uns regeln:


<script type="text/javascript">
    $(function () {
        if (!Modernizr.inputtypes.date) {
            $("input[type='datetime']").datepicker();
        }
    });
</script>

Mit dem Befehl $("input[type='datetime']").datepicker(); werden alle Input-Felder vom Typ DateTime durch den jQuery Picker ersetzt. Coole Sache!

Unser Browser kennt den Typ nicht :(

Unser Browser kennt den Typ nicht :(

Kleiner Hinweis am Rande: Das ist nicht wirklich best practice da die Skripte auch geladen werden wenn sie gar nicht benötigt werden (der Browser kennt das HTML5 Feld) ich habe das hier der Einfachheit geopfert.

Und das wars auch schon! Alle Browser die das Feld unterstützen blenden nun ihren Picker ein, ansonsten wird der jQuery Fallback genutzt.

Wir müssen uns auch nicht mit irgendwelchen UserAgent-Prüfungen herumschlagen und lassen das alles Modernizr erledigen, der das intern über JavaScript prüft und somit immer aktuell ist. Das heißt dass wenn irgendwann der IE plötzlich die neuen Typen kennt werden diese auch funktionieren!

Genauso könnt ihr auch bei all den anderen neuen HTML5 Typen vorgehen und euren Usern ein bestmögliches Bedienerlebnis bereiten ;-)

kick it on dotnet-kicks.de

5Jan/120

ASP.NET MVC: Zugriff auf Dateien einschraenken

Oft kommt es vor dass man bestimmte Inhalte nicht der breiten Öffentlichkeit präsentieren möchte. So will man beispielsweise bestimmte Inhalte nur autorisierten Benutzern oder nur Admins zur Verfügung stellen, andere User die nicht eingeloggt sind sollen diese Inhalte nicht abrufen können. Ein weiterer Anwendungsfall wäre auch hotlinking zu verhindern.

Dieses Verhalten kann man in ASP.NET MVC über verschiedene Wege erreichen. Eine beliebte Möglichkeit ist es

die Dateien über einen Controller zu verteilen.

Hierbei erstellt man einen Controller der eine Action hat in der die gewünschte Datei geladen und an den Browser zurückgegeben wird. Hier hat man dann die Bequemlichkeit, dass man problemlos prüfen kann ob der User eingeloggt ist und über die nötigen Rechte für das Bild verfügt. Das geht zum Beispiel mit dem Authorized-Attribut für die Actions.

Implementiert würde so eine Action dann wohl ca so aussehen:


public ActionResult Image(string id)
{
    var dir = Server.MapPath("/Private/Images");
    var path = Path.Combine(dir, id + ".jpg");
    return base.File(path, "image/jpeg");
}

Das Bild kann man dann so abrufen:


<img src="@Html.Content("~/Content/Image/" + model.pictureID)" />

Dieser Ansatz funktioniert problemlos hat aber den kleinen Nachteil, dass er nicht so performant ist wie der direkte Zugriff auf die Ressource.

Eine weitere (und auch meine bevorzugte) Möglichkeit eine solche Zugriffskontrolle zu implementieren ist es durch

einen eigenen RouteHandler.

Eine Beispielimplementierung wie man auf diese Weise Hotlinking verhindern kann habe ich in Mike Brinds Blog gefunden.

Hier sind im Grunde drei kleine Schritte nötig um einen eigenen RouteHandler zum Laufen zu bekommen. (Code von Mikes Blog)

Zunächst müssen wir die RouteHandler Klasse anlegen, die das IRouteHandler Interface und dessen einzige Methode GetHttpHandler implementiert:


public class ImageRouteHandler : IRouteHandler
{
  public IHttpHandler GetHttpHandler(RequestContext requestContext)
  {
    return new ImageHandler(requestContext);
  }
}

Als nächstes erstellen wir die oben genutzte ImageHandler Klasse in der wir das das IHttpHandler Interface implementieren:


public class ImageHandler : IHttpHandler
{
  public ImageHandler(RequestContext context)
  {
    ProcessRequest(context);
  }

  private static void ProcessRequest(RequestContext requestContext)
  {
    var response = requestContext.HttpContext.Response;
    var request = requestContext.HttpContext.Request;
    var server = requestContext.HttpContext.Server;

    var validRequestFile = requestContext.RouteData.Values["filename"].ToString();
    const string invalidRequestFile = "thief.gif";
    var path = server.MapPath("~/graphics/");

    response.Clear();
    response.ContentType = GetContentType(request.Url.ToString());

    if (request.ServerVariables["HTTP_REFERER"] != null &&
        request.ServerVariables["HTTP_REFERER"].Contains("yourdomain.com"))
    {
      response.TransmitFile(path + validRequestFile);
    }
    else
    {
      response.TransmitFile(path + invalidRequestFile);
    }
    response.End();
  }

  private static string GetContentType(string url)
  {
    switch (Path.GetExtension(url))
    {
      case ".gif":
        return "Image/gif";
      case ".jpg":
        return "Image/jpeg";
      case ".png":
        return "Image/png";
      default:
        break;
    }
    return null;
  }

  public bool IsReusable
  {
    get { return false; }
  }
}

Zu Schluss müssen wir diesen neuen Handler mit einer Route in der Global.asax registrieren:


routes.Add("ImagesRoute",
                 new Route("images/{filename}", new ImageRouteHandler()));

Hierbei ist noch wichtig, dass ihr die Route an der passenden Stelle registriert, sodass diese nicht ungewollt von einer anderen Route blockiert wird weil diese auch auf dieses Pattern matcht.

Nun werden alle Requests die auf die Route "images/{filename}" gehen und von einer anderen Domain stammen als eurer eigenen auf eine andere Grafik umgeleitet.

Nun bleibt noch ein kleines Problem, denn wenn das Verzeichnis mit den Bildern in eurem Content Verzeichnis liegt kann immer noch darauf verlinkt werden wenn man den vollen Pfad nimmt, also statt dem virtuellem Pfad

http://localhost/images/myimg.jpg

die URL benutzt unter der tatsächlich alle Bilder liegen:

http://localhost/Content/graphics/myimg.jpg

Dies liegt daran, dass der neue RouteHandler in diesem Fall nicht greift und die Default Route genutzt wird. Am einfachsten kann man dies verhindern wenn man den Zugriff für Unbefugte über die web.config sperrt:


  <location path="Content/graphics">
    <system.web>
      <authorization>
        <allow roles="admin" />
        <deny users="*" />
      </authorization>
    </system.web>
  </location>

Das war dann auch schon, ich nutze gern den zweiten Weg, da dieser mir einfacher und sauberer erscheint. Wenn euch noch andere Möglichkeiten einfallen wie man das anstellen kann dann immer her damit!

17Sep/110

Nice2Know: Attribut-basierende Routen in ASP.NET MVC

Für Routen ist in ASP.NET MVC für gewöhnlich die Global.asax zuständig, so werden hier alle Routen definiert und gemappt. Doch wenn man viele verschiedene Routen nutzen will wird es schnell voll in der eigentlich sehr übersichtlichen Klasse.

Da wäre es doch eigentlich cool wenn wir die Routen direct in unseren Controllern definieren könnten. Genau das können wir mit der Erweiterung AttributeRouting machen.

Installation

Installation über NuGet

Dank NuGet ist die Installation in wenigen Sekunden abgeschlossen und wir können unsere Actions mit Routing-Attributen dekorieren.

Die Attribute sind hierbei sehr einfach gestaltet und bieten uns alles was wir benötigen.


    public class DemoController : Controller
    {
        [GET("Ist/Das/Cool/Oder/Was")]
        public ActionResult Start()
        {
            return View();
        }
    }

Nun muss man nur noch die Attribut-basierten Routen registrieren:


    public static void RegisterRoutes(RouteCollection routes) {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapAttributeRoutes(); //--- das wars!

        routes.MapRoute(
            "Default", // Route name
            "{controller}/{action}/{id}", // URL with parameters
            new { controller = "Home", action = "Start", 
                id = UrlParameter.Optional } // Parameter defaults
        );
    }

Und fertig! Nun können wir die Seite starten und unsere Route testen:

Das Ergebnis

Das Ergebnis

Die Routen die wir definieren können, beschränken sich nicht darauf, so können diese auch problemlos parametrisiert werden, inklusive Defaultparameter:


    public class DemoController : Controller
    {
        [GET("Artikel/{artikelid}/{version?}")]
        [RouteDefault("artikelid", "120")]
        public ActionResult Start(int artikelid, string version)
        {
            ViewBag.ArtikelId = artikelid;
            ViewBag.Version = version;

            return View();
        }
    }

Hier wird der Parameter artikelid mit einem Defaultwert ausgestattet und der zweite optional gemacht. Das Ergebnis kann sich sehen lasen:

Läuft!

Läuft!

Hier hört das Framework nicht auf, so könnt ihr auch Regular Expressions in die Routen integrieren und auch mehrere Routen an eine Action heften. Die Möglichkeiten sind grenzenlos ;)

Für weitere Infos schaut einfach auf der Projekthomepage vorbei, es lohnt sich!

Get Adobe Flash player