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

Breaking down the CRM monolith

In my previous posts, I have shared some theory regarding microservices. But it's time to start some implementation. I love to write code and see and feel things working. So I will start a series to refactor a monolithic CRM system and transform it into microservices based flexible software. Big ball of mud. Customer Relationship Management(CRM) is that giant software which existed since time immemorial and is used by all companies in some form or shape. Big enterprises will buy CRM software (also known as packages) from top CRM vendors like Oracle, SAP, Salesforce etc and then employ an army of consultants to try and implement it. Most of the classic CRM systems in the market today, even if deployed on the cloud are the big monolithic ball of mud. They are the gigantic piece of software with the huge feature set. Most often those requirements are surplus to the requirement or they will not fit into the processes of the company. So the company has to hire these certified consu

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