Monday, November 9, 2015

Enable basic Spring Security authentication and authorization in a Google App Engine site

After some effort, I managed to get a Google App Engine project to work with a simple Spring Security authentication and authorization over a secure HTTPS protocol. In addition to Spring Core and Spring MVC, Spring Security needs to be installed and configured for the App Engine project.

Add Spring Security to the project's pom.xml file
  1. Open up the pom.xml in a text editor, e.g. C:\MyProject\myapp\pom.xml. Add in a convenience property for the Spring Security version.



  2. Add in the dependencies for Spring Security.
<!-- Spring security dependencies -->
<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>

Add in the Spring Security filters and the security configuration file to web.xml
  1. Open up the configuration web.xml file e.g. C:\MyProject\myapp\src\main\webapp\WEB-INF\web.xml in a text editor.
  2. Add in the filter and filter mapping sections for Spring Security.



  3. Add in the name and location of the Spring Security configuration file e.g. C:\MyProject\myapp\src\main\webapp\WEB-INF\spring-security.xml in the context-param section as shown below.



An example of the resultant web.xml file

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 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">

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
		/WEB-INF/dispatcher-servlet.xml,
		/WEB-INF/spring-security.xml
      	</param-value>
	</context-param>

	<context-param>
		<param-name>log4jConfigLocation</param-name>
		<param-value>/WEB-INF/classes/log4j.properties</param-value>
	</context-param>

	<!-- Bootstrap the root application context as usual using ContextLoaderListener -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

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

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

	<!-- Add support for HTTPS -->
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>MySecureSite</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>

	<!-- For Spring Security -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring-security.xml</param-value>
	</context-param>

	<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>

Create the Spring Security configuration file spring-security.xml
  1. Use a text editor and create the Spring Security configuration file e.g. C:\MyProject\myapp\src\main\webapp\WEB-INF\spring-security.xml.

  2. Create an http element and add in one or more intercept-url elements to define which URLs you want to enable Spring Security, e.g. "/*" for all.



An example listing is shown below. This example enables security for all mappings from the root for any user with the role ROLE_ADMIN. There is only one user admin with the password admin authorized with the role ROLE_ADMIN. Add in more roles and users as required.

<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="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-4.0.xsd">

	<http auto-config="true">
		<intercept-url pattern="/*" access="hasRole('ROLE_ADMIN')" />
	</http>
	
	<authentication-manager>
		<authentication-provider>
			<user-service>
				<user name="admin" password="admin" authorities="ROLE_ADMIN" />
			</user-service>
		</authentication-provider>
	</authentication-manager>
	
</beans:beans>

Code the Spring controller
  1. Develop the Java application logic for the Spring Controller. Add in the Spring annotations @Controller and map one or more methods to URLs using the @RequestMapping annotations as shown below.


package com.mycompany.myapp;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MySvc {
	@RequestMapping("/hello")
	public @ResponseBody String index(){
		return "Greetings from Spring Boot!";
	}	
}


Compile and deploy the application to Google App Engine. Now when the site URL e.g. https://myapp.appspot.com/hello is first accessed, a default Spring login prompt will appear.

If an incorrect user name or password is submitted, the authentication will fail.

If the correct user name and password is submitted, Spring Security will check the user's authorization for the requested URL. If it's in the user's role, then the URL will return the requested resource to the user as shown below.
 

No comments: