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
Listing 2 - web.xml
Listing 3 - spring-view.xml
Listing 4 - spring-security-config.xml
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
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
Listing 7 - GuestController.java
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"; } }
Related articles, courtesy of Zemanta:
- Spring Security Part 1 - Simple Login application with database
- Containerless Spring MVC
- Authenticate against a hippo repository using spring security
- ClassNotFoundException: org.springframework.web.context.ContextLoaderListener
- Spring Custom Namespaces
- Web.xml Servlet Configuration
- MVC on the server and on the client
- What is Servlet Mapping ?
Comments
Post a Comment