Spring Security Tutorial

Spring Security Tutorial

1. Web Configuration
The 'springSecurityFilterChain' filter needs to be configured to intercept all URLs so Spring Security can control access to them. The filter must be named this to match the default bean it retrieves from the Spring context.
/WEB-INF/web.xml
               
<?xml version="1.0" encoding="UTF-8"?>
<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" metadata-complete="true">

    <display-name>simple-security</display-name>
   
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/*-context.xml
        </param-value>
    </context-param>

    <!-- Enables Spring Security -->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>
            org.springframework.web.filter.DelegatingFilterProxy
        </filter-class>
    </filter>

    <filter>
        <filter-name>encoding-filter</filter-name>
        <filter-class>
            org.springframework.web.filter.CharacterEncodingFilter
        </filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>encoding-filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <servlet>
        <servlet-name>simple-form</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value></param-value>
        </init-param>
    </servlet>
   
    <servlet-mapping>
        <servlet-name>simple-form</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
   
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

</web-app>

2. Spring Configuration
The security:global-method-security element configures annotation based security so @Secured can be used to restrict access to methods.
The security:http is set to auto-configure basic HTTP security. Inside the the login, logout, and main style sheet are set to the anonymous role (unrestricted access). The rest of the site is restricted to an authenticated user in the user role. The default login and logout configuration is also customized to use custom pages to maintain the sites look & feel.
The authentication is set to use jdbc based user authentication. Only the DataSource needs to be set on the security:jdbc-user-service element if the default tables are used. Although other tables can be used by setting custom queries on the element.
/WEB-INF/spring/security-context.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.xsd
                           http://www.springframework.org/schema/security
                           http://www.springframework.org/schema/security/spring-security-3.1.xsd">

    <security:global-method-security secured-annotations="enabled" />
   
    <security:http auto-config="true">
        <!-- Restrict URLs based on role -->
        <security:intercept-url pattern="/login*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
        <security:intercept-url pattern="/logoutSuccess*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
       
        <security:intercept-url pattern="/css/main.css" access="IS_AUTHENTICATED_ANONYMOUSLY" />
        <security:intercept-url pattern="/resources/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />

        <security:intercept-url pattern="/**" access="ROLE_USER" />

        <!-- Override default login and logout pages -->
        <security:form-login login-page="/login.html"
                             login-processing-url="/loginProcess"
                             default-target-url="/index.jsp"
                             authentication-failure-url="/login.html?login_error=1" />
        <security:logout logout-url="/logout" logout-success-url="/logoutSuccess.html" />
    </security:http>

    <security:authentication-manager>
        <security:authentication-provider >
            <security:jdbc-user-service data-source-ref="dataSource" />
        </security:authentication-provider>
    </security:authentication-manager>

</beans>
               
           

3. JSP Example
The security tag is defined at the top of the page with a prefix of 'sec'. Then around delete link the sec:authorize tag is configured to only show the link if the user is in the role 'ROLE_ADMIN'. Now, this doesn't actually stop someone from executing a delete query if they know the URL. Below, in the PersonService, the @Secured tag is configured to enforce the rule that only an admin can delete a record.
/WEB-INF/jsp/person/search.jsp
               
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

<h1><fmt:message key="person.search.title"/></h1>

<table class="search">
    <tr>
        <th><fmt:message key="person.form.firstName"/></th>
        <th><fmt:message key="person.form.lastName"/></th>
    </tr>
<c:forEach var="person" items="${persons}" varStatus="status">
    <tr>
        <c:set var="personFormId" value="person${status.index}"/>

        <c:url var="editUrl" value="/person/form.html">
            <c:param name="id" value="${person.id}" />
        </c:url>
        <sec:authorize ifAllGranted="ROLE_ADMIN">
            <c:url var="deleteUrl" value="/person/delete.html"/>
            <form id="${personFormId}" action="${deleteUrl}" method="POST">
                <input id="id" name="id" type="hidden" value="${person.id}"/>
            </form>
        </sec:authorize>

        <td>${person.firstName}</td>
        <td>${person.lastName}</td>
        <td>
            <a href='<c:out value="${editUrl}"/>'><fmt:message key="button.edit"/></a>
            <sec:authorize ifAllGranted="ROLE_ADMIN">
                <a href="javascript:document.forms['${personFormId}'].submit();"><fmt:message key="button.delete"/></a>
            </sec:authorize>
        </td>
    </tr>
</c:forEach>
</table>
               
            
4. Code Example
Example 1. PersonService
The delete method has access restricted to users in the admin role by putting the @Secured annotation above it and setting the allowed roles. Which in this case is only the 'ROLE_ADMIN' role. By securing the service interface, even if a non-admin user tries to execute the delete URL they will not be able to delete a record.
               
public interface PersonService {

    /**
     * Find person by id.
     */
    public Person findById(Integer id);

    /**
     * Find persons.
     */
    public Collection<Person> find();

    /**
     * Saves person.
     */
    public Person save(Person person);

    /**
     * Deletes person.
     */
    @Secured ({"ROLE_ADMIN"})
    public void delete(Person person);

}
               
           


You may wonder why the '/delete/person*' URL wasn't restricted. For the current application, this would have been sufficient. But we don't really want to restrict the URL, we want to restrict the actual delete action. Spring Security makes it very easy to restrict access to actual methods. If at some point in the future the another URL is made to also delete a record, our rule will still be enforced. Also, if at some point someone creates a method that calls delete that is accessed by a completely different URL, only admins will be able to execute this new part of the application successfully. A new implementation of the interface will als

5. SQL Script
security_schema.sql
               
SET IGNORECASE TRUE;

CREATE TABLE users (
   username VARCHAR(50) NOT NULL PRIMARY KEY,
   password VARCHAR(50) NOT NULL,
   enabled BIT NOT NULL
);

CREATE TABLE authorities (
   username VARCHAR(50) NOT NULL,
   authority VARCHAR(50) NOT NULL
);
CREATE UNIQUE INDEX ix_auth_username ON authorities (username, authority);

ALTER TABLE authorities ADD CONSTRAINT fk_authorities_users foreign key (username) REFERENCES users(username);

INSERT INTO users VALUES ('david', 'newyork', true);
INSERT INTO users VALUES ('alex', 'newjersey', true);
INSERT INTO users VALUES ('tim', 'illinois', true);

INSERT INTO authorities VALUES ('david', 'ROLE_USER');
INSERT INTO authorities VALUES ('david', 'ROLE_ADMIN');
INSERT INTO authorities VALUES ('alex', 'ROLE_USER');
INSERT INTO authorities VALUES ('tim', 'ROLE_USER');
               
            

6. Reference

Related Links



Comments

Popular posts from this blog

Top 10 technological advances in IT industry

Spring Boot security latest

Spring Boot Application Deployment on Google Cloud Platform (GCP)