RSS

Tag Archives: custom authentication

Stateless Spring Security on REST API


First I would like you to go through my previous blog post that I have written for Spring Security on REST Api. In the above spring security scenario based on state full mechanism. It is using the default user details service which is defined through the security.xml but we know that once we are going to develop real world application those use the custom user stores to store the user details so we need to plug those databases to our authentication process. Another thing that we know in the REST apis should be stateless so what I’m going to show you how to secure the REST Api with stateless basic authentication by using the custom user details service.

First of all you need to understand the flow of this security mechanism. See the following diagram.

Spring-Security

Lets look at the configuration and cording. I assume that you have clear idea about spring security configuration so I’m not going to explain each and every thing on this project. If you have doubt about the spring configurations please follow my previous post carefully.

webSecurityConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:sec="http://www.springframework.org/schema/security"
	xsi:schemaLocation="
      http://www.springframework.org/schema/security
      http://www.springframework.org/schema/security/spring-security-3.2.xsd
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

	<!-- Rest authentication entry point configuration -->
	<http use-expressions="true" create-session="stateless"
		entry-point-ref="restServicesEntryPoint" authentication-manager-ref="authenticationManagerForRest">
		<intercept-url pattern="/api/**" />
		<sec:form-login authentication-success-handler-ref="mySuccessHandler" />
		<sec:access-denied-handler ref="myAuthenticationAccessDeniedHandler" />
		<http-basic />
	</http>

	<!-- Entry point for REST service. -->
	<beans:bean id="restServicesEntryPoint"
		class="spring.security.custom.rest.api.security.RestAuthenticationEntryPoint" />

	<!-- Custom User details service which is provide the user data -->
	<beans:bean id="customUserDetailsService"
		class="spring.security.custom.rest.api.security.CustomUserDetailsService" />

	<!-- Connect the custom authentication success handler -->
	<beans:bean id="mySuccessHandler"
		class="spring.security.custom.rest.api.security.RestAuthenticationSuccessHandler" />

	<!-- Using Authentication Access Denied handler -->
	<beans:bean id="myAuthenticationAccessDeniedHandler"
		class="spring.security.custom.rest.api.security.RestAuthenticationAccessDeniedHandler" />

	<!-- Authentication manager -->
	<authentication-manager alias="authenticationManagerForRest">
		<authentication-provider user-service-ref="customUserDetailsService" />
	</authentication-manager>

	<!-- Enable the annotations for defining the secure role -->
	<global-method-security secured-annotations="enabled" />

</beans:beans>

Now you can focus on the http configuration in above xml. Within the http name tag you can see I have defined the http-basic that means this url should be secured by basic authentication. You have to send the username and password by Base64 encoding as follows.

admin:adminpass encoded by Base64 (YWRtaW46YWRtaW5wYXNz)

Second main point of this project is custom user detail service. As I mentioned earlier in the real world application you have to use the existing authentication source to do the authentication.

package spring.security.custom.rest.api.security;

import java.util.ArrayList;
import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

/**
 * CustomUserDetailsService provides the connection point to external data
 * source
 * 
 * @author malalanayake
 * 
 */
public class CustomUserDetailsService implements UserDetailsService {
	private String USER_ADMIN = "admin";
	private String PASS_ADMIN = "adminpass";

	private String USER = "user";
	private String PASS = "userpass";

	@Override
	public UserDetails loadUserByUsername(String authentication) throws UsernameNotFoundException {
		CustomUserData customUserData = new CustomUserData();
		// You can talk to any of your user details service and get the
		// authentication data and return as CustomUserData object then spring
		// framework will take care of the authentication
		if (USER_ADMIN.equals(authentication)) {
			customUserData.setAuthentication(true);
			customUserData.setPassword(PASS_ADMIN);
			Collection<CustomRole> roles = new ArrayList<CustomRole>();
			CustomRole customRole = new CustomRole();
			customRole.setAuthority("ROLE_ADMIN");
			roles.add(customRole);
			customUserData.setAuthorities(roles);
			return customUserData;
		} else if (USER.equals(authentication)) {
			customUserData.setAuthentication(true);
			customUserData.setPassword(PASS);
			Collection<CustomRole> roles = new ArrayList<CustomRole>();
			CustomRole customRole = new CustomRole();
			customRole.setAuthority("ROLE_USER");
			roles.add(customRole);
			customUserData.setAuthorities(roles);
			return customUserData;
		} else {
			return null;
		}
	}

	/**
	 * Custom Role class for manage the authorities
	 * 
	 * @author malalanayake
	 * 
	 */
	private class CustomRole implements GrantedAuthority {
		String role = null;

		@Override
		public String getAuthority() {
			return role;
		}

		public void setAuthority(String roleName) {
			this.role = roleName;
		}

	}

}

In the above code you can see I have implemented the UserDetailsService interface and override the method loadUserByUsername. Within this method you need to connect to the external user store and get the credentials and the roles associated with the user name. I have hardcoded the values for your understanding.

Another special thing is you need to pass the object which is implemented by the UserDetails so you can see I have created the following class for that.

package spring.security.custom.rest.api.security;

import java.util.ArrayList;
import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

/**
 * This class is provide the user details which is needed for authentication
 * 
 * @author malalanayake
 * 
 */
public class CustomUserData implements UserDetails {
	Collection<? extends GrantedAuthority> list = null;
	String userName = null;
	String password = null;
	boolean status = false;

	public CustomUserData() {
		list = new ArrayList<GrantedAuthority>();
	}

	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		return this.list;
	}

	public void setAuthorities(Collection<? extends GrantedAuthority> roles) {
		this.list = roles;
	}

	public void setAuthentication(boolean status) {
		this.status = status;
	}

	@Override
	public String getPassword() {
		return this.password;
	}

	public void setPassword(String pass) {
		this.password = pass;
	}

	@Override
	public String getUsername() {
		return this.userName;
	}

	@Override
	public boolean isAccountNonExpired() {
		return true;
	}

	@Override
	public boolean isAccountNonLocked() {
		return true;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}

	@Override
	public boolean isEnabled() {
		return true;
	}

}

Finally we need to take care of the unauthenticated responses so there are two possibilities that we can throw the 401 Unauthorized response.

1. User come to access the service without proper authentication. Then the spring framework redirect the user to get the authentication but this is a REST api so we don’t need to redirect the user to get the authentication thats why we simply pass the 401 response in RestAuthenticationEntryPoint class.

package spring.security.custom.rest.api.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

/**
 * This entry point is called once the request missing their authentication.
 * 
 * @author malalanayake
 * 
 */
@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {

	@Override
	public void commence(HttpServletRequest arg0, HttpServletResponse arg1,
			AuthenticationException arg2) throws IOException, ServletException {
		arg1.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");

	}

}

2. Second possible scenario is user has proper authentication but he doesn’t have proper authorization that means he doesn’t have the proper ROLE. This scenario spring framework push the request to the RestAuthenticationAccessDeniedHandler then we need to simply pass the 401 Unauthorized response. If we didn’t set this handler the spring framework push the 403 Forbidden response.

package spring.security.custom.rest.api.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;

public class RestAuthenticationAccessDeniedHandler implements AccessDeniedHandler {

	@Override
	public void handle(HttpServletRequest request, HttpServletResponse response,
			AccessDeniedException arg2) throws IOException, ServletException {
		response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");

	}
}

I hope you will enjoy the spring security with REST Api. You can download the total source of this project from here.

Advertisements
 
13 Comments

Posted by on June 30, 2014 in java, spring

 

Tags: , ,

 
%d bloggers like this: