Spring Security 4.0 hello world example with annotation and xml


Introduction :

This article is all about implementing Spring Security in your Spring MVC web application to secure a URL access with authentication. We use Spring Annotation based configuration for Servlet 3.0 containers,so there is no web.xml in your web application.
Let us take Helloworld example and demonstrate Security using annontation comparing with classic XML configuration.

Technologies & Tools :

  1. java 1.7
  2. tomcat 8
  3. spring 4.1.6.RELEASE
  4. spring-security 4.0.1.RELEASE
  5. Maven 3.0

Maven dependencies and plugins  :

    <properties>
        <spring-version>4.1.6.RELEASE</spring-version>
        <spring-security-version>4.0.1.RELEASE</spring-security-version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</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-config</artifactId>
            <version>${spring-security-version}</version>
 </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>

    <build>
        <finalName>spring-security-helloworld-annotation</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <warSourceDirectory>src/main/webapp</warSourceDirectory>
                    <warName>spring-security-helloworld-annotation</warName>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>

here we used two plugins,one is compile code using java 1.7 and second plugin is maven-war plugin,as we are not using web.xml ,so wee need to configure this plugin to avoid failures in building war package.

Spring security configuration :

Let first see how to declare spring security configuration in XML approach leter using annotation.Here is the xml Configuration

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:security="http://www.springframework.org/schema/security"

       xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/mvc
                http://www.springframework.org/schema/mvc/spring-mvc.xsd
                http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context.xsd
                http://www.springframework.org/schema/security
                http://www.springframework.org/schema/security/spring-security.xsd">

    <context:component-scan base-package="com.spring.security" />

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix">
            <value>/WEB-INF/pages/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>

    <security:http auto-config="true" use-expressions="true">
        <!-- Interceptor urls -->
        <security:intercept-url pattern="/" access="permitAll" />
        <security:intercept-url pattern="/userpage*" access="hasRole('USER')" />
        <security:intercept-url pattern="/products**" access="hasRole('USER')" />
        <security:intercept-url pattern="/admin**" access="hasRole('ADMIN')" />

        <security:form-login />

        <!-- Logout -->
        <security:logout logout-success-url="/logout" />
    </security:http>


    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
                <security:user name="testuser" password="testuser" authorities="USER" />
                <security:user name="admin" password="admin" authorities="ADMIN" />
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>
</beans>

<security:http ... > tag you need to mention what resources you want to secure. As you can see I have defined four interceptors using <security:intercept-url> tags.
Here I have mentioned what URLs or URL patterns are permitted to all users including anonymous users and what URLs or areas are restricted to accessible by certain User Roles only.

In the above code snippets the <security:authentication-manager>, <security:authentication-provider> and <security:user-service> are meant for Authentication. The authentication-manager delegates the user credentials to UserDetailServices i.e the User Repository to check and get the user identity and granted authorities. And <security:http> is meant for the security checks or authorization.

Spring-security provides a default built-in login page, which has two text boxes to enter the Username and Password, and a Submit button to submit the credentials for validation. You don't have to design a login form for your application. As soon as the user opens the application url on his browser, spring-security checks if the user has not log in, then it redirects the user to the default login form provided by spring-security.

If you want to use a custom login page for your application, then you can configure spring-security to use your custom login page instead. You can use the <security:form-login> tag to define your custom login form page within the <security:http> … </security:http> tag. Below is the configuration example.

         <security:form-login login-page="/login"
                             default-target-url="/userpage"
                             login-processing-url="/j_spring_security_check"
                             authentication-failure-url="/accessdenied"
                             username-parameter="username"
                             password-parameter="password"
                />

        <!-- Logout -->
        <security:logout logout-success-url="/logout" />


login-page: login-page: Custom login page url path.
default-target-url : url where the user should navigate after successful login.
authentication-failure-url: url where the user should navigate after a login failure.
username-parameter and password-parameter : username-parameter and password-parameter - These two are optional. By default spring-security accepts the parameter names “j_username” and “j_password” as username and password in the login form. If you want to given any other name to the username and password input fields in the login form, then you can specify the custom parameter names in these two attributes in <security:form-login ...> tag.

In “logout-success-url” you can specify the page url path which should execute when user uses spring-security logout process.


That's all on the Spring Security setup and configuration part.

Lets see spring security declaration using annotation

This is how we can add view resolver through annotation.
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.spring.security")
public class HelloWorldConfiguration {

    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/pages/");
        viewResolver.setSuffix(".jsp");

        return viewResolver;
    }
}

Adding security configuration through annotation.
@Configuration
@EnableWebSecurity
public class HelloWorldSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("testuser").password("testuser").roles("USER");
        auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/userpage*").access("hasRole('USER')")
                .antMatchers("/products**").access("hasRole('USER')")
                .antMatchers("/admin**").access("hasRole('ADMIN')")
                .and().formLogin()
                .and().logout().logoutSuccessUrl("/logout");
    }
}

Method configureGlobalSecurity in above class configures AuthenticationManagerBuilder with user credentials and allowed roles. This AuthenticationManagerBuilder creates AuthenticationManager which is responsible for processing any authentication request. Notice that in above example, we have used in-memory authentication while you are free to choose from JDBC, LDAP and other authentications.

The overridden Method Configure configures HttpSecurity which allows configuring web based security for specific http requests. By default it will be applied to all requests, but can be restricted using requestMatcher(RequestMatcher)/antMathchers or other similar methods.

In above configuration, we say that URL’s ‘/’ is permitted to all and to access /userpage user shoud have USER role and to access admin page user should have ADMIN role.And we are using spring provided login page and for logout url is redirecting to "/logout".

Register the springSecurityFilter with war : 

In XML approach, we used to declare filter in web.xml.

    <servlet>
        <servlet-name>SpringSecurityApp</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>SpringSecurityApp</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

Same can be achieve using AbstractSecurityWebApplicationInitializer.

public class HelloWorldSecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

}

Deployment descriptor :

In XML appraoch we used to declare Dispatcher servlet,filters and spring context file path in 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_2_5.xsd"
         version="2.5">
    <display-name>Project Name</display-name>
    <welcome-file-list>
        <welcome-file></welcome-file>
    </welcome-file-list>
    <servlet>
        <servlet-name>SpringSecurityApp</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>SpringSecurityApp</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>


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

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/SpringSecurityApp-servlet.xml</param-value>
    </context-param>

    <!-- Enable Spring Security -->
    <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>
</web-app>

With out web.xml file, Let us see how to configure dispatcher servlet

public class HelloWorldSpringMvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { HelloWorldConfiguration.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return null;
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

}

Add Controller: 

This controller is same for both XML and Annotation approach.Here we exposed three rest services, which serves for user page, admin page and welcome page.

@Controller
public class HelloWorldController {

    @RequestMapping(value = { "/" }, method = RequestMethod.GET)
    public ModelAndView homePage() {
        ModelAndView model = new ModelAndView();
        model.addObject("greeting", "Hi, Welcome to hello world app. ");
        model.setViewName("welcome");
        return model;
    }

    @RequestMapping(value = { "/userpage" }, method = RequestMethod.GET)
    public ModelAndView userPage() {
        ModelAndView model = new ModelAndView();
        model.addObject("title", "Spring Security Hello World");
        model.addObject("user", getUser());
        model.setViewName("user");
        return model;
    }

    @RequestMapping(value = "/adminpage", method = RequestMethod.GET)
    public ModelAndView adminPage() {

        ModelAndView model = new ModelAndView();
        model.addObject("title", "Spring Security Hello World");
        model.addObject("user", getUser());
        model.setViewName("admin");
        return model;
    }

    @RequestMapping(value="/logout", method = RequestMethod.GET)
    public ModelAndView logoutPage (HttpServletRequest request, HttpServletResponse response) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null){
            new SecurityContextLogoutHandler().logout(request, response, auth);
        }
        return homePage();
    }

    private String getUser(){
        String userName = null;
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (principal instanceof UserDetails) {
            userName = ((UserDetails)principal).getUsername();
        } else {
            userName = principal.toString();
        }
        return userName;
    }
}

Adding Views :

 1) welcome.jsp :

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
    <title>HelloWorld page</title>
</head>
<body>
    Greeting : ${greeting}
    <br/>
    <h3>This is a welcome page!!!</h3>

    Click here for <a href="<c:url value="/userpage" />">Login</a>.
</body>
</html>

2) user.jsp :


<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
    <title>HelloWorld page</title>
</head>
<body>
    ${title}<br/><br/>
    Dear ${user}, you are successfully logged into this application.
    <br/>
    <a href="<c:url value="/logout" />">Logout</a>
</body>
</html>

3) admin.jsp :


<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
    <title>HelloWorld page</title>
</head>
<body>
    ${title}<br/><br/>
    Dear ${user}, you are successfully logged into this application as admin.
    <br/>
    <a href="<c:url value="/logout" />">Logout</a>
</body>
</html>

Build and Deploy : 

Do mvn clean install and deploy spring-security-helloworld-annotation.war into webserver
(recomended Tomcat-8).
And Access this url in browser http://localhost:8080/spring-security-helloworld-annotation/

Welcome Page



Login Page :


Access denied (wrong credentials) : 


User Login Successful Page :


Admin Login Page :


Hope you got idea on how Spring Security works and how you can integrate spring security in your spring application easily using annotations and XML configuration approaches with this post.

You can download Source from Github



Show Comments: OR

0 comments: