This is the challenge I had to perform. I have to open services to business partners and I want to secure them. I use a Java based application with Jersey API (JAX-RS).
The first thing to consider is : what kind of solution offers the best compromise between security and the effort I'll have to provide to maintain this solution?
After some discussions with developers and experts, the conclusion is : expose your services over https and use Basic authentication (Digest authentication and certificate based authentication are too complex for partners)
So, how to implement that? I delegate "https" to my Apache http servers. But I still need to handle authentication (and authorization of course). After long hours on Google, I understood that it is possible to manage security with Jersey by many many ways. Here is a short list :
Here are the stepsto implements that :
In the web.xml :
The applicationContext-security.xml file :
A Java method exposed as REST service with Jersey
If I want to expose a service to anonymous users, I just have to change @PreAuthorize("hasRole('ROLE_DUMMY')") to @PreAuthorize("permitAll") or simply remove the annotation.
That's all! Of course, Spring saved my day once again...
The first thing to consider is : what kind of solution offers the best compromise between security and the effort I'll have to provide to maintain this solution?
After some discussions with developers and experts, the conclusion is : expose your services over https and use Basic authentication (Digest authentication and certificate based authentication are too complex for partners)
So, how to implement that? I delegate "https" to my Apache http servers. But I still need to handle authentication (and authorization of course). After long hours on Google, I understood that it is possible to manage security with Jersey by many many ways. Here is a short list :
- Delegate simple authentication and authorization to your container (Tomcat) or to your frontend (Apache)
- Delegate authentication to your container or frontend and let Jersey manage authorization with JSR-250 annotations such as @PermitAll, @DenyAll, @RolesAllowed written on methods. (For example : http://www.butonic.de/2010/06/18/a-simple-jax-rs-security-context-example-in-glassfish/)
- Be agnostic from any container and let your "Jersey-based" application take in charge authentication and authorization by implementing your own ContainerRequestFilter. You can read these two excellents posts : http://simplapi.wordpress.com/2013/01/24/jersey-jax-rs-implements-a-http-basic-auth-decoder/ and http://anismiles.wordpress.com/2012/03/02/securing-versioning-and-auditing-rest-jax-rs-jersey-apis
- Be agnostic from any container like the solution 3 but let Spring Security manage authentication and authorization.
Here are the stepsto implements that :
- Update your maven configuration (optional)
- Add a servlet filter to enable Spring Security (web.xml)
- Update my Spring configuration (dedicated applicationContext-security.xml file)
- Add annotation on methods I want to secure (Java code)
<!-- SECURITY (start) -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
<!-- SECURITY (end) -->
In the web.xml :
<!-- Filter to secure Jersey (JAX-RS) services -->
<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>
The applicationContext-security.xml file :
<?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">
<!-- To allow public access by default and to set authentication mode to basic login/password -->
<security:http>
<security:anonymous enabled="true" />
<security:http-basic />
</security:http>
<!-- To delegate authorization to method calls rather than to urls -->
<!-- (Thus, we don't need to set any url-interceptor in this conf) -->
<security:global-method-security pre-post-annotations="enabled" />
<!-- To create user/password with roles -->
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider>
<security:user-service>
<security:user authorities="ROLE_DUMMY"
name="user1" password="strongpassword1" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
</beans>
A Java method exposed as REST service with Jersey
@Component
@Path("/userinfo")
public class UserInfoService {
@Autowired
private UserInfoDao dao;
@GET @Path("{userid}")
@Produces({MediaType.TEXT_XML})
@PreAuthorize("hasRole('ROLE_DUMMY')")
public UserInfo findUserInfoByCode(@PathParam("userid") String userid) {
return dao.getUserInfo(userid);
}
...
If I want to expose a service to anonymous users, I just have to change @PreAuthorize("hasRole('ROLE_DUMMY')") to @PreAuthorize("permitAll") or simply remove the annotation.
That's all! Of course, Spring saved my day once again...