Skip to main content

Part 3 - Introducing Spring Security, Thymeleaf and Spring MVC

Security is one of the key aspect of an application, especially web application. In our application we intend to integrate Spring Security as we are already on Spring framework. Alternative was Apache Shiro. But we prefer Spring Security. Here is how we can integrate Spring Security with Spring MVC and Thymeleaf.
The first step to this integration is to include the Spring Security and Thymeleaf Spring Security jars into our application. This can be done by adding Maven dependencies. Here is the snippet from the parent/pom.xml file.


Listing 1 - parent/pom.xml - snippet
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
        <!-- SPRING SECURITY          -->
        <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${spring-security.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${spring-security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>${spring-security.version}</version>
        </dependency>
    <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity3</artifactId>
            <version>1.0.0-beta1</version>
        </dependency>
Now the next step is to include the Spring Security filter in the web.xml. Note some subtle changes in the Spring config file changes. Spring contexts have parent child relationship. The context loaded by ContextLoaderListener is the parent and that by dispatcher servlet is child. The child context now has the beans related to view and contoller. The service and repository beans will be loaded by the parent context. The contoller accesses the service beans. Since child context has full access to parent context the controllers can easily use the service beans and not the other way round. The modified web.xml is shown in Listing 2.
Listing 2 - web.xml
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">
    <!-- Log4j -->
    <context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>file:${catalina.home}/temp/effectivcrm/conf/log4j.xml</param-value>
    </context-param>

    <context-param>
        <param-name>log4jExposeWebAppRoot</param-name>
        <param-value>false</param-value>
    </context-param>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath*:conf/spring-*-config.xml
        </param-value>
    </context-param>

    <!-- Log4j listerner -->
    <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>

    <!-- Spring Listeners -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <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>

    <servlet>
        <servlet-name>Spring MVC 3 Servlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                classpath*:conf/spring-view.xml
                classpath*:conf/spring-controller.xml
            </param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Spring MVC 3 Servlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

</web-app>
The spring-web-config.xml is renamed as spring-view.xml and we add the Thymeleaf Spring Security dialect here. This exposes Spring security variables to Thymeleaf. Refer to the Spring Security 3 Thymeleaf integration project for details.
Listing 3 - spring-view.xml
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <mvc:annotation-driven />

    <mvc:resources location="classpath:/META-INF/assets/img/" mapping="/assets/img/**" />
    <mvc:resources location="classpath:/META-INF/assets/css/" mapping="/assets/css/**" />
    <mvc:resources location="classpath:/META-INF/assets/js/" mapping="/assets/js/**" />

    <bean
        class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">

    </bean>

    <bean
        class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="cacheSeconds" value="0" /> <!-- NO CACHE -->
    </bean>

    <bean id="contentNegotiatingResolver"
        class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="mediaTypes">
            <map>
                <entry key="html" value="text/html" />
                <entry key="pdf" value="application/pdf" />
                <entry key="xsl" value="application/vnd.ms-excel" />
                <entry key="xml" value="application/xml" />
                <entry key="json" value="application/json" />
            </map>
        </property>
    </bean>

    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!-- Tiles                                -->
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->

    <bean id="tilesConfigurer" class="org.thymeleaf.extras.tiles2.spring.web.configurer.ThymeleafTilesConfigurer">
      <property name="definitions">
        <list>
          <value>classpath:/META-INF/tiles/tiles-*.xml</value>
        </list>
      </property>
    </bean>

    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!-- Themeleaf View Config -->
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->

    <bean id="templateResolver"
        class="org.thymeleaf.templateresolver.ClassLoaderTemplateResolver">

        <property name="suffix" value=".html" />
        <property name="templateMode" value="HTML5" />
        <property name="cacheable" value="false" />
    </bean>

    <bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">
        <property name="templateResolver" ref="templateResolver" />

        <property name="additionalDialects">
            <set>
                <bean class="com.effectivcrm.view.form.ExtraSpringDialect" />
                <bean class="org.thymeleaf.extras.tiles2.dialect.TilesDialect"/>
                <bean class="org.thymeleaf.extras.springsecurity3.dialect.SpringSecurityDialect"/>
            </set>
        </property>
    </bean>

    <bean id="tilesViewResolver" class="org.thymeleaf.spring3.view.ThymeleafViewResolver">
      <property name="viewClass" value="org.thymeleaf.extras.tiles2.spring.web.view.ThymeleafTilesView"/>
      <property name="templateEngine" ref="templateEngine" />
      <property name="characterEncoding" value="UTF-8" />
    </bean>

</beans>
 Now we introduce the Spring security configuration as shown in Listing 4.
Listing 4 - spring-security-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:security="http://www.springframework.org/schema/security"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">

    <security:http use-expressions="true">
        <security:form-login login-page="/signin"
            default-target-url="/listlead"
            authentication-failure-url="/signinfailure" />
        <security:intercept-url pattern="/signin*" access="permitAll" />
        <security:intercept-url pattern="/**/*.js" access="permitAll" />
        <security:intercept-url pattern="/**/*.css" access="permitAll" />
        <security:intercept-url pattern="/**/*.gif" access="permitAll" />

        <security:intercept-url pattern="/list*" access="fullyAuthenticated" />
        <security:intercept-url pattern="/create*" access="fullyAuthenticated" />

    </security:http>

    <security:authentication-manager>
      <security:authentication-provider>
        <security:user-service>
        <security:user name="dhrubo" password="123456" authorities="ROLE_USER" />
        </security:user-service>
      </security:authentication-provider>
    </security:authentication-manager>

</beans>

I will also modify the signinform.html to allow it to submit to Spring Security filter to initiate the authentication and subsequent redirection on successful authentication.
Listing 5 - signinform.html
<h3>Sign In</h3>
<form xmlns:th="http://www.thymeleaf.org"
    action="authentication" th:action="@{/j_spring_security_check}"
    method="post" class="well">
    <fieldset>
        <label>Username :</label> <input id="j_username" name="j_username"
            type="text" required="required" autofocus="autofocus"
            class="input span5" placeholder="Username" /> <label>Password
            :</label> <input id="j_password" name="j_password" type="password"
            required="required" class="input span5"
            placeholder="Password" />
    </fieldset>
    <button type="submit" class="btn btn-success">Sign In</button>
    <button type="submit" class="btn btn-info">Recover password</button>
</form>

The header.html is also modified to check if authentication is working fine and also check Thymeleaf Spring Security 3 integration by printing the name of the logged in user.
Listing 6 - header.html
<div xmlns:th="http://www.thymeleaf.org"
    class="navbar navbar-inverse navbar-fixed-top">
    <div class="navbar-inner">

        <div class="container-fluid">
            <a class="btn btn-navbar" data-toggle="collapse"
                data-target=".nav-collapse"> <span class="icon-bar"></span> <span
                class="icon-bar"></span> <span class="icon-bar"></span>
            </a> <a class="brand" href="#">Project name</a>
            <div class="nav-collapse collapse">

                <p class="navbar-text pull-right">
                    Logged in as <a href="#" class="navbar-link" th:text="${#authentication.name}">Username</a>
                </p>
                <ul class="nav">
                    <li class="active"><a href="#">Home</a></li>
                    <li><a href="#about">About</a></li>
                    <li><a href="#contact">Contact</a></li>

                </ul>
            </div>
            <!--/.nav-collapse -->
        </div>
    </div>
</div>
We will now add 1 controller mapping - signin in the GuestController.
Listing 7 - GuestController.java
/**
 *
 */
package com.effectivcrm.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author Dhrubo
 *
 */
@Controller
public class GuestController {

    @RequestMapping(value="/signin")
    public String gotoSignIn(){
        return "signin";
    }

    @RequestMapping(value="/")
    public String index(){
        return "signin";
    }

}

Comments

Popular posts from this blog

Part 3 - Integrating Tiles, Thymeleaf and Spring MVC 3

In this post I will demonstrate how to integrate Apache Tiles with Thymeleaf. This is very simple. The first step is to include the tiles and thymeleaf-tiles extension dependencies. I will include them in the pom.xml. Note we wil lbe using Tiles 2.2.2 Listing 1 - parent/pom.xml --- thymeleaf-tiles and tiles dependencies <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <!-- Tiles --> <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <dependency> <groupId>org.apache.tiles</groupId> <artifactId>tiles-core</artifactId> <version>${tiles.version}</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.apache.tiles</groupId> <artifactId>tiles-template</artifactId> <version>${tiles.version}</version> <scope>compile</s...

CKEDITOR 3.x - Simplest Ajax Submit Plugin

  I have assumed that you have downloaded and got started with CKEDITOR. Step 1 – The html file is shown below: <html> <head> <title>Writer</title> <meta content="text/html; charset=utf-8" http-equiv="content-type" /> <script type="text/javascript" src="ckeditor/ckeditor.js"></script> <script type="text/javascript" src="js/jquery-1.4.2.min.js"></script> <style> .cke_contents { height: 400px !important; } </style> </head> <body> <form action="sample_posteddata.php" method="post"> <textarea id="editor" > </textarea> <script type="text/javascript"> //<![CDATA[ CKEDITOR.replace( 'editor', { fullPage : true, uiColor : '#9AB8F3', toolbar : 'MyToolbar' }); //]]> </script> </form> </body> </html> Note that the jquery js...

Load your Spring beans as plug-ins

There are different ways of loading Spring beans in a web application – viz using the dispatcher servlet or the context loader listener. A more modular and flexible approach (which also allows you to minimize coupling and dependencies in your applications) is to load the barebones of your application with the core context loaders and load the rest of your beans in a pluggable way. For example you may be interested in loading your page controllers, business tier and data access code as three separate modules. Off course you can do this and then use import in a main spring config file or a comma separate list in your web.xml where you configure the spring bean definition xmls. But what if you just wanted to drop in a jar with Spring beans and be sure they will be loaded and registered in the container. This also eases the pain of swapping an implementation. The goal is to put all your Spring beans in a particular layer, package it as a jar with one or more configuration file(with one fol...