diff --git a/docs/src/docs/asciidoc/guides/rest.adoc b/docs/src/docs/asciidoc/guides/rest.adoc new file mode 100644 index 0000000..628ad6e --- /dev/null +++ b/docs/src/docs/asciidoc/guides/rest.adoc @@ -0,0 +1,208 @@ += Spring Session - REST +Rob Winch +:toc: + +This guide describes how to use Spring Session to transparently leverage Redis to back a web application's `HttpSession` when using REST endpoints. + +NOTE: The completed guide can be found in the <>. + +// tag::config[] + +== Updating Dependencies +Before you use Spring Session, you must ensure to update your dependencies. +If you are using Maven, ensure to add the following dependencies: + +.pom.xml +[source,xml] +[subs="verbatim,attributes"] +---- + + + + + org.springframework.session + spring-session-data-redis + {spring-session-version} + pom + + + org.springframework + spring-web + {spring-version} + + +---- + +ifeval::["{version-snapshot}" == "true"] +Since We are using a SNAPSHOT version, we need to ensure to add the Spring Snapshot Maven Repository. +Ensure you have the following in your pom.xml: + +.pom.xml +[source,xml] +---- + + + + + + spring-snapshot + https://repo.spring.io/libs-snapshot + + +---- +endif::[] + +ifeval::["{version-milestone}" == "true"] +Since We are using a Milestone version, we need to ensure to add the Spring Milestone Maven Repository. +Ensure you have the following in your pom.xml: + +.pom.xml +[source,xml] +---- + + spring-milestone + https://repo.spring.io/libs-milestone + +---- +endif::[] + +[[rest-spring-configuration]] +== Spring Configuration + +After adding the required dependencies, we can create our Spring configuration. +The Spring configuration is responsible for creating a Servlet Filter that replaces the `HttpSession` implementation with an implementation backed by Spring Session. +Add the following Spring Configuration: + +[source,java] +---- +include::{samples-dir}rest/src/main/java/sample/HttpSessionConfig.java[] +---- + +<1> We import an embedded Redis Server so that there is no need to start up Redis external of our application. +In a production application this is not necessary since we would point our connection to an external Redis instance. +<2> The `@EnableRedisHttpSession` annotation creates a Spring Bean with the name of `springSessionRepositoryFilter` that implements Filter. +The filter is what is in charge of replacing the `HttpSession` implementation to be backed by Spring Session. +In this instance Spring Session is backed by Redis. +<3> We create a `RedisConnectionFactory` that connects Spring Session to the Redis Server. +In our example, we are connecting to localhost on the default port (6379). +For more information on configuring Spring Data Redis, refer to the http://docs.spring.io/spring-data/data-redis/docs/current/reference/html/[reference documentation]. +<4> We customize Spring Session's HttpSession integration to use HTTP headers to convey the current session information instead of cookies. + +== Servlet Container Initialization + +Our <> created a Spring Bean named `springSessionRepositoryFilter` that implements `Filter`. +The `springSessionRepositoryFilter` bean is responsible for replacing the `HttpSession` with a custom implementation that is backed by Spring Session. + +In order for our `Filter` to do its magic, Spring needs to load our `Config` class. We provide the configuration in our Spring `MvcInitializer` as shown below: + +.src/main/java/sample/mvc/MvcInitializer.java +[source,java,indent=0] +---- +include::{samples-dir}rest/src/main/java/sample/mvc/MvcInitializer.java[tags=config] +---- + +Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our `springSessionRepositoryFilter` for every request. +Fortunately, Spring Session provides a utility class named `AbstractHttpSessionApplicationInitializer` that makes this extremely easy. Simply extend the class with the default constructor as shown below: + +.src/main/java/sample/Initializer.java +[source,java] +---- +include::{samples-dir}rest/src/main/java/sample/Initializer.java[] +---- + +NOTE: The name of our class (Initializer) does not matter. What is important is that we extend `AbstractHttpSessionApplicationInitializer`. + +// end::config[] + +[[rest-sample]] +== rest Sample Application + +=== Running the rest Sample Application + +You can run the sample by obtaining the {download-url}[source code] and invoking the following command: + + $ ./gradlew :samples:rest:tomcatRun + +You should now be able to access the application at http://localhost:8080/ + +=== Exploring the rest Sample Application + +Try using the application. Use your favorite REST client to request http://localhost:8080/ + + $ curl -v http://localhost:8080/ + +Observe that we are prompted for basic authentication. Provide the following information for the username and password: + +* **Username** *user* +* **Password** *password* + + $ curl -v http://localhost:8080/ -u user:password + +In the output you will notice the following: + +---- +HTTP/1.1 200 OK +... +x-auth-token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3 + +{"username":"user"} +---- + +Specifically, we notice the following things about our response: + +* The HTTP Status is now a 200 +* We have a header with the name of *x-auth-token* which contains a new session id +* The current username is displayed + +We can now use the *x-auth-token* to make another request without providing the username and password again. For example, the following outputs the the username just as before: + + $ curl -v http://localhost:8080/ -H "x-auth-token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3" + +The only difference is that the session id is not provided in the response headers because we are reusing an existing session. + +If we invalidate the session, then the x-auth-token is displayed in the response with an empty value. For example, the following will invalidate our session: + + $ curl -v http://localhost:8080/logout -H "x-auth-token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3" + +You will see in the output that the x-auth-token provides an empty String indicating that the previous session was invalidated. + +---- +HTTP/1.1 204 No Content +... +x-auth-token: +--- + +=== How does it work? + +Spring Security interacts with the standard `HttpSession` in `SecurityContextPersistenceFilter`. + +Instead of using Tomcat's `HttpSession`, Spring Security is now persisting the values in Redis. +Spring Session creates a header named x-auth-token in your browser that contains the id of your session. + +If you like, you can easily see that the session is created in Redis. First create a session using the following: + + $ curl -v http://localhost:8080/ -u user:password + +In the output you will notice the following: + +---- +HTTP/1.1 200 OK +... +x-auth-token: 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e + +{"username":"user"} +---- + +Now remove the session using redis-cli. For example, on a Linux based system you can type: + + $ redis-cli keys '*' | xargs redis-cli del + +TIP: The Redis documentation has instructions for http://redis.io/topics/quickstart[installing redis-cli]. + +Alternatively, you can also delete the explicit key. Enter the following into your terminal ensuring to replace `7e8383a4-082c-4ffe-a4bc-c40fd3363c5e` with the value of your SESSION cookie: + + $ redis-cli del spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e + +We can now use the *x-auth-token* to make another request with the session we deleted and observe we are prompted for a authentication. For example, the following returns an HTTP 401: + + $ curl -v http://localhost:8080/ -H "x-auth-token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3" \ No newline at end of file diff --git a/docs/src/docs/asciidoc/index.adoc b/docs/src/docs/asciidoc/index.adoc index 08a26ee..5bc479f 100644 --- a/docs/src/docs/asciidoc/index.adoc +++ b/docs/src/docs/asciidoc/index.adoc @@ -39,7 +39,7 @@ If you are looking to get started with Spring Session, the best place to start i | {gh-samples-url}rest[REST] | Demonstrates how to use Spring Session in a REST application to support authenticating with a header. -| TBD +| link:guides/security.html[REST Guide] | {gh-samples-url}users[Multiple Sessions] | Demonstrates how to use Spring Session to manage multiple simultaneous browser sessions (i.e Google Accounts). @@ -134,6 +134,12 @@ The <> provides a complete working example of include::guides/users.adoc[tags=how-does-it-work] +== RESTful APIs + +Spring Session has can work with RESTful APIs by allowing the session to be provided in a header. + +The <> provides a complete working example of using Spring Session with a RESTful API. + [[websocket]] = WebSocket Integration diff --git a/samples/rest/src/main/java/sample/mvc/MvcInializer.java b/samples/rest/src/main/java/sample/mvc/MvcInitializer.java similarity index 90% rename from samples/rest/src/main/java/sample/mvc/MvcInializer.java rename to samples/rest/src/main/java/sample/mvc/MvcInitializer.java index c859d68..a711da6 100644 --- a/samples/rest/src/main/java/sample/mvc/MvcInializer.java +++ b/samples/rest/src/main/java/sample/mvc/MvcInitializer.java @@ -22,11 +22,13 @@ import sample.SecurityConfig; /** * @author Rob Winch */ -public class MvcInializer extends AbstractAnnotationConfigDispatcherServletInitializer { +public class MvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { + // tag::config[] @Override protected Class[] getRootConfigClasses() { return new Class[] {SecurityConfig.class, HttpSessionConfig.class}; } + // end::config[] @Override protected Class[] getServletConfigClasses() {