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

Why do you need Spring Cloud Config server?

Last month I wrote a primer on concepts around 12 factor app. Before getting into the details of the Spring Cloud Config Server, I must refresh on the principle #3 from the list presented in that post.

3 – ConfigurationStore config in the environments
Configuration information must be separate from the source code. This may seem so obvious, but often we are guilty of leaving critical configuration parameters in the scattered in the code. Instead, applications should have environment specific configuration files. The sensitive information like database password or API key should be stored in these environment configuration files in encrypted format.
 The key takeaways from this postulate for a cloud-native microservices application are:
Do not store configuration as part of the deployable unit (in the case of lead microservice - inside the jar or war if you are still deploying war like the good old days). Instead, store it in an external location and make it easily accessible during run-…

Upgrading Lead Microservice - Use MariaDB and Flyway with Spring Boot

So far I have been using an in-memory H2 database or Mockito for testing the lead microservice. To make the transition towards using the Spring Cloud Config server, I need to upgrade the micro-application to use MariaDB. I will be adding the configuration in the application.yml  the file which in the subsequent post will move over to the config server store. I will also be using Flyway to make it easy to maintain the database schema changes in future. I will use this post to introduce Flyway in the mix. Spring Boot also provides first class integration with Flyway. I am using Flyway as its really quick and easy to get started, minimal learning curve (no DSL) and I am comfortable with it having used it in the past.

Assumptions

MariaDB 10 is installedBasic familiarity with FlywayHeidi SQL client is installed.
Step 1 - Update build.gradle to include the MariaDB JDBC and Flyway dependencies.
Do not forget to do a Gradle refresh on your IDE (I am using STS 3.8.4 on Java 8)

Step 2 - Rename the…

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.2Listing 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</scope>