We have seen 'A Simple Web Service Using Spring-WS' in the previous post.
Now we need to secure the service from unauthorized access. We are going to apply HTTP Digest Authentication[1][2] for this.
Note that we are not implementing Message Signing or Encryption here. We will only ensure 'Authentication' using Spring XwsSecurityInterceptor[3], so that only authenticated users can access the add service we defined in the previous post.
First modify the ws-servlet.xml file and add the following bean definitions:
Add a Role enumeration to define User's role:
Add the domain(/model/entity) class:
Now add the dao class (with dummy user population):
com.test.ws.dao.UserDao
We need to create a CustomUserDetailsService which implements UserDetailsService
This is how the project-structure should look like (underlined items are newly added over the project in the previous post):
The project can be downloaded using git from spring-ws-test project (secured_user_auth_digest branch):
git clone https://github.com/tariqmnasim/spring-ws-test.git -b secured_user_auth_digest
References:
[1] HTTP Authentication: Basic and Digest Access Authentication
[2] Wikipedia: Digest access Authentication
[3] Securing your web services using Spring-WS
Now we need to secure the service from unauthorized access. We are going to apply HTTP Digest Authentication[1][2] for this.
Note that we are not implementing Message Signing or Encryption here. We will only ensure 'Authentication' using Spring XwsSecurityInterceptor[3], so that only authenticated users can access the add service we defined in the previous post.
First modify the ws-servlet.xml file and add the following bean definitions:
- XwsSecurityInterceptor bean (inside the <sws:interceptors/> tag),
- callBackHandlerDigest bean definition,
- userDetailsService
ws-servlet.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:context="http://www.springframework.org/schema/context"
xmlns:sws="http://www.springframework.org/schema/web-services"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd">
<sws:annotation-driven />
<!-- To detect @Endpoint, @Service, @Component etc -->
<context:component-scan base-package="com.test.ws" />
<!-- To generate dynamic wsdl -->
<sws:dynamic-wsdl id="wstest" portTypeName="add"
locationUri="/services/add" targetNamespace="http://develop-for-fun.blogspot.com/spring-ws">
<sws:xsd location="/WEB-INF/spring/service-definitions.xsd" />
</sws:dynamic-wsdl>
<!-- For validating your request and response -->
<!-- So that you don't send a string instead of an integer -->
<sws:interceptors>
<bean id="validatingInterceptor"
class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
<property name="schema" value="/WEB-INF/spring/service-definitions.xsd" />
<property name="validateRequest" value="true" />
<property name="validateResponse" value="true" />
</bean>
<bean id="wsSecurityInterceptor"
class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
<property name="policyConfiguration" value="/WEB-INF/spring/securityPolicy.xml"/>
<property name="callbackHandlers">
<list>
<ref bean="callbackHandlerDigest"/>
</list>
</property>
</bean>
</sws:interceptors>
<bean id="callbackHandlerDigest" class="org.springframework.ws.soap.security.xwss.callback.SpringDigestPasswordValidationCallbackHandler">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
<bean id="userDetailsService" class="com.test.ws.services.CustomUserDetailsService" />
</beans>
security-policy.xml
<xwss:securityconfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
<xwss:requireusernametoken noncerequired="true" passworddigestrequired="true">
</xwss:requireusernametoken></xwss:securityconfiguration>
Add a Role enumeration to define User's role:
com.test.ws.enumeration.Role
package com.test.ws.enumeration;
public enum Role {
ROLE_ADMIN(1),
ROLE_MEMBER(2);
private final int value;
Role(int p) {
this.value = p;
}
public int getValue() {
return this.value;
}
}
Add the domain(/model/entity) class:
com.test.ws.domain.User
package com.test.ws.domain;
import com.test.ws.enumeration.Role;
/**
* The User entity
*/
public class User {
private int id;
private String username;
private String password;
private String email;
private String name;
private Role role;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Now add the dao class (with dummy user population):
com.test.ws.dao.UserDao
package com.test.ws.dao;
import com.test.ws.domain.User;
import com.test.ws.enumeration.Role;
public class UserDao {
private static final String DEFAULT_ADMIN_PASS = "adminpass";
private static final String DEFAULT_MEMBER_PASS = "memberpass";
private static final String DUMMY_ADMIN_EMAIL = "admin@domain.com";
private static final String DUMMY_MEMBER_EMAIL = "member@domain.com";
public User getUser(String email) {
/** We are only allowing now two dummy users here
* Actually here we have to retrieve the users by the email address from Database **/
User user = new User();
if(email.equals(DUMMY_ADMIN_EMAIL)) {
user.setId(1);
user.setName("Administrator");
user.setPassword(DEFAULT_ADMIN_PASS);
user.setRole(Role.ROLE_ADMIN);
} else if(email.equals(DUMMY_MEMBER_EMAIL)) {
user.setId(2);
user.setName("Member");
user.setPassword(DEFAULT_MEMBER_PASS);
user.setRole(Role.ROLE_ADMIN);
}
user.setEmail(email);
return user;
}
}
We need to create a CustomUserDetailsService which implements UserDetailsService
com.test.ws.services.CustomUserDetailsService
package com.test.ws.services;
import com.test.ws.dao.UserDao;
import com.test.ws.domain.User;
import com.test.ws.enumeration.Role;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Service
public class CustomUserDetailsService implements UserDetailsService {
UserDao userDao = new UserDao();
@Override
public UserDetails loadUserByUsername(String email) {
try {
// Retrieve the user by email
User user = userDao.getUser(email);
int roleId = user.getRole().getValue();
return new org.springframework.security.core.userdetails.User (
user.getEmail(), user.getPassword(), true, true, true, true, getAuthorities(roleId)
);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public Collection<? extends GrantedAuthority> getAuthorities(Integer role) {
List<GrantedAuthority> authList = getGrantedAuthorities(getRoles(role));
return authList;
}
public List<String> getRoles(Integer role) {
List<String> roles = new ArrayList<String>();
if (role.intValue() == Role.ROLE_ADMIN.getValue()) {
roles.add(String.valueOf(Role.ROLE_ADMIN));
} else if (role.intValue() == Role.ROLE_MEMBER.getValue()) {
roles.add(String.valueOf(Role.ROLE_MEMBER));
}
return roles;
}
public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
}
import com.test.ws.dao.UserDao;
import com.test.ws.domain.User;
import com.test.ws.enumeration.Role;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Service
public class CustomUserDetailsService implements UserDetailsService {
UserDao userDao = new UserDao();
@Override
public UserDetails loadUserByUsername(String email) {
try {
// Retrieve the user by email
User user = userDao.getUser(email);
int roleId = user.getRole().getValue();
return new org.springframework.security.core.userdetails.User (
user.getEmail(), user.getPassword(), true, true, true, true, getAuthorities(roleId)
);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public Collection<? extends GrantedAuthority> getAuthorities(Integer role) {
List<GrantedAuthority> authList = getGrantedAuthorities(getRoles(role));
return authList;
}
public List<String> getRoles(Integer role) {
List<String> roles = new ArrayList<String>();
if (role.intValue() == Role.ROLE_ADMIN.getValue()) {
roles.add(String.valueOf(Role.ROLE_ADMIN));
} else if (role.intValue() == Role.ROLE_MEMBER.getValue()) {
roles.add(String.valueOf(Role.ROLE_MEMBER));
}
return roles;
}
public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
}
This is how the project-structure should look like (underlined items are newly added over the project in the previous post):
The project can be downloaded using git from spring-ws-test project (secured_user_auth_digest branch):
git clone https://github.com/tariqmnasim/spring-ws-test.git -b secured_user_auth_digest
References:
[1] HTTP Authentication: Basic and Digest Access Authentication
[2] Wikipedia: Digest access Authentication
[3] Securing your web services using Spring-WS






