From 23dbe0dff56f8a5ca659d68b985ff8a361213907 Mon Sep 17 00:00:00 2001 From: DOHA Date: Sun, 7 Dec 2014 22:03:28 +0200 Subject: [PATCH] Modify fop --- .../baeldung/java/ApacheFOPHeroldTest.java | 2 +- .../src/test/resources/final_output.pdf | Bin 0 -> 76708 bytes apache-fop/src/test/resources/input.xml | 1713 ++++++++--------- 3 files changed, 758 insertions(+), 957 deletions(-) create mode 100644 apache-fop/src/test/resources/final_output.pdf diff --git a/apache-fop/src/test/java/org/baeldung/java/ApacheFOPHeroldTest.java b/apache-fop/src/test/java/org/baeldung/java/ApacheFOPHeroldTest.java index a2b2b17b88..24a657b73a 100644 --- a/apache-fop/src/test/java/org/baeldung/java/ApacheFOPHeroldTest.java +++ b/apache-fop/src/test/java/org/baeldung/java/ApacheFOPHeroldTest.java @@ -32,7 +32,7 @@ import org.w3c.dom.Document; public class ApacheFOPHeroldTest { private String[] inputUrls = {// @formatter:off - // "http://www.baeldung.com/2011/10/20/bootstraping-a-web-application-with-spring-3-1-and-java-based-configuration-part-1/", + "http://www.baeldung.com/2011/10/20/bootstraping-a-web-application-with-spring-3-1-and-java-based-configuration-part-1/", // "http://www.baeldung.com/2011/10/25/building-a-restful-web-service-with-spring-3-1-and-java-based-configuration-part-2/", // "http://www.baeldung.com/2011/10/31/securing-a-restful-web-service-with-spring-security-3-1-part-3/", "http://www.baeldung.com/spring-security-basic-authentication", diff --git a/apache-fop/src/test/resources/final_output.pdf b/apache-fop/src/test/resources/final_output.pdf new file mode 100644 index 0000000000000000000000000000000000000000..cf548e5cf8bdc0a7038cb120e1d0aa34423ff35e GIT binary patch literal 76708 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
- Spring Security for a REST API - - How to Secure a REST API with Spring Security 3 - the XML Configuration, the web.xml, the HTTP status codes for authentication, etc. - + Bootstrap a Web Application with Spring 4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- <emphasis role="bold">I usually post about Security on Google+ - you can follow me there: </emphasis> - -
- <anchor xml:id="Table_of_Contents"/><emphasis role="bold">Table of Contents</emphasis> - - - 1. Overview - - - 2. Introducing Spring Security in the web.xml - - - 3. The Security Configuration - - -     3.1. The basics - - -     3.2. The Entry Point - - -     3.3. The Login - - -     3.4. Authentication should return 200 instead of 301 - - -     3.5. Failed Authentication should return 401 instead of 302 - - -     3.6. The Authentication Manager and Provider - - -     3.7. Finally – Authentication against the running REST Service - - - 4. Maven and other trouble - - - 5. Conclusion - - -
+
+ <anchor xml:id="Table_of_Contents"/><emphasis role="bold">Table of Contents</emphasis> + + + 1. Overview + + + 2. The Maven pom.xml + + +     2.1. Justification of the cglib dependency + + +     2.2. The cglib dependency in Spring 3.2 and beyond + + + 3. The Java based web configuration + + +    3.1. The web.xml + + + 4. Conclusion + +
<anchor xml:id="dbdoclet.1_Overview"/><emphasis role="bold">1. Overview</emphasis> - This tutorial shows how to Secure a REST Service using Spring and Spring Security 3.1 with Java based configuration. The article will focus on how to set up the Security Configuration specifically for the REST API using a Login and Cookie approach. -
-
- <anchor xml:id="dbdoclet.2_Spring_Security_in_the_webxml"/><emphasis role="bold">2. Spring Security in the web.xml</emphasis> - The architecture of Spring Security is based entirely on Servlet Filters and, as such, comes before Spring MVC in regards to the processing of HTTP requests. Keeping this in mind, to begin with, a filter needs to be declared in the web.xml of the application: - <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 filter must necessarily be named ‘springSecurityFilterChain’  to match the default bean created by Spring Security in the container. - Note that the defined filter is not the actual class implementing the security logic but a DelegatingFilterProxy with the purpose of delegating the Filter’s methods to an internal bean. This is done so that the target bean can still benefit from the Spring context lifecycle and flexibility. - The URL pattern used to configure the Filter is /* even though the entire web service is mapped to /api/* so that the security configuration has the option to secure other possible mappings as well, if required. -
-
- <anchor xml:id="dbdoclet.3_The_Security_Configuration"/><emphasis role="bold">3. The Security Configuration</emphasis> - <?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"> - - <http entry-point-ref="restAuthenticationEntryPoint"> - <intercept-url pattern="/api/admin/**" access="ROLE_ADMIN"/> - - <form-login - authentication-success-handler-ref="mySuccessHandler" - authentication-failure-handler-ref="myFailureHandler" - /> - - <logout /> - </http> - - <beans:bean id="mySuccessHandler" - class="org.rest.security.MySavedRequestAwareAuthenticationSuccessHandler"/> - <beans:bean id="myFailureHandler" - class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"/> - - <authentication-manager alias="authenticationManager"> - <authentication-provider> - <user-service> - <user name="temporary" password="temporary" authorities="ROLE_ADMIN"/> - <user name="user" password="user" authorities="ROLE_USER"/> - </user-service> - </authentication-provider> - </authentication-manager> - -</beans:beans> - Most of the configuration is done using the security namespace – for this to be enabled, the schema locations must be defined and pointed to the correct 3.1 or 3.2 XSD versions. The namespace is designed so that it expresses the common uses of Spring Security while still providing hooks raw beans to accommodate more advanced scenarios. >> Signup for my upcoming Video Course on Building a REST API with Spring 4 -
- <emphasis role="bold">3.1. The <http> element</emphasis> - The <http> element is the main container element for HTTP security configuration. In the current implementation, it only secured a single mapping: /api/admin/**. Note that the mapping is relative to the root context of the web application, not to the rest Servlet; this is because the entire security configuration lives in the root Spring context and not in the child context of the Servlet. -
-
- <emphasis role="bold">3.2. The Entry Point</emphasis> - In a standard web application, the authentication process may be automatically triggered when the client tries to access a secured resource without being authenticated – this is usually done by redirecting to a login page so that the user can enter credentials. However, for a REST Web Service this behavior doesn’t make much sense – Authentication should only be done by a request to the correct URI and all other requests should simply fail with a 401 UNAUTHORIZED status code if the user is not authenticated. - Spring Security handles this automatic triggering of the authentication process with the concept of an Entry Point – this is a required part of the configuration, and can be injected via the entry-point-ref attribute of the <http> element. Keeping in mind that this functionality doesn’t make sense in the context of the REST Service, the new custom entry point is defined to simply return 401 whenever it is triggered: - @Component( "restAuthenticationEntryPoint" ) -public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint{ - - @Override - public void commence( HttpServletRequest request, HttpServletResponse response, - AuthenticationException authException ) throws IOException{ - response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized" ); - } -} - A quick sidenote here is that the 401 is sent without the WWW-Authenticate header, as required by the HTTP Spec – we can of course set the value manually if we need to. -
-
- <emphasis role="bold">3.3. The Login Form for REST</emphasis> - There are multiple ways to do Authentication for a REST API – one of the default Spring Security provides is Form Login – which uses an authentication processing filter – org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter. - The <form-login> element will create this filter and will also allow us to set our custom authentication success handler on it. This can also be done manually by using the <custom-filter> element to register a filter at the position FORM_LOGIN_FILTER – but the namespace support is flexible enough. - Note that for a standard web application, the auto-config attribute of the <http> element is shorthand syntax for some useful security configuration. While this may be appropriate for some very simple configurations, it doesn’t fit and should not be used for a REST API. -
-
- <emphasis role="bold">3.4. Authentication should return 200 instead of 301</emphasis> - By default, form login will answer a successful authentication request with a 301 MOVED PERMANENTLY status code; this makes sense in the context of an actual login form which needs to redirect after login. For a RESTful web service however, the desired response for a successful authentication should be 200 OK. - This is done by injecting a custom authentication success handler in the form login filter, to replace the default one. The new handler implements the exact same login as the default org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler with one notable difference – the redirect logic is removed: - public class MySavedRequestAwareAuthenticationSuccessHandler - extends SimpleUrlAuthenticationSuccessHandler { - - private RequestCache requestCache = new HttpSessionRequestCache(); - - @Override - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, - Authentication authentication) throws ServletException, IOException { - SavedRequest savedRequest = requestCache.getRequest(request, response); - - if (savedRequest == null) { - clearAuthenticationAttributes(request); - return; - } - String targetUrlParam = getTargetUrlParameter(); - if (isAlwaysUseDefaultTargetUrl() || - (targetUrlParam != null && - StringUtils.hasText(request.getParameter(targetUrlParam)))) { - requestCache.removeRequest(request, response); - clearAuthenticationAttributes(request); - return; - } - - clearAuthenticationAttributes(request); - } - - public void setRequestCache(RequestCache requestCache) { - this.requestCache = requestCache; - } -} -
-
- <emphasis role="bold">3.5. Failed Authentication should return 401 instead of 302</emphasis> - Similarly – we configured the authentication failure handler – same way we did with the success handler. - Luckily – in this case, we don’t need to actually define a new class for this handler – the standard implementation – SimpleUrlAuthenticationFailureHandler – does just fine. - The only difference is that – now that we’re defining this explicitly in our XML config – it’s not going to get a default defaultFailureUrl from Spring – and so it won’t redirect. -
-
- <emphasis role="bold">3.6. The Authentication Manager and Provider</emphasis> - The authentication process uses an in-memory provider to perform authentication – this is meant to simplify the configuration as a production implementation of these artifacts is outside the scope of this post. -
-
- <emphasis role="bold">3.7 Finally – Authentication against the running REST Service</emphasis> - Now let’s see how we can authenticate against the REST API – the URL for login is /j_spring_security_check – and a simple curl command performing login would be: - curl -i -X POST -d j_username=user -d j_password=userPass -http://localhost:8080/spring-security-rest/j_spring_security_check - This request will return the Cookie which will then be used by any subsequent request against the REST Service. - We can use curl to authentication and store the cookie it receives in a file: - curl -i -X POST -d j_username=user -d j_password=userPass -c /opt/cookies.txt -http://localhost:8080/spring-security-rest/j_spring_security_check - Then we can use the cookie from the file to do further authenticated requests: - curl -i --header "Accept:application/json" -X GET -b /opt/cookies.txt -http://localhost:8080/spring-security-rest/api/foos - This authenticated request will correctly result in a 200 OK: - HTTP/1.1 200 OK -Server: Apache-Coyote/1.1 -Content-Type: application/json;charset=UTF-8 -Transfer-Encoding: chunked -Date: Wed, 24 Jul 2013 20:31:13 GMT - -[{"id":0,"name":"JbidXc"}] -
+ The tutorial illustrates how to Bootstrap a Web Application with Spring and also discusses how to make the jump from XML to Java without having to completely migrate the entire XML configuration.
- <anchor xml:id="dbdoclet.4_Maven_and_other_trouble"/><emphasis role="bold">4. Maven and other trouble</emphasis> - The Spring core dependencies necessary for a web application and for the REST Service have been discussed in detail. For security, we’ll need to add: spring-security-web and spring-security-config – all of these have also been covered in the Maven for Spring Security tutorial. - It’s worth paying close attention to the way Maven will resolve the older Spring dependencies – the resolution strategy will start causing problems once the security artifacts are added to the pom. To address this problem, some of the core dependencies will need to be overridden in order to keep them at the right version. + <anchor xml:id="dbdoclet.2_The_Maven_pomxml"/><emphasis role="bold">2. The Maven pom.xml</emphasis> + <project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation=" + http://maven.apache.org/POM/4.0.0 + http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org</groupId> + <artifactId>rest</artifactId> + <version>0.0.1-SNAPSHOT</version> + <packaging>war</packaging> + + <dependencies> + + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-webmvc</artifactId> + <version>${spring.version}</version> + <exclusions> + <exclusion> + <artifactId>commons-logging</artifactId> + <groupId>commons-logging</groupId> + </exclusion> + </exclusions> + </dependency> + + </dependencies> + + <build> + <finalName>rest</finalName> + + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.1</version> + <configuration> + <source>1.6</source> + <target>1.6</target> + <encoding>UTF-8</encoding> + </configuration> + </plugin> + </plugins> + </build> + + <properties> + <spring.version>4.0.5.RELEASE</spring.version> + </properties> + +</project> +
+ <emphasis role="bold">2.1. The cglib dependency before Spring 3.2</emphasis> + You may wonder why cglib is a dependency – it turns out there is a valid reason to include it – the entire configuration cannot function without it. If removed, Spring will throw: + Caused by: java.lang.IllegalStateException: CGLIB is required to process @Configuration classes. Either add CGLIB to the classpath or remove the following @Configuration bean definitions + The reason this happens is explained by the way Spring deals with @Configuration classes. These classes are effectively beans, and because of this they need to be aware of the Context, and respect scope and other bean semantics. This is achieved by dynamically creating a cglib proxy with this awareness for each @Configuration class, hence the cglib dependency. + Also, because of this, there are a few restrictions for Configuration annotated classes: + + + Configuration classes should not be final + + + They should have a constructor with no arguments + + +
+
+ <emphasis role="bold">2.2. The cglib dependency in Spring 3.2 and beyond</emphasis> + Starting with Spring 3.2, it is no longer necessary to add cglib as an explicit dependency. This is because Spring is in now inlining cglib – which will ensure that all class based proxying functionality will work out of the box with Spring 3.2. + The new cglib code is placed under the Spring package: org.springframework.cglib (replacing the original net.sf.cglib). The reason for the package change is to avoid conflicts with any cglib versions already existing on the classpath. + Also, the new cglib 3.0 is now used, upgraded from the older 2.2 dependency (see this JIRA issue for more details). + Finally, now that Spring 4.0 is out in the wild, changes like this one (removing the cglib dependency) are to be expected with Java 8 just around the corner – you can watch this Spring Jira to keep track of the Spring support, and the Java 8 Resources page to keep tabs on the that. +
+
+
+ <anchor xml:id="dbdoclet.3_The_Java_based_Web_Configuration"/><emphasis role="bold">3. The Java based Web Configuration</emphasis> + @Configuration +@ImportResource( { "classpath*:/rest_config.xml" } ) +@ComponentScan( basePackages = "org.rest" ) +@PropertySource({ "classpath:rest.properties", "classpath:web.properties" }) +public class AppConfig{ + + @Bean +   public static PropertySourcesPlaceholderConfigurer properties() { +   return new PropertySourcesPlaceholderConfigurer(); +   } +} + First, the @Configuration annotation – this is the main artifact used by the Java based Spring configuration; it is itself meta-annotated with @Component, which makes the annotated classes standard beans and as such, also candidates for component scanning. The main purpose of @Configuration classes is to be sources of bean definitions for the Spring IoC Container. For a more detailed description, see the official docs. + Then, @ImportResource is used to import the existing XML based Spring configuration. This may be configuration which is still being migrated from XML to Java, or simply legacy configuration that you wish to keep. Either way, importing it into the Container is essential for a successful migration, allowing small steps without to much risk. The equivalent XML annotation that is replaced is: + <import resource=”classpath*:/rest_config.xml” /> + Moving on to @ComponentScan – this configures the component scanning directive, effectively replacing the XML: + <context:component-scan base-package="org.rest" /> + As of Spring 3.1, the @Configuration are excluded from classpath scanning by default – see this JIRA issue. Before Spring 3.1 though, these classes should have been excluded explicitly: + excludeFilters = { @ComponentScan.Filter( Configuration.class ) } + The @Configuration classes should not be autodiscovered because they are already specified and used by the Container – allowing them to be rediscovered and introduced into the Spring context will result in the following error: + Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name ‘webConfig’ for bean class [org.rest.spring.AppConfig] conflicts with existing, non-compatible bean definition of same name and class [org.rest.spring.AppConfig] + And finally, using the @Bean annotation to configure the properties support – PropertySourcesPlaceholderConfigurer is initialized in a @Bean annotated method, indicating it will produce a Spring bean managed by the Container. This new configuration has replaced the following XML: + <context:property-placeholder +location="classpath:persistence.properties, classpath:web.properties" +ignore-unresolvable="true"/> + For a more in depth discussion on why it was necessary to manually register the PropertySourcesPlaceholderConfigurer bean, see the Properties with Spring Tutorial. +
+ <emphasis role="bold">3.1. The web.xml</emphasis> + <?xml version="1.0" encoding="UTF-8"?> +<web-app xmlns=" + http://java.sun.com/xml/ns/javaee" +     xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" +     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" +    id="rest" version="3.0"> + + <context-param> + <param-name>contextClass</param-name> + <param-value> + org.springframework.web.context.support.AnnotationConfigWebApplicationContext + </param-value> + </context-param> + <context-param> + <param-name>contextConfigLocation</param-name> + <param-value>org.rest.spring.root</param-value> + </context-param> + <listener> + <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> + </listener> + + <servlet> + <servlet-name>rest</servlet-name> + <servlet-class> + org.springframework.web.servlet.DispatcherServlet + </servlet-class> + <init-param> + <param-name>contextClass</param-name> + <param-value> + org.springframework.web.context.support.AnnotationConfigWebApplicationContext + </param-value> + </init-param> + <init-param> + <param-name>contextConfigLocation</param-name> + <param-value>org.rest.spring.rest</param-value> + </init-param> + <load-on-startup>1</load-on-startup> + </servlet> + <servlet-mapping> + <servlet-name>rest</servlet-name> + <url-pattern>/api/*</url-pattern> + </servlet-mapping> + + <welcome-file-list> + <welcome-file /> + </welcome-file-list> + +</web-app> + First, the root context is defined and configured to use AnnotationConfigWebApplicationContext instead of the default XmlWebApplicationContext. The newer AnnotationConfigWebApplicationContext accepts @Configuration annotated classes as input for the Container configuration and is needed in order to set up the Java based context. Unlike XmlWebApplicationContext, it assumes no default configuration class locations, so the “contextConfigLocation”init-param for the Servlet must be set. This will point to the java package where the @Configuration classes are located; the fully qualified name(s) of the classes are also supported. + Next, the DispatcherServlet is configured to use the same kind of context, with the only difference that it’s loading configuration classes out of a different package. + Other than this, the web.xml doesn’t really change from a XML to a Java based configuration. +
- <anchor xml:id="dbdoclet.5_Conclusion"/><emphasis role="bold">5. Conclusion</emphasis> - This post covered the basic security configuration and implementation for a RESTful Service using Spring Security 3.1, discussing the web.xml, the security configuration, the HTTP status codes for the authentication process and the Maven resolution of the security artifacts. - The implementation of this Spring Security REST Tutorial can be downloaded as a working sample project.This is an Eclipse based project, so it should be easy to import and run as it is. + <anchor xml:id="dbdoclet.4_Conclusion"/><emphasis role="bold">4. Conclusion</emphasis> + The presented approach allows for a smooth migration of the Spring configuration from XML to Java, mixing the old and the new. This is important for older projects, which may have a lot of XML based configuration that cannot be migrated all at once. + This way, in a migration, the XML beans can be ported in small increments. + In the next article on REST with Spring, I cover setting up MVC in the project, configuration of the HTTP status codes, payload marshalling and content negotiation. + The implementation of this Bootstrap a Spring Web App Tutorial can be downloaded as a working sample project. + This is an Eclipse based project, so it should be easy to import and run as it is. -
- <emphasis role="bold">I usually post about Security on Google+ - you can follow me there: </emphasis> - - - - - - - - - REST, security, Spring - - - - - - http://www.toptreadmillsforhome.com/ top 10 treadmills for home Great information, thanks for the share! - - - - Ben The issue for me is having users inside my xml. How would you redirect the user auth to an external resource. Would you overwrite authenticationManager? - - - - Eugen As it is mentioned in the article, the in memory authentication provider is only used because using a real provider is outside the scope of this post. Thanks for the feedback. - - - - Sigmund Lundgren Hmm successful auth still redirects for my, done in the spring base class. Had to comment this line in the successhandler: - //super.onAuthenticationSuccess(request, response, authentication); - - - René Fleischhauer Hi Sigmund, all, - I have the same problem… - super.onAuthenticationSuccess(request, response, authentication) calls SimpleUrlAuthenticationSuccessHandler#handle which contains the following line - redirectStrategy.sendRedirect(request, response, targetUrl); - Seems to be pretty non-sense to call the super method and therefore, I also removed it. Additionally I added a response.setStatus(200) for safety purposes… - Best, - René - - - - - - - - fadi Hi, - Am having a problem in understanding one thing, how the actual login will be done. I added the configuration and when trying to access a rest method/url I get 401 error, but am unable to figure out how and where to login. can help me pleas? - - - http://www.baeldung.com/ Eugen Paraschiv The article is now updated with the exact process of how to perform the login and how to use the cookie in further requests. - - - - - - - - Naama Great article! - - - - http://profile.yahoo.com/DULVM64SMPX6Q74MRXYD4TFHPA Marc de Verdelhan Your article was just what I needed. But I tested your solution and it always returned a “401 Unauthorized”. - Finally I used the following config: - Now it’s simpler and it works fine. Could you explain why? - Thank you. - - - http://www.baeldung.com/ Eugen Paraschiv Hey – thanks for the feedback – I updated the article and explained how exactly to authentication and then how to use the resulting cookie in further requests against the REST API – hope it helps. - - - - - - - - René Fleischhauer Hi all, - thanks for the tutorial. Great stuff! I have a minor question: - I added within the element in order to customize the login url. After starting the application again the following exception occurs: - org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Filter beans ” and ” have the same ‘order’ value. When using custom filters, please make sure the positions do not conflict with default filters. Alternatively you can disable the default filters by removing the corresponding child elements from and avoiding the use of . - The problem is clear, however I’m not sure how I can avoid the use of j_spring_security_check. I’d highly appreciate any hint. - Best, - René - - - http://www.baeldung.com/ Eugen Paraschiv Hi – yes, using the element is a good alternative as it does keep the configuration simple – please check out the updated configuration section and the new github project for a working implementation. Thanks. - - - - - - - - glz Great article, however every time I get 401 Unauthorized, regardless sending or not valid username and password (temporary/temporary) in username or j_username and password or j_password. Tried to figure it out looking at your git project, but there seems to be a lot of other stuff, and the only authentication used in this project is Digest authentication. Did I miss something important here? - - - http://www.baeldung.com/ Eugen Paraschiv Hi – I updated the article to better explain how to perform login and how to interact with the service; I also added a specific github project to only cover this article – should be simpler to understand. Thanks. - - - - - - - - bhecht This was very helpful. - I as well got an 401 Unauthorized. - After dubugging i found out the problem is at UsernamePasswordAuthenticationFilter.requiresAuthentication() which returns true only if the URI ends with /j_spring_security_check. - I had to override this class and return true in the requiresAuthentication(), so it will work. - - - http://www.baeldung.com/ Eugen Paraschiv Hi – the article is now updated to better explain how to interact with the REST Service – in short, yes, the first request is to /j_spring_security_check – this will return the Cookie which will be used in any further requests against the service. - - - - - - - - Stephane Hi, - I already have in place a working form based authentication for non-REST requests with a custom provider against a legacy database table of existing administrator credentials. - Now, I’m trying to add an authentication, for the REST requests this time. - I wish to use the same authentication provider if possible. - My existing setup is: - I added another http element: - before the previous one but it still gives me a: A universal match pattern (‘/**’) is defined before other patterns in the filter chain… - I wonder what to do at this point. - You’d have some tips on how to have two authentication setups, one for browser web page authentication and one for REST based ? - Thanks for the cool article ! - - - http://www.baeldung.com/ Eugen Paraschiv I suggest using two different patterns for the 2 http elements – map the rest one on /rest or something similar and the standard one to /mvc or similar. This should keep things nice and separate. - - - - - - - - Stephane Hello, - I was trying to use a as you suggested. But it kept doing a redirect if no user credentials were given. This issue is explained and solved (thank you Rob Winch at http://forum.springsource.org/showthread.php?139586-Two-lt-http-gt-container-elements-with-one-for-the-browser-and-one-for-REST - - - - Franklin Antony “Authentication should return 200 instead of 301″ portion doesn’t work as expected. In “MySavedRequestAwareAuthenticationSuccessHandler” the super.onAuthenticationSuccess(request, response, authentication) actually triggers a redirectStrategy.sendRedirect(request, response, targetUrl) from the parent AbstractAuthenticationTargetUrlRequestHandler. Just commenting out works, but am not sure if that is expected. Any suggestions ? - - - http://www.baeldung.com/ Eugen Paraschiv Actually, the sending of the redirect should already be commented - out: MySavedRequestAwareAuthenticationSuccessHandler. - Please let me know if it still doesn’t work. - Thanks. Eugen. - - - Franklin Antony The sending of redirect is commented out as required but the problem lies in the parent class “AbstractAuthenticationTargetUrlRequestHandler” which “SimpleUrlAuthenticationSuccessHandler” extends from. When the “super.onAuthenticationSuccess(request, response, authentication) ” is called from “MySavedRequestAwareAuthenticationSuccessHandler” it triggers “redirectStrategy.sendRedirect(request, response, targetUrl);” in the “AbstractAuthenticationTargetUrlRequestHandler”. - I have seen in this article “http://www.petrikainulainen.net/programming/spring-framework/integration-testing-of-spring-mvc-applications-security” that the author is not calling the “super.onAuthenticationSuccess(request, response, authentication) ” and is just calling “response.setStatus(HttpServletResponse.SC_OK);” - Regards, - Franklin - - - http://www.baeldung.com/ Eugen Paraschiv Nice spot on the redirect still possibly occurring in this example – I have updated the example by preserving the exact functionality from the parent classes and just removing the redirect. - Thanks. Eugen. - - - Franklin Antony Cool. Looks fine now. Any idea why “clearAuthenticationAttributes(request)” is being reported as a compilation error “The method clearAuthenticationAttributes(HttpServletRequest) is undefined for the type” ? - Its supposed to be a protected method in - http://static.springsource.org/spring-security/site/docs/3.0.x/apidocs/org/springframework/security/web/authentication/SimpleUrlAuthenticationSuccessHandler.html#clearAuthenticationAttributes(javax.servlet.http.HttpServletRequest) - I am using Spring Security 3.0.2 but somehow its not there in my parent class . Aaarg!! - - - - http://www.baeldung.com/ Eugen Paraschiv I’m compiling against 3.1.4, so the implementation has probably been opened up a bit. - - - - Franklin Antony Ok. Thanks. Should be fine. - - - - - - - - - - - - - - - - - - - - kiran This is very helpful.But when ever login fails with bad credentials it’s returing 302 . - And for logout too it’s returning 302. - Shouln’t that be changed ? - - - - Sebastian I am using a combination of form-based login and basic authentication. So the REST endpoints can be used with the users session if he has one but falls back to basic auth if no session exists. So I like your approach with the “restAuthenticationEntryPoint”. The problem however is, that according to the HTTP documentation, a HTTP 401 response must include a WWW-Authenticate header, which you are not sending. On the other hand, this is good, because it means the browser will not display a basic authentication login dialog as it would if the WWW-Authenticate header was present. While my solution now works I am thinking if there is a better solution that allows me to use basic authentication fallback but does not violate the HTTP specification. - Update: I have now found a better solution. Instead of calling response.setError(…) I set the Header and Status code individually: - response.setHeader(“WWW-Authenticate”, “FormBased”); - response.setStatus( HttpServletResponse.SC_UNAUTHORIZED ); - Setting WWW-Authenticate to “FormBased” still prevents the browser from showing the login screen. - - - Abhishek Amte Hello Sebastian, - How did you configure your basic authentication to use the credentials from the form-based? - - - Sebastian Hello Abhishek, - I did not. Sending WWW-Authenticate to “FormBased” is only a means to prevent the browser from showing the basic authentication login dialog, so I can implement it in HTML. You could also set it to “Foobar” or “Custom”, it just has to be different than “Basic”. - I now have the following in my spring security: - authenticationFailureHandler is just an instance of SimpleUrlAuthenticationFailureHandler (without any properties set). - This allows me to login in two ways: - 1. By HTTP Basic Auth, sending a HTTP header: - Authorization: Basic - 2. With a POST to /session: - method: ‘POST’, - url: ‘../rest/session’, - data: “j_username=” + username + “&j_password=” + password, - headers: {‘Content-Type': ‘application/x-www-form-urlencoded’ } - - - Abhishek Amte Thanks a lot Sebastian - - - - - - - - - - - - http://www.baeldung.com/ Eugen Paraschiv Yes, the WWW-Authenticate header is not included as the spec requires. This is because the current solution is a hybrid between a REST API secured with Basic/Digest authentication (for example) and a form-based authentication mechanism. - Now – setting your own custom value for WWW-Authenticate is defninitly an option but what may be better – and what I’ve been doing – is to separate the two responsibilities – the API and the UI. - What I mean is – the REST API is secured with Basic Auth (for this example – in practice I would suggest Digest) – and that’s it. No fallback and no complexity. Now – the UI is a form-based web application – again, no additional complexity. The bridget between them is a proxy for all of the requests from the UI to the API. - Hope that makes sense. - Cheers, - Eugen. - - - - - - - - Pingback: Spring MVC Token Based Authentication to a REST Service – Part 2 | notesofguclu() - - - - Himalay Whenever login fails with bad credentials it’s returing 302, shouldn’t it return 401? - - - http://www.baeldung.com/ Eugen Paraschiv You’re right – for correct semantics, it would have to return the 401 – I updated the article and the github project – thanks for the suggestion. - Cheers, - Eugen. - - - - - - - - Alessandro Scuderetti Very useful article. I’m also interesting in => How to use the “login rest” with restful @Controller that accept and response in json format? - This: http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=2002229 according to you, is ti good? - - - http://www.baeldung.com/ Eugen Paraschiv Hey Alessandro – a few notes on that particular article: - – first, it doesn’t really handle authentication for a RESTful API – but instead for a more standard webapp; a REST API will have additional architectural constraints which will impact what you’ll be able to do with Spring Security - – next – Spring Security already has all the elements necessary for authentication in place – so adding a custom controller to replace these elements is not really necessary; what’s more – using the framework will help you deal with all of the corner cases and potential vulnerabilities that you will need to keep in mind when rolling your own controller – so, without a solid reason to do so, I would stay away from doing that - – and finally, both the framework as well as AJAX are powerful/flexible enough to do this without a custom auth controller; what I mean is – from AJAX – you can definitely handle the default (non-json) response of Spring Security if you need to; also, from Spring Security, you can hook into the authentication process and only override the success handler if you really need to return json to the client - Hope that helps. - Cheers, - Eugen. - - - - - - - - Richard Have you tried to implement this in spring security 3.2 with the java config? I’ve put most of it together, but the form does not render on accessing /login as a GET request. I wonder if anyone has had the same experience? (Or have I missed the point and you can’t get to the login form with this configuration?) - - - http://www.baeldung.com/ Eugen Paraschiv Hey Richard – everything should work fine, but just to be on the safe side, I’m going to go through the implementation again and will get back to you. Cheers, - Eugen. - - - elysch Hi. - How did you register the restAuthenticationEntryPoint using only java config? - Thanks - - - http://www.baeldung.com/ Eugen Paraschiv I didn’t – there’s some XML configuration for security in the project. That being said, if you do want to only use Java config, that’s possible now with the new release. Cheers, - Eugen. - - - - - - - - - - - - http://www.baeldung.com/ Eugen Paraschiv Hey Richard – I checked and everything works well. The project is indeed using Spring 3.2, and some java config. However, note that the security configuration is still XML, so if that is what you meant by java config – that support is still very new and I have not integrated it into these tutorials yet. Hope this helps. Cheers, - Eugen. - - - - elysch Hi Richard. - I asked this to Eugen by mistake, but it was intended for you: - How did you register the restAuthenticationEntryPoint using only java config? - Thanks - - - Richard http.exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint()) - - - - - - - - http://www.baeldung.com/ Eugen Paraschiv I haven’t yet, no. I’ll use the java config variant at some point, but I didn’t have the chance yet. Cheers, - Eugen. - - - - - - - - ohadr dev what about logout? - - - http://www.baeldung.com/ Eugen Paraschiv Hey Ohadr – logout is indeed enabled in the Spring Security config – notice the – so you’ll be able to log out at /j_spring_security_logout. Hope this helps. Cheers, - Eugen. - - - OhadR thanks for your reply! the regular logout redirects to a default target URL. it needs a special treatment… - - - - ohadr dev i solved it. you can see here… http://www.codeproject.com/Tips/521847/Logout-Spring-s-LogoutFilter. thanks! - - - - - - - - - - - - Kaan Can someone give an example how to use restTemplate instead of ‘curl -i -X POST -d j_username=user -d j_password=userPass -c /opt/cookies.txt’ . I am able to do it without cookie file but other requests gives me 401 unauthorized response. Or does anyone know how i can handle the problem? Thanks - - - http://www.baeldung.com/ Eugen Paraschiv Hey Kann – sending the cookie with RestTemplate is nothing special – just a matter of adding the cookie header with the cookie value – that should allow you to correctly send the cookie in the request. Cheers, - Eugen. - - - - - - - - http://justincalleja.com/ Justin Hi Eugen, - This post is really helpful – thanks a bunch - I would love to hear your opinion regarding the following: I have a login page which I would like to use in a “normal” fashion – i.e. I want spring security to redirect sending back a 301 on success and have the browser take care of storing the session. - The idea is that the page which the browser is redirected to is an SPA in which I’ll be making use of Javascript to get the JSESSIONID out of the the cookie and basically replicate the same kind of request a browser would make but using Javascript to access the protected endpoints. - The thing that’s annoying me is that I only want this behaviour when hitting security/session (a re-map of j_spring_security_check) from a browser (say login.html). I would love to have the behaviour you illustrated in this post when I’m trying to get the JSESSIONID from a non-browser environment i.e. 200 or 401. - What do you think about this situation? Personally, I don’t think I’ll be going down the 2 servers route, one maintaing session state and proxying for the “real” REST server. - I was thinking maybe Spring Security can be configured to have 2 or something of the sort. Note that the endpoints they affect would be the same, it’s just that it would be awesome if I could have something like security/session for the browser and api/security/session for REST clients (with different behaviours). I’m not familiar enough with Spring Security to know if this is possible. Is it? Or perhaps you would tackle it differently? - Regards, - Justin - Edit: - Note, I am just exploring options. I am currently thinking I will ditch the whole browser shortcut and just store it in a cookie or local storage using Javascript. But I was just curious about the two endpoint for security approach. - - - http://www.baeldung.com/ Eugen Paraschiv Hey Justin, - Two notes about your usecase. First thing is that – since you’re basically replicating some of the browser behavior in js – why do you need the server response to be a 301? You could simply do a redirect, or hide the login popup regardless of the status code. - Second – you probably don’t need 2 services, but yes – you can have 2 authentication paths if you really need to – one for the standard login and the other that’s more focused on the API. Keep in mind that now (since 3.1) you can have multiple elements. If that’s not an option you can always do it manually (but your intuition is right – it would be quite low level and would require a solid understanding of the framework). Hope it helps. Cheers, - Eugen. - - - http://justincalleja.com/ Justin Cheers Eugen - True, I was leaning towards sticking to the more REST-like behaviour and doing the rest with Javascript. I was thinking that maybe it would be a shortcut to let the browser handle that part – was considering using a normal for the login. Also, the thought “I could have 2 endpoints for session management one of which is easier to work with in a browser – maybe that would be useful to other people building apps against the REST API. So that’s why I asked. - I will continue with the Javascript approach. Maybe later I’ll do some research on the 2 endpoint approach. - Thanks a lot! - Regards, - Justin - - - http://www.baeldung.com/ Eugen Paraschiv Sounds good Justin. Also – it’s probably the kind of thing that will help someone else, so you can always write about it as well. Cheers, - Eugen. - - - http://justincalleja.com/ Justin hehe yes I have been meaning to set up a blog for quite some time. It will happen eventually (even if just to keep track of things). - Thanks for the suggestion - Regards, - Justin - - - - http://justincalleja.com/ Justin btw, as an update – I ended up going for the page for login and page for SPA approach using a normal no JS for the login page. I came up against problems with server sending back HttpOnly with cookies so couldn’t access from JS. I figured it would be more work (and code) than I liked to change the behaviour on the server side (overriding Spring Security classes – maybe haven’t actually done it). - The 2 page approach is working for now and I can keep a simple Spring Security config. - Regards, - Justin - - - - http://www.baeldung.com/ Eugen Paraschiv Glad it worked out – I’m getting these kinds of questions a lot lately, so I’m thinking of covering the various options in a series. Cheers, - Eugen. - - - - http://justincalleja.com/ Justin +1 to that - Regards, - Justin - - - - - - - - - - - - - - - - - - - - fadi Thank you for the response, but am using FF plugin and I added the Authorization header to the url, the only place fired in my code is the EntryPoint and it never enters my AuthenticationProvider. which makes wounder if am doing something wrong! - Thanks - - - - DropDeadFred If you are using the REST service through AJAX from a browser can you had the necessary Authentication headers? For example, if you are using form based authentication to your website and want to retrieve data from your REST service. I guess I must just be missing something here. - - - - Branislav Vidovic This is the most interesting part in my opinion…. I am interested which other additional component of spring security you have extended to read request headers and build a required authentication object. Besides i had a look into your git project but in there you did not do it totally like in this post.. - - - - http://www.baeldung.com/ Eugen Paraschiv You can take a look at: http://code.google.com/p/crypto-js/ - - - - http://www.baeldung.com/ Eugen Paraschiv Not sure how you can add the header to the URL – do you mean you added the header to the request? Also, to follow some working examples, you can always clone the project from github and run the tests. - - - - - - - - - - - - - © 2014 Baeldung. All Rights Reserved. - - - - - - - +
+
+
+ +
+ + Spring Security Basic Authentication + + + + + + Return to Content + + + + + + Contents + + + 1. Overview + + + 2. The Spring Security Configuration + + + 3. Consuming The Secured Application + + + 4. Further Configuration – The Entry Point + + + 5. The Maven Dependencies + + + 6. Conclusion + + + If you're new here, you may want to get my "REST APIs with Spring" eBook. Thanks for visiting! + + + +
+ <anchor xml:id="dbdoclet.1_Overview"/><emphasis role="bold">1. Overview</emphasis> + This tutorial shows how to set up, configure and customize Basic Authentication with Spring. We’re going to built on top of the simple Spring MVC example, and secure the UI of the MVC application with the Basic Auth mechanism provided by Spring Security. +
+ <anchor xml:id="dbdoclet.2_The_Spring_Security_Configuration"/><emphasis role="bold">2. The Spring Security Configuration</emphasis> + The Configuration for Spring Security is still 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" + xsi:schemaLocation=" + http://www.springframework.org/schema/security + http://www.springframework.org/schema/security/spring-security-3.1.xsd + http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> + + <http use-expressions="true"> + <intercept-url pattern="/**" access="isAuthenticated()" /> + + <http-basic /> + </http> + + <authentication-manager> + <authentication-provider> + <user-service> + <user name="user1" password="user1Pass" authorities="ROLE_USER" /> + </user-service> + </authentication-provider> + </authentication-manager> + +</beans:beans> + This is one of the last pieces of configuration in Spring that still need XML – Java Configuration for Spring Security is still a work in progress. + What is relevant here is the <http-basic> element inside the main <http> element of the configuration – this is enough to enable Basic Authentication for the entire application. The Authentication Manager is not the focus of this tutorial, so we are using an in memory manager with the user and password defined in plaintext. + The web.xml of the web application enabling Spring Security has already been discussed in the Spring Logout tutorial. +
+
+ <anchor xml:id="dbdoclet.3_Consuming_The_Secured_Application"/><emphasis role="bold">3. Consuming The Secured Application</emphasis> + The curl command is our go to tool for consuming the secured application. + First, let’s try to request the /homepage.html without providing any security credentials: + curl -i http://localhost:8080/spring-security-mvc-basic-auth/homepage.html + We get back the expected 401 Unauthorized and the Authentication Challenge: + HTTP/1.1 401 Unauthorized +Server: Apache-Coyote/1.1 +Set-Cookie: JSESSIONID=E5A8D3C16B65A0A007CFAACAEEE6916B; Path=/spring-security-mvc-basic-auth/; HttpOnly +WWW-Authenticate: Basic realm="Spring Security Application" +Content-Type: text/html;charset=utf-8 +Content-Length: 1061 +Date: Wed, 29 May 2013 15:14:08 GMT + The browser would interpret this challenge and prompt us for credentials with a simple dialog, but since we’re using curl, this isn’t the case. + Now, let’s request the same resource – the homepage – but provide the credentials to access it as well: + curl -i --user user1:user1Pass http://localhost:8080/spring-security-mvc-basic-auth/homepage.html + Now, the response from the server is 200 OK along with a Cookie: + HTTP/1.1 200 OK +Server: Apache-Coyote/1.1 +Set-Cookie: JSESSIONID=301225C7AE7C74B0892887389996785D; Path=/spring-security-mvc-basic-auth/; HttpOnly +Content-Type: text/html;charset=ISO-8859-1 +Content-Language: en-US +Content-Length: 90 +Date: Wed, 29 May 2013 15:19:38 GMT + From the browser, the application can be consumed normally – the only difference is that a login page is no longer a hard requirement since all browsers support Basic Authentication and use a dialog to prompt the user for credentials. +
+
+ <anchor xml:id="dbdoclet.4_Further_Configuration_8211_The_Entry_Point"/><emphasis role="bold">4. Further Configuration – </emphasis><emphasis role="bold">The Entry Point</emphasis> + By default, the BasicAuthenticationEntryPoint provisioned by Spring Security returns a full html page for a 401 Unauthorized response back to the client. This html representation of the error renders well in a browser, but it not well suited for other scenarios, such as a REST API where a json representation may be preferred. + The namespace is flexible enough for this new requirement as well – to address this – the entry point can be overridden: + <http-basic entry-point-ref="myBasicAuthenticationEntryPoint" /> + The new entry point is defined as a standard bean: + @Component +public class MyBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint { + + @Override + public void commence + (HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx) + throws IOException, ServletException { + response.addHeader("WWW-Authenticate", "Basic realm=\"" + getRealmName() + "\""); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + PrintWriter writer = response.getWriter(); + writer.println("HTTP Status 401 - " + authEx.getMessage()); + } + + @Override + public void afterPropertiesSet() throws Exception { + setRealmName("Baeldung"); + super.afterPropertiesSet(); + } +} + By writing directly to the HTTP Response we now have full control over the format of the response body. +
+
+ <anchor xml:id="dbdoclet.5_The_Maven_Dependencies"/><emphasis role="bold">5. The Maven Dependencies</emphasis> + The Maven dependencies for Spring Security have been discussed before in the Spring Security with Maven article – we will need both spring-security-web and spring-security-config available at runtime. +
+
+ <anchor xml:id="dbdoclet.6_Conclusion"/><emphasis role="bold">6. Conclusion</emphasis> + In this example we secured an MVC application with Spring Security and Basic Authentication. We discussed the XML configuration and we consumed the application with simple curl commands. Finally took control of the exact error message format – moving from the standard HTML error page to a custom text or json format. + The implementation of this Spring tutorial can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is. When the project runs locally, the sample html can be accessed at: + http://localhost:8080/spring-security-mvc-basic-auth/homepage.html + + + + + + + + + + security, Spring + + + + + + + + + + + © 2014 Baeldung. All Rights Reserved. + + + + + + + +
+
+
+ +
+ + REST Pagination in Spring + + + + + + Return to Content + + + + + + Contents + + + Table of Contents + + + 1. Overview + + + 2. Page as Resource vs Page as Representation + + + 3. The Controller + + + 4. Discoverability for REST pagination + + + 5. Test Driving Pagination + + + 6. Test Driving Pagination Discoverability + + + 7. Getting All Resources + + + 8. REST Paging with Range HTTP headers + + + 9. Conclusion + + + If you're new here, you may want to get my "REST APIs with Spring" eBook. Thanks for visiting! + + + +
+ <anchor xml:id="Table_of_Contents"/><emphasis role="bold">Table of Contents</emphasis> + + + 1. Overview + + + 2. Page as Resource vs Page as Representation + + + 3. The Controller + + + 4. Discoverability for REST pagination + + + 5. Test Driving Pagination + + + 6. Test Driving Pagination Discoverability + + + 7. Getting All Resources + + + 8. REST Paging with Range HTTP headers + + + 9. Conclusion + + +
+ <anchor xml:id="dbdoclet.1_Overview"/><emphasis role="bold">1. Overview</emphasis> + This tutorial will focus on the implementation of pagination in a REST API, using Spring MVC and Spring Data. +
+
+ <anchor xml:id="dbdoclet.2_Page_as_Resource_vs_Page_as_Representation"/><emphasis role="bold">2. Page as Resource vs Page as Representation</emphasis> + The first question when designing pagination in the context of a RESTful architecture is whether to consider the page an actual Resource or just a Representation of Resources. + Treating the page itself as a resource introduces a host of problems such as no longer being able to uniquely identify resources between calls. This, coupled with the fact that, in the persistence layer, the page is not proper entity but a holder that is constructed when necessary, makes the choice straightforward: the page is part of the representation. + The next question in the pagination design in the context of REST is where to include the paging information: + + + in the URI path: /foo/page/1 + + + the URI query: /foo?page=1 + + + Keeping in mind that a page is not a Resource, encoding the page information in the URI is no longer an option. + We are going to use the standard way of solving this problem by encoding the paging information in a URI query. +
+
+ <anchor xml:id="dbdoclet.3_The_Controller"/><emphasis role="bold">3. The Controller</emphasis> + Now, for the  implementation – the Spring MVC Controller for pagination is straightforward: + @RequestMapping( value = "admin/foo",params = { "page", "size" },method = GET ) +@ResponseBody +public List< Foo > findPaginated( + @RequestParam( "page" ) int page, @RequestParam( "size" ) int size, + UriComponentsBuilder uriBuilder, HttpServletResponse response ){ + + Page< Foo > resultPage = service.findPaginated( page, size ); + if( page > resultPage.getTotalPages() ){ + throw new ResourceNotFoundException(); + } + eventPublisher.publishEvent( new PaginatedResultsRetrievedEvent< Foo > + ( Foo.class, uriBuilder, response, page, resultPage.getTotalPages(), size ) ); + + return resultPage.getContent(); +} + The two query parameters are injected into the Controller method via @RequestParam. + We’re also injecting both the Http Response and the UriComponentsBuilder to help with Discoverability – which we are decoupling via a custom event. If that is not a goal of the API, you can simply remove the custom event and be done. + Finally – note that the focus of this article is only the REST and the web layer – to go deeper into the data access part of pagination you can check out this article about Pagination with Spring Data. +
+
+ <anchor xml:id="dbdoclet.4_Discoverability_for_REST_pagination"/><emphasis role="bold">4. Discoverability for REST pagination</emphasis> + Withing the scope of pagination, satisfying the HATEOAS constraint of REST means enabling the client of the API to discover the next and previous pages based on the current page in the navigation. For this purpose, we’re going to use the Link HTTP header, coupled with the official “next“, “prev“, “first” and “last” link relation types. + In REST, Discoverability is a cross cutting concern, applicable not only to specific operations but to types of operations. For example, each time a Resource is created, the URI of that Resource should be discoverable by the client. Since this requirement is relevant for the creation of ANY Resource, it should be dealt with separately and decoupled from the main Controller flow. + With Spring, this decoupling is done with Events, as was thoroughly discussed in the previous article focusing on Discoverability of a REST Service. In the case of pagination, the event – PaginatedResultsRetrievedEvent – is fired in the controller layer, and discoverability is implemented with a custom listener for this event: + void addLinkHeaderOnPagedResourceRetrieval( + UriComponentsBuilder uriBuilder, HttpServletResponse response, + Class clazz, int page, int totalPages, int size ){ + + String resourceName = clazz.getSimpleName().toString().toLowerCase(); + uriBuilder.path( "/admin/" + resourceName ); + + StringBuilder linkHeader = new StringBuilder(); + if( hasNextPage( page, totalPages ) ){ + String uriNextPage = constructNextPageUri( uriBuilder, page, size ); + linkHeader.append( createLinkHeader( uriNextPage, "next" ) ); + } + if( hasPreviousPage( page ) ){ + String uriPrevPage = constructPrevPageUri( uriBuilder, page, size ); + appendCommaIfNecessary( linkHeader ); + linkHeader.append( createLinkHeader( uriPrevPage, "prev" ) ); + } + if( hasFirstPage( page ) ){ + String uriFirstPage = constructFirstPageUri( uriBuilder, size ); + appendCommaIfNecessary( linkHeader ); + linkHeader.append( createLinkHeader( uriFirstPage, "first" ) ); + } + if( hasLastPage( page, totalPages ) ){ + String uriLastPage = constructLastPageUri( uriBuilder, totalPages, size ); + appendCommaIfNecessary( linkHeader ); + linkHeader.append( createLinkHeader( uriLastPage, "last" ) ); + } + response.addHeader( "Link", linkHeader.toString() ); +} + In short, the listener checks if the navigation allows for a next, previous, first and last pages and – if it does – adds the relevant URIs to the Link HTTP Header. + Note that, for brevity, I included only a partial code sample and the full code here. +
+
+ <anchor xml:id="dbdoclet.5_Test_Driving_Pagination"/><emphasis role="bold">5. Test Driving Pagination</emphasis> + Both the main logic of pagination and discoverability are covered by small, focused integration tests; as in the previous article, the rest-assured library is used to consume the REST service and to verify the results. + These are a few example of pagination integration tests; for a full test suite, check out the github project (link at the end of the article): + @Test +public void whenResourcesAreRetrievedPaged_then200IsReceived(){ + Response response = givenAuth().get( paths.getFooURL() + "?page=0&size=2" ); + + assertThat( response.getStatusCode(), is( 200 ) ); +} +@Test +public void whenPageOfResourcesAreRetrievedOutOfBounds_then404IsReceived(){ + String url = getFooURL() + "?page=" + randomNumeric(5) + "&size=2"; +   Response response = givenAuth().get(url); + + assertThat( response.getStatusCode(), is( 404 ) ); +} +@Test +public void givenResourcesExist_whenFirstPageIsRetrieved_thenPageContainsResources(){ + createResource(); + + Response response = givenAuth().get( paths.getFooURL() + "?page=0&size=2" ); + + assertFalse( response.body().as( List.class ).isEmpty() ); +} +
+
+ <anchor xml:id="dbdoclet.6_Test_Driving_Pagination_Discoverability"/><emphasis role="bold">6. Test Driving Pagination Discoverability</emphasis> + Testing that pagination is discoverable by a client is relatively straightforward, although there is a lot of ground to cover. The tests are focused on the position of the current page in navigation and the different URIs that should be discoverable from each position: + @Test +public void whenFirstPageOfResourcesAreRetrieved_thenSecondPageIsNext(){ + Response response = givenAuth().get( getFooURL()+"?page=0&size=2" ); + + String uriToNextPage = extractURIByRel( response.getHeader( "Link" ), "next" ); + assertEquals( getFooURL()+"?page=1&size=2", uriToNextPage ); +} +@Test +public void whenFirstPageOfResourcesAreRetrieved_thenNoPreviousPage(){ + Response response = givenAuth().get( getFooURL()+"?page=0&size=2" ); + + String uriToPrevPage = extractURIByRel( response.getHeader( "Link" ), "prev" ); + assertNull( uriToPrevPage ); +} +@Test +public void whenSecondPageOfResourcesAreRetrieved_thenFirstPageIsPrevious(){ + Response response = givenAuth().get( getFooURL()+"?page=1&size=2" ); + + String uriToPrevPage = extractURIByRel( response.getHeader( "Link" ), "prev" ); + assertEquals( getFooURL()+"?page=0&size=2", uriToPrevPage ); +} +@Test +public void whenLastPageOfResourcesIsRetrieved_thenNoNextPageIsDiscoverable(){ + Response first = givenAuth().get( getFooURL()+"?page=0&size=2" ); + String uriToLastPage = extractURIByRel( first.getHeader( "Link" ), "last" ); + + Response response = givenAuth().get( uriToLastPage ); + + String uriToNextPage = extractURIByRel( response.getHeader( "Link" ), "next" ); + assertNull( uriToNextPage ); +} + Note that the full low level code for extractURIByRel – responsible for extracting the URIs by rel relation is here. +
+
+ <anchor xml:id="dbdoclet.7_Getting_All_Resources"/><emphasis role="bold">7. Getting All Resources</emphasis> + On the same topic of pagination and discoverability, the choice must be made if a client is allowed to retrieve all the Resources in the system at once, or if the client MUST ask for them paginated. If the choice is made that the client cannot retrieve all Resources with a single request, and pagination is not optional but required, then several options are available for the response to a get all request. One option is to return a 404 (Not Found) and use the Link header to make the first page discoverable: +
+ Link=<http://localhost:8080/rest/api/admin/foo?page=0&size=2>; rel=”first“, <http://localhost:8080/rest/api/admin/foo?page=103&size=2>; rel=”last +
+ Another option is to return redirect – 303 (See Other) – to the first page. A more conservative route would be to simply return to the client a 405 (Method Not Allowed) for the GET request. +
+
+ <anchor xml:id="dbdoclet.8_REST_Paging_with_Range_HTTP_headers"/><emphasis role="bold">8. REST Paging with Range HTTP headers</emphasis> + A relatively different way of implementing pagination is to work with the HTTP Range headersRange, Content-Range, If-Range, Accept-Ranges – and HTTP status codes – 206 (Partial Content), 413 (Request Entity Too Large), 416 (Requested Range Not Satisfiable). One view on this approach is that the HTTP Range extensions were not intended for pagination, and that they should be managed by the Server, not by the Application. Implementing pagination based on the HTTP Range header extensions is nevertheless technically possible, although not nearly as common as the implementation discussed in this article. +
+
+ <anchor xml:id="dbdoclet.9_Conclusion"/><emphasis role="bold">9. Conclusion</emphasis> + This tutorial illustrated how to implement Pagination in a REST API using Spring, and discussed how to set up and test Discoverability. + If you want to go in depth on pagination in the persistence level, check out my JPA or Hibernate pagination tutorials. + The implementation of all these examples and code snippets can be found in my github project – this is an Eclipse based project, so it should be easy to import and run as it is. + + + + + + + + + + HATEOAS, java, REST, testing + + + + + + + + + + + © 2014 Baeldung. All Rights Reserved. + + + + + + + - - -