diff --git a/docs/manual/src/docs/asciidoc/_includes/about/community.adoc b/docs/manual/src/docs/asciidoc/_includes/about/community.adoc new file mode 100644 index 00000000000..2273ac7d583 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/about/community.adoc @@ -0,0 +1,37 @@ +[[community]] += Spring Security Community + +Welcome to the Spring Security Community! +This section discusses how you can make the most of our vast community. + + +[[community-help]] +== Getting Help +If you need help with Spring Security, we are here to help. +The following are some of the best ways to get help: + +* Read through this documentation. +* Try one of our many <>. +* Ask a question on https://stackoverflow.com/questions/tagged/spring-security[https://stackoverflow.com] with the `spring-security` tag. +* Report bugs and enhancement requests at https://github.com/spring-projects/spring-security/issues + +[[community-becoming-involved]] +== Becoming Involved +We welcome your involvement in the Spring Security project. +There are many ways to contribute, including answering questions on StackOverflow, writing new code, improving existing code, assisting with documentation, developing samples or tutorials, reporting bugs, or simply making suggestions. +For more information, see our https://github.com/spring-projects/spring-security/blob/master/CONTRIBUTING.md[Contributing] documentation. + +[[community-source]] +== Source Code + +You can find Spring Security's source code on GitHub at https://github.com/spring-projects/spring-security/ + +[[community-license]] +== Apache 2 License + +Spring Security is Open Source software released under the http://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license]. + +== Social Media + +You can follow https://twitter.com/SpringSecurity[@SpringSecurity] and the https://twitter.com/SpringSecurity/lists/team[Spring Security team] on Twitter to stay up to date with the latest news. +You can also follow https://twitter.com/SpringCentral[@SpringCentral] to keep up to date with the entire Spring portfolio. diff --git a/docs/manual/src/docs/asciidoc/_includes/preface/getting-spring-security.adoc b/docs/manual/src/docs/asciidoc/_includes/about/getting-spring-security.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/preface/getting-spring-security.adoc rename to docs/manual/src/docs/asciidoc/_includes/about/getting-spring-security.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/about/index.adoc b/docs/manual/src/docs/asciidoc/_includes/about/index.adoc new file mode 100644 index 00000000000..3dcc7491dc6 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/about/index.adoc @@ -0,0 +1,17 @@ += About + +// FIXME: guidance on getting started +Spring Security provides first class authentication, authorization, and protection against common attacks for web applications deployed in <> and <> applications. +Continue reading to learn more about the <>, <>, <>, and our extensive collection of <>. + +include::prerequisites.adoc[leveloffset=+1] + +include::community.adoc[leveloffset=+1] + +include::whats-new.adoc[] + +include::getting-spring-security.adoc[leveloffset=+1] + +include::modules.adoc[leveloffset=+1] + +include::samples.adoc[leveloffset=+1] diff --git a/docs/manual/src/docs/asciidoc/_includes/about/modules.adoc b/docs/manual/src/docs/asciidoc/_includes/about/modules.adoc new file mode 100644 index 00000000000..0f8cee62325 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/about/modules.adoc @@ -0,0 +1,108 @@ +// FIXME: This might make sense in Getting Spring Security along with the artifact information + +[[modules]] += Project Modules +In Spring Security 3.0, the codebase was sub-divided into separate jars that more clearly separate different functionality areas and third-party dependencies. +If you use Maven to build your project, these are the modules you should add to your `pom.xml`. +Even if you do not use Maven, we recommend that you consult the `pom.xml` files to get an idea of third-party dependencies and versions. +Another good idea is to examine the libraries that are included in the sample applications. + + +[[spring-security-core]] +== Core -- `spring-security-core.jar` +This module contains core authentication and access-contol classes and interfaces, remoting support, and basic provisioning APIs. +It is required by any application that uses Spring Security. +It supports standalone applications, remote clients, method (service layer) security, and JDBC user provisioning. +It contains the following top-level packages: + +* `org.springframework.security.core` +* `org.springframework.security.access` +* `org.springframework.security.authentication` +* `org.springframework.security.provisioning` + +[[spring-security-remoting]] +== Remoting -- `spring-security-remoting.jar` +This module provides intergration with Spring Remoting. +You do not need this unless you are writing a remote client that uses Spring Remoting. +The main package is `org.springframework.security.remoting`. + + +[[spring-security-web]] +== Web -- `spring-security-web.jar` +This module contains filters and related web-security infrastructure code. +It contains anything with a servlet API dependency. +You need it if you require Spring Security web authentication services and URL-based access-control. +The main package is `org.springframework.security.web`. + + +[[spring-security-config]] +== Config -- `spring-security-config.jar` +This module contains the security namespace parsing code and Java configuration code. +You need it if you use the Spring Security XML namespace for configuration or Spring Security's Java Configuration support. +The main package is `org.springframework.security.config`. +None of the classes are intended for direct use in an application. + + +[[spring-security-ldap]] +== LDAP -- `spring-security-ldap.jar` +This module provides LDAP authentication and provisioning code. +It is required if you need to use LDAP authentication or manage LDAP user entries. +The top-level package is `org.springframework.security.ldap`. + + +[[spring-security-oauth2-core]] +== OAuth 2.0 Core -- `spring-security-oauth2-core.jar` +`spring-security-oauth2-core.jar` contains core classes and interfaces that provide support for the OAuth 2.0 Authorization Framework and for OpenID Connect Core 1.0. +It is required by applications that use OAuth 2.0 or OpenID Connect Core 1.0, such as client, resource server, and authorization server. +The top-level package is `org.springframework.security.oauth2.core`. + + +[[spring-security-oauth2-client]] +== OAuth 2.0 Client -- `spring-security-oauth2-client.jar` +`spring-security-oauth2-client.jar` contains Spring Security's client support for OAuth 2.0 Authorization Framework and OpenID Connect Core 1.0. +It is required by applications that use OAuth 2.0 Login or OAuth Client support. +The top-level package is `org.springframework.security.oauth2.client`. + + +[[spring-security-oauth2-jose]] +== OAuth 2.0 JOSE -- `spring-security-oauth2-jose.jar` +`spring-security-oauth2-jose.jar` contains Spring Security's support for the JOSE (Javascript Object Signing and Encryption) framework. +The JOSE framework is intended to provide a method to securely transfer claims between parties. +It is built from a collection of specifications: + +* JSON Web Token (JWT) +* JSON Web Signature (JWS) +* JSON Web Encryption (JWE) +* JSON Web Key (JWK) + +It contains the following top-level packages: + +* `org.springframework.security.oauth2.jwt` +* `org.springframework.security.oauth2.jose` + + +[[spring-security-acl]] +== ACL -- `spring-security-acl.jar` +This module contains a specialized domain object ACL implementation. +It is used to apply security to specific domain object instances within your application. +The top-level package is `org.springframework.security.acls`. + + +[[spring-security-cas]] +== CAS -- `spring-security-cas.jar` +This module contains Spring Security's CAS client integration. +You should use it if you want to use Spring Security web authentication with a CAS single sign-on server. +The top-level package is `org.springframework.security.cas`. + + +[[spring-security-openid]] +== OpenID -- `spring-security-openid.jar` +This module contains OpenID web authentication support. +It is used to authenticate users against an external OpenID server. +The top-level package is `org.springframework.security.openid`. +It requires OpenID4Java. + + +[[spring-security-test]] +== Test -- `spring-security-test.jar` +This module contains support for testing with Spring Security. diff --git a/docs/manual/src/docs/asciidoc/_includes/about/prerequisites.adoc b/docs/manual/src/docs/asciidoc/_includes/about/prerequisites.adoc new file mode 100644 index 00000000000..641271d465f --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/about/prerequisites.adoc @@ -0,0 +1,12 @@ +[[prerequisites]] += Prerequisites + +Spring Security requires a Java 8 or higher Runtime Environment. + +As Spring Security aims to operate in a self-contained manner, you do not need to place any special configuration files in your Java Runtime Environment. +In particular, you need not configure a special Java Authentication and Authorization Service (JAAS) policy file or place Spring Security into common classpath locations. + +Similarly, if you use an EJB Container or Servlet Container, you need not put any special configuration files anywhere nor include Spring Security in a server classloader. +All the required files are contained within your application. + +This design offers maximum deployment time flexibility, as you can copy your target artifact (be it a JAR, WAR, or EAR) from one system to another and it immediately works. diff --git a/docs/manual/src/docs/asciidoc/_includes/about/samples.adoc b/docs/manual/src/docs/asciidoc/_includes/about/samples.adoc new file mode 100644 index 00000000000..47f72cebc82 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/about/samples.adoc @@ -0,0 +1,3 @@ += Samples + +Spring Security includes many {gh-samples-url}[samples] applications. diff --git a/docs/manual/src/docs/asciidoc/_includes/preface/whats-new.adoc b/docs/manual/src/docs/asciidoc/_includes/about/whats-new.adoc similarity index 98% rename from docs/manual/src/docs/asciidoc/_includes/preface/whats-new.adoc rename to docs/manual/src/docs/asciidoc/_includes/about/whats-new.adoc index 0508f8adeea..e167a8e032f 100644 --- a/docs/manual/src/docs/asciidoc/_includes/preface/whats-new.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/about/whats-new.adoc @@ -4,6 +4,7 @@ Spring Security 5.1 provides a number of new features. Below are the highlights of the release. +[[new-servlet]] === Servlet * Automatic password storage upgrades through {security-api-url}org/springframework/security/core/userdetails/UserDetailsPasswordService.html[UserDetailsPasswordService] @@ -20,6 +21,7 @@ Below are the highlights of the release. * Added {security-api-url}core/src/main/java/org/springframework/security/core/Transient.java[@Transient] authentication tokens * A modern look-and-feel for the default log in page +[[new-webflux]] === WebFlux * Automatic password storage upgrades through {security-api-url}org/springframework/security/core/userdetails/ReactiveUserDetailsPasswordService.html[ReactiveUserDetailsPasswordService] @@ -38,6 +40,7 @@ Below are the highlights of the release. ** Support for resolving beans ** Support for resolving `errorOnInvalidType` +[[new-integrations]] === Integrations * <> works with `BadCredentialsException` diff --git a/docs/manual/src/docs/asciidoc/_includes/preface/community.adoc b/docs/manual/src/docs/asciidoc/_includes/preface/community.adoc deleted file mode 100644 index 89e1a3fc0e3..00000000000 --- a/docs/manual/src/docs/asciidoc/_includes/preface/community.adoc +++ /dev/null @@ -1,40 +0,0 @@ -[[community]] -== Spring Security Community - -Welcome to the Spring Security Community! -This section discusses how you to make the most of our vast community. - - -[[community-help]] -=== Getting Help -If you need help with Spring Security, we are here to help. -Below are some of the best steps to getting help: - -* Read our reference documentation -* Try one of our many sample applications -// FIXME: Add a link to the samples section -* Ask a question on https://stackoverflow.com with the tag `spring-security` -* Report a bugs and enhancement requests at https://github.com/spring-projects/spring-security/issues - -[[community-becoming-involved]] -=== Becoming Involved -We welcome your involvement in the Spring Security project. -There are many ways of contributing, including answering questions on StackOverflow, writing new code, improving existing code, assisting with documentation, developing samples or tutorials, reporting bugs, or simply making suggestions. - -[[community-source]] -=== Source Code - -Spring Security's source code can be found on GitHub at https://github.com/spring-projects/spring-security/ - -[[community-license]] -=== Apache 2 License - -Spring Security is Open Source software released under the http://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license]. - -=== Social Media - -You may follow https://twitter.com/SpringSecurity[@SpringSecurity] and https://twitter.com/SpringSecurity/lists/team[Spring Security team] on Twitter to stay up to date with the latest news. -You can also follow https://twitter.com/SpringCentral[@SpringCentral] to keep up to date with the entire Spring portfolio. - -// == Getting Started -// FIXME: Add links to samples diff --git a/docs/manual/src/docs/asciidoc/_includes/preface/index.adoc b/docs/manual/src/docs/asciidoc/_includes/preface/index.adoc deleted file mode 100644 index 834f535679c..00000000000 --- a/docs/manual/src/docs/asciidoc/_includes/preface/index.adoc +++ /dev/null @@ -1,13 +0,0 @@ -= Preface - -This section discusses the logistics of Spring Security. - -include::community.adoc[] - -include::whats-new.adoc[] - -include::getting-spring-security.adoc[leveloffset=+1] - -include::modules.adoc[leveloffset=+1] - -include::samples.adoc[] diff --git a/docs/manual/src/docs/asciidoc/_includes/preface/modules.adoc b/docs/manual/src/docs/asciidoc/_includes/preface/modules.adoc deleted file mode 100644 index 5f01c2b9b57..00000000000 --- a/docs/manual/src/docs/asciidoc/_includes/preface/modules.adoc +++ /dev/null @@ -1,114 +0,0 @@ - -[[modules]] -= Project Modules -In Spring Security 3.0, the codebase has been sub-divided into separate jars which more clearly separate different functionality areas and third-party dependencies. -If you are using Maven to build your project, then these are the modules you will add to your `pom.xml`. -Even if you're not using Maven, we'd recommend that you consult the `pom.xml` files to get an idea of third-party dependencies and versions. -Alternatively, a good idea is to examine the libraries that are included in the sample applications. - - -[[spring-security-core]] -== Core - spring-security-core.jar -Contains core authentication and access-contol classes and interfaces, remoting support and basic provisioning APIs. -Required by any application which uses Spring Security. -Supports standalone applications, remote clients, method (service layer) security and JDBC user provisioning. -Contains the top-level packages: - -* `org.springframework.security.core` - -* `org.springframework.security.access` - -* `org.springframework.security.authentication` - -* `org.springframework.security.provisioning` - - - - - -[[spring-security-remoting]] -== Remoting - spring-security-remoting.jar -Provides intergration with Spring Remoting. -You don't need this unless you are writing a remote client which uses Spring Remoting. -The main package is `org.springframework.security.remoting`. - - -[[spring-security-web]] -== Web - spring-security-web.jar -Contains filters and related web-security infrastructure code. -Anything with a servlet API dependency. -You'll need it if you require Spring Security web authentication services and URL-based access-control. -The main package is `org.springframework.security.web`. - - -[[spring-security-config]] -== Config - spring-security-config.jar -Contains the security namespace parsing code & Java configuration code. -You need it if you are using the Spring Security XML namespace for configuration or Spring Security's Java Configuration support. -The main package is `org.springframework.security.config`. -None of the classes are intended for direct use in an application. - - -[[spring-security-ldap]] -== LDAP - spring-security-ldap.jar -LDAP authentication and provisioning code. -Required if you need to use LDAP authentication or manage LDAP user entries. -The top-level package is `org.springframework.security.ldap`. - - -[[spring-security-oauth2-core]] -== OAuth 2.0 Core - spring-security-oauth2-core.jar -`spring-security-oauth2-core.jar` contains core classes and interfaces that provide support for the _OAuth 2.0 Authorization Framework_ and for _OpenID Connect Core 1.0_. -It is required by applications that use _OAuth 2.0_ or _OpenID Connect Core 1.0_, such as Client, Resource Server, and Authorization Server. -The top-level package is `org.springframework.security.oauth2.core`. - - -[[spring-security-oauth2-client]] -== OAuth 2.0 Client - spring-security-oauth2-client.jar -`spring-security-oauth2-client.jar` is Spring Security's client support for _OAuth 2.0 Authorization Framework_ and _OpenID Connect Core 1.0_. -Required by applications leveraging *OAuth 2.0 Login* and/or OAuth Client support. -The top-level package is `org.springframework.security.oauth2.client`. - - -[[spring-security-oauth2-jose]] -== OAuth 2.0 JOSE - spring-security-oauth2-jose.jar -`spring-security-oauth2-jose.jar` contains Spring Security's support for the _JOSE_ (Javascript Object Signing and Encryption) framework. -The _JOSE_ framework is intended to provide a method to securely transfer claims between parties. -It is built from a collection of specifications: - -* JSON Web Token (JWT) -* JSON Web Signature (JWS) -* JSON Web Encryption (JWE) -* JSON Web Key (JWK) - -It contains the top-level packages: - -* `org.springframework.security.oauth2.jwt` -* `org.springframework.security.oauth2.jose` - - -[[spring-security-acl]] -== ACL - spring-security-acl.jar -Specialized domain object ACL implementation. -Used to apply security to specific domain object instances within your application. -The top-level package is `org.springframework.security.acls`. - - -[[spring-security-cas]] -== CAS - spring-security-cas.jar -Spring Security's CAS client integration. -If you want to use Spring Security web authentication with a CAS single sign-on server. -The top-level package is `org.springframework.security.cas`. - - -[[spring-security-openid]] -== OpenID - spring-security-openid.jar -OpenID web authentication support. -Used to authenticate users against an external OpenID server. -`org.springframework.security.openid`. -Requires OpenID4Java. - - -[[spring-security-test]] -== Test - spring-security-test.jar -Support for testing with Spring Security. diff --git a/docs/manual/src/docs/asciidoc/_includes/preface/samples.adoc b/docs/manual/src/docs/asciidoc/_includes/preface/samples.adoc deleted file mode 100644 index 708a64a2bc5..00000000000 --- a/docs/manual/src/docs/asciidoc/_includes/preface/samples.adoc +++ /dev/null @@ -1,119 +0,0 @@ - - -[[sample-apps]] -== Sample Applications -There are several sample web applications that are available with the project. -To avoid an overly large download, only the "tutorial" and "contacts" samples are included in the distribution zip file. -The others can be built directly from the source which you can obtain as described in <>. -It's easy to build the project yourself and there's more information on the project web site at http://spring.io/spring-security/[http://spring.io/spring-security/]. -All paths referred to in this chapter are relative to the project source directory. - - -[[tutorial-sample]] -=== Tutorial Sample -The tutorial sample is a nice basic example to get you started. -It uses simple namespace configuration throughout. -The compiled application is included in the distribution zip file, ready to be deployed into your web container (`spring-security-samples-tutorial-3.1.x.war`). -The <> authentication mechanism is used in combination with the commonly-used <> authentication provider to automatically remember the login using cookies. - -We recommend you start with the tutorial sample, as the XML is minimal and easy to follow. -Most importantly, you can easily add this one XML file (and its corresponding `web.xml` entries) to your existing application. -Only when this basic integration is achieved do we suggest you attempt adding in method authorization or domain object security. - - -[[contacts-sample]] -=== Contacts -The Contacts Sample is an advanced example in that it illustrates the more powerful features of domain object access control lists (ACLs) in addition to basic application security. -The application provides an interface with which the users are able to administer a simple database of contacts (the domain objects). - -To deploy, simply copy the WAR file from Spring Security distribution into your container's `webapps` directory. -The war should be called `spring-security-samples-contacts-3.1.x.war` (the appended version number will vary depending on what release you are using). - -After starting your container, check the application can load. -Visit http://localhost:8080/contacts (or whichever URL is appropriate for your web container and the WAR you deployed). - -Next, click "Debug". -You will be prompted to authenticate, and a series of usernames and passwords are suggested on that page. -Simply authenticate with any of these and view the resulting page. -It should contain a success message similar to the following: ----- - -Security Debug Information - -Authentication object is of type: -org.springframework.security.authentication.UsernamePasswordAuthenticationToken - -Authentication object as a String: - -org.springframework.security.authentication.UsernamePasswordAuthenticationToken@1f127853: -Principal: org.springframework.security.core.userdetails.User@b07ed00: Username: rod; \ -Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; -credentialsNonExpired: true; AccountNonLocked: true; \ -Granted Authorities: ROLE_SUPERVISOR, ROLE_USER; \ -Password: [PROTECTED]; Authenticated: true; \ -Details: org.springframework.security.web.authentication.WebAuthenticationDetails@0: \ -RemoteIpAddress: 127.0.0.1; SessionId: 8fkp8t83ohar; \ -Granted Authorities: ROLE_SUPERVISOR, ROLE_USER - -Authentication object holds the following granted authorities: - -ROLE_SUPERVISOR (getAuthority(): ROLE_SUPERVISOR) -ROLE_USER (getAuthority(): ROLE_USER) - -Success! Your web filters appear to be properly configured! - ----- - - -Once you successfully receive the above message, return to the sample application's home page and click "Manage". -You can then try out the application. -Notice that only the contacts available to the currently logged on user are displayed, and only users with `ROLE_SUPERVISOR` are granted access to delete their contacts. -Behind the scenes, the `MethodSecurityInterceptor` is securing the business objects. - -The application allows you to modify the access control lists associated with different contacts. -Be sure to give this a try and understand how it works by reviewing the application context XML files. - - -[[ldap-sample]] -=== LDAP Sample -The LDAP sample application provides a basic configuration and sets up both a namespace configuration and an equivalent configuration using traditional beans, both in the same application context file. -This means there are actually two identical authentication providers configured in this application. - - -[[openid-sample]] -=== OpenID Sample -The OpenID sample demonstrates how to use the namespace to configure OpenID and how to set up http://openid.net/specs/openid-attribute-exchange-1_0.html[attribute exchange] configurations for Google, Yahoo and MyOpenID identity providers (you can experiment with adding others if you wish). -It uses the JQuery-based http://code.google.com/p/openid-selector/[openid-selector] project to provide a user-friendly login page which allows the user to easily select a provider, rather than typing in the full OpenID identifier. - -The application differs from normal authentication scenarios in that it allows any user to access the site (provided their OpenID authentication is successful). -The first time you login, you will get a "Welcome [your name]"" message. -If you logout and log back in (with the same OpenID identity) then this should change to "Welcome Back". -This is achieved by using a custom `UserDetailsService` which assigns a standard role to any user and stores the identities internally in a map. -Obviously a real application would use a database instead. -Have a look at the source form more information. -This class also takes into account the fact that different attributes may be returned from different providers and builds the name with which it addresses the user accordingly. - - -[[cas-sample]] -=== CAS Sample -The CAS sample requires that you run both a CAS server and CAS client. -It isn't included in the distribution so you should check out the project code as described in <>. -You'll find the relevant files under the `sample/cas` directory. -There's also a `Readme.txt` file in there which explains how to run both the server and the client directly from the source tree, complete with SSL support. - - -[[jaas-sample]] -=== JAAS Sample -The JAAS sample is very simple example of how to use a JAAS LoginModule with Spring Security. -The provided LoginModule will successfully authenticate a user if the username equals the password otherwise a LoginException is thrown. -The AuthorityGranter used in this example always grants the role ROLE_USER. -The sample application also demonstrates how to run as the JAAS Subject returned by the LoginModule by setting <> equal to "true". - - -[[preauth-sample]] -=== Pre-Authentication Sample -This sample application demonstrates how to wire up beans from the <> framework to make use of login information from a Java EE container. -The user name and roles are those setup by the container. - -The code is in `samples/preauth`. - diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/index.adoc b/docs/manual/src/docs/asciidoc/_includes/reactive/index.adoc index e29fbd9f19e..b610f1bc52d 100644 --- a/docs/manual/src/docs/asciidoc/_includes/reactive/index.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/reactive/index.adoc @@ -1,3 +1,4 @@ +[[reactive]] = Reactive Applications include::webflux.adoc[leveloffset=+1] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/index.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/index.adoc deleted file mode 100644 index ee2f52094dd..00000000000 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/index.adoc +++ /dev/null @@ -1,30 +0,0 @@ - -[[advanced-topics]] -= Additional Topics -In this part we cover features which require knowledge of previous chapters as well as some of the more advanced and less-commonly used features of the framework. - -include::acls.adoc[] - -include::preauth.adoc[] - -include::ldap.adoc[] - -include::oauth2.adoc[] - -include::webclient.adoc[] - -include::jsp-taglibs.adoc[] - -include::jaas.adoc[] - -include::cas.adoc[] - -include::x509.adoc[] - -include::runas.adoc[] - -include::crypto.adoc[] - -include::concurrency.adoc[] - -include::mvc.adoc[] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/web/core-filters.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/core-filters.adoc similarity index 97% rename from docs/manual/src/docs/asciidoc/_includes/servlet/web/core-filters.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/architecture/core-filters.adoc index d964a83725a..195dfc3901b 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/web/core-filters.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/core-filters.adoc @@ -1,11 +1,10 @@ - [[core-web-filters]] -== Core Security Filters += Core Security Filters There are some key filters which will always be used in a web application which uses Spring Security, so we'll look at these and their supporting classes and interfaces first. We won't cover every feature, so be sure to look at the Javadoc for them if you want to get the complete picture. [[filter-security-interceptor]] -=== FilterSecurityInterceptor +== FilterSecurityInterceptor We've already seen `FilterSecurityInterceptor` briefly when discussing <>, and we've already used it with the namespace where the `` elements are combined to configure it internally. Now we'll see how to explicitly configure it for use with a `FilterChainProxy`, along with its companion filter `ExceptionTranslationFilter`. A typical configuration example is shown below: @@ -71,7 +70,7 @@ This is reflected in our example above, where the more specific `/secure/super/` If they were reversed, the `/secure/` pattern would always match and the `/secure/super/` pattern would never be evaluated. [[exception-translation-filter]] -=== ExceptionTranslationFilter +== ExceptionTranslationFilter The `ExceptionTranslationFilter` sits above the `FilterSecurityInterceptor` in the security filter stack. It doesn't do any actual security enforcement itself, but handles exceptions thrown by the security interceptors and provides suitable and HTTP responses. @@ -97,7 +96,7 @@ class="org.springframework.security.web.authentication.LoginUrlAuthenticationEnt ---- [[auth-entry-point]] -==== AuthenticationEntryPoint +=== AuthenticationEntryPoint The `AuthenticationEntryPoint` will be called if the user requests a secure HTTP resource but they are not authenticated. An appropriate `AuthenticationException` or `AccessDeniedException` will be thrown by a security interceptor further down the call stack, triggering the `commence` method on the entry point. This does the job of presenting the appropriate response to the user so that authentication can begin. @@ -106,7 +105,7 @@ The actual implementation used will depend on the authentication mechanism you w [[access-denied-handler]] -==== AccessDeniedHandler +=== AccessDeniedHandler What happens if a user is already authenticated and they try to access a protected resource? In normal usage, this shouldn't happen because the application workflow should be restricted to operations to which a user has access. For example, an HTML link to an administration page might be hidden from users who do not have an admin role. You can't rely on hiding links for security though, as there's always a possibility that a user will just enter the URL directly in an attempt to bypass the restrictions. @@ -129,7 +128,7 @@ See <> for more details. [[request-caching]] -==== SavedRequest s and the RequestCache Interface +=== SavedRequest s and the RequestCache Interface Another responsibility of `ExceptionTranslationFilter` responsibilities is to save the current request before invoking the `AuthenticationEntryPoint`. This allows the request to be restored after the user has authenticated (see previous overview of <>). A typical example would be where the user logs in with a form, and is then redirected to the original URL by the default `SavedRequestAwareAuthenticationSuccessHandler` (see <>). @@ -143,7 +142,7 @@ The use of these interfaces makes it fully pluggable from Spring Security 3.0 on [[security-context-persistence-filter]] -=== SecurityContextPersistenceFilter +== SecurityContextPersistenceFilter We covered the purpose of this all-important filter in the <> chapter so you might want to re-read that section at this point. Let's first take a look at how you would configure it for use with a `FilterChainProxy`. A basic configuration only requires the bean itself @@ -161,7 +160,7 @@ This thread might then be used at a later stage, performing operations with the [[security-context-repository]] -==== SecurityContextRepository +=== SecurityContextRepository From Spring Security 3.0, the job of loading and storing the security context is now delegated to a separate strategy interface: [source,java] @@ -199,7 +198,7 @@ Alternatively you could provide an instance of `NullSecurityContextRepository`, [[form-login-filter]] -=== UsernamePasswordAuthenticationFilter +== UsernamePasswordAuthenticationFilter We've now seen the three main filters which are always present in a Spring Security web configuration. These are also the three which are automatically created by the namespace `` element and cannot be substituted with alternatives. The only thing that's missing now is an actual authentication mechanism, something that will allow a user to authenticate. @@ -225,7 +224,7 @@ The basic filter configuration looks something like this: ---- [[form-login-flow-handling]] -==== Application Flow on Authentication Success and Failure +=== Application Flow on Authentication Success and Failure The filter calls the configured `AuthenticationManager` to process each authentication request. The destination following a successful authentication or an authentication failure is controlled by the `AuthenticationSuccessHandler` and `AuthenticationFailureHandler` strategy interfaces, respectively. The filter has properties which allow you to set these so you can customize the behaviour completely footnote:[In versions prior to 3.0, the application flow at this point had evolved to a stage was controlled by a mix of properties on this class and strategy plugins. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/core-services.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/core-services.adoc index c050bcc2838..d28ead9c6f6 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/core-services.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/core-services.adoc @@ -1,12 +1,12 @@ [[core-services]] -== Core Services += Core Services Now that we have a high-level overview of the Spring Security architecture and its core classes, let's take a closer look at one or two of the core interfaces and their implementations, in particular the `AuthenticationManager`, `UserDetailsService` and the `AccessDecisionManager`. These crop up regularly throughout the remainder of this document so it's important you know how they are configured and how they operate. [[core-services-authentication-manager]] -=== The AuthenticationManager, ProviderManager and AuthenticationProvider +== The AuthenticationManager, ProviderManager and AuthenticationProvider The `AuthenticationManager` is just an interface, so the implementation can be anything we choose, but how does it work in practice? What if we need to check multiple authentication databases or a combination of different authentication services such as a database and an LDAP server? The default implementation in Spring Security is called `ProviderManager` and rather than handling the authentication request itself, it delegates to a list of configured `AuthenticationProvider` s, each of which is queried in turn to see if it can perform the authentication. @@ -18,7 +18,7 @@ The loaded `UserDetails` object - and particularly the `GrantedAuthority` s it c If you are using the namespace, an instance of `ProviderManager` is created and maintained internally, and you add providers to it by using the namespace authentication provider elements (see <>). In this case, you should not declare a `ProviderManager` bean in your application context. -However, if you are not using the namespace then you would declare it like so: +However, if you are not using the namesace then you would declare it like so: [source,xml] ---- @@ -49,7 +49,7 @@ You needn't be too concerned about this, because if you forget to register a sui [[core-services-erasing-credentials]] -==== Erasing Credentials on Successful Authentication +=== Erasing Credentials on Successful Authentication By default (from Spring Security 3.1 onwards) the `ProviderManager` will attempt to clear any sensitive credentials information from the `Authentication` object which is returned by a successful authentication request. This prevents information like passwords being retained longer than necessary. @@ -62,7 +62,7 @@ See the Javadoc for more information. [[core-services-dao-provider]] -==== DaoAuthenticationProvider +=== DaoAuthenticationProvider The simplest `AuthenticationProvider` implemented by Spring Security is `DaoAuthenticationProvider`, which is also one of the earliest supported by the framework. It leverages a `UserDetailsService` (as a DAO) in order to lookup the username, password and `GrantedAuthority` s. It authenticates the user simply by comparing the password submitted in a `UsernamePasswordAuthenticationToken` against the one loaded by the `UserDetailsService`. @@ -83,7 +83,7 @@ A `PasswordEncoder` provides encoding and decoding of passwords presented in the This will be discussed in more detail <>. -=== UserDetailsService Implementations +== UserDetailsService Implementations As mentioned in the earlier in this reference guide, most authentication providers take advantage of the `UserDetails` and `UserDetailsService` interfaces. Recall that the contract for `UserDetailsService` is a single method: @@ -101,7 +101,7 @@ Having said that, Spring Security does include a couple of useful base implement [[core-services-in-memory-service]] -==== In-Memory Authentication +=== In-Memory Authentication Is easy to use create a custom `UserDetailsService` implementation that extracts information from a persistence engine of choice, but many applications do not require such complexity. This is particularly true if you're building a prototype application or just starting integrating Spring Security, when you don't really want to spend time configuring databases or writing `UserDetailsService` implementations. For this sort of situation, a simple option is to use the `user-service` element from the security <>: @@ -141,7 +141,7 @@ bob=bobspassword,ROLE_USER,enabled ---- [[core-services-jdbc-user-service]] -==== JdbcDaoImpl +=== JdbcDaoImpl Spring Security also includes a `UserDetailsService` that can obtain authentication information from a JDBC data source. Internally Spring JDBC is used, so it avoids the complexity of a fully-featured object relational mapper (ORM) just to store user details. If your application does use an ORM tool, you might prefer to write a custom `UserDetailsService` to reuse the mapping files you've probably already created. @@ -165,14 +165,12 @@ Returning to `JdbcDaoImpl`, an example configuration is shown below: You can use different relational database management systems by modifying the `DriverManagerDataSource` shown above. You can also use a global data source obtained from JNDI, as with any other Spring configuration. -===== Authority Groups +==== Authority Groups By default, `JdbcDaoImpl` loads the authorities for a single user with the assumption that the authorities are mapped directly to users (see the <>). An alternative approach is to partition the authorities into groups and assign groups to the user. Some people prefer this approach as a means of administering user rights. See the `JdbcDaoImpl` Javadoc for more information on how to enable the use of group authorities. The group schema is also included in the appendix. -include::password-encoder.adoc[leveloffset=+2] -include::jackson.adoc[] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/index.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/index.adoc index 47590edfe79..8c428875844 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/index.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/index.adoc @@ -1,9 +1,4 @@ -[[overall-architecture]] -= Architecture and Implementation -Once you are familiar with setting up and running some namespace-configuration based applications, you may wish to develop more of an understanding of how the framework actually works behind the namespace facade. -Like most software, Spring Security has certain central interfaces, classes and conceptual abstractions that are commonly used throughout the framework. -In this part of the reference guide we will look at some of these and see how they work together to support authentication and access-control within Spring Security. +[[servlet-architecture]] += Servlet Architecture Overview -include::technical-overview.adoc[] - -include::core-services.adoc[] +include::overview.adoc[leveloffset=+1] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/overview.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/overview.adoc new file mode 100644 index 00000000000..c431b9aec34 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/overview.adoc @@ -0,0 +1,122 @@ += Servlet Architecture Overview + +Spring Security’s servlet support relies on the servlet `Filter` API. +This means that it can work with any application that runs in a servlet container. +It does not require that you use Spring in any other part of your application. + +// FIXME: This might be a primer to describe in summary some of the external items (servlet container, Spring Frameworks's ApplicationContext being initialized, etc) + +== Understanding the Servlet Filter API + +Spring Security's servlet container support (whether you run in Apache Tomcat or other servlet container) is built on top of the `Filter` API. +This is done because the `Filter` API is a standard that allows injecting logic into an application in any servlet container. + +Since Spring Security is built on top of `javax.servlet.Filter` objects. +It will greatly benefit you if you understand the concept of interceptors and how `Filter` objects work. + +The following example shows a simple `Filter` implementation: + +.SimpleFilter Example +==== +[source,java] +---- +public class SimpleFilter implements Filter { + + public void init(FilterConfig filterConfig) + throws ServletException { + // optional initialization + } + + // invoked every request the Filter is mapped for + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + System.out.println("Before"); // <1> + chain.doFilter(request, response); // <2> + System.out.println("After"); // <3> + } + + public void destroy() { + // optional cleanup + } +} +---- +<1> Happens before the application is invoked. +<2> Invokes the rest of the application. This could be additional `Filter` objects, a `Servlet`, or both. +<3> Happens after the application is invoked +==== + +IMPORTANT: If `chain.doFilter` is never invoked, neither is the rest of the application. + +A servlet `Filter` lets us apply logic around the rest of the application. +This means that we can perform logic before, conditionally invoke the rest of the application, and perform logic afterwards in any servlet container. + +== Registering a Servlet Filter + +In order for a servlet `Filter` to be invoked, it must be registered with the servlet container. + +The following example shows how to do so in XML: + +.web.xml +==== +[source,xml] +---- + + + simpleFilter + sample.SimpleFilter + + + + + simpleFilter + /* + +---- +<1> Define the `Filter` to be mapped +<2> Provide one or more mappings for the `Filter`. +In this case, `simpleFilter` is mapped by using the `/*` pattern, which signifies that it should be mapped for every request. +==== + +In Java configuration, you can use a `ServletContextListener`. +The following example shows how to do so: + +.RegisterServletContextListener.java +==== +[source,java] +---- +@WebListener +public class FilterStartupListener + implements ServletContextListener { + + @Override + public void contextInitialized(ServletContextEvent sce) { + ServletContext ctx = sce.getServletContext(); + + // <1> + FilterRegistration fltrReg = ctx.addFilter("simpleFilter", SimpleFilter.class); + + // <2> + EnumSet dispatchers = + EnumSet.of(DispatcherType.REQUEST); + // <3> + boolean isMatchAfter = true; + // <4> + fltrReg.addMappingsForUrlPatterns(dispatchers, isMatchAfter, "/*"); + } +} +---- +<1> Define the `Filter` to be mapped. +<2> In Java configuration, we must explicitly provide the <>. +<3> In Java configuration, we must explicitly indicate whether the `Filter` should be before or after already registered `Filter` objects. +<4> Provide one more mapping for the Filter. +In this case, `simpleFilter` is mapped by using the `/*` pattern, which signifies that it should be mapped for every request. +==== + +== Dispatch Types + +== Filter Ordering + +== Using `DelegatingFilterProxy` + +At the heart of integrating Spring with a servlet `Filter` is the `DelegatingFilterProxy`. +The `DelegatingFilterProxy` is registered directly with the servlet container, and it delegates all work to a Spring Bean that implements `Filter`. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/web/security-filter-chain.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/security-filter-chain.adoc similarity index 98% rename from docs/manual/src/docs/asciidoc/_includes/servlet/web/security-filter-chain.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/architecture/security-filter-chain.adoc index 4333b182634..36c324ffc17 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/web/security-filter-chain.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/security-filter-chain.adoc @@ -1,6 +1,6 @@ [[security-filter-chain]] -== The Security Filter Chain += The Security Filter Chain Spring Security's web infrastructure is based entirely on standard servlet filters. It doesn't use servlets or any other servlet-based frameworks (such as Spring MVC) internally, so it has no strong links to any particular web technology. It deals in `HttpServletRequest` s and `HttpServletResponse` s and doesn't care whether the requests come from a browser, a web service client, an `HttpInvoker` or an AJAX application. @@ -11,7 +11,7 @@ If you have been using <>, then the filters a [[delegating-filter-proxy]] -=== DelegatingFilterProxy +== DelegatingFilterProxy When using servlet filters, you obviously need to declare them in your `web.xml`, or they will be ignored by the servlet container. In Spring Security, the filter classes are also Spring beans defined in the application context and thus able to take advantage of Spring's rich dependency-injection facilities and lifecycle interfaces. Spring's `DelegatingFilterProxy` provides the link between `web.xml` and the application context. @@ -22,7 +22,7 @@ When using `DelegatingFilterProxy`, you will see something like this in the `web ---- myFilter -org.springframework.web.filter.DelegatingFilterProxy +rg.springframework.web.filter.DelegatingFilterProxy @@ -39,7 +39,7 @@ Read the Javadoc for `DelegatingFilterProxy` for more information [[filter-chain-proxy]] -=== FilterChainProxy +== FilterChainProxy Spring Security's web infrastructure should only be used by delegating to an instance of `FilterChainProxy`. The security filters should not be used by themselves. In theory you could declare each Spring Security filter bean that you require in your application context file and add a corresponding `DelegatingFilterProxy` entry to `web.xml` for each filter, making sure that they are ordered correctly, but this would be cumbersome and would clutter up the `web.xml` file quickly if you have a lot of filters. @@ -88,7 +88,7 @@ When we looked at how to set up web security using <>. -===== RunAsManager +==== RunAsManager Assuming `AccessDecisionManager` decides to allow the request, the `AbstractSecurityInterceptor` will normally just proceed with the request. Having said that, on rare occasions users may want to replace the `Authentication` inside the `SecurityContext` with a different `Authentication`, which is handled by the `AccessDecisionManager` calling a `RunAsManager`. This might be useful in reasonably unusual situations, such as if a services layer method needs to call a remote system and present a different identity. Because Spring Security automatically propagates security identity from one server to another (assuming you're using a properly-configured RMI or HttpInvoker remoting protocol client), this may be useful. -===== AfterInvocationManager +==== AfterInvocationManager Following the secure object invocation proceeding and then returning - which may mean a method invocation completing or a filter chain proceeding - the `AbstractSecurityInterceptor` gets one final chance to handle the invocation. At this stage the `AbstractSecurityInterceptor` is interested in possibly modifying the return object. We might want this to happen because an authorization decision couldn't be made "on the way in" to a secure object invocation. @@ -417,46 +407,9 @@ If an exception occurs, the additional checks will be skipped. .Security interceptors and the "secure object" model image::images/security-interception.png[Abstract Security Interceptor] -===== Extending the Secure Object Model +==== Extending the Secure Object Model Only developers contemplating an entirely new way of intercepting and authorizing requests would need to use secure objects directly. For example, it would be possible to build a new secure object to secure calls to a messaging system. Anything that requires security and also provides a way of intercepting a call (like the AOP around advice semantics) is capable of being made into a secure object. Having said that, most Spring applications will simply use the three currently supported secure object types (AOP Alliance `MethodInvocation`, AspectJ `JoinPoint` and web request `FilterInvocation`) with complete transparency. - -[[localization]] -=== Localization -Spring Security supports localization of exception messages that end users are likely to see. -If your application is designed for English-speaking users, you don't need to do anything as by default all Security messages are in English. -If you need to support other locales, everything you need to know is contained in this section. - -All exception messages can be localized, including messages related to authentication failures and access being denied (authorization failures). -Exceptions and logging messages that are focused on developers or system deployers (including incorrect attributes, interface contract violations, using incorrect constructors, startup time validation, debug-level logging) are not localized and instead are hard-coded in English within Spring Security's code. - -Shipping in the `spring-security-core-xx.jar` you will find an `org.springframework.security` package that in turn contains a `messages.properties` file, as well as localized versions for some common languages. -This should be referred to by your `ApplicationContext`, as Spring Security classes implement Spring's `MessageSourceAware` interface and expect the message resolver to be dependency injected at application context startup time. -Usually all you need to do is register a bean inside your application context to refer to the messages. -An example is shown below: - -[source,xml] ----- - - - ----- - -The `messages.properties` is named in accordance with standard resource bundles and represents the default language supported by Spring Security messages. -This default file is in English. - -If you wish to customize the `messages.properties` file, or support other languages, you should copy the file, rename it accordingly, and register it inside the above bean definition. -There are not a large number of message keys inside this file, so localization should not be considered a major initiative. -If you do perform localization of this file, please consider sharing your work with the community by logging a JIRA task and attaching your appropriately-named localized version of `messages.properties`. - -Spring Security relies on Spring's localization support in order to actually lookup the appropriate message. -In order for this to work, you have to make sure that the locale from the incoming request is stored in Spring's `org.springframework.context.i18n.LocaleContextHolder`. -Spring MVC's `DispatcherServlet` does this for your application automatically, but since Spring Security's filters are invoked before this, the `LocaleContextHolder` needs to be set up to contain the correct `Locale` before the filters are called. -You can either do this in a filter yourself (which must come before the Spring Security filters in `web.xml`) or you can use Spring's `RequestContextFilter`. -Please refer to the Spring Framework documentation for further details on using localization with Spring. - -The "contacts" sample application is set up to use localized messages. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/xml.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/xml.adoc new file mode 100644 index 00000000000..f9a9b89f512 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/xml.adoc @@ -0,0 +1,83 @@ += XML Architecture + +// FIXME this is likely heavy for hello-security, but just restructuring for now +Namespace configuration has been available since version 2.0 of the Spring Framework. +It lets you supplement the traditional Spring beans application context syntax with elements from additional XML schema. +You can find more information in the Spring Framework http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/[Reference Documentation]. +You can use a namespace element to allow a more concise way of configuring an individual bean or, more powerfully, to define an alternative configuration syntax that more closely matches the problem domain and hides the underlying complexity from the user. +A simple element may conceal the fact that multiple beans and processing steps are being added to the application context. +For example, adding the following element from the security namespace to an application context starts up an embedded LDAP server for testing use within the application: + +[source,xml] +---- + +---- + +This is much simpler than wiring up the equivalent Apache Directory Server beans. +The most common alternative configuration requirements are supported by attributes on the `ldap-server` element. +You are isolated from worrying about which beans you need to create and what the bean property names are. +(You can find out more about the use of the `ldap-server` element in the chapter on pass:specialcharacters,macros[<>].) +Use of a good XML editor while editing the application context file should provide information on the attributes and elements that are available. +We recommend trying out the http://spring.io/tools/sts[Spring Tool Suite], as it has special features for working with standard Spring namespaces. + +To start using the security namespace in your application context, you need to have the `spring-security-config` jar on your classpath. +Then all you need to do is add the schema declaration to your application context file. +The following example shows how to do so: + +==== +[source,xml] +---- + + ... + +---- +==== + +In many of the examples (and in the sample applications), we often use `security` as the default namespace rather than `beans`, which means we can omit the prefix on all the security namespace elements, making the content easier to read. +You may also want to do this if you have your application context divided up into separate files and have most of your security configuration in one of them. +Your security application context file would then start as follows: + +==== +[source,xml] +---- + + ... + +---- +==== + +We assume this syntax is being used from now on in this chapter. + + +== Design of the Namespace + +The namespace is designed to capture the most common uses of the framework and provide a simplified and concise syntax for enabling them within an application. +The design is based around the large-scale dependencies within the framework and can be divided up into the following areas: + +* Web/HTTP Security: The most complex part, it sets up the filters and related service beans used to apply the framework authentication mechanisms, to secure URLs, render login and error pages, and much more. + +* Business Object (Method) Security: Options for securing the service layer. + +* `AuthenticationManager`: Handles authentication requests from other parts of the framework. + +* `AccessDecisionManager`: Provides access decisions for web and method security. +A default one is registered, but you can also choose to use a custom one, which you can declare by using normal Spring bean syntax. + +* `AuthenticationProvider` instances: Mechanisms against which the authentication manager authenticates users. +The namespace provides supports for several standard options and a means of adding custom beans declared using a traditional syntax. + +* `UserDetailsService`: Closely related to authentication providers but often also required by other beans. + +We see how to configure these in the next several sections. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/web/anonymous.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/anonymous.adoc similarity index 98% rename from docs/manual/src/docs/asciidoc/_includes/servlet/web/anonymous.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/authentication/anonymous.adoc index 38684e478ae..8348d8cde9e 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/web/anonymous.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/anonymous.adoc @@ -1,10 +1,9 @@ - [[anonymous]] -== Anonymous Authentication += Anonymous Authentication [[anonymous-overview]] -=== Overview +== Overview It's generally considered good security practice to adopt a "deny-by-default" where you explicitly specify what is allowed and disallow everything else. Defining what is accessible to unauthenticated users is a similar situation, particularly for web applications. Many sites require that users must be authenticated for anything other than a few URLs (for example the home and login pages). @@ -22,7 +21,7 @@ Classes can be authored more robustly if they know the `SecurityContextHolder` a [[anonymous-config]] -=== Configuration +== Configuration Anonymous authentication support is provided automatically when using the HTTP configuration Spring Security 3.0 and can be customized (or disabled) using the `` element. You don't need to configure the beans described here unless you are using traditional bean configuration. @@ -89,7 +88,7 @@ For example: [[anonymous-auth-trust-resolver]] -=== AuthenticationTrustResolver +== AuthenticationTrustResolver Rounding out the anonymous authentication discussion is the `AuthenticationTrustResolver` interface, with its corresponding `AuthenticationTrustResolverImpl` implementation. This interface provides an `isAnonymous(Authentication)` method, which allows interested classes to take into account this special type of authentication status. The `ExceptionTranslationFilter` uses this interface in processing `AccessDeniedException` s. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/authentication-entry-point.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/authentication-entry-point.adoc new file mode 100644 index 00000000000..7b0ca7b98f6 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/authentication-entry-point.adoc @@ -0,0 +1,7 @@ +[[ns-entry-point-ref]] += Setting a Custom AuthenticationEntryPoint +If you aren't using form login, OpenID or basic authentication through the namespace, you may want to define an authentication filter and entry point using a traditional bean syntax and link them into the namespace, as we've just seen. +The corresponding `AuthenticationEntryPoint` can be set using the `entry-point-ref` attribute on the `` element. + +The CAS sample application is a good example of the use of custom beans with the namespace, including this syntax. +If you aren't familiar with authentication entry points, they are discussed in the <> chapter. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/authentication-manager.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/authentication-manager.adoc new file mode 100644 index 00000000000..50daaefc538 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/authentication-manager.adoc @@ -0,0 +1,38 @@ +[[ns-auth-manager]] += The Authentication Manager and the Namespace +The main interface which provides authentication services in Spring Security is the `AuthenticationManager`. +This is usually an instance of Spring Security's `ProviderManager` class, which you may already be familiar with if you've used the framework before. +If not, it will be covered later, in the <>. +The bean instance is registered using the `authentication-manager` namespace element. +You can't use a custom `AuthenticationManager` if you are using either HTTP or method security through the namespace, but this should not be a problem as you have full control over the `AuthenticationProvider` s that are used. + +You may want to register additional `AuthenticationProvider` beans with the `ProviderManager` and you can do this using the `` element with the `ref` attribute, where the value of the attribute is the name of the provider bean you want to add. +For example: + +[source,xml] +---- + + + + + +... + +---- + +Another common requirement is that another bean in the context may require a reference to the `AuthenticationManager`. +You can easily register an alias for the `AuthenticationManager` and use this name elsewhere in your application context. + +[source,xml] +---- + +... + + + + +... + +---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/authenticationprovider.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/authenticationprovider.adoc new file mode 100644 index 00000000000..d1b166a3d96 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/authenticationprovider.adoc @@ -0,0 +1,74 @@ +[[jc-authentication-authenticationprovider]] += AuthenticationProvider + +== AuthenticationProvider Java Configuration + +You can define custom authentication by exposing a custom `AuthenticationProvider` as a bean. +For example, the following will customize authentication assuming that `SpringAuthenticationProvider` implements `AuthenticationProvider`: + +NOTE: This is only used if the `AuthenticationManagerBuilder` has not been populated + +[source,java] +---- +@Bean +public SpringAuthenticationProvider springAuthenticationProvider() { + return new SpringAuthenticationProvider(); +} +---- + +[[ns-auth-providers]] +== AuthenticationProvider XML Configuration +In practice you will need a more scalable source of user information than a few names added to the application context file. +Most likely you will want to store your user information in something like a database or an LDAP server. +LDAP namespace configuration is dealt with in the <>, so we won't cover it here. +If you have a custom implementation of Spring Security's `UserDetailsService`, called "myUserDetailsService" in your application context, then you can authenticate against this using + +[source,xml] +---- + + + + + +---- + +If you want to use a database, then you can use + +[source,xml] +---- + + + + + +---- + +Where "securityDataSource" is the name of a `DataSource` bean in the application context, pointing at a database containing the standard Spring Security <>. +Alternatively, you could configure a Spring Security `JdbcDaoImpl` bean and point at that using the `user-service-ref` attribute: + +[source,xml] +---- + + + + + + + +---- + +You can also use standard `AuthenticationProvider` beans as follows + +[source,xml] +---- + + + + + +---- + +where `myAuthenticationProvider` is the name of a bean in your application context which implements `AuthenticationProvider`. +You can use multiple `authentication-provider` elements, in which case the providers will be queried in the order they are declared. +See <> for more information on how the Spring Security `AuthenticationManager` is configured using the namespace. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/basic.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/basic.adoc new file mode 100644 index 00000000000..84b98c16891 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/basic.adoc @@ -0,0 +1,41 @@ +[[basic]] += Basic Authentication +Basic and digest authentication are alternative authentication mechanisms which are popular in web applications. +Basic authentication is often used with stateless clients which pass their credentials on each request. +It's quite common to use it in combination with form-based authentication where an application is used through both a browser-based user interface and as a web-service. +However, basic authentication transmits the password as plain text so it should only really be used over an encrypted transport layer such as HTTPS. + +[[basic-processing-filter]] +== BasicAuthenticationFilter +`BasicAuthenticationFilter` is responsible for processing basic authentication credentials presented in HTTP headers. +This can be used for authenticating calls made by Spring remoting protocols (such as Hessian and Burlap), as well as normal browser user agents (such as Firefox and Internet Explorer). +The standard governing HTTP Basic Authentication is defined by RFC 1945, Section 11, and `BasicAuthenticationFilter` conforms with this RFC. +Basic Authentication is an attractive approach to authentication, because it is very widely deployed in user agents and implementation is extremely simple (it's just a Base64 encoding of the username:password, specified in an HTTP header). + +[[basic-config]] +== Configuration +To implement HTTP Basic Authentication, you need to add a `BasicAuthenticationFilter` to your filter chain. +The application context should contain `BasicAuthenticationFilter` and its required collaborator: + + +[source,xml] +---- + + + + + + + + +---- + +The configured `AuthenticationManager` processes each authentication request. +If authentication fails, the configured `AuthenticationEntryPoint` will be used to retry the authentication process. +Usually you will use the filter in combination with a `BasicAuthenticationEntryPoint`, which returns a 401 response with a suitable header to retry HTTP Basic authentication. +If authentication is successful, the resulting `Authentication` object will be placed into the `SecurityContextHolder` as usual. + +If the authentication event was successful, or authentication was not attempted because the HTTP header did not contain a supported authentication request, the filter chain will continue as normal. +The only time the filter chain will be interrupted is if authentication fails and the `AuthenticationEntryPoint` is called. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/cas.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/cas.adoc similarity index 98% rename from docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/cas.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/authentication/cas.adoc index 1045a160366..f87cd400897 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/cas.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/cas.adoc @@ -1,10 +1,8 @@ - - [[cas]] -== CAS Authentication += CAS Authentication [[cas-overview]] -=== Overview +== Overview JA-SIG produces an enterprise-wide single sign on system known as CAS. Unlike other initiatives, JA-SIG's Central Authentication Service is open source, widely used, simple to understand, platform independent, and supports proxy capabilities. Spring Security fully supports CAS, and provides an easy migration path from single-application deployments of Spring Security through to multiple-application deployments secured by an enterprise-wide CAS server. @@ -13,7 +11,7 @@ You can learn more about CAS at http://www.ja-sig.org/cas. You will also need to visit this site to download the CAS Server files. [[cas-how-it-works]] -=== How CAS Works +== How CAS Works Whilst the CAS web site contains documents that detail the architecture of CAS, we present the general overview again here within the context of Spring Security. Spring Security 3.x supports CAS 3. At the time of writing, the CAS server was at version 3.4. @@ -36,7 +34,7 @@ Authenticating a proxy ticket differs because the list of proxies must be valida [[cas-sequence]] -==== Spring Security and CAS Interaction Sequence +=== Spring Security and CAS Interaction Sequence The basic interaction between a web browser, CAS server and a Spring Security-secured service is as follows: * The web user is browsing the service's public pages. @@ -89,7 +87,7 @@ It's good that you're still here! Let's now look at how this is configured [[cas-client]] -=== Configuration of CAS Client +== Configuration of CAS Client The web application side of CAS is made easy due to Spring Security. It is assumed you already know the basics of using Spring Security, so these are not covered again below. We'll assume a namespace based configuration is being used and add in the CAS beans as required. @@ -98,7 +96,7 @@ A full<> can be found in the Spring Security [[cas-st]] -==== Service Ticket Authentication +=== Service Ticket Authentication This section describes how to setup Spring Security to authenticate Service Tickets. Often times this is all a web application requires. You will need to add a `ServiceProperties` bean to your application context. @@ -196,7 +194,7 @@ In the following sections we will discuss some (optional) more advanced configur [[cas-singlelogout]] -==== Single Logout +=== Single Logout The CAS protocol supports Single Logout and can be easily added to your Spring Security configuration. Below are updates to the Spring Security configuration that handle Single Logout @@ -273,14 +271,14 @@ The `SingleSignOutHttpSessionListener` ensures that when an `HttpSession` expire [[cas-pt-client]] -==== Authenticating to a Stateless Service with CAS +=== Authenticating to a Stateless Service with CAS This section describes how to authenticate to a service using CAS. In other words, this section discusses how to setup a client that uses a service that authenticates with CAS. The next section describes how to setup a stateless service to Authenticate using CAS. [[cas-pt-client-config]] -===== Configuring CAS to Obtain Proxy Granting Tickets +==== Configuring CAS to Obtain Proxy Granting Tickets In order to authenticate to a stateless service, the application needs to obtain a proxy granting ticket (PGT). This section describes how to configure Spring Security to obtain a PGT building upon thencas-st[Service Ticket Authentication] configuration. @@ -337,7 +335,7 @@ An example configuration is shown below. ---- [[cas-pt-client-sample]] -===== Calling a Stateless Service Using a Proxy Ticket +==== Calling a Stateless Service Using a Proxy Ticket Now that Spring Security obtains PGTs, you can use them to create proxy tickets which can be used to authenticate to a stateless service. The <> contains a working example in the `ProxyTicketSampleServlet`. Example code can be found below: @@ -361,7 +359,7 @@ String proxyResponse = CommonUtils.getResponseFromServer(serviceUrl, "UTF-8"); ---- [[cas-pt]] -==== Proxy Ticket Authentication +=== Proxy Ticket Authentication The `CasAuthenticationProvider` distinguishes between stateful and stateless clients. A stateful client is considered any that submits to the `filterProcessUrl` of the `CasAuthenticationFilter`. A stateless client is any that presents an authentication request to `CasAuthenticationFilter` on a URL other than the `filterProcessUrl`. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/web/basic.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/digest.adoc similarity index 68% rename from docs/manual/src/docs/asciidoc/_includes/servlet/web/basic.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/authentication/digest.adoc index b9e31ea884a..efdd0c6efeb 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/web/basic.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/digest.adoc @@ -1,48 +1,5 @@ - -[[basic]] -== Basic and Digest Authentication -Basic and digest authentication are alternative authentication mechanisms which are popular in web applications. -Basic authentication is often used with stateless clients which pass their credentials on each request. -It's quite common to use it in combination with form-based authentication where an application is used through both a browser-based user interface and as a web-service. -However, basic authentication transmits the password as plain text so it should only really be used over an encrypted transport layer such as HTTPS. - -[[basic-processing-filter]] -=== BasicAuthenticationFilter -`BasicAuthenticationFilter` is responsible for processing basic authentication credentials presented in HTTP headers. -This can be used for authenticating calls made by Spring remoting protocols (such as Hessian and Burlap), as well as normal browser user agents (such as Firefox and Internet Explorer). -The standard governing HTTP Basic Authentication is defined by RFC 1945, Section 11, and `BasicAuthenticationFilter` conforms with this RFC. -Basic Authentication is an attractive approach to authentication, because it is very widely deployed in user agents and implementation is extremely simple (it's just a Base64 encoding of the username:password, specified in an HTTP header). - -[[basic-config]] -==== Configuration -To implement HTTP Basic Authentication, you need to add a `BasicAuthenticationFilter` to your filter chain. -The application context should contain `BasicAuthenticationFilter` and its required collaborator: - - -[source,xml] ----- - - - - - - - - ----- - -The configured `AuthenticationManager` processes each authentication request. -If authentication fails, the configured `AuthenticationEntryPoint` will be used to retry the authentication process. -Usually you will use the filter in combination with a `BasicAuthenticationEntryPoint`, which returns a 401 response with a suitable header to retry HTTP Basic authentication. -If authentication is successful, the resulting `Authentication` object will be placed into the `SecurityContextHolder` as usual. - -If the authentication event was successful, or authentication was not attempted because the HTTP header did not contain a supported authentication request, the filter chain will continue as normal. -The only time the filter chain will be interrupted is if authentication fails and the `AuthenticationEntryPoint` is called. - -[[digest-processing-filter]] -=== DigestAuthenticationFilter +[[digest]] += DigestAuthenticationFilter `DigestAuthenticationFilter` is capable of processing digest authentication credentials presented in HTTP headers. Digest Authentication attempts to solve many of the weaknesses of Basic authentication, specifically by ensuring credentials are never sent in clear text across the wire. Many user agents support Digest Authentication, including Mozilla Firefox and Internet Explorer. @@ -53,12 +10,12 @@ Digest Authentication is a more attractive option if you need to use unencrypted Indeed Digest Authentication is a mandatory requirement for the WebDAV protocol, as noted by RFC 2518 Section 17.1. [NOTE] -==== +== You should not use Digest in modern applications because it is not considered secure. The most obvious problem is that you must store your passwords in plaintext, encrypted, or an MD5 format. All of these storage formats are considered insecure. Instead, you should use a one way adaptive password hash (i.e. bCrypt, PBKDF2, SCrypt, etc). -==== +== Central to Digest Authentication is a "nonce". This is a value the server generates. @@ -88,7 +45,7 @@ In our testing, Spring Security's implementation works reliably with Mozilla Fir [[digest-config]] -==== Configuration +== Configuration Now that we've reviewed the theory, let's see how to use it. To implement HTTP Digest Authentication, it is necessary to define `DigestAuthenticationFilter` in the filter chain. The application context will need to define the `DigestAuthenticationFilter` and its required collaborators: diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/form.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/form.adoc new file mode 100644 index 00000000000..8cc5a3f7190 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/form.adoc @@ -0,0 +1,115 @@ +[[form]] += Form Login +You might be wondering where the login form came from when you were prompted to log in, since we made no mention of any HTML files or JSPs. +Since Spring Security's default configuration does not explicitly set a URL for the login page, Spring Security generates one automatically, based on the features that are enabled and using standard values for the URL which processes the submitted login, the default target URL the user will be sent to after logging in and so on. + +== Form Login Java Configuration + +While the automatically generated log in page is convenient to get up and running quickly, most applications will want to provide their own log in page. +To do so we can update our configuration as seen below: + + +[source,java] +---- +protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().authenticated() + .and() + .formLogin() + .loginPage("/login") // <1> + .permitAll(); // <2> +} +---- + +<1> The updated configuration specifies the location of the log in page. +<2> We must grant all users (i.e. unauthenticated users) access to our log in page. +The `formLogin().permitAll()` method allows granting access to all users for all URLs associated with form based log in. + +An example log in page implemented with JSPs for our current configuration can be seen below: + +NOTE: The login page below represents our current configuration. +We could easily update our configuration if some of the defaults do not meet our needs. + +[source,html] +---- + +
<1> + <2> +

+ Invalid username and password. +

+
+ <3> +

+ You have been logged out. +

+
+

+ + <4> +

+

+ + <5> +

+ + name="${_csrf.parameterName}" + value="${_csrf.token}"/> + +
+---- + +<1> A POST to the `/login` URL will attempt to authenticate the user +<2> If the query parameter `error` exists, authentication was attempted and failed +<3> If the query parameter `logout` exists, the user was successfully logged out +<4> The username must be present as the HTTP parameter named __username__ +<5> The password must be present as the HTTP parameter named __password__ +<6> We must <> To learn more read the <> section of the reference + +== Form Login XML Configuration + +[[ns-form-and-basic]] +=== Form and Basic Login Options + +If we don't explicitly set a URL for the login page, Spring Security generates one automatically, based on the features that are enabled and using standard values for the URL which processes the submitted login, the default target URL the user will be sent to after logging in and so on. +However, the namespace offers plenty of support to allow you to customize these options. +For example, if you want to supply your own login page, you could use: + +[source,xml] +---- + + + + + +---- + +Also note that we've added an extra `intercept-url` element to say that any requests for the login page should be available to anonymous users footnote:[See the chapter on pass:specialcharacters,macros[<>]] and also the <> class for more details on how the value `IS_AUTHENTICATED_ANONYMOUSLY` is processed.]. +Otherwise the request would be matched by the pattern /** and it wouldn't be possible to access the login page itself! +This is a common configuration error and will result in an infinite loop in the application. +Spring Security will emit a warning in the log if your login page appears to be secured. + + + + +[[ns-form-target]] +==== Setting a Default Post-Login Destination +If a form login isn't prompted by an attempt to access a protected resource, the `default-target-url` option comes into play. +This is the URL the user will be taken to after successfully logging in, and defaults to "/". +You can also configure things so that the user __always__ ends up at this page (regardless of whether the login was "on-demand" or they explicitly chose to log in) by setting the `always-use-default-target` attribute to "true". +This is useful if your application always requires that the user starts at a "home" page, for example: + +[source,xml] +---- + + + + + +---- + +For even more control over the destination, you can use the `authentication-success-handler-ref` attribute as an alternative to `default-target-url`. +The referenced bean should be an instance of `AuthenticationSuccessHandler`. +You'll find more on this in the <> chapter and also in the namespace appendix, as well as information on how to customize the flow when authentication fails. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/index.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/index.adoc new file mode 100644 index 00000000000..fc4e90f2817 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/index.adoc @@ -0,0 +1,25 @@ +[[servlet-authentication]] += Authentication + +include::inmemory.adoc[leveloffset=+1] +include::jdbc.adoc[leveloffset=+1] +include::ldap.adoc[leveloffset=+1] +include::authenticationprovider.adoc[leveloffset=+1] +include::userdetailsservice.adoc[leveloffset=+1] +include::password-encoder.adoc[leveloffset=+1] +include::authentication-manager.adoc[leveloffset=+1] +include::session-management.adoc[leveloffset=+1] +include::rememberme.adoc[leveloffset=+1] +include::openid.adoc[leveloffset=+1] +include::anonymous.adoc[leveloffset=+1] +include::preauth.adoc[leveloffset=+1] +include::jaas.adoc[leveloffset=+1] +include::cas.adoc[leveloffset=+1] +include::x509.adoc[leveloffset=+1] +include::runas.adoc[leveloffset=+1] + +include::form.adoc[leveloffset=+1] +include::basic.adoc[leveloffset=+1] +include::digest.adoc[leveloffset=+1] +include::logout.adoc[leveloffset=+1] +include::authentication-entry-point.adoc[leveloffset=+1] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/inmemory.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/inmemory.adoc new file mode 100644 index 00000000000..9f68d1be238 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/inmemory.adoc @@ -0,0 +1,18 @@ +[[jc-authentication-inmemory]] += In-Memory Authentication + +We have already seen an example of configuring in-memory authentication for a single user. +Below is an example to configure multiple users: + +[source,java] +---- +@Bean +public UserDetailsService userDetailsService() throws Exception { + // ensure the passwords are encoded properly + UserBuilder users = User.withDefaultPasswordEncoder(); + InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); + manager.createUser(users.username("user").password("password").roles("USER").build()); + manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build()); + return manager; +} +---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/jaas.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/jaas.adoc similarity index 95% rename from docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/jaas.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/authentication/jaas.adoc index 8d2aede379d..d4548031632 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/jaas.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/jaas.adoc @@ -1,22 +1,21 @@ - [[jaas]] -== Java Authentication and Authorization Service (JAAS) Provider += Java Authentication and Authorization Service (JAAS) Provider -=== Overview +== Overview Spring Security provides a package able to delegate authentication requests to the Java Authentication and Authorization Service (JAAS). This package is discussed in detail below. [[jaas-abstractjaasauthenticationprovider]] -=== AbstractJaasAuthenticationProvider +== AbstractJaasAuthenticationProvider The `AbstractJaasAuthenticationProvider` is the basis for the provided JAAS `AuthenticationProvider` implementations. Subclasses must implement a method that creates the `LoginContext`. The `AbstractJaasAuthenticationProvider` has a number of dependencies that can be injected into it that are discussed below. [[jaas-callbackhandler]] -==== JAAS CallbackHandler +=== JAAS CallbackHandler Most JAAS `LoginModule` s require a callback of some sort. These callbacks are usually used to obtain the username and password from the user. @@ -34,7 +33,7 @@ If the `LoginModule` requests a callback against the `InternalCallbackHandler` s [[jaas-authoritygranter]] -==== JAAS AuthorityGranter +=== JAAS AuthorityGranter JAAS works with principals. Even "roles" are represented as principals in JAAS. Spring Security, on the other hand, works with `Authentication` objects. @@ -51,14 +50,14 @@ However, there is a `TestAuthorityGranter` in the unit tests that demonstrates a [[jaas-defaultjaasauthenticationprovider]] -=== DefaultJaasAuthenticationProvider +== DefaultJaasAuthenticationProvider The `DefaultJaasAuthenticationProvider` allows a JAAS `Configuration` object to be injected into it as a dependency. It then creates a `LoginContext` using the injected JAAS `Configuration`. This means that `DefaultJaasAuthenticationProvider` is not bound any particular implementation of `Configuration` as `JaasAuthenticationProvider` is. [[jaas-inmemoryconfiguration]] -==== InMemoryConfiguration +=== InMemoryConfiguration In order to make it easy to inject a `Configuration` into `DefaultJaasAuthenticationProvider`, a default in-memory implementation named `InMemoryConfiguration` is provided. The implementation constructor accepts a `Map` where each key represents a login configuration name and the value represents an `Array` of `AppConfigurationEntry` s. `InMemoryConfiguration` also supports a default `Array` of `AppConfigurationEntry` objects that will be used if no mapping is found within the provided `Map`. @@ -66,7 +65,7 @@ For details, refer to the class level javadoc of `InMemoryConfiguration`. [[jaas-djap-config]] -==== DefaultJaasAuthenticationProvider Example Configuration +=== DefaultJaasAuthenticationProvider Example Configuration While the Spring configuration for `InMemoryConfiguration` can be more verbose than the standarad JAAS configuration files, using it in conjuction with `DefaultJaasAuthenticationProvider` is more flexible than `JaasAuthenticationProvider` since it not dependant on the default `Configuration` implementation. An example configuration of `DefaultJaasAuthenticationProvider` using `InMemoryConfiguration` is provided below. @@ -117,7 +116,7 @@ class="org.springframework.security.authentication.jaas.DefaultJaasAuthenticatio [[jaas-jaasauthenticationprovider]] -=== JaasAuthenticationProvider +== JaasAuthenticationProvider The `JaasAuthenticationProvider` assumes the default `Configuration` is an instance of http://download.oracle.com/javase/1.4.2/docs/guide/security/jaas/spec/com/sun/security/auth/login/ConfigFile.html[ ConfigFile]. This assumption is made in order to attempt to update the `Configuration`. The `JaasAuthenticationProvider` then uses the default `Configuration` to create the `LoginContext`. @@ -158,7 +157,7 @@ class="org.springframework.security.authentication.jaas.JaasAuthenticationProvid ---- [[jaas-apiprovision]] -=== Running as a Subject +== Running as a Subject If configured, the `JaasApiIntegrationFilter` will attempt to run as the `Subject` on the `JaasAuthenticationToken`. This means that the `Subject` can be accessed using: diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/jdbc.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/jdbc.adoc new file mode 100644 index 00000000000..5c959a93b08 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/jdbc.adoc @@ -0,0 +1,24 @@ +[[jc-authentication-jdbc]] += JDBC Authentication + +You can find the updates to support JDBC based authentication. +The example below assumes that you have already defined a `DataSource` within your application. +The https://github.com/spring-projects/spring-security/tree/master/samples/javaconfig/jdbc[jdbc-javaconfig] sample provides a complete example of using JDBC based authentication. + +[source,java] +---- +@Autowired +private DataSource dataSource; + +@Autowired +public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + // ensure the passwords are encoded properly + UserBuilder users = User.withDefaultPasswordEncoder(); + auth + .jdbcAuthentication() + .dataSource(dataSource) + .withDefaultSchema() + .withUser(users.username("user").password("password").roles("USER")) + .withUser(users.username("admin").password("password").roles("USER","ADMIN")); +} +---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/ldap.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/ldap.adoc similarity index 89% rename from docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/ldap.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/authentication/ldap.adoc index 8de9f438a32..edb5d4b227a 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/ldap.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/ldap.adoc @@ -1,9 +1,8 @@ -[[ldap]] -== LDAP Authentication += LDAP Authentication [[ldap-overview]] -=== Overview +== Overview LDAP is often used by organizations as a central repository for user information and as an authentication service. It can also be used to store the role information for application users. @@ -18,7 +17,7 @@ We don't use any third-party LDAP libraries (Mozilla, JLDAP etc.) in the LDAP pr When using LDAP authentication, it is important to ensure that you configure LDAP connection pooling properly. If you are unfamiliar with how to do this, you can refer to the http://docs.oracle.com/javase/jndi/tutorial/ldap/connect/config.html[Java LDAP documentation]. -=== Using LDAP with Spring Security +== Using LDAP with Spring Security LDAP authentication in Spring Security can be roughly divided into the following stages. * Obtaining the unique LDAP "Distinguished Name", or DN, from the login name. @@ -37,7 +36,7 @@ For full information on available configuration options, please consult the secu [[ldap-server]] -=== Configuring an LDAP Server += Configuring an LDAP Server The first thing you need to do is configure the server against which authentication should take place. This is done using the `` element from the security namespace. This can be configured to point at an external LDAP server, using the `url` attribute: @@ -47,7 +46,7 @@ This can be configured to point at an external LDAP server, using the `url` attr ---- -==== Using an Embedded Test Server +== Using an Embedded Test Server The `` element can also be used to create an embedded server, which can be very useful for testing and demonstrations. In this case you use it without the `url` attribute: @@ -71,7 +70,7 @@ Using plain Spring Beans the configuration would be much more cluttered. You must have the necessary Apache Directory dependency jars available for your application to use. These can be obtained from the LDAP sample application. -==== Using Bind Authentication +== Using Bind Authentication This is the most common LDAP authentication scenario. [source,xml] @@ -93,7 +92,7 @@ If used with the server definition above, this would perform a search under the Again the user login name is substituted for the parameter in the filter name, so it will search for an entry with the `uid` attribute equal to the user name. If `user-search-base` isn't supplied, the search will be performed from the root. -==== Loading Authorities +== Loading Authorities How authorities are loaded from groups in the LDAP directory is controlled by the following attributes. * `group-search-base`. @@ -121,7 +120,7 @@ You can change this using the `role-prefix` attribute. If you don't want any prefix, use `role-prefix="none"`. For more information on loading authorities, see the Javadoc for the `DefaultLdapAuthoritiesPopulator` class. -=== Implementation Classes += Implementation Classes The namespace configuration options we've used above are simple to use and much more concise than using Spring beans explicitly. There are situations when you may need to know how to configure Spring Security LDAP directly in your application context. You may wish to customize the behaviour of some of the classes, for example. @@ -131,7 +130,7 @@ The main LDAP provider class, `LdapAuthenticationProvider`, doesn't actually do [[ldap-ldap-authenticators]] -==== LdapAuthenticator Implementations +== LdapAuthenticator Implementations The authenticator is also responsible for retrieving any required user attributes. This is because the permissions on the attributes may depend on the type of authentication being used. For example, if binding as the user, it may be necessary to read them with the user's own permissions. @@ -144,7 +143,7 @@ There are currently two authentication strategies supplied with Spring Security: This can either be done by retrieving the value of the password attribute and checking it locally or by performing an LDAP "compare" operation, where the supplied password is passed to the server for comparison and the real password value is never retrieved. [[ldap-ldap-authenticators-common]] -===== Common Functionality +=== Common Functionality Before it is possible to authenticate a user (by either strategy), the distinguished name (DN) has to be obtained from the login name supplied to the application. This can be done either by simple pattern-matching (by setting the `setUserDnPatterns` array property) or by setting the `userSearch` property. For the DN pattern-matching approach, a standard Java pattern format is used, and the login name will be substituted for the parameter `{0}`. @@ -156,18 +155,18 @@ A combination of the two approaches can also be used - the patterns will be chec [[ldap-ldap-authenticators-bind]] -===== BindAuthenticator +=== BindAuthenticator The class `BindAuthenticator` in the package `org.springframework.security.ldap.authentication` implements the bind authentication strategy. It simply attempts to bind as the user. [[ldap-ldap-authenticators-password]] -===== PasswordComparisonAuthenticator +=== PasswordComparisonAuthenticator The class `PasswordComparisonAuthenticator` implements the password comparison authentication strategy. [[ldap-context-source]] -==== Connecting to the LDAP Server +== Connecting to the LDAP Server The beans discussed above have to be able to connect to the server. They both have to be supplied with a `SpringSecurityContextSource` which is an extension of Spring LDAP's `ContextSource`. Unless you have special requirements, you will usually configure a `DefaultSpringSecurityContextSource` bean, which can be configured with the URL of your LDAP server and optionally with the username and password of a "manager" user which will be used by default when binding to the server (instead of binding anonymously). @@ -175,14 +174,14 @@ For more information read the Javadoc for this class and for Spring LDAP's `Abst [[ldap-searchobjects]] -==== LDAP Search Objects +== LDAP Search Objects Often a more complicated strategy than simple DN-matching is required to locate a user entry in the directory. This can be encapsulated in an `LdapUserSearch` instance which can be supplied to the authenticator implementations, for example, to allow them to locate a user. The supplied implementation is `FilterBasedLdapUserSearch`. [[ldap-searchobjects-filter]] -===== FilterBasedLdapUserSearch +=== FilterBasedLdapUserSearch This bean uses an LDAP filter to match the user object in the directory. The process is explained in the Javadoc for the corresponding search method on the http://java.sun.com/j2se/1.4.2/docs/api/javax/naming/directory/DirContext.html#search(javax.naming.Name%2C%2520java.lang.String%2C%2520java.lang.Object%5B%5D%2C%2520javax.naming.directory.SearchControls)[JDK DirContext class]. As explained there, the search filter can be supplied with parameters. @@ -190,7 +189,7 @@ For this class, the only valid parameter is `{0}` which will be replaced with th [[ldap-authorities]] -==== LdapAuthoritiesPopulator +== LdapAuthoritiesPopulator After authenticating the user successfully, the `LdapAuthenticationProvider` will attempt to load a set of authorities for the user by calling the configured `LdapAuthoritiesPopulator` bean. The `DefaultLdapAuthoritiesPopulator` is an implementation which will load the authorities by searching the directory for groups of which the user is a member (typically these will be `groupOfNames` or `groupOfUniqueNames` entries in the directory). Consult the Javadoc for this class for more details on how it works. @@ -198,7 +197,7 @@ Consult the Javadoc for this class for more details on how it works. If you want to use LDAP only for authentication, but load the authorities from a difference source (such as a database) then you can provide your own implementation of this interface and inject that instead. [[ldap-bean-config]] -==== Spring Bean Configuration +== Spring Bean Configuration A typical configuration, using some of the beans we've discussed here, might look like this: [source,xml] @@ -253,7 +252,7 @@ The authenticator would then call the search object to obtain the correct user's [[ldap-custom-user-details]] -==== LDAP Attributes and Customized UserDetails +== LDAP Attributes and Customized UserDetails The net result of an authentication using `LdapAuthenticationProvider` is the same as a normal Spring Security authentication using the standard `UserDetailsService` interface. A `UserDetails` object is created and stored in the returned `Authentication` object. As with using a `UserDetailsService`, a common requirement is to be able to customize this implementation and add extra properties. @@ -281,13 +280,13 @@ With the `BindAuthenticator`, the context returned from the bind operation will [[ldap-active-directory]] -=== Active Directory Authentication += Active Directory Authentication Active Directory supports its own non-standard authentication options, and the normal usage pattern doesn't fit too cleanly with the standard `LdapAuthenticationProvider`. Typically authentication is performed using the domain username (in the form `user@domain`), rather than using an LDAP distinguished name. To make this easier, Spring Security 3.1 has an authentication provider which is customized for a typical Active Directory setup. -==== ActiveDirectoryLdapAuthenticationProvider +== ActiveDirectoryLdapAuthenticationProvider Configuring `ActiveDirectoryLdapAuthenticationProvider` is quite straightforward. You just need to supply the domain name and an LDAP URL supplying the address of the server footnote:[It is also possible to obtain the server's IP address using a DNS lookup. This is not currently supported, but hopefully will be in a future version.]. @@ -313,7 +312,75 @@ By default, the user authorities are obtained from the `memberOf` attribute valu The authorities allocated to the user can again be customized using a `UserDetailsContextMapper`. You can also inject a `GrantedAuthoritiesMapper` into the provider instance to control the authorities which end up in the `Authentication` object. -===== Active Directory Error Codes +=== Active Directory Error Codes By default, a failed result will cause a standard Spring Security `BadCredentialsException`. If you set the property `convertSubErrorCodesToExceptions` to `true`, the exception messages will be parsed to attempt to extract the Active Directory-specific error code and raise a more specific exception. Check the class Javadoc for more information. + + +== LDAP Java Configuration + +You can find the updates to support LDAP based authentication. +The https://github.com/spring-projects/spring-security/tree/master/samples/javaconfig/ldap[ldap-javaconfig] sample provides a complete example of using LDAP based authentication. + +[source,java] +---- +@Autowired +private DataSource dataSource; + +@Autowired +public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth + .ldapAuthentication() + .userDnPatterns("uid={0},ou=people") + .groupSearchBase("ou=groups"); +} +---- + +The example above uses the following LDIF and an embedded Apache DS LDAP instance. + +.users.ldif +---- +dn: ou=groups,dc=springframework,dc=org +objectclass: top +objectclass: organizationalUnit +ou: groups + +dn: ou=people,dc=springframework,dc=org +objectclass: top +objectclass: organizationalUnit +ou: people + +dn: uid=admin,ou=people,dc=springframework,dc=org +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +cn: Rod Johnson +sn: Johnson +uid: admin +userPassword: password + +dn: uid=user,ou=people,dc=springframework,dc=org +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +cn: Dianne Emu +sn: Emu +uid: user +userPassword: password + +dn: cn=user,ou=groups,dc=springframework,dc=org +objectclass: top +objectclass: groupOfNames +cn: user +uniqueMember: uid=admin,ou=people,dc=springframework,dc=org +uniqueMember: uid=user,ou=people,dc=springframework,dc=org + +dn: cn=admin,ou=groups,dc=springframework,dc=org +objectclass: top +objectclass: groupOfNames +cn: admin +uniqueMember: uid=admin,ou=people,dc=springframework,dc=org +---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/logout.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/logout.adoc new file mode 100644 index 00000000000..0f062e5e148 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/logout.adoc @@ -0,0 +1,120 @@ + +[[jc-logout]] += Handling Logouts + +== Logout Java Configuration + +When using the `{security-api-url}org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.html[WebSecurityConfigurerAdapter]`, logout capabilities are automatically applied. +The default is that accessing the URL `/logout` will log the user out by: + +- Invalidating the HTTP Session +- Cleaning up any RememberMe authentication that was configured +- Clearing the `SecurityContextHolder` +- Redirect to `/login?logout` + +Similar to configuring login capabilities, however, you also have various options to further customize your logout requirements: + +[source,java] +---- +protected void configure(HttpSecurity http) throws Exception { + http + .logout() <1> + .logoutUrl("/my/logout") <2> + .logoutSuccessUrl("/my/index") <3> + .logoutSuccessHandler(logoutSuccessHandler) <4> + .invalidateHttpSession(true) <5> + .addLogoutHandler(logoutHandler) <6> + .deleteCookies(cookieNamesToClear) <7> + .and() + ... +} +---- + +<1> Provides logout support. +This is automatically applied when using `WebSecurityConfigurerAdapter`. +<2> The URL that triggers log out to occur (default is `/logout`). +If CSRF protection is enabled (default), then the request must also be a POST. +For more information, please consult the {security-api-url}org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.html#logoutUrl-java.lang.String-[JavaDoc]. +<3> The URL to redirect to after logout has occurred. +The default is `/login?logout`. +For more information, please consult the {security-api-url}org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.html#logoutSuccessUrl-java.lang.String-[JavaDoc]. +<4> Let's you specify a custom `LogoutSuccessHandler`. +If this is specified, `logoutSuccessUrl()` is ignored. +For more information, please consult the {security-api-url}org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.html#logoutSuccessHandler-org.springframework.security.web.authentication.logout.LogoutSuccessHandler-[JavaDoc]. +<5> Specify whether to invalidate the `HttpSession` at the time of logout. +This is *true* by default. +Configures the `SecurityContextLogoutHandler` under the covers. +For more information, please consult the {security-api-url}org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.html#invalidateHttpSession-boolean-[JavaDoc]. +<6> Adds a `LogoutHandler`. +`SecurityContextLogoutHandler` is added as the last `LogoutHandler` by default. +<7> Allows specifying the names of cookies to be removed on logout success. +This is a shortcut for adding a `CookieClearingLogoutHandler` explicitly. + +Generally, in order to customize logout functionality, you can add +`{security-api-url}org/springframework/security/web/authentication/logout/LogoutHandler.html[LogoutHandler]` +and/or +`{security-api-url}org/springframework/security/web/authentication/logout/LogoutSuccessHandler.html[LogoutSuccessHandler]` +implementations. +For many common scenarios, these handlers are applied under the +covers when using the fluent API. + +== Logout XML Configuration + +The `logout` element adds support for logging out by navigating to a particular URL. +The default logout URL is `/logout`, but you can set it to something else using the `logout-url` attribute. +More information on other available attributes may be found in the namespace appendix. + +[[jc-logout-handler]] +== LogoutHandler + +Generally, `{security-api-url}org/springframework/security/web/authentication/logout/LogoutHandler.html[LogoutHandler]` +implementations indicate classes that are able to participate in logout handling. +They are expected to be invoked to perform necessary clean-up. +As such they should +not throw exceptions. +Various implementations are provided: + +- {security-api-url}org/springframework/security/web/authentication/rememberme/PersistentTokenBasedRememberMeServices.html[PersistentTokenBasedRememberMeServices] +- {security-api-url}org/springframework/security/web/authentication/rememberme/TokenBasedRememberMeServices.html[TokenBasedRememberMeServices] +- {security-api-url}org/springframework/security/web/authentication/logout/CookieClearingLogoutHandler.html[CookieClearingLogoutHandler] +- {security-api-url}org/springframework/security/web/csrf/CsrfLogoutHandler.html[CsrfLogoutHandler] +- {security-api-url}org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[SecurityContextLogoutHandler] + +Please see <> for details. + +Instead of providing `LogoutHandler` implementations directly, the fluent API also provides shortcuts that provide the respective `LogoutHandler` implementations under the covers. +E.g. `deleteCookies()` allows specifying the names of one or more cookies to be removed on logout success. +This is a shortcut compared to adding a `CookieClearingLogoutHandler`. + +[[jc-logout-success-handler]] +== LogoutSuccessHandler + +The `LogoutSuccessHandler` is called after a successful logout by the `LogoutFilter`, to handle e.g. +redirection or forwarding to the appropriate destination. +Note that the interface is almost the same as the `LogoutHandler` but may raise an exception. + +The following implementations are provided: + +- {security-api-url}org/springframework/security/web/authentication/logout/SimpleUrlLogoutSuccessHandler.html[SimpleUrlLogoutSuccessHandler] +- HttpStatusReturningLogoutSuccessHandler + +As mentioned above, you don't need to specify the `SimpleUrlLogoutSuccessHandler` directly. +Instead, the fluent API provides a shortcut by setting the `logoutSuccessUrl()`. +This will setup the `SimpleUrlLogoutSuccessHandler` under the covers. +The provided URL will be redirected to after a logout has occurred. +The default is `/login?logout`. + +The `HttpStatusReturningLogoutSuccessHandler` can be interesting in REST API type scenarios. +Instead of redirecting to a URL upon the successful logout, this `LogoutSuccessHandler` allows you to provide a plain HTTP status code to be returned. +If not configured a status code 200 will be returned by default. + +[[jc-logout-references]] +== Further Logout-Related References + +- <> +- <> +- <> +- <> +- <> in section CSRF Caveats +- Section <> (CAS protocol) +- Documentation for the <> in the Spring Security XML Namespace section diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/openid.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/openid.adoc new file mode 100644 index 00000000000..cf4ab1a82fd --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/openid.adoc @@ -0,0 +1,58 @@ +[[ns-openid]] += OpenID Support +The namespace supports http://openid.net/[OpenID] login either instead of, or in addition to normal form-based login, with a simple change: + +[source,xml] +---- + + + + +---- + +You should then register yourself with an OpenID provider (such as myopenid.com), and add the user information to your in-memory ``: + +[source,xml] +---- + +---- + +You should be able to login using the `myopenid.com` site to authenticate. +It is also possible to select a specific `UserDetailsService` bean for use OpenID by setting the `user-service-ref` attribute on the `openid-login` element. +See the previous section on <> for more information. +Note that we have omitted the password attribute from the above user configuration, since this set of user data is only being used to load the authorities for the user. +A random password will be generated internally, preventing you from accidentally using this user data as an authentication source elsewhere in your configuration. + + +== Attribute Exchange +Support for OpenID http://openid.net/specs/openid-attribute-exchange-1_0.html[attribute exchange]. +As an example, the following configuration would attempt to retrieve the email and full name from the OpenID provider, for use by the application: + +[source,xml] +---- + + + + + + +---- + +The "type" of each OpenID attribute is a URI, determined by a particular schema, in this case http://axschema.org/[http://axschema.org/]. +If an attribute must be retrieved for successful authentication, the `required` attribute can be set. +The exact schema and attributes supported will depend on your OpenID provider. +The attribute values are returned as part of the authentication process and can be accessed afterwards using the following code: + +[source,java] +---- +OpenIDAuthenticationToken token = + (OpenIDAuthenticationToken)SecurityContextHolder.getContext().getAuthentication(); +List attributes = token.getAttributes(); +---- + +The `OpenIDAttribute` contains the attribute type and the retrieved value (or values in the case of multi-valued attributes). +We'll see more about how the `SecurityContextHolder` class is used when we look at core Spring Security components in the <> chapter. +Multiple attribute exchange configurations are also be supported, if you wish to use multiple identity providers. +You can supply multiple `attribute-exchange` elements, using an `identifier-matcher` attribute on each. +This contains a regular expression which will be matched against the OpenID identifier supplied by the user. +See the OpenID sample application in the codebase for an example configuration, providing different attribute lists for the Google, Yahoo and MyOpenID providers. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/password-encoder.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/password-encoder.adoc similarity index 92% rename from docs/manual/src/docs/asciidoc/_includes/servlet/architecture/password-encoder.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/authentication/password-encoder.adoc index f8bd794abfe..2de9599468a 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/password-encoder.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/password-encoder.adoc @@ -1,5 +1,4 @@ -[[core-services-password-encoding]] -= Password Encoding += PasswordEncoder Spring Security's `PasswordEncoder` interface is used to perform a one way transformation of a password to allow the password to be stored securely. Given `PasswordEncoder` is a one way transformation, it is not intended when the password transformation needs to be two way (i.e. storing credentials used to authenticate to a database). @@ -301,3 +300,36 @@ assertTrue(encoder.matches("myPassword", result)); There are a significant number of other `PasswordEncoder` implementations that exist entirely for backward compatibility. They are all deprecated to indicate that they are no longer considered secure. However, there are no plans to remove them since it is difficult to migrate existing legacy systems. + +[[ns-password-encoder]] +== PasswordEncoder XML Configuration + +Passwords should always be encoded using a secure hashing algorithm designed for the purpose (not a standard algorithm like SHA or MD5). +This is supported by the `` element. +With bcrypt encoded passwords, the original authentication provider configuration would look like this: + +[source,xml] +---- + + + + + + + + + + + + +---- + + + +bcrypt is a good choice for most cases, unless you have a legacy system which forces you to use a different algorithm. +If you are using a simple hashing algorithm or, even worse, storing plain text passwords, then you should consider migrating to a more secure option like bcrypt. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/preauth.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/preauth.adoc similarity index 95% rename from docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/preauth.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/authentication/preauth.adoc index 91b2ebc8d98..bee170d6ce4 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/preauth.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/preauth.adoc @@ -1,5 +1,5 @@ [[preauth]] -== Pre-Authentication Scenarios += Pre-Authentication Scenarios There are situations where you want to use Spring Security for authorization, but the user has already been reliably authenticated by some external system prior to accessing the application. We refer to these situations as "pre-authenticated" scenarios. Examples include X.509, Siteminder and authentication by the Java EE container in which the application is running. @@ -16,7 +16,7 @@ If relying on container authentication, the user will be identified by calling t In some cases, the external mechanism may supply role/authority information for the user but in others the authorities must be obtained from a separate source, such as a `UserDetailsService`. -=== Pre-Authentication Framework Classes +== Pre-Authentication Framework Classes Because most pre-authentication mechanisms follow the same pattern, Spring Security has a set of classes which provide an internal framework for implementing pre-authenticated authentication providers. This removes duplication and allows new implementations to be added in a structured fashion, without having to write everything from scratch. You don't need to know about these classes if you want to use something like <>, as it already has a namespace configuration option which is simpler to use and get started with. @@ -25,7 +25,7 @@ You will find classes under the `org.springframework.security.web.authentication We just provide an outline here so you should consult the Javadoc and source where appropriate. -==== AbstractPreAuthenticatedProcessingFilter +=== AbstractPreAuthenticatedProcessingFilter This class will check the current contents of the security context and, if empty, it will attempt to extract user information from the HTTP request and submit it to the `AuthenticationManager`. Subclasses override the following methods to obtain this information: @@ -47,7 +47,7 @@ We'll look at a concrete example next. [[j2ee-preauth-details]] -===== J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource +==== J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource If the filter is configured with an `authenticationDetailsSource` which is an instance of this class, the authority information is obtained by calling the `isUserInRole(String role)` method for each of a pre-determined set of "mappable roles". The class gets these from a configured `MappableAttributesRetriever`. Possible implementations include hard-coding a list in the application context and reading the role information from the `` information in a `web.xml` file. @@ -57,7 +57,7 @@ There is an additional stage where the roles (or attributes) are mapped to Sprin The default will just add the usual `ROLE_` prefix to the names, but it gives you full control over the behaviour. -==== PreAuthenticatedAuthenticationProvider +=== PreAuthenticatedAuthenticationProvider The pre-authenticated provider has little more to do than load the `UserDetails` object for the user. It does this by delegating to an `AuthenticationUserDetailsService`. The latter is similar to the standard `UserDetailsService` but takes an `Authentication` object rather than just user name: @@ -73,7 +73,7 @@ This interface may have also other uses but with pre-authentication it allows ac The `PreAuthenticatedGrantedAuthoritiesUserDetailsService` class does this. Alternatively, it may delegate to a standard `UserDetailsService` via the `UserDetailsByNameServiceWrapper` implementation. -==== Http403ForbiddenEntryPoint +=== Http403ForbiddenEntryPoint The `AuthenticationEntryPoint` was discussed in the <> chapter. Normally it is responsible for kick-starting the authentication process for an unauthenticated user (when they try to access a protected resource), but in the pre-authenticated case this doesn't apply. You would only configure the `ExceptionTranslationFilter` with an instance of this class if you aren't using pre-authentication in combination with other authentication mechanisms. @@ -81,12 +81,12 @@ It will be called if the user is rejected by the `AbstractPreAuthenticatedProces It always returns a `403`-forbidden response code if called. -=== Concrete Implementations +== Concrete Implementations X.509 authentication is covered in its <>. Here we'll look at some classes which provide support for other pre-authenticated scenarios. -==== Request-Header Authentication (Siteminder) +=== Request-Header Authentication (Siteminder) An external authentication system may supply information to the application by setting specific headers on the HTTP request. A well-known example of this is Siteminder, which passes the username in a header called `SM_USER`. This mechanism is supported by the class `RequestHeaderAuthenticationFilter` which simply extracts the username from the header. @@ -99,7 +99,7 @@ Note that when using a system like this, the framework performs no authenticatio If an attacker is able to forge the headers in their original request without this being detected then they could potentially choose any username they wished. ==== -===== Siteminder Example Configuration +==== Siteminder Example Configuration A typical configuration using this filter would look like this: [source,xml] @@ -132,7 +132,7 @@ We've assumed here that the <> is being used for c It's also assumed that you have added a `UserDetailsService` (called "userDetailsService") to your configuration to load the user's roles. -==== Java EE Container Authentication +=== Java EE Container Authentication The class `J2eePreAuthenticatedProcessingFilter` will extract the username from the `userPrincipal` property of the `HttpServletRequest`. Use of this filter would usually be combined with the use of Java EE roles as described above in <>. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/web/rememberme.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/rememberme.adoc similarity index 96% rename from docs/manual/src/docs/asciidoc/_includes/servlet/web/rememberme.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/authentication/rememberme.adoc index 01d0e7c9525..0b045799fa4 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/web/rememberme.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/rememberme.adoc @@ -1,11 +1,9 @@ - - [[remember-me]] -== Remember-Me Authentication += Remember-Me Authentication [[remember-me-overview]] -=== Overview +== Overview Remember-me or persistent-login authentication refers to web sites being able to remember the identity of a principal between sessions. This is typically accomplished by sending a cookie to the browser, with the cookie being detected during future sessions and causing automated login to take place. Spring Security provides the necessary hooks for these operations to take place, and has two concrete remember-me implementations. @@ -16,7 +14,7 @@ If you are using an authentication provider which doesn't use a `UserDetailsServ [[remember-me-hash-token]] -=== Simple Hash-Based Token Approach +== Simple Hash-Based Token Approach This approach uses hashing to achieve a useful remember-me strategy. In essence a cookie is sent to the browser upon successful interactive authentication, with the cookie being composed as follows: @@ -52,7 +50,7 @@ The `UserDetailsService` will normally be selected automatically. If you have more than one in your application context, you need to specify which one should be used with the `user-service-ref` attribute, where the value is the name of your `UserDetailsService` bean. [[remember-me-persistent-token]] -=== Persistent Token Approach +== Persistent Token Approach This approach is based on the article http://jaspan.com/improved_persistent_login_cookie_best_practice[http://jaspan.com/improved_persistent_login_cookie_best_practice] with some minor modifications footnote:[Essentially, the username is not included in the cookie, to prevent exposing a valid login name unecessarily. There is a discussion on this in the comments section of this article.]. To use the this approach with namespace configuration, you would supply a datasource reference: @@ -76,7 +74,7 @@ create table persistent_logins (username varchar(64) not null, ---- [[remember-me-impls]] -=== Remember-Me Interfaces and Implementations +== Remember-Me Interfaces and Implementations Remember-me is used with `UsernamePasswordAuthenticationFilter`, and is implemented via hooks in the `AbstractAuthenticationProcessingFilter` superclass. It is also used within `BasicAuthenticationFilter`. The hooks will invoke a concrete `RememberMeServices` at the appropriate times. @@ -99,7 +97,7 @@ This design allows any number of remember-me implementation strategies. We've seen above that Spring Security provides two implementations. We'll look at these in turn. -==== TokenBasedRememberMeServices +=== TokenBasedRememberMeServices This implementation supports the simpler approach described in <>. `TokenBasedRememberMeServices` generates a `RememberMeAuthenticationToken`, which is processed by `RememberMeAuthenticationProvider`. A `key` is shared between this authentication provider and the `TokenBasedRememberMeServices`. @@ -132,7 +130,7 @@ The beans required in an application context to enable remember-me services are Don't forget to add your `RememberMeServices` implementation to your `UsernamePasswordAuthenticationFilter.setRememberMeServices()` property, include the `RememberMeAuthenticationProvider` in your `AuthenticationManager.setProviders()` list, and add `RememberMeAuthenticationFilter` into your `FilterChainProxy` (typically immediately after your `UsernamePasswordAuthenticationFilter`). -==== PersistentTokenBasedRememberMeServices +=== PersistentTokenBasedRememberMeServices This class can be used in the same way as `TokenBasedRememberMeServices`, but it additionally needs to be configured with a `PersistentTokenRepository` to store the tokens. There are two standard implementations. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/runas.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/runas.adoc similarity index 98% rename from docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/runas.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/authentication/runas.adoc index 0366d08463b..27207f68c88 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/runas.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/runas.adoc @@ -1,9 +1,8 @@ - [[runas]] -== Run-As Authentication Replacement += Run-As Authentication Replacement [[runas-overview]] -=== Overview +== Overview The `AbstractSecurityInterceptor` is able to temporarily replace the `Authentication` object in the `SecurityContext` and `SecurityContextHolder` during the secure object callback phase. This only occurs if the original `Authentication` object was successfully processed by the `AuthenticationManager` and `AccessDecisionManager`. The `RunAsManager` will indicate the replacement `Authentication` object, if any, that should be used during the `SecurityInterceptorCallback`. @@ -13,7 +12,7 @@ It will also be able to perform any internal security checks for specific `Grant Because Spring Security provides a number of helper classes that automatically configure remoting protocols based on the contents of the `SecurityContextHolder`, these run-as replacements are particularly useful when calling remote web services [[runas-config]] -=== Configuration +== Configuration A `RunAsManager` interface is provided by Spring Security: [source,java] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/web/session-management.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/session-management.adoc similarity index 62% rename from docs/manual/src/docs/asciidoc/_includes/servlet/web/session-management.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/authentication/session-management.adoc index 590d4cfe6bc..71e366bf219 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/web/session-management.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/session-management.adoc @@ -1,11 +1,122 @@ - [[session-mgmt]] -== Session Management += Session Management HTTP session related functionality is handled by a combination of the `SessionManagementFilter` and the `SessionAuthenticationStrategy` interface, which the filter delegates to. Typical usage includes session-fixation protection attack prevention, detection of session timeouts and restrictions on how many sessions an authenticated user may have open concurrently. -=== SessionManagementFilter +== Detecting Timeouts +You can configure Spring Security to detect the submission of an invalid session ID and redirect the user to an appropriate URL. +This is achieved through the `session-management` element: + +[source,xml] +---- + +... + + +---- + +Note that if you use this mechanism to detect session timeouts, it may falsely report an error if the user logs out and then logs back in without closing the browser. +This is because the session cookie is not cleared when you invalidate the session and will be resubmitted even if the user has logged out. +You may be able to explicitly delete the JSESSIONID cookie on logging out, for example by using the following syntax in the logout handler: + +[source,xml] +---- + + + +---- + +Unfortunately this can't be guaranteed to work with every servlet container, so you will need to test it in your environment + +[NOTE] +==== +If you are running your application behind a proxy, you may also be able to remove the session cookie by configuring the proxy server. +For example, using Apache HTTPD's mod_headers, the following directive would delete the `JSESSIONID` cookie by expiring it in the response to a logout request (assuming the application is deployed under the path `/tutorial`): + +[source,xml] +---- + +Header always set Set-Cookie "JSESSIONID=;Path=/tutorial;Expires=Thu, 01 Jan 1970 00:00:00 GMT" + +---- +==== + + +[[ns-concurrent-sessions]] +== Concurrent Session Control +If you wish to place constraints on a single user's ability to log in to your application, Spring Security supports this out of the box with the following simple additions. +First you need to add the following listener to your `web.xml` file to keep Spring Security updated about session lifecycle events: + +[source,xml] +---- + + + org.springframework.security.web.session.HttpSessionEventPublisher + + +---- + +Then add the following lines to your application context: + +[source,xml] +---- + +... + + + + +---- + +This will prevent a user from logging in multiple times - a second login will cause the first to be invalidated. +Often you would prefer to prevent a second login, in which case you can use + +[source,xml] +---- + +... + + + + +---- + +The second login will then be rejected. +By "rejected", we mean that the user will be sent to the `authentication-failure-url` if form-based login is being used. +If the second authentication takes place through another non-interactive mechanism, such as "remember-me", an "unauthorized" (401) error will be sent to the client. +If instead you want to use an error page, you can add the attribute `session-authentication-error-url` to the `session-management` element. + +If you are using a customized authentication filter for form-based login, then you have to configure concurrent session control support explicitly. +More details can be found in the <>. + +[[ns-session-fixation]] +== Session Fixation Attack Protection +http://en.wikipedia.org/wiki/Session_fixation[Session fixation] attacks are a potential risk where it is possible for a malicious attacker to create a session by accessing a site, then persuade another user to log in with the same session (by sending them a link containing the session identifier as a parameter, for example). +Spring Security protects against this automatically by creating a new session or otherwise changing the session ID when a user logs in. +If you don't require this protection, or it conflicts with some other requirement, you can control the behavior using the `session-fixation-protection` attribute on ``, which has four options + +* `none` - Don't do anything. +The original session will be retained. + +* `newSession` - Create a new "clean" session, without copying the existing session data (Spring Security-related attributes will still be copied). + +* `migrateSession` - Create a new session and copy all existing session attributes to the new session. +This is the default in Servlet 3.0 or older containers. + +* `changeSessionId` - Do not create a new session. +Instead, use the session fixation protection provided by the Servlet container (`HttpServletRequest#changeSessionId()`). +This option is only available in Servlet 3.1 (Java EE 7) and newer containers. +Specifying it in older containers will result in an exception. +This is the default in Servlet 3.1 and newer containers. + + +When session fixation protection occurs, it results in a `SessionFixationProtectionEvent` being published in the application context. +If you use `changeSessionId`, this protection will __also__ result in any `javax.servlet.http.HttpSessionIdListener` s being notified, so use caution if your code listens for both events. +See the <> chapter for additional information. + + +== SessionManagementFilter The `SessionManagementFilter` checks the contents of the `SecurityContextRepository` against the current contents of the `SecurityContextHolder` to determine whether a user has been authenticated during the current request, typically by a non-interactive authentication mechanism, such as pre-authentication or remember-me footnote:[ Authentication by mechanisms which perform a redirect after authenticating (such as form-login) will not be detected by `SessionManagementFilter`, as the filter will not be invoked during the authenticating request. Session-management functionality has to be handled separately in these cases. @@ -19,7 +130,7 @@ The most common behaviour is just to redirect to a fixed URL and this is encapsu The latter is also used when configuring an invalid session URL through the namespace,<>. -=== SessionAuthenticationStrategy +== SessionAuthenticationStrategy `SessionAuthenticationStrategy` is used by both `SessionManagementFilter` and `AbstractAuthenticationProcessingFilter`, so if you are using a customized form-login class, for example, you will need to inject it into both of these. In this case, a typical configuration, combining the namespace and custom beans might look like this: @@ -46,7 +157,7 @@ Note that the use of the default, `SessionFixationProtectionStrategy` may cause See the Javadoc for this class for more information. [[concurrent-sessions]] -=== Concurrency Control +== Concurrency Control Spring Security is able to prevent a principal from concurrently authenticating to the same application more than a specified number of times. Many ISVs take advantage of this to enforce licensing, whilst network administrators like this feature because it helps prevent people from sharing login names. You can, for example, stop user "Batman" from logging onto the web application from two different sessions. @@ -143,7 +254,7 @@ Without it, a user will never be able to log back in again once they have exceed [[list-authenticated-principals]] -==== Querying the SessionRegistry for currently authenticated users and their sessions +=== Querying the SessionRegistry for currently authenticated users and their sessions Setting up concurrency-control, either through the namespace or using plain beans has the useful side effect of providing you with a reference to the `SessionRegistry` which you can use directly within your application, so even if you don't want to restrict the number of sessions a user may have, it may be worth setting up the infrastructure anyway. You can set the `maximumSession` property to -1 to allow unlimited sessions. If you're using the namespace, you can set an alias for the internally-created `SessionRegistry` using the `session-registry-alias` attribute, providing a reference which you can inject into your own beans. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/userdetailsservice.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/userdetailsservice.adoc new file mode 100644 index 00000000000..088bddeec01 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/userdetailsservice.adoc @@ -0,0 +1,26 @@ +[[jc-authentication-userdetailsservice]] += UserDetailsService + +You can define custom authentication by exposing a custom `UserDetailsService` as a bean. +For example, the following will customize authentication assuming that `SpringDataUserDetailsService` implements `UserDetailsService`: + +NOTE: This is only used if the `AuthenticationManagerBuilder` has not been populated and no `AuthenticationProviderBean` is defined. + +[source,java] +---- +@Bean +public SpringDataUserDetailsService springDataUserDetailsService() { + return new SpringDataUserDetailsService(); +} +---- + +You can also customize how passwords are encoded by exposing a `PasswordEncoder` as a bean. +For example, if you use bcrypt you can add a bean definition as shown below: + +[source,java] +---- +@Bean +public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); +} +---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/x509.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/x509.adoc similarity index 97% rename from docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/x509.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/authentication/x509.adoc index 6cac537d453..a0d1d19aacd 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/x509.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/x509.adoc @@ -1,10 +1,9 @@ - [[x509]] -== X.509 Authentication += X.509 Authentication [[x509-overview]] -=== Overview +== Overview The most common use of X.509 certificate authentication is in verifying the identity of a server when using SSL, most commonly when using HTTPS from a browser. The browser will automatically check that the certificate presented by a server has been issued (ie digitally signed) by one of a list of trusted certificate authorities which it maintains. @@ -20,7 +19,7 @@ For example, if you're using Tomcat then read the instructions here http://tomca It's important that you get this working before trying it out with Spring Security -=== Adding X.509 Authentication to Your Web Application +== Adding X.509 Authentication to Your Web Application Enabling X.509 client authentication is very straightforward. Just add the `` element to your http security namespace configuration. @@ -52,7 +51,7 @@ If no certificate is found, or no corresponding user could be found then the sec This means that you can easily use X.509 authentication with other options such as a form-based login. [[x509-ssl-config]] -=== Setting up SSL in Tomcat +== Setting up SSL in Tomcat There are some pre-generated certificates in the `samples/certificate` directory in the Spring Security project. You can use these to enable SSL for testing if you don't want to generate your own. The file `server.jks` contains the server certificate, private key and the issuing certificate authority certificate. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/access-decision-manager.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/access-decision-manager.adoc new file mode 100644 index 00000000000..87817195b5c --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/access-decision-manager.adoc @@ -0,0 +1,33 @@ +[[ns-access-manager]] += The Default AccessDecisionManager +This section assumes you have some knowledge of the underlying architecture for access-control within Spring Security. +If you don't you can skip it and come back to it later, as this section is only really relevant for people who need to do some customization in order to use more than simple role-based security. + +When you use a namespace configuration, a default instance of `AccessDecisionManager` is automatically registered for you and will be used for making access decisions for method invocations and web URL access, based on the access attributes you specify in your `intercept-url` and `protect-pointcut` declarations (and in annotations if you are using annotation secured methods). + +The default strategy is to use an `AffirmativeBased` `AccessDecisionManager` with a `RoleVoter` and an `AuthenticatedVoter`. +You can find out more about these in the chapter on <>. + + +[[ns-custom-access-mgr]] +== Customizing the AccessDecisionManager +If you need to use a more complicated access control strategy then it is easy to set an alternative for both method and web security. + +For method security, you do this by setting the `access-decision-manager-ref` attribute on `global-method-security` to the `id` of the appropriate `AccessDecisionManager` bean in the application context: + +[source,xml] +---- + +... + +---- + +The syntax for web security is the same, but on the `http` element: + +[source,xml] +---- + +... + +---- + diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/acls.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/acls.adoc similarity index 99% rename from docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/acls.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/authorization/acls.adoc index 41eb2763570..1e6059ab271 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/acls.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/acls.adoc @@ -1,8 +1,8 @@ [[domain-acls]] -== Domain Object Security (ACLs) += Domain Object Security (ACLs) [[domain-acls-overview]] -=== Overview +== Overview Complex applications often will find the need to define access permissions not simply at a web request or method invocation level. Instead, security decisions need to comprise both who (`Authentication`), where (`MethodInvocation`) and what (`SomeDomainObject`). In other words, authorization decisions also need to consider the actual domain object instance subject of a method invocation. @@ -37,7 +37,7 @@ Fortunately, there is another alternative, which we'll talk about below. [[domain-acls-key-concepts]] -=== Key Concepts +== Key Concepts Spring Security's ACL services are shipped in the `spring-security-acl-xxx.jar`. You will need to add this JAR to your classpath to use Spring Security's domain object instance security capabilities. @@ -130,7 +130,7 @@ We suggest taking a look over these for examples. [[domain-acls-getting-started]] -=== Getting Started +== Getting Started To get starting using Spring Security's ACL capability, you will need to store your ACL information somewhere. This necessitates the instantiation of a `DataSource` using Spring. The `DataSource` is then injected into a `JdbcMutableAclService` and `BasicLookupStrategy` instance. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/index.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/index.adoc index a02e896f429..5dc47a09170 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/index.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/index.adoc @@ -1,5 +1,4 @@ - -[[authorization]] +[[servlet-authorization]] = Authorization The advanced authorization capabilities within Spring Security represent one of the most compelling reasons for its popularity. Irrespective of how you choose to authenticate - whether using a Spring Security-provided mechanism and provider, or integrating with a container or other non-Spring Security authentication authority - you will find the authorization services can be used within your application in a consistent and simple way. @@ -13,3 +12,9 @@ include::secure-objects.adoc[] include::expression-based.adoc[] +include::web.adoc[leveloffset=+1] + +include::method.adoc[leveloffset=+1] + +include::acls.adoc[leveloffset=+1] + diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/method.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/method.adoc new file mode 100644 index 00000000000..797c89b9f1e --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/method.adoc @@ -0,0 +1,193 @@ +[[jc-method]] += Method Security + +From version 2.0 onwards Spring Security has improved support substantially for adding security to your service layer methods. +It provides support for JSR-250 annotation security as well as the framework's original `@Secured` annotation. +From 3.0 you can also make use of new <>. +You can apply security to a single bean, using the `intercept-methods` element to decorate the bean declaration, or you can secure multiple beans across the entire service layer using the AspectJ style pointcuts. + +== EnableGlobalMethodSecurity + +We can enable annotation-based security using the `@EnableGlobalMethodSecurity` annotation on any `@Configuration` instance. +For example, the following would enable Spring Security's `@Secured` annotation. + +[source,java] +---- +@EnableGlobalMethodSecurity(securedEnabled = true) +public class MethodSecurityConfig { +// ... +} +---- + +Adding an annotation to a method (on a class or interface) would then limit the access to that method accordingly. +Spring Security's native annotation support defines a set of attributes for the method. +These will be passed to the AccessDecisionManager for it to make the actual decision: + +[source,java] +---- +public interface BankService { + +@Secured("IS_AUTHENTICATED_ANONYMOUSLY") +public Account readAccount(Long id); + +@Secured("IS_AUTHENTICATED_ANONYMOUSLY") +public Account[] findAccounts(); + +@Secured("ROLE_TELLER") +public Account post(Account account, double amount); +} +---- + +Support for JSR-250 annotations can be enabled using + +[source,java] +---- +@EnableGlobalMethodSecurity(jsr250Enabled = true) +public class MethodSecurityConfig { +// ... +} +---- + +These are standards-based and allow simple role-based constraints to be applied but do not have the power Spring Security's native annotations. +To use the new expression-based syntax, you would use + +[source,java] +---- +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class MethodSecurityConfig { +// ... +} +---- + +and the equivalent Java code would be + +[source,java] +---- +public interface BankService { + +@PreAuthorize("isAnonymous()") +public Account readAccount(Long id); + +@PreAuthorize("isAnonymous()") +public Account[] findAccounts(); + +@PreAuthorize("hasAuthority('ROLE_TELLER')") +public Account post(Account account, double amount); +} +---- + +== GlobalMethodSecurityConfiguration + +Sometimes you may need to perform operations that are more complicated than are possible with the `@EnableGlobalMethodSecurity` annotation allow. +For these instances, you can extend the `GlobalMethodSecurityConfiguration` ensuring that the `@EnableGlobalMethodSecurity` annotation is present on your subclass. +For example, if you wanted to provide a custom `MethodSecurityExpressionHandler`, you could use the following configuration: + +[source,java] +---- +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration { + @Override + protected MethodSecurityExpressionHandler createExpressionHandler() { + // ... create and return custom MethodSecurityExpressionHandler ... + return expressionHandler; + } +} +---- + +For additional information about methods that can be overridden, refer to the `GlobalMethodSecurityConfiguration` Javadoc. + + +[[ns-global-method]] +== The Element +This element is used to enable annotation-based security in your application (by setting the appropriate attributes on the element), and also to group together security pointcut declarations which will be applied across your entire application context. +You should only declare one `` element. +The following declaration would enable support for Spring Security's `@Secured`: + +[source,xml] +---- + +---- + +Adding an annotation to a method (on an class or interface) would then limit the access to that method accordingly. +Spring Security's native annotation support defines a set of attributes for the method. +These will be passed to the `AccessDecisionManager` for it to make the actual decision: + +[source,java] +---- +public interface BankService { + +@Secured("IS_AUTHENTICATED_ANONYMOUSLY") +public Account readAccount(Long id); + +@Secured("IS_AUTHENTICATED_ANONYMOUSLY") +public Account[] findAccounts(); + +@Secured("ROLE_TELLER") +public Account post(Account account, double amount); +} +---- + +Support for JSR-250 annotations can be enabled using + +[source,xml] +---- + +---- + +These are standards-based and allow simple role-based constraints to be applied but do not have the power Spring Security's native annotations. +To use the new expression-based syntax, you would use + +[source,xml] +---- + +---- + +and the equivalent Java code would be + +[source,java] +---- +public interface BankService { + +@PreAuthorize("isAnonymous()") +public Account readAccount(Long id); + +@PreAuthorize("isAnonymous()") +public Account[] findAccounts(); + +@PreAuthorize("hasAuthority('ROLE_TELLER')") +public Account post(Account account, double amount); +} +---- + +Expression-based annotations are a good choice if you need to define simple rules that go beyond checking the role names against the user's list of authorities. + +[NOTE] +==== +The annotated methods will only be secured for instances which are defined as Spring beans (in the same application context in which method-security is enabled). +If you want to secure instances which are not created by Spring (using the `new` operator, for example) then you need to use AspectJ. +==== + +[NOTE] +==== +You can enable more than one type of annotation in the same application, but only one type should be used for any interface or class as the behaviour will not be well-defined otherwise. +If two annotations are found which apply to a particular method, then only one of them will be applied. +==== + +[[ns-protect-pointcut]] +== Adding Security Pointcuts using protect-pointcut + +The use of `protect-pointcut` is particularly powerful, as it allows you to apply security to many beans with only a simple declaration. +Consider the following example: + +[source,xml] +---- + + + +---- + +This will protect all methods on beans declared in the application context whose classes are in the `com.mycompany` package and whose class names end in "Service". +Only users with the `ROLE_USER` role will be able to invoke these methods. +As with URL matching, the most specific matches must come first in the list of pointcuts, as the first matching expression will be used. +Security annotations take precedence over pointcuts. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/web.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/web.adoc new file mode 100644 index 00000000000..e42068c2b06 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/web.adoc @@ -0,0 +1,30 @@ +[[jc-authorize-requests]] += Authorize Requests +Our examples have only required users to be authenticated and have done so for every URL in our application. +We can specify custom requirements for our URLs by adding multiple children to our `http.authorizeRequests()` method. +For example: + + +[source,java] +---- +protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() <1> + .antMatchers("/resources/**", "/signup", "/about").permitAll() <2> + .antMatchers("/admin/**").hasRole("ADMIN") <3> + .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") <4> + .anyRequest().authenticated() <5> + .and() + // ... + .formLogin(); +} +---- + +<1> There are multiple children to the `http.authorizeRequests()` method each matcher is considered in the order they were declared. +<2> We specified multiple URL patterns that any user can access. +Specifically, any user can access a request if the URL starts with "/resources/", equals "/signup", or equals "/about". +<3> Any URL that starts with "/admin/" will be restricted to users who have the role "ROLE_ADMIN". +You will notice that since we are invoking the `hasRole` method we do not need to specify the "ROLE_" prefix. +<4> Any URL that starts with "/db/" requires the user to have both "ROLE_ADMIN" and "ROLE_DBA". +You will notice that since we are using the `hasRole` expression we do not need to specify the "ROLE_" prefix. +<5> Any URL that has not already been matched on only requires that the user be authenticated diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/crypto.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/crypto/index.adoc similarity index 96% rename from docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/crypto.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/crypto/index.adoc index 8ed89233d9f..753c46c4b15 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/crypto.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/crypto/index.adoc @@ -1,22 +1,22 @@ [[crypto]] -== Spring Security Crypto Module += Spring Security Crypto Module [[spring-security-crypto-introduction]] -=== Introduction +== Introduction The Spring Security Crypto module provides support for symmetric encryption, key generation, and password encoding. The code is distributed as part of the core module but has no dependencies on any other Spring Security (or Spring) code. [[spring-security-crypto-encryption]] -=== Encryptors +== Encryptors The Encryptors class provides factory methods for constructing symmetric encryptors. Using this class, you can create ByteEncryptors to encrypt data in raw byte[] form. You can also construct TextEncryptors to encrypt text strings. Encryptors are thread-safe. [[spring-security-crypto-encryption-bytes]] -==== BytesEncryptor +=== BytesEncryptor Use the Encryptors.standard factory method to construct a "standard" BytesEncryptor: [source,java] @@ -39,7 +39,7 @@ String salt = KeyGenerators.string().generateKey(); // generates a random 8-byte ---- [[spring-security-crypto-encryption-text]] -==== TextEncryptor +=== TextEncryptor Use the Encryptors.text factory method to construct a standard TextEncryptor: [source,java] @@ -65,13 +65,13 @@ This is less secure, but necessary for encrypted data that needs to be queried a An example of queryable encrypted text would be an OAuth apiKey. [[spring-security-crypto-keygenerators]] -=== Key Generators +== Key Generators The KeyGenerators class provides a number of convenience factory methods for constructing different types of key generators. Using this class, you can create a BytesKeyGenerator to generate byte[] keys. You can also construct a StringKeyGenerator to generate string keys. KeyGenerators are thread-safe. -==== BytesKeyGenerator +=== BytesKeyGenerator Use the KeyGenerators.secureRandom factory methods to generate a BytesKeyGenerator backed by a SecureRandom instance: [source,java] @@ -95,7 +95,7 @@ Use the KeyGenerators.shared factory method to construct a BytesKeyGenerator tha KeyGenerators.shared(16); ---- -==== StringKeyGenerator +=== StringKeyGenerator Use the KeyGenerators.string factory method to construct a 8-byte, SecureRandom KeyGenerator that hex-encodes each key as a String: [source,java] @@ -104,7 +104,7 @@ KeyGenerators.string(); ---- [[spring-security-crypto-passwordencoders]] -=== Password Encoding +== Password Encoding The password package of the spring-security-crypto module provides support for encoding passwords. `PasswordEncoder` is the central service interface and has the following signature: diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/attacks.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/attacks.adoc new file mode 100644 index 00000000000..4306e4fdeeb --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/attacks.adoc @@ -0,0 +1,45 @@ += CSRF Attacks +Before we discuss how Spring Security can protect applications from CSRF attacks, we will explain what a CSRF attack is. +Let's take a look at a concrete example to get a better understanding. + +Assume that your bank's website provides a form that allows transferring money from the currently logged in user to another bank account. +For example, the HTTP request might look like: + + +[source] +---- +POST /transfer HTTP/1.1 +Host: bank.example.com +Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly +Content-Type: application/x-www-form-urlencoded + +amount=100.00&routingNumber=1234&account=9876 +---- + +Now pretend you authenticate to your bank's website and then, without logging out, visit an evil website. +The evil website contains an HTML page with the following form: + +[source,xml] +---- +
+ + + + +
+---- + +You like to win money, so you click on the submit button. +In the process, you have unintentionally transferred $100 to a malicious user. +This happens because, while the evil website cannot see your cookies, the cookies associated with your bank are still sent along with the request. + +Worst yet, this whole process could have been automated using JavaScript. +This means you didn't even need to click on the button. +So how do we protect ourselves from such attacks? diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/caveats.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/caveats.adoc new file mode 100644 index 00000000000..00f948ea678 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/caveats.adoc @@ -0,0 +1,146 @@ +[[csrf-caveats]] += CSRF Caveats +There are a few caveats when implementing CSRF. + +// FIXME: I think that log in and log out should possible go into when + +[[csrf-timeouts]] +== Timeouts +One issue is that the expected CSRF token is stored in the HttpSession, so as soon as the HttpSession expires your configured `AccessDeniedHandler` will receive a InvalidCsrfTokenException. +If you are using the default `AccessDeniedHandler`, the browser will get an HTTP 403 and display a poor error message. + +[NOTE] +==== +One might ask why the expected `CsrfToken` isn't stored in a cookie by default. +This is because there are known exploits in which headers (i.e. specify the cookies) can be set by another domain. +This is the same reason Ruby on Rails http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails/[no longer skips CSRF checks when the header X-Requested-With is present]. +See http://lists.webappsec.org/pipermail/websecurity_lists.webappsec.org/2011-February/007533.html[this webappsec.org thread] for details on how to perform the exploit. +Another disadvantage is that by removing the state (i.e. the timeout) you lose the ability to forcibly terminate the token if it is compromised. +==== + +A simple way to mitigate an active user experiencing a timeout is to have some JavaScript that lets the user know their session is about to expire. +The user can click a button to continue and refresh the session. + +Alternatively, specifying a custom `AccessDeniedHandler` allows you to process the `InvalidCsrfTokenException` any way you like. +For an example of how to customize the `AccessDeniedHandler` refer to the provided links for both <> and https://github.com/spring-projects/spring-security/blob/3.2.0.RC1/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpAccessDeniedHandlerTests.groovy#L64[Java configuration]. + +Finally, the application can be configured to use <> which will not expire. +As previously mentioned, this is not as secure as using a session, but in many cases can be good enough. + + +[[csrf-login]] +== Logging In +In order to protect against http://en.wikipedia.org/wiki/Cross-site_request_forgery#Forging_login_requests[forging log in requests] the log in form should be protected against CSRF attacks too. +Since the `CsrfToken` is stored in HttpSession, this means an HttpSession will be created as soon as `CsrfToken` token attribute is accessed. +While this sounds bad in a RESTful / stateless architecture the reality is that state is necessary to implement practical security. +Without state, we have nothing we can do if a token is compromised. +Practically speaking, the CSRF token is quite small in size and should have a negligible impact on our architecture. + +A common technique to protect the log in form is by using a JavaScript function to obtain a valid CSRF token before the form submission. +By doing this, there is no need to think about session timeouts (discussed in the previous section) because the session is created right before the form submission (assuming that <> isn't configured instead), so the user can stay on the login page and submit the username/password when he wants. +In order to achieve this, you can take advantage of the `CsrfTokenArgumentResolver` provided by Spring Security and expose an endpoint like it's described on <>. + + +[[csrf-logout]] +== Logging Out +Adding CSRF will update the LogoutFilter to only use HTTP POST. +This ensures that log out requires a CSRF token and that a malicious user cannot forcibly log out your users. + +One approach is to use a form for log out. +If you really want a link, you can use JavaScript to have the link perform a POST (i.e. maybe on a hidden form). +For browsers with JavaScript that is disabled, you can optionally have the link take the user to a log out confirmation page that will perform the POST. + +If you really want to use HTTP GET with logout you can do so, but remember this is generally not recommended. +For example, the following Java Configuration will perform logout with the URL /logout is requested with any HTTP method: + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends +WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .logout() + .logoutRequestMatcher(new AntPathRequestMatcher("/logout")); + } +} +---- + +[[csrf-multipart]] +== Multipart (file upload) +There are two options to using CSRF protection with multipart/form-data. +Each option has its tradeoffs. + +* <> +* <> + +[NOTE] +==== +Before you integrate Spring Security's CSRF protection with multipart file upload, ensure that you can upload without the CSRF protection first. +More information about using multipart forms with Spring can be found within the http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-multipart[17.10 Spring's multipart (file upload) support] section of the Spring reference and the http://docs.spring.io/spring/docs/3.2.x/javadoc-api/org/springframework/web/multipart/support/MultipartFilter.html[MultipartFilter javadoc]. +==== + +[[csrf-multipartfilter]] +=== Placing MultipartFilter before Spring Security +The first option is to ensure that the `MultipartFilter` is specified before the Spring Security filter. +Specifying the `MultipartFilter` before the Spring Security filter means that there is no authorization for invoking the `MultipartFilter` which means anyone can place temporary files on your server. +However, only authorized users will be able to submit a File that is processed by your application. +In general, this is the recommended approach because the temporary file upload should have a negligble impact on most servers. + +To ensure `MultipartFilter` is specified before the Spring Security filter with java configuration, users can override beforeSpringSecurityFilterChain as shown below: + +[source,java] +---- +public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer { + + @Override + protected void beforeSpringSecurityFilterChain(ServletContext servletContext) { + insertFilters(servletContext, new MultipartFilter()); + } +} +---- + +To ensure `MultipartFilter` is specified before the Spring Security filter with XML configuration, users can ensure the element of the `MultipartFilter` is placed before the springSecurityFilterChain within the web.xml as shown below: + +[source,xml] +---- + + MultipartFilter + org.springframework.web.multipart.support.MultipartFilter + + + springSecurityFilterChain + org.springframework.web.filter.DelegatingFilterProxy + + + MultipartFilter + /* + + + springSecurityFilterChain + /* + +---- + +[[csrf-include-csrf-token-in-action]] +=== Include CSRF token in action +If allowing unauthorized users to upload temporariy files is not acceptable, an alternative is to place the `MultipartFilter` after the Spring Security filter and include the CSRF as a query parameter in the action attribute of the form. +An example with a jsp is shown below + +[source,xml] +---- +
+---- + +The disadvantage to this approach is that query parameters can be leaked. +More genearlly, it is considered best practice to place sensitive data within the body or headers to ensure it is not leaked. +Additional information can be found in http://www.w3.org/Protocols/rfc2616/rfc2616-sec15.html#sec15.1.3[RFC 2616 Section 15.1.3 Encoding Sensitive Information in URI's]. + +== HiddenHttpMethodFilter +The HiddenHttpMethodFilter should be placed before the Spring Security filter. +In general this is true, but it could have additional implications when protecting against CSRF attacks. + +Note that the HiddenHttpMethodFilter only overrides the HTTP method on a POST, so this is actually unlikely to cause any real problems. +However, it is still best practice to ensure it is placed before Spring Security's filters. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/index.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/index.adoc new file mode 100644 index 00000000000..f3773335abd --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/index.adoc @@ -0,0 +1,10 @@ +[[csrf]] += Cross Site Request Forgery (CSRF) +This section discusses Spring Security's http://en.wikipedia.org/wiki/Cross-site_request_forgery[ Cross Site Request Forgery (CSRF)] support. + +include::attacks.adoc[leveloffset=+1] +include::synchronized-token-pattern.adoc[leveloffset=+1] +include::when.adoc[leveloffset=+1] +include::using.adoc[leveloffset=+1] +include::caveats.adoc[leveloffset=+1] +include::overriding-defaults.adoc[leveloffset=+1] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/overriding-defaults.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/overriding-defaults.adoc new file mode 100644 index 00000000000..9a339338fd1 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/overriding-defaults.adoc @@ -0,0 +1,9 @@ += Overriding Defaults +Spring Security's goal is to provide defaults that protect your users from exploits. +This does not mean that you are forced to accept all of its defaults. + +For example, you can provide a custom CsrfTokenRepository to override the way in which the `CsrfToken` is stored. + +You can also specify a custom RequestMatcher to determine which requests are protected by CSRF (i.e. perhaps you don't care if log out is exploited). +In short, if Spring Security's CSRF protection doesn't behave exactly as you want it, you are able to customize the behavior. +Refer to the <> documentation for details on how to make these customizations with XML and the `CsrfConfigurer` javadoc for details on how to make these customizations when using Java configuration. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/synchronized-token-pattern.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/synchronized-token-pattern.adoc new file mode 100644 index 00000000000..c54c69a531d --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/synchronized-token-pattern.adoc @@ -0,0 +1,32 @@ += Synchronizer Token Pattern +The issue is that the HTTP request from the bank's website and the request from the evil website are exactly the same. +This means there is no way to reject requests coming from the evil website and allow requests coming from the bank's website. +To protect against CSRF attacks we need to ensure there is something in the request that the evil site is unable to provide. + +One solution is to use the https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#General_Recommendation:_Synchronizer_Token_Pattern[Synchronizer Token Pattern]. +This solution is to ensure that each request requires, in addition to our session cookie, a randomly generated token as an HTTP parameter. +When a request is submitted, the server must look up the expected value for the parameter and compare it against the actual value in the request. +If the values do not match, the request should fail. + +We can relax the expectations to only require the token for each HTTP request that updates state. +This can be safely done since the same origin policy ensures the evil site cannot read the response. +Additionally, we do not want to include the random token in HTTP GET as this can cause the tokens to be leaked. + +Let's take a look at how our example would change. +Assume the randomly generated token is present in an HTTP parameter named _csrf. +For example, the request to transfer money would look like this: + + +[source] +---- +POST /transfer HTTP/1.1 +Host: bank.example.com +Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly +Content-Type: application/x-www-form-urlencoded + +amount=100.00&routingNumber=1234&account=9876&_csrf= +---- + + +You will notice that we added the _csrf parameter with a random value. +Now the evil website will not be able to guess the correct value for the _csrf parameter (which must be explicitly provided on the evil website) and the transfer will fail when the server compares the actual token to the expected token. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/using.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/using.adoc new file mode 100644 index 00000000000..4edfed9ea50 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/using.adoc @@ -0,0 +1,188 @@ +[[csrf-using]] += Using Spring Security CSRF Protection +So what are the steps necessary to use Spring Security's to protect our site against CSRF attacks? The steps to using Spring Security's CSRF protection are outlined below: + +* <> +* <> +* <> + +[[csrf-use-proper-verbs]] +== Use proper HTTP verbs +The first step to protecting against CSRF attacks is to ensure your website uses proper HTTP verbs. +Specifically, before Spring Security's CSRF support can be of use, you need to be certain that your application is using PATCH, POST, PUT, and/or DELETE for anything that modifies state. + +This is not a limitation of Spring Security's support, but instead a general requirement for proper CSRF prevention. +The reason is that including private information in an HTTP GET can cause the information to be leaked. +See http://www.w3.org/Protocols/rfc2616/rfc2616-sec15.html#sec15.1.3[RFC 2616 Section 15.1.3 Encoding Sensitive Information in URI's] for general guidance on using POST instead of GET for sensitive information. + + +[[csrf-configure]] +== Configure CSRF Protection +The next step is to include Spring Security's CSRF protection within your application. +Some frameworks handle invalid CSRF tokens by invaliding the user's session, but this causes <>. +Instead by default Spring Security's CSRF protection will produce an HTTP 403 access denied. +This can be customized by configuring the <> to process `InvalidCsrfTokenException` differently. + +As of Spring Security 4.0, CSRF protection is enabled by default with XML configuration. +If you would like to disable CSRF protection, the corresponding XML configuration can be seen below. + +[source,xml] +---- + + + + +---- + +CSRF protection is enabled by default with Java Configuration. +If you would like to disable CSRF, the corresponding Java configuration can be seen below. +Refer to the Javadoc of csrf() for additional customizations in how CSRF protection is configured. + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends +WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .csrf().disable(); + } +} +---- + +[[csrf-include-csrf-token]] +== Include the CSRF Token + +[[csrf-include-csrf-token-form]] +=== Form Submissions +The last step is to ensure that you include the CSRF token in all PATCH, POST, PUT, and DELETE methods. +One way to approach this is to use the `_csrf` request attribute to obtain the current `CsrfToken`. +An example of doing this with a JSP is shown below: + +[source,xml] +---- + + + + + +---- + +An easier approach is to use <> from the Spring Security JSP tag library. + +[NOTE] +==== +If you are using Spring MVC `` tag or http://www.thymeleaf.org/whatsnew21.html#reqdata[Thymeleaf 2.1+] and are using `@EnableWebSecurity`, the `CsrfToken` is automatically included for you (using the `CsrfRequestDataValueProcessor`). +==== + +[[csrf-include-csrf-token-ajax]] +=== Ajax and JSON Requests +If you are using JSON, then it is not possible to submit the CSRF token within an HTTP parameter. +Instead you can submit the token within a HTTP header. +A typical pattern would be to include the CSRF token within your meta tags. +An example with a JSP is shown below: + + +[source,xml] +---- + + + + + + + + +---- + +Instead of manually creating the meta tags, you can use the simpler <> from the Spring Security JSP tag library. + +You can then include the token within all your Ajax requests. +If you were using jQuery, this could be done with the following: + +[source,javascript] +---- +$(function () { +var token = $("meta[name='_csrf']").attr("content"); +var header = $("meta[name='_csrf_header']").attr("content"); +$(document).ajaxSend(function(e, xhr, options) { + xhr.setRequestHeader(header, token); +}); +}); +---- + +As an alternative to jQuery, we recommend using http://cujojs.com/[cujoJS's] rest.js. +The https://github.com/cujojs/rest[rest.js] module provides advanced support for working with HTTP requests and responses in RESTful ways. +A core capability is the ability to contextualize the HTTP client adding behavior as needed by chaining interceptors on to the client. + +[source,javascript] +---- +var client = rest.chain(csrf, { +token: $("meta[name='_csrf']").attr("content"), +name: $("meta[name='_csrf_header']").attr("content") +}); +---- + + +The configured client can be shared with any component of the application that needs to make a request to the CSRF protected resource. +One significant difference between rest.js and jQuery is that only requests made with the configured client will contain the CSRF token, vs jQuery where __all__ requests will include the token. +The ability to scope which requests receive the token helps guard against leaking the CSRF token to a third party. +Please refer to the https://github.com/cujojs/rest/tree/master/docs[rest.js reference documentation] for more information on rest.js. + +[[csrf-cookie]] +=== CookieCsrfTokenRepository + +There can be cases where users will want to persist the `CsrfToken` in a cookie. +By default the `CookieCsrfTokenRepository` will write to a cookie named `XSRF-TOKEN` and read it from a header named `X-XSRF-TOKEN` or the HTTP parameter `_csrf`. +These defaults come from https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection[AngularJS] + +You can configure `CookieCsrfTokenRepository` in XML using the following: + +[source,xml] +---- + + + + + +---- + +[NOTE] +==== +The sample explicitly sets `cookieHttpOnly=false`. +This is necessary to allow JavaScript (i.e. AngularJS) to read it. +If you do not need the ability to read the cookie with JavaScript directly, it is recommended to omit `cookieHttpOnly=false` to improve security. +==== + + +You can configure `CookieCsrfTokenRepository` in Java Configuration using: + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends + WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .csrf() + .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); + } +} +---- + +[NOTE] +==== +The sample explicitly sets `cookieHttpOnly=false`. +This is necessary to allow JavaScript (i.e. AngularJS) to read it. +If you do not need the ability to read the cookie with JavaScript directly, it is recommended to omit `cookieHttpOnly=false` (by using `new CookieCsrfTokenRepository()` instead) to improve security. +==== diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/when.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/when.adoc new file mode 100644 index 00000000000..c51e96f4ae7 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf/when.adoc @@ -0,0 +1,53 @@ += When to use CSRF protection +When should you use CSRF protection? Our recommendation is to use CSRF protection for any request that could be processed by a browser by normal users. +If you are only creating a service that is used by non-browser clients, you will likely want to disable CSRF protection. + + +== CSRF protection and JSON +A common question is "do I need to protect JSON requests made by javascript?" The short answer is, it depends. +However, you must be very careful as there are CSRF exploits that can impact JSON requests. +For example, a malicious user can create a http://blog.opensecurityresearch.com/2012/02/json-csrf-with-parameter-padding.html[CSRF with JSON using the following form]: + + +[source,xml] +---- +
+ + +
+---- + + +This will produce the following JSON structure + + +[source,javascript] +---- +{ "amount": 100, +"routingNumber": "evilsRoutingNumber", +"account": "evilsAccountNumber", +"ignore_me": "=test" +} +---- + +If an application were not validating the Content-Type, then it would be exposed to this exploit. +Depending on the setup, a Spring MVC application that validates the Content-Type could still be exploited by updating the URL suffix to end with ".json" as shown below: + +[source,xml] +---- +
+ + +
+---- + +== CSRF and Stateless Browser Applications +What if my application is stateless? That doesn't necessarily mean you are protected. +In fact, if a user does not need to perform any actions in the web browser for a given request, they are likely still vulnerable to CSRF attacks. + +For example, consider an application uses a custom cookie that contains all the state within it for authentication instead of the JSESSIONID. +When the CSRF attack is made the custom cookie will be sent with the request in the same manner that the JSESSIONID cookie was sent in our previous example. + +Users using basic authentication are also vulnerable to CSRF attacks since the browser will automatically include the username password in any requests in the same manner that the JSESSIONID cookie was sent in our previous example. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/cache-control.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/cache-control.adoc new file mode 100644 index 00000000000..375b286bac4 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/cache-control.adoc @@ -0,0 +1,69 @@ +[[headers-cache-control]] += Cache Control +In the past Spring Security required you to provide your own cache control for your web application. +This seemed reasonable at the time, but browser caches have evolved to include caches for secure connections as well. +This means that a user may view an authenticated page, log out, and then a malicious user can use the browser history to view the cached page. +To help mitigate this Spring Security has added cache control support which will insert the following headers into you response. + +[source] +---- +Cache-Control: no-cache, no-store, max-age=0, must-revalidate +Pragma: no-cache +Expires: 0 +---- + +Simply adding the <>> element with no child elements will automatically add Cache Control and quite a few other protections. +However, if you only want cache control, you can enable this feature using Spring Security's XML namespace with the <>> element and the <> attribute. + +[source,xml] +---- + + + + + + + +---- + +Similarly, you can enable only cache control within Java Configuration with the following: + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends +WebSecurityConfigurerAdapter { + +@Override +protected void configure(HttpSecurity http) throws Exception { + http + // ... + .headers() + .defaultsDisabled() + .cacheControl(); +} +} +---- + +If you actually want to cache specific responses, your application can selectively invoke http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html#setHeader(java.lang.String,java.lang.String)[HttpServletResponse.setHeader(String,String)] to override the header set by Spring Security. +This is useful to ensure things like CSS, JavaScript, and images are properly cached. + +When using Spring Web MVC, this is typically done within your configuration. +For example, the following configuration will ensure that the cache headers are set for all of your resources: + +[source,java] +---- +@EnableWebMvc +public class WebMvcConfiguration implements WebMvcConfigurer { + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry + .addResourceHandler("/resources/**") + .addResourceLocations("/resources/") + .setCachePeriod(31556926); + } + + // ... +} +---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/content-type-options.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/content-type-options.adoc new file mode 100644 index 00000000000..5b4dc34778e --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/content-type-options.adoc @@ -0,0 +1,57 @@ +[[headers-content-type-options]] += Content Type Options +Historically browsers, including Internet Explorer, would try to guess the content type of a request using http://en.wikipedia.org/wiki/Content_sniffing[content sniffing]. +This allowed browsers to improve the user experience by guessing the content type on resources that had not specified the content type. +For example, if a browser encountered a JavaScript file that did not have the content type specified, it would be able to guess the content type and then execute it. + +[NOTE] +==== +There are many additional things one should do (i.e. only display the document in a distinct domain, ensure Content-Type header is set, sanitize the document, etc) when allowing content to be uploaded. +However, these measures are out of the scope of what Spring Security provides. +It is also important to point out when disabling content sniffing, you must specify the content type in order for things to work properly. +==== + +The problem with content sniffing is that this allowed malicious users to use polyglots (i.e. a file that is valid as multiple content types) to execute XSS attacks. +For example, some sites may allow users to submit a valid postscript document to a website and view it. +A malicious user might create a http://webblaze.cs.berkeley.edu/papers/barth-caballero-song.pdf[postscript document that is also a valid JavaScript file] and execute a XSS attack with it. + +Content sniffing can be disabled by adding the following header to our response: + +[source] +---- +X-Content-Type-Options: nosniff +---- + +Just as with the cache control element, the nosniff directive is added by default when using the element with no child elements. +However, if you want more control over which headers are added you can use the <>> element and the <> attribute as shown below: + +[source,xml] +---- + + + + + + + +---- + +The X-Content-Type-Options header is added by default with Spring Security Java configuration. +If you want more control over the headers, you can explicitly specify the content type options with the following: + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends +WebSecurityConfigurerAdapter { + +@Override +protected void configure(HttpSecurity http) throws Exception { + http + // ... + .headers() + .defaultsDisabled() + .contentTypeOptions(); +} +} +---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/csp.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/csp.adoc new file mode 100644 index 00000000000..0c2a0c3ce5c --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/csp.adoc @@ -0,0 +1,144 @@ +[[headers-csp]] += Content Security Policy (CSP) + +https://www.w3.org/TR/CSP2/[Content Security Policy (CSP)] is a mechanism that web applications can leverage to mitigate content injection vulnerabilities, such as cross-site scripting (XSS). +CSP is a declarative policy that provides a facility for web application authors to declare and ultimately inform the client (user-agent) about the sources from which the web application expects to load resources. + +[NOTE] +==== +Content Security Policy is not intended to solve all content injection vulnerabilities. +Instead, CSP can be leveraged to help reduce the harm caused by content injection attacks. +As a first line of defense, web application authors should validate their input and encode their output. +==== + +A web application may employ the use of CSP by including one of the following HTTP headers in the response: + +* *_Content-Security-Policy_* +* *_Content-Security-Policy-Report-Only_* + +Each of these headers are used as a mechanism to deliver a *_security policy_* to the client. +A security policy contains a set of *_security policy directives_* (for example, _script-src_ and _object-src_), each responsible for declaring the restrictions for a particular resource representation. + +For example, a web application can declare that it expects to load scripts from specific, trusted sources, by including the following header in the response: + +[source] +---- +Content-Security-Policy: script-src https://trustedscripts.example.com +---- + +An attempt to load a script from another source other than what is declared in the _script-src_ directive will be blocked by the user-agent. +Additionally, if the https://www.w3.org/TR/CSP2/#directive-report-uri[*_report-uri_*] directive is declared in the security policy, then the violation will be reported by the user-agent to the declared URL. + +For example, if a web application violates the declared security policy, the following response header will instruct the user-agent to send violation reports to the URL specified in the policy's _report-uri_ directive. + +[source] +---- +Content-Security-Policy: script-src https://trustedscripts.example.com; report-uri /csp-report-endpoint/ +---- + +https://www.w3.org/TR/CSP2/#violation-reports[*_Violation reports_*] are standard JSON structures that can be captured either by the web application's own API or by a publicly hosted CSP violation reporting service, such as, https://report-uri.io/[*_REPORT-URI_*]. + +The *_Content-Security-Policy-Report-Only_* header provides the capability for web application authors and administrators to monitor security policies, rather than enforce them. +This header is typically used when experimenting and/or developing security policies for a site. +When a policy is deemed effective, it can be enforced by using the _Content-Security-Policy_ header field instead. + +Given the following response header, the policy declares that scripts may be loaded from one of two possible sources. + +[source] +---- +Content-Security-Policy-Report-Only: script-src 'self' https://trustedscripts.example.com; report-uri /csp-report-endpoint/ +---- + +If the site violates this policy, by attempting to load a script from _evil.com_, the user-agent will send a violation report to the declared URL specified by the _report-uri_ directive, but still allow the violating resource to load nevertheless. + + +[[headers-csp-configure]] +== Configuring Content Security Policy + +It's important to note that Spring Security *_does not add_* Content Security Policy by default. +The web application author must declare the security policy(s) to enforce and/or monitor for the protected resources. + +For example, given the following security policy: + +[source] +---- +script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/ +---- + +You can enable the CSP header using XML configuration with the <>> element as shown below: + +[source,xml] +---- + + + + + + + +---- + +To enable the CSP _'report-only'_ header, configure the element as follows: + +[source,xml] +---- + + + + + + + +---- + +Similarly, you can enable the CSP header using Java configuration as shown below: + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends +WebSecurityConfigurerAdapter { + +@Override +protected void configure(HttpSecurity http) throws Exception { + http + // ... + .headers() + .contentSecurityPolicy("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/"); +} +} +---- + +To enable the CSP _'report-only'_ header, provide the following Java configuration: + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends +WebSecurityConfigurerAdapter { + +@Override +protected void configure(HttpSecurity http) throws Exception { + http + // ... + .headers() + .contentSecurityPolicy("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/") + .reportOnly(); +} +} +---- + +[[headers-csp-links]] +== Additional Resources + +Applying Content Security Policy to a web application is often a non-trivial undertaking. +The following resources may provide further assistance in developing effective security policies for your site. + +http://www.html5rocks.com/en/tutorials/security/content-security-policy/[An Introduction to Content Security Policy] + +https://developer.mozilla.org/en-US/docs/Web/Security/CSP[CSP Guide - Mozilla Developer Network] + +https://www.w3.org/TR/CSP2/[W3C Candidate Recommendation] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/custom.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/custom.adoc new file mode 100644 index 00000000000..dd9a3cec4e6 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/custom.adoc @@ -0,0 +1,148 @@ +[[headers-custom]] += Custom Headers +Spring Security has mechanisms to make it convenient to add the more common security headers to your application. +However, it also provides hooks to enable adding custom headers. + +[[headers-static]] +== Static Headers +There may be times you wish to inject custom security headers into your application that are not supported out of the box. +For example, given the following custom security header: + +[source] +---- +X-Custom-Security-Header: header-value +---- + +When using the XML namespace, these headers can be added to the response using the <>> element as shown below: + +[source,xml] +---- + + + + +
+ + +---- + +Similarly, the headers could be added to the response using Java Configuration as shown in the following: + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends +WebSecurityConfigurerAdapter { + +@Override +protected void configure(HttpSecurity http) throws Exception { + http + // ... + .headers() + .addHeaderWriter(new StaticHeadersWriter("X-Custom-Security-Header","header-value")); +} +} +---- + +[[headers-writer]] +== Headers Writer +When the namespace or Java configuration does not support the headers you want, you can create a custom `HeadersWriter` instance or even provide a custom implementation of the `HeadersWriter`. + +Let's take a look at an example of using an custom instance of `XFrameOptionsHeaderWriter`. +Perhaps you want to allow framing of content for the same origin. +This is easily supported by setting the <> attribute to "SAMEORIGIN", but let's take a look at a more explicit example using the <> attribute. + +[source,xml] +---- + + + + +
+ + + + +---- + + +We could also restrict framing of content to the same origin with Java configuration: + + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends +WebSecurityConfigurerAdapter { + +@Override +protected void configure(HttpSecurity http) throws Exception { + http + // ... + .headers() + .addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN)); +} +} +---- + + + +[[headers-delegatingrequestmatcherheaderwriter]] +== DelegatingRequestMatcherHeaderWriter +At times you may want to only write a header for certain requests. +For example, perhaps you want to only protect your log in page from being framed. +You could use the `DelegatingRequestMatcherHeaderWriter` to do so. +When using the XML namespace configuration, this can be done with the following: + + +[source,xml] +---- + + + + + +
+ + + + + + + + + + + +---- + + +We could also prevent framing of content to the log in page using java configuration: + + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends +WebSecurityConfigurerAdapter { + +@Override +protected void configure(HttpSecurity http) throws Exception { + RequestMatcher matcher = new AntPathRequestMatcher("/login"); + DelegatingRequestMatcherHeaderWriter headerWriter = + new DelegatingRequestMatcherHeaderWriter(matcher,new XFrameOptionsHeaderWriter()); + http + // ... + .headers() + .frameOptions().disabled() + .addHeaderWriter(headerWriter); +} +} +---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/default.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/default.adoc new file mode 100644 index 00000000000..359f7c4f947 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/default.adoc @@ -0,0 +1,142 @@ += Default Security Headers +Spring Security allows users to easily inject the default security headers to assist in protecting their application. +The default for Spring Security is to include the following headers: + +[source,http] +---- +Cache-Control: no-cache, no-store, max-age=0, must-revalidate +Pragma: no-cache +Expires: 0 +X-Content-Type-Options: nosniff +Strict-Transport-Security: max-age=31536000 ; includeSubDomains +X-Frame-Options: DENY +X-XSS-Protection: 1; mode=block +---- + +NOTE: Strict-Transport-Security is only added on HTTPS requests + +For additional details on each of these headers, refer to the corresponding sections: + +* <> +* <> +* <> +* <> +* <> + +While each of these headers are considered best practice, it should be noted that not all clients utilize the headers, so additional testing is encouraged. + +You can customize specific headers. +For example, assume that want your HTTP response headers to look like the following: + +[source,http] +---- +Cache-Control: no-cache, no-store, max-age=0, must-revalidate +Pragma: no-cache +Expires: 0 +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +X-XSS-Protection: 1; mode=block +---- + +Specifically, you want all of the default headers with the following customizations: + +* <> to allow any request from same domain +* <> will not be addded to the response + +You can easily do this with the following Java Configuration: + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends + WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + // ... + .headers() + .frameOptions().sameOrigin() + .httpStrictTransportSecurity().disable(); + } +} +---- + +Alternatively, if you are using Spring Security XML Configuration, you can use the following: + +[source,xml] +---- + + + + + + + + +---- + +If you do not want the defaults to be added and want explicit control over what should be used, you can disable the defaults. +An example for both Java and XML based configuration is provided below: + +If you are using Spring Security's Java Configuration the following will only add <>. + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends +WebSecurityConfigurerAdapter { + +@Override +protected void configure(HttpSecurity http) throws Exception { + http + // ... + .headers() + // do not use any default headers unless explicitly listed + .defaultsDisabled() + .cacheControl(); +} +} +---- + +The following XML will only add <>. + +[source,xml] +---- + + + + + + + +---- + + +If necessary, you can disable all of the HTTP Security response headers with the following Java Configuration: + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends +WebSecurityConfigurerAdapter { + +@Override +protected void configure(HttpSecurity http) throws Exception { + http + // ... + .headers().disable(); +} +} +---- + +If necessary, you can disable all of the HTTP Security response headers with the following XML configuration below: + +[source,xml] +---- + + + + + +---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/feature-policy.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/feature-policy.adoc new file mode 100644 index 00000000000..2c74e4e07ad --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/feature-policy.adoc @@ -0,0 +1,48 @@ +[[headers-feature]] += Feature Policy + +https://wicg.github.io/feature-policy/[Feature Policy] is a mechanism that allows web developers to selectively enable, disable, and modify the behavior of certain APIs and web features in the browser. + +[source] +---- +Feature-Policy: geolocation 'self' +---- + +With Feature Policy, developers can opt-in to a set of "policies" for the browser to enforce on specific features used throughout your site. +These policies restrict what APIs the site can access or modify the browser's default behavior for certain features. + +[[headers-feature-configure]] +== Configuring Feature Policy + +Spring Security *_doesn't add_* Feature Policy header by default. + +You can enable the Feature-Policy header using XML configuration with the <>> element as shown below: + +[source,xml] +---- + + + + + + + +---- + +Similarly, you can enable the Feature Policy header using Java configuration as shown below: + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends +WebSecurityConfigurerAdapter { + +@Override +protected void configure(HttpSecurity http) throws Exception { + http + // ... + .headers() + .featurePolicy("geolocation 'self'"); +} +} +---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/hpkp.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/hpkp.adoc new file mode 100644 index 00000000000..f8af2d6a8f0 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/hpkp.adoc @@ -0,0 +1,71 @@ +[[headers-hpkp]] += HTTP Public Key Pinning (HPKP) +HTTP Public Key Pinning (HPKP) is a security feature that tells a web client to associate a specific cryptographic public key with a certain web server to prevent Man in the Middle (MITM) attacks with forged certificates. + +To ensure the authenticity of a server's public key used in TLS sessions, this public key is wrapped into a X.509 certificate which is usually signed by a certificate authority (CA). +Web clients such as browsers trust a lot of these CAs, which can all create certificates for arbitrary domain names. +If an attacker is able to compromise a single CA, they can perform MITM attacks on various TLS connections. +HPKP can circumvent this threat for the HTTPS protocol by telling the client which public key belongs to a certain web server. +HPKP is a Trust on First Use (TOFU) technique. +The first time a web server tells a client via a special HTTP header which public keys belong to it, the client stores this information for a given period of time. +When the client visits the server again, it expects a certificate containing a public key whose fingerprint is already known via HPKP. +If the server delivers an unknown public key, the client should present a warning to the user. + +[NOTE] +==== +Because the user-agent needs to validate the pins against the SSL certificate chain, the HPKP header is only injected into HTTPS responses. +==== + +Enabling this feature for your site is as simple as returning the Public-Key-Pins HTTP header when your site is accessed over HTTPS. +For example, the following would instruct the user-agent to only report pin validation failures to a given URI (via the https://tools.ietf.org/html/rfc7469#section-2.1.4[*_report-uri_*] directive) for 2 pins: + +[source] +---- +Public-Key-Pins-Report-Only: max-age=5184000 ; pin-sha256="d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=" ; pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=" ; report-uri="http://example.net/pkp-report" ; includeSubDomains +---- + +A https://tools.ietf.org/html/rfc7469#section-3[*_pin validation failure report_*] is a standard JSON structure that can be captured either by the web application's own API or by a publicly hosted HPKP reporting service, such as, https://report-uri.io/[*_REPORT-URI_*]. + +The optional includeSubDomains directive instructs the browser to also validate subdomains with the given pins. + +Opposed to the other headers, Spring Security does not add HPKP by default. +You can customize HPKP headers with the <>> element as shown below: + +[source,xml] +---- + + + + + + + d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM= + E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g= + + + + +---- + +Similarly, you can enable HPKP headers with Java Configuration: + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends +WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + // ... + .headers() + .httpPublicKeyPinning() + .includeSubdomains(true) + .reportUri("http://example.net/pkp-report") + .addSha256Pins("d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=", "E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g="; + } +} +---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/hsts.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/hsts.adoc new file mode 100644 index 00000000000..ba182c68d0a --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/hsts.adoc @@ -0,0 +1,61 @@ +[[headers-hsts]] += HTTP Strict Transport Security (HSTS) +When you type in your bank's website, do you enter mybank.example.com or do you enter https://mybank.example.com[]? If you omit the https protocol, you are potentially vulnerable to http://en.wikipedia.org/wiki/Man-in-the-middle_attack[Man in the Middle attacks]. +Even if the website performs a redirect to https://mybank.example.com a malicious user could intercept the initial HTTP request and manipulate the response (i.e. redirect to https://mibank.example.com and steal their credentials). + +Many users omit the https protocol and this is why http://tools.ietf.org/html/rfc6797[HTTP Strict Transport Security (HSTS)] was created. +Once mybank.example.com is added as a http://tools.ietf.org/html/rfc6797#section-5.1[HSTS host], a browser can know ahead of time that any request to mybank.example.com should be interpreted as https://mybank.example.com. +This greatly reduces the possibility of a Man in the Middle attack occurring. + +[NOTE] +=== +In accordance with http://tools.ietf.org/html/rfc6797#section-7.2[RFC6797], the HSTS header is only injected into HTTPS responses. +In order for the browser to acknowledge the header, the browser must first trust the CA that signed the SSL certificate used to make the connection (not just the SSL certificate). +=== + +One way for a site to be marked as a HSTS host is to have the host preloaded into the browser. +Another is to add the "Strict-Transport-Security" header to the response. +For example the following would instruct the browser to treat the domain as an HSTS host for a year (there are approximately 31536000 seconds in a year): + +[source] +---- +Strict-Transport-Security: max-age=31536000 ; includeSubDomains +---- + +The optional includeSubDomains directive instructs Spring Security that subdomains (i.e. secure.mybank.example.com) should also be treated as an HSTS domain. + +As with the other headers, Spring Security adds HSTS by default. +You can customize HSTS headers with the <>> element as shown below: + +[source,xml] +---- + + + + + + + +---- + +Similarly, you can enable only HSTS headers with Java Configuration: + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends +WebSecurityConfigurerAdapter { + +@Override +protected void configure(HttpSecurity http) throws Exception { + http + // ... + .headers() + .httpStrictTransportSecurity() + .includeSubdomains(true) + .maxAgeSeconds(31536000); +} +} +---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/index.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/index.adoc new file mode 100644 index 00000000000..b9e36f67bd6 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/index.adoc @@ -0,0 +1,15 @@ +[[headers]] += Security HTTP Response Headers +This section discusses Spring Security's support for adding various security headers to the response. + +include::default.adoc[leveloffset=+1] +include::cache-control.adoc[leveloffset=+1] +include::content-type-options.adoc[leveloffset=+1] +include::hsts.adoc[leveloffset=+1] +include::hpkp.adoc[leveloffset=+1] +include::x-frame-options.adoc[leveloffset=+1] +include::x-xss-protection.adoc[leveloffset=+1] +include::csp.adoc[leveloffset=+1] +include::referrer-policy.adoc[leveloffset=+1] +include::feature-policy.adoc[leveloffset=+1] +include::custom.adoc[leveloffset=+1] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/referrer-policy.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/referrer-policy.adoc new file mode 100644 index 00000000000..554b8278485 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/referrer-policy.adoc @@ -0,0 +1,50 @@ +[[headers-referrer]] += Referrer Policy + +https://www.w3.org/TR/referrer-policy[Referrer Policy] is a mechanism that web applications can leverage to manage the referrer field, which contains the last +page the user was on. + +Spring Security's approach is to use https://www.w3.org/TR/referrer-policy/[Referrer Policy] header, which provides different https://www.w3.org/TR/referrer-policy/#referrer-policies[policies]: + +[source] +---- +Referrer-Policy: same-origin +---- + +The Referrer-Policy response header instructs the browser to let the destination knows the source where the user was previously. + +[[headers-referrer-configure]] +== Configuring Referrer Policy + +Spring Security *_doesn't add_* Referrer Policy header by default. + +You can enable the Referrer-Policy header using XML configuration with the <>> element as shown below: + +[source,xml] +---- + + + + + + + +---- + +Similarly, you can enable the Referrer Policy header using Java configuration as shown below: + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends +WebSecurityConfigurerAdapter { + +@Override +protected void configure(HttpSecurity http) throws Exception { + http + // ... + .headers() + .referrerPolicy(ReferrerPolicy.SAME_ORIGIN); +} +} +---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/x-frame-options.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/x-frame-options.adoc new file mode 100644 index 00000000000..c31651e479d --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/x-frame-options.adoc @@ -0,0 +1,59 @@ +[[headers-frame-options]] += X-Frame-Options +Allowing your website to be added to a frame can be a security issue. +For example, using clever CSS styling users could be tricked into clicking on something that they were not intending (http://www.youtube.com/watch?v=3mk0RySeNsU[video demo]). +For example, a user that is logged into their bank might click a button that grants access to other users. +This sort of attack is known as http://en.wikipedia.org/wiki/Clickjacking[Clickjacking]. + +[NOTE] +==== +Another modern approach to dealing with clickjacking is to use <>. +==== + +There are a number ways to mitigate clickjacking attacks. +For example, to protect legacy browsers from clickjacking attacks you can use https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet#Best-for-now_Legacy_Browser_Frame_Breaking_Script[frame breaking code]. +While not perfect, the frame breaking code is the best you can do for the legacy browsers. + +A more modern approach to address clickjacking is to use https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options[X-Frame-Options] header: + +[source] +---- +X-Frame-Options: DENY +---- + +The X-Frame-Options response header instructs the browser to prevent any site with this header in the response from being rendered within a frame. +By default, Spring Security disables rendering within an iframe. + +You can customize X-Frame-Options with the <> element. +For example, the following will instruct Spring Security to use "X-Frame-Options: SAMEORIGIN" which allows iframes within the same domain: + +[source,xml] +---- + + + + + + + +---- + +Similarly, you can customize frame options to use the same origin within Java Configuration using the following: + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends +WebSecurityConfigurerAdapter { + +@Override +protected void configure(HttpSecurity http) throws Exception { + http + // ... + .headers() + .frameOptions() + .sameOrigin(); +} +} +---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/x-xss-protection.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/x-xss-protection.adoc new file mode 100644 index 00000000000..8c0a56003b8 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers/x-xss-protection.adoc @@ -0,0 +1,49 @@ +[[headers-xss-protection]] += X-XSS-Protection +Some browsers have built in support for filtering out https://www.owasp.org/index.php/Testing_for_Reflected_Cross_site_scripting_(OWASP-DV-001)[reflected XSS attacks]. +This is by no means foolproof, but does assist in XSS protection. + +The filtering is typically enabled by default, so adding the header typically just ensures it is enabled and instructs the browser what to do when a XSS attack is detected. +For example, the filter might try to change the content in the least invasive way to still render everything. +At times, this type of replacement can become a http://hackademix.net/2009/11/21/ies-xss-filter-creates-xss-vulnerabilities/[XSS vulnerability in itself]. +Instead, it is best to block the content rather than attempt to fix it. +To do this we can add the following header: + +[source] +---- +X-XSS-Protection: 1; mode=block +---- + +This header is included by default. +However, we can customize it if we wanted. +For example: + +[source,xml] +---- + + + + + + + +---- + +Similarly, you can customize XSS protection within Java Configuration with the following: + +[source,java] +---- +@EnableWebSecurity +public class WebSecurityConfig extends +WebSecurityConfigurerAdapter { + +@Override +protected void configure(HttpSecurity http) throws Exception { + http + // ... + .headers() + .xssProtection() + .block(false); +} +} +---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/https/channel.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/https/channel.adoc new file mode 100644 index 00000000000..b83dd77eb3b --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/https/channel.adoc @@ -0,0 +1,31 @@ +[[ns-requires-channel]] += Adding HTTP/HTTPS Channel Security +If your application supports both HTTP and HTTPS, and you require that particular URLs can only be accessed over HTTPS, then this is directly supported using the `requires-channel` attribute on ``: + +[source,xml] +---- + + + +... + +---- + +With this configuration in place, if a user attempts to access anything matching the "/secure/**" pattern using HTTP, they will first be redirected to an HTTPS URL footnote:[For more details on how channel-processing is implemented, see the Javadoc for `ChannelProcessingFilter` and related classes.]. +The available options are "http", "https" or "any". +Using the value "any" means that either HTTP or HTTPS can be used. + +If your application uses non-standard ports for HTTP and/or HTTPS, you can specify a list of port mappings as follows: + +[source,xml] +---- + +... + + + + +---- + +Note that in order to be truly secure, an application should not use HTTP at all or switch between HTTP and HTTPS. +It should start in HTTPS (with the user entering an HTTPS URL) and use a secure connection throughout to avoid any possibility of man-in-the-middle attacks. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/https/index.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/https/index.adoc new file mode 100644 index 00000000000..8ae31d50115 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/https/index.adoc @@ -0,0 +1,3 @@ += HTTPS +// FIXME: link to HTTP Headers HSTS +include::channel.adoc[leveloffset=+1] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/index.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/index.adoc new file mode 100644 index 00000000000..150d392a908 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/index.adoc @@ -0,0 +1,6 @@ +[[servlet-exploits]] += Protection Against Exploits + +include::csrf/index.adoc[leveloffset=+1] +include::headers/index.adoc[leveloffset=+1] +include::https/index.adoc[leveloffset=+1] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/hello/boot.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/hello/boot.adoc new file mode 100644 index 00000000000..328cfe1481e --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/hello/boot.adoc @@ -0,0 +1,67 @@ +[[servlet-hello-boot]] += Hello Spring Security (Boot) + +This section covers the minimum setup for how to use Spring Security with Spring Boot. +For how to use Spring Security with Java Configuration, see <>. +For how to use Spring Security with XML Configuration, see <>. + +NOTE: The completed application can be found at {gh-samples-url}/boot/helloworld[samples/boot/helloworld] + +[[servlet-hello-boot-dependencies]] +== Updating Dependencies + +The only step you need to do is update the dependencies by using <> or <>. +For your convenience, you can download a minimal Spring Boot + Spring Security application by https://start.spring.io/starter.zip?type=maven-project&language=java&bootVersion=2.1.2.RELEASE&baseDir=hello-spring-security&groupId=sample&artifactId=sample&name=hello-spring-security&description=Demo+project+for+Spring+Boot&packageName=sample&packaging=jar&javaVersion=1.8&autocomplete=&style=security&style=web&generate-project=[clicking here]. + +== Starting Hello Spring Security Boot + +You can now https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-running-with-the-maven-plugin[run the Spring Boot application] by using the Maven Plugin's `run` goal. +The following example shows how to do so (and the beginning of the output from doing so): + +.Running Spring Boot Application +==== +[source,bash] +---- +$ ./mvn spring-boot:run +... +INFO 23689 --- [ restartedMain] .s.s.UserDetailsServiceAutoConfiguration : + +Using generated security password: 8e557245-73e2-4286-969a-ff57fe326336 + +... +---- +==== + + +[[servlet-hello-boot-auto-configuration]] +== Spring Boot Auto Configuration + +Spring Boot automatically: + +* Enables Spring Security's default configuration, which creates a servlet `Filter` as a bean named `springSecurityFilterChain`. +This bean is responsible for all the security (protecting the application URLs, validating submitted username and passwords, redirecting to the log in form, and so on) within your application. +* Creates a `UserDetailsService` bean with a username of `user` and a randomly generated password that is logged to the console. +* Registers the `Filter` with a bean named `springSecurityFilterChain` with the Servlet container for every request. + +Spring Boot is not configuring much, but it does a lot. +A summary of the features follows: + +* Require an authenticated user for any interaction with the application +* Generate a default login form for you +* Let the user with a username of `user` and a password that is logged to the console to authenticate with form-based authentication (in the preceding example, the password is `8e557245-73e2-4286-969a-ff57fe326336`) +* Protects the password storage with BCrypt +* Lets the user log out +* http://en.wikipedia.org/wiki/Cross-site_request_forgery[CSRF attack] prevention +* http://en.wikipedia.org/wiki/Session_fixation[Session Fixation] protection +* Security Header integration +** http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security[HTTP Strict Transport Security] for secure requests +** http://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx[X-Content-Type-Options] integration +** Cache Control (can be overridden later by your application to allow caching of your static resources) +** http://msdn.microsoft.com/en-us/library/dd565647(v=vs.85).aspx[X-XSS-Protection] integration +** X-Frame-Options integration to help prevent http://en.wikipedia.org/wiki/Clickjacking[Clickjacking] +* Integrate with the following Servlet API methods: +** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[`HttpServletRequest#getRemoteUser()`] +** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[`HttpServletRequest.html#getUserPrincipal()`] +** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[`HttpServletRequest.html#isUserInRole(java.lang.String)`] +** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login(java.lang.String,%20java.lang.String)[`HttpServletRequest.html#login(java.lang.String, java.lang.String)`] +** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout()[`HttpServletRequest.html#logout()`] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/hello/index.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/hello/index.adoc new file mode 100644 index 00000000000..e0aeec66504 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/hello/index.adoc @@ -0,0 +1,8 @@ += Hello Spring Security + +This section covers a minimal Spring Security application that uses <>, <>, or <>. +// FIXME add Spring Boot + +include::boot.adoc[leveloffset=+1] +include::java-configuration.adoc[leveloffset=+1] +include::xml-configuration.adoc[leveloffset=+1] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/hello/java-configuration.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/hello/java-configuration.adoc new file mode 100644 index 00000000000..43ecd5fc041 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/hello/java-configuration.adoc @@ -0,0 +1,138 @@ +[[servlet-hello-jc]] += Hello Spring Security (Java Configuration) + +This section covers how to use Spring Security with Java Configuration. +For how to use Spring Security with XML configuration, see <>. +For how to use Spring Security with Spring Boot configuration, see <>. + +NOTE: You can find the completed application at {gh-samples-url}/javaconfig/helloworld[samples/javaconfig/helloworld]. + +== Updating Dependencies + +The first step is to update the dependencies by using <> or <>. + + +[[servlet-hello-jc-ews]] +== Minimal `@EnableWebSecurity` Configuration + +The first step is to create our Spring Security Java configuration. +The configuration creates a servlet `Filter` (known as the `springSecurityFilterChain`), which is responsible for all the security features (protecting the application URLs, validating submitted username and passwords, redirecting to the log in form, and so on) within your application. +The following example shows the most basic example of a Spring Security Java Configuration: + +.WebSecurity.java +==== +[source,java] +---- +import org.springframework.context.annotation.*; +import org.springframework.security.config.annotation.web.configuration.*; +import org.springframework.security.core.userdetails.*; +import org.springframework.security.provisioning.*; + +@EnableWebSecurity +public class WebSecurityConfig { + + // @formatter:off + @Bean + public UserDetailsService userDetailsService() { + UserDetails user = User.withDefaultPasswordEncoder() + .username("user") + .password("password") + .roles("USER") + .build(); + return new InMemoryUserDetailsManager(user); + } + // @formatter:on +} +---- +==== + +There really is not much to this configuration, but it does a lot. +A summary of the features follows: + +* Require an authenticated user for any interaction with the application +* Generate a default login form for you +* Lets the user with a username of `user` and a password of `password` authenticate with form-based authentication +* Protects the password storage with BCrypt +* Lets the user log out +* http://en.wikipedia.org/wiki/Cross-site_request_forgery[CSRF attack] prevention +* http://en.wikipedia.org/wiki/Session_fixation[Session Fixation] protection +* Security Header integration +** http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security[HTTP Strict Transport Security] for secure requests +** http://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx[X-Content-Type-Options] integration +** Cache Control (can be overridden later by your application to allow caching of your static resources) +** http://msdn.microsoft.com/en-us/library/dd565647(v=vs.85).aspx[X-XSS-Protection] integration +** X-Frame-Options integration to help prevent http://en.wikipedia.org/wiki/Clickjacking[Clickjacking] +* Integrate with the following Servlet API methods: +** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[`HttpServletRequest#getRemoteUser()`] +** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[`HttpServletRequest.html#getUserPrincipal()`] +** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[`HttpServletRequest.html#isUserInRole(java.lang.String)`] +** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login(java.lang.String,%20java.lang.String)[`HttpServletRequest.html#login(java.lang.String, java.lang.String)`] +** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout()[`HttpServletRequest.html#logout()`] + +// FIXME: After completed rewriting, link to all the sections of doc that this relates to + +== Using `AbstractSecurityWebApplicationInitializer` + +The next step is to register the `springSecurityFilterChain` with the war. +Spring Security provides a base class (`AbstractSecurityWebApplicationInitializer`) that leverages https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-servlet[Spring's WebApplicationInitializer support]. + +The following example shows an example configuration: + +.SecurityInitializer.java +==== +[source,java] +---- +import org.springframework.security.web.context.*; + +public class SecurityInitializer + extends AbstractSecurityWebApplicationInitializer { + + public SecurityInitializer() { + super(WebSecurityConfig.class); + } +} +---- +==== + +The `SecurityInitializer` does the following things: + +* Adds a `ContextLoaderListener` that loads the <>. +* Finds the bean of type `Filter` named `springSecurityFilterChain` and registers it to process every URL in the application. + + +[NOTE] +==== +If you are integrating with a Spring MVC application, be sure to configure the `DispatcherServlet` to load the configuration from the root `ApplicationContext`. +The following example shows how to do so: + +.MvcInitializer.java +===== +[source,java] +---- +public class MvcInitializer extends + AbstractAnnotationConfigDispatcherServletInitializer { + + // the Root Config is registered in SecurityInitializer + @Override + protected Class[] getRootConfigClasses() { + return null; + } + + // the Spring MVC configuration should be added to SecurityInitializer constructor + // i.e. + // super(MvcConfig.class, WebSecurityConfig.class); + @Override + protected Class[] getServletConfigClasses() { + return null; + } + + @Override + protected String[] getServletMappings() { + return new String[] { "/" }; + } + +} + +---- +===== +==== diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/hello/xml-configuration.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/hello/xml-configuration.adoc new file mode 100644 index 00000000000..cb049247217 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/hello/xml-configuration.adoc @@ -0,0 +1,148 @@ +[[servlet-hello-xml]] += Hello Spring Security (XML) + +This section covers how to use Spring Security with XML Configuration. +For how to use Spring Security with Java configuration, see <>. +For how to use Spring Security with Spring Boot configuration, see <>. + +== Updating Dependencies + +The first step is to update the dependencies by using <> or <>. + + +[[servlet-hello-xml-http]] +== Minimal `` Configuration + +In this section, we discuss how to use Spring Security with XML Configuration. + +NOTE: The completed application can be found at {gh-samples-url}/xml/helloworld[samples/xml/helloworld] +// FIXME: Link to Java Configuration and Boot + +The first step is to create our Spring Security XML Configuration. +The configuration creates a Servlet `Filter` (known as the `springSecurityFilterChain`), which is responsible for all the security (protecting the application URLs, validating submitted username and passwords, redirecting to the log in form, and so on) within your application. +The following example shows the most basic example of a Spring Security XML Configuration: + +.src/main/webapp/WEB-INF/spring/security.xml +==== +[source,xml] +---- + + + + + + + + +---- +==== + + +There really is not much to this configuration, but it does a lot. +A summary of the features follows: + +* Require an authenticated user for any interaction with the application +* Generate a default login form for you +* Lets the user with a username of `user` and a password of `password` authenticate with form-based authentication +* Protects the password storage with BCrypt +* Lets the user to log out +* http://en.wikipedia.org/wiki/Cross-site_request_forgery[CSRF attack] prevention +* http://en.wikipedia.org/wiki/Session_fixation[Session Fixation] protection +* Security Header integration +** http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security[HTTP Strict Transport Security] for secure requests +** http://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx[X-Content-Type-Options] integration +** Cache Control (can be overridden later by your application to allow caching of your static resources) +** http://msdn.microsoft.com/en-us/library/dd565647(v=vs.85).aspx[X-XSS-Protection] integration +** X-Frame-Options integration to help prevent http://en.wikipedia.org/wiki/Clickjacking[Clickjacking] +* Integrate with the following Servlet API methods: +** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[`HttpServletRequest#getRemoteUser()`] +** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[`HttpServletRequest.html#getUserPrincipal()`] +** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[`HttpServletRequest.html#isUserInRole(java.lang.String)`] +** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login(java.lang.String,%20java.lang.String)[`HttpServletRequest.html#login(java.lang.String, java.lang.String)`] +** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout()[`HttpServletRequest.html#logout()`] + +// FIXME: After completed rewriting, link to all the sections of doc that this relates to + + +[[servlet-hello-xml-webxml]] +== `web.xml` Configuration + +The next step is to ensure that our Security configuration is being read in. +To do so, we need to ensure a `ContextLoaderListener` is registered and the `contextConfigLocation` is including the configuration. +The following example shows how to do so: + +.src/main/webapp/WEB-INF/web.xml +==== +[source,xml] +---- + + + + + + org.springframework.web.context.ContextLoaderListener + + + + + contextConfigLocation + + /WEB-INF/spring/*.xml + + + + + + springSecurityFilterChain + org.springframework.web.filter.DelegatingFilterProxy + + + springSecurityFilterChain + /* + + + +---- +==== + +[NOTE] +==== +If you integrate with an existing Spring MVC application, be sure to configure the `DispatcherServlet` to load the configuration from the root `ApplicationContext`. +The following example shows how to do so: + +===== +.src/main/webapp/WEB-INF/web.xml +[source,xml] +---- + + spring + org.springframework.web.servlet.DispatcherServlet + + + contextConfigLocation + + + + + + spring + / + +---- +===== +==== diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/index.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/index.adoc index 217607f709e..09aca81edc8 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/index.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/index.adoc @@ -1,17 +1,28 @@ +[[servlet]] = Servlet Applications -include::preface/index.adoc[leveloffset=+1] +Spring Security integrates with the Servlet Container by using a standard Servlet `Filter`. +This means it works with any application that runs in a Servlet Container. +More concretely, you do not need to use Spring in your Servlet-based application to take advantage of Spring Security. -include::architecture/index.adoc[leveloffset=+1] +include::hello/index.adoc[leveloffset=+1] -include::test/index.adoc[leveloffset=+1] +include::architecture/index.adoc[leveloffset=+1] -include::web/index.adoc[leveloffset=+1] +include::authentication/index.adoc[leveloffset=+1] include::authorization/index.adoc[leveloffset=+1] -include::additional-topics/index.adoc[leveloffset=+1] +include::oauth2/index.adoc[leveloffset=+1] + +include::exploits/index.adoc[leveloffset=+1] + +include::integrations/index.adoc[leveloffset=+1] + +include::preface/index.adoc[leveloffset=+1] + +include::test/index.adoc[leveloffset=+1] -include::data/index.adoc[leveloffset=+1] +include::crypto/index.adoc[leveloffset=+1] include::appendix/index.adoc[leveloffset=+1] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/concurrency.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/concurrency.adoc similarity index 98% rename from docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/concurrency.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/integrations/concurrency.adoc index a010514c8dc..1c516a00e61 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/concurrency.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/concurrency.adoc @@ -1,7 +1,5 @@ - - [[concurrency]] -== Concurrency Support += Concurrency Support In most environments, Security is stored on a per `Thread` basis. This means that when work is done on a new `Thread`, the `SecurityContext` is lost. @@ -9,7 +7,7 @@ Spring Security provides some infrastructure to help make this much easier for u Spring Security provides low level abstractions for working with Spring Security in multi-threaded environments. In fact, this is what Spring Security builds on to integration with <> and <>. -=== DelegatingSecurityContextRunnable +== DelegatingSecurityContextRunnable One of the most fundamental building blocks within Spring Security's concurrency support is the `DelegatingSecurityContextRunnable`. It wraps a delegate `Runnable` in order to initialize the `SecurityContextHolder` with a specified `SecurityContext` for the delegate. @@ -78,7 +76,7 @@ new Thread(wrappedRunnable).start(); The code we have is simple to use, but it still requires knowledge that we are using Spring Security. In the next section we will take a look at how we can utilize `DelegatingSecurityContextExecutor` to hide the fact that we are using Spring Security. -=== DelegatingSecurityContextExecutor +== DelegatingSecurityContextExecutor In the previous section we found that it was easy to use the `DelegatingSecurityContextRunnable`, but it was not ideal since we had to be aware of Spring Security in order to use it. Let's take a look at how `DelegatingSecurityContextExecutor` can shield our code from any knowledge that we are using Spring Security. @@ -152,7 +150,7 @@ DelegatingSecurityContextExecutor executor = Now anytime `executor.execute(Runnable)` is executed the `SecurityContext` is first obtained by the `SecurityContextHolder` and then that `SecurityContext` is used to create our `DelegatingSecurityContextRunnable`. This means that we are executing our `Runnable` with the same user that was used to invoke the `executor.execute(Runnable)` code. -=== Spring Security Concurrency Classes +== Spring Security Concurrency Classes Refer to the Javadoc for additional integrations with both the Java concurrent APIs and the Spring Task abstractions. They are quite self-explanatory once you understand the previous code. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/web/cors.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/cors.adoc similarity index 98% rename from docs/manual/src/docs/asciidoc/_includes/servlet/web/cors.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/integrations/cors.adoc index 11fd3f9e39e..b3969b4ce06 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/web/cors.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/cors.adoc @@ -1,6 +1,5 @@ - -[[cors]] -== CORS +[[servlet-integrations-cors]] += CORS Spring Framework provides https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-cors[first class support for CORS]. CORS must be processed before Spring Security because the pre-flight request will not contain any cookies (i.e. the `JSESSIONID`). diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/index.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/index.adoc new file mode 100644 index 00000000000..0b810327a88 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/index.adoc @@ -0,0 +1,13 @@ += Integrations + +Integrations with other frameworks, libraries, and APIs. + +include::servlet-api.adoc[leveloffset=+1] +include::spring-data.adoc[leveloffset=+1] +include::concurrency.adoc[leveloffset=+1] +include::jackson.adoc[leveloffset=+1] +include::localization.adoc[leveloffset=+1] +include::mvc.adoc[leveloffset=+1] +include::websocket.adoc[leveloffset=+1] +include::cors.adoc[leveloffset=+1] +include::jsp-taglibs.adoc[leveloffset=+1] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/jackson.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/jackson.adoc similarity index 97% rename from docs/manual/src/docs/asciidoc/_includes/servlet/architecture/jackson.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/integrations/jackson.adoc index d702b6dabd0..4d688f05645 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/jackson.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/jackson.adoc @@ -1,5 +1,5 @@ [[jackson]] -=== Jackson Support += Jackson Support Spring Security has added Jackson Support for persisting Spring Security related classes. This can improve the performance of serializing Spring Security related classes when working with distributed sessions (i.e. session replication, Spring Session, etc). diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/jsp-taglibs.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/jsp-taglibs.adoc similarity index 97% rename from docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/jsp-taglibs.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/integrations/jsp-taglibs.adoc index d0c1c50dc5f..0831308d1d7 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/jsp-taglibs.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/jsp-taglibs.adoc @@ -1,10 +1,9 @@ - [[taglibs]] -== JSP Tag Libraries += JSP Tag Libraries Spring Security has its own taglib which provides basic support for accessing security information and applying security constraints in JSPs. -=== Declaring the Taglib +== Declaring the Taglib To use any of the tags, you must have the security taglib declared in your JSP: [source,xml] @@ -13,7 +12,7 @@ To use any of the tags, you must have the security taglib declared in your JSP: ---- [[taglibs-authorize]] -=== The authorize Tag +== The authorize Tag This tag is used to determine whether its contents should be evaluated or not. In Spring Security 3.0, it can be used in two ways footnote:[ The legacy options from Spring Security 2.0 are also supported, but discouraged. @@ -66,7 +65,7 @@ This approach can also be combined with a `method` attribute, supplying the HTTP The Boolean result of evaluating the tag (whether it grants or denies access) can be stored in a page context scope variable by setting the `var` attribute to the variable name, avoiding the need for duplicating and re-evaluating the condition at other points in the page. -==== Disabling Tag Authorization for Testing +=== Disabling Tag Authorization for Testing Hiding a link in a page for unauthorized users doesn't prevent them from accessing the URL. They could just type it into their browser directly, for example. As part of your testing process, you may want to reveal the hidden areas in order to check that links really are secured at the back end. @@ -78,7 +77,7 @@ Try running the "tutorial" sample application with this property enabled, for ex You can also set the properties `spring.security.securedUIPrefix` and `spring.security.securedUISuffix` if you want to change surrounding text from the default `span` tags (or use empty strings to remove it completely). -=== The authentication Tag +== The authentication Tag This tag allows access to the current `Authentication` object stored in the security context. It renders a property of the object directly in the JSP. So, for example, if the `principal` property of the `Authentication` is an instance of Spring Security's `UserDetails` object, then using `` will render the name of the current user. @@ -87,7 +86,7 @@ Of course, it isn't necessary to use JSP tags for this kind of thing and some pe You can access the `Authentication` object in your MVC controller (by calling `SecurityContextHolder.getContext().getAuthentication()`) and add the data directly to your model for rendering by the view. -=== The accesscontrollist Tag +== The accesscontrollist Tag This tag is only valid when used with Spring Security's ACL module. It checks a comma-separated list of required permissions for a specified domain object. If the current user has all of those permissions, then the tag body will be evaluated. @@ -114,7 +113,7 @@ The `Acl` will be invoked with the required permissions to check if all of them This tag also supports the `var` attribute, in the same way as the `authorize` tag. -=== The csrfInput Tag +== The csrfInput Tag If CSRF protection is enabled, this tag inserts a hidden form field with the correct name and value for the CSRF protection token. If CSRF protection is not enabled, this tag outputs nothing. @@ -135,7 +134,7 @@ Spring Security handles Spring forms automatically. ---- -=== The csrfMetaTags Tag +== The csrfMetaTags Tag If CSRF protection is enabled, this tag inserts meta tags containing the CSRF protection token form field and header names and CSRF protection token value. These meta tags are useful for employing CSRF protection within JavaScript in your applications. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/localization.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/localization.adoc new file mode 100644 index 00000000000..0236b31696a --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/localization.adoc @@ -0,0 +1,36 @@ +[[localization]] += Localization +Spring Security supports localization of exception messages that end users are likely to see. +If your application is designed for English-speaking users, you don't need to do anything as by default all Security messages are in English. +If you need to support other locales, everything you need to know is contained in this section. + +All exception messages can be localized, including messages related to authentication failures and access being denied (authorization failures). +Exceptions and logging messages that are focused on developers or system deployers (including incorrect attributes, interface contract violations, using incorrect constructors, startup time validation, debug-level logging) are not localized and instead are hard-coded in English within Spring Security's code. + +Shipping in the `spring-security-core-xx.jar` you will find an `org.springframework.security` package that in turn contains a `messages.properties` file, as well as localized versions for some common languages. +This should be referred to by your `ApplicationContext`, as Spring Security classes implement Spring's `MessageSourceAware` interface and expect the message resolver to be dependency injected at application context startup time. +Usually all you need to do is register a bean inside your application context to refer to the messages. +An example is shown below: + +[source,xml] +---- + + + +---- + +The `messages.properties` is named in accordance with standard resource bundles and represents the default language supported by Spring Security messages. +This default file is in English. + +If you wish to customize the `messages.properties` file, or support other languages, you should copy the file, rename it accordingly, and register it inside the above bean definition. +There are not a large number of message keys inside this file, so localization should not be considered a major initiative. +If you do perform localization of this file, please consider sharing your work with the community by logging a JIRA task and attaching your appropriately-named localized version of `messages.properties`. + +Spring Security relies on Spring's localization support in order to actually lookup the appropriate message. +In order for this to work, you have to make sure that the locale from the incoming request is stored in Spring's `org.springframework.context.i18n.LocaleContextHolder`. +Spring MVC's `DispatcherServlet` does this for your application automatically, but since Spring Security's filters are invoked before this, the `LocaleContextHolder` needs to be set up to contain the correct `Locale` before the filters are called. +You can either do this in a filter yourself (which must come before the Spring Security filters in `web.xml`) or you can use Spring's `RequestContextFilter`. +Please refer to the Spring Framework documentation for further details on using localization with Spring. + +The "contacts" sample application is set up to use localized messages. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/mvc.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/mvc.adoc similarity index 98% rename from docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/mvc.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/integrations/mvc.adoc index 5bce3f3f30c..c6d8f92bc6c 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/mvc.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/mvc.adoc @@ -1,13 +1,11 @@ - - [[mvc]] -== Spring MVC Integration += Spring MVC Integration Spring Security provides a number of optional integrations with Spring MVC. This section covers the integration in further detail. [[mvc-enablewebmvcsecurity]] -=== @EnableWebMvcSecurity +== @EnableWebMvcSecurity NOTE: As of Spring Security 4.0, `@EnableWebMvcSecurity` is deprecated. The replacement is `@EnableWebSecurity` which will determine adding the Spring MVC features based upon the classpath. @@ -18,7 +16,7 @@ NOTE: Spring Security provides the configuration using Spring MVC's https://docs This means that if you are using more advanced options, like integrating with `WebMvcConfigurationSupport` directly, then you will need to manually provide the Spring Security configuration. [[mvc-requestmatcher]] -=== MvcRequestMatcher +== MvcRequestMatcher Spring Security provides deep integration with how Spring MVC matches on URLs with `MvcRequestMatcher`. This is helpful to ensure your Security rules match the logic used to handle your requests. @@ -148,7 +146,7 @@ or in XML ---- [[mvc-authentication-principal]] -=== @AuthenticationPrincipal +== @AuthenticationPrincipal Spring Security provides `AuthenticationPrincipalArgumentResolver` which can automatically resolve the current `Authentication.getPrincipal()` for Spring MVC arguments. By using `@EnableWebSecurity` you will automatically have this added to your Spring MVC configuration. @@ -274,7 +272,7 @@ public ModelAndView findMessagesForUser(@CurrentUser CustomUser customUser) { [[mvc-async]] -=== Spring MVC Async Integration +== Spring MVC Async Integration Spring Web MVC 3.2+ has excellent support for http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-ann-async[Asynchronous Request Processing]. With no additional configuration, Spring Security will automatically setup the `SecurityContext` to the `Thread` that executes a `Callable` returned by your controllers. @@ -306,9 +304,9 @@ This is because `DeferredResult` is processed by the users and thus there is no However, you can still use <> to provide transparent integration with Spring Security. [[mvc-csrf]] -=== Spring MVC and CSRF Integration +== Spring MVC and CSRF Integration -==== Automatic Token Inclusion +=== Automatic Token Inclusion Spring Security will automatically <> within forms that use the http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/view.html#view-jsp-formtaglib-formtag[Spring MVC form tag]. For example, the following JSP: @@ -352,7 +350,7 @@ Will output HTML that is similar to the following: ---- [[mvc-csrf-resolver]] -==== Resolving the CsrfToken +=== Resolving the CsrfToken Spring Security provides `CsrfTokenArgumentResolver` which can automatically resolve the current `CsrfToken` for Spring MVC arguments. By using <> you will automatically have this added to your Spring MVC configuration. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/web/servlet-api.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/servlet-api.adoc similarity index 94% rename from docs/manual/src/docs/asciidoc/_includes/servlet/web/servlet-api.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/integrations/servlet-api.adoc index 3b1c38ea04c..ad44c770c80 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/web/servlet-api.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/servlet-api.adoc @@ -1,16 +1,15 @@ - [[servletapi]] -== Servlet API integration += Servlet API integration This section describes how Spring Security is integrated with the Servlet API. The https://github.com/spring-projects/spring-security/tree/master/samples/xml/servletapi[servletapi-xml] sample application demonstrates the usage of each of these methods. [[servletapi-25]] -=== Servlet 2.5+ Integration +== Servlet 2.5+ Integration [[servletapi-remote-user]] -==== HttpServletRequest.getRemoteUser() +=== HttpServletRequest.getRemoteUser() The http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[HttpServletRequest.getRemoteUser()] will return the result of `SecurityContextHolder.getContext().getAuthentication().getName()` which is typically the current username. This can be useful if you want to display the current username in your application. Additionally, checking if this is null can be used to indicate if a user has authenticated or is anonymous. @@ -18,7 +17,7 @@ Knowing if the user is authenticated or not can be useful for determining if cer [[servletapi-user-principal]] -==== HttpServletRequest.getUserPrincipal() +=== HttpServletRequest.getUserPrincipal() The http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[HttpServletRequest.getUserPrincipal()] will return the result of `SecurityContextHolder.getContext().getAuthentication()`. This means it is an `Authentication` which is typically an instance of `UsernamePasswordAuthenticationToken` when using username and password based authentication. This can be useful if you need additional information about your user. @@ -43,7 +42,7 @@ Instead, one should centralize it to reduce any coupling of Spring Security and ==== [[servletapi-user-in-role]] -==== HttpServletRequest.isUserInRole(String) +=== HttpServletRequest.isUserInRole(String) The http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[HttpServletRequest.isUserInRole(String)] will determine if `SecurityContextHolder.getContext().getAuthentication().getAuthorities()` contains a `GrantedAuthority` with the role passed into `isUserInRole(String)`. Typically users should not pass in the "ROLE_" prefix into this method since it is added automatically. For example, if you want to determine if the current user has the authority "ROLE_ADMIN", you could use the following: @@ -57,18 +56,18 @@ This might be useful to determine if certain UI components should be displayed. For example, you might display admin links only if the current user is an admin. [[servletapi-3]] -=== Servlet 3+ Integration +== Servlet 3+ Integration The following section describes the Servlet 3 methods that Spring Security integrates with. [[servletapi-authenticate]] -==== HttpServletRequest.authenticate(HttpServletRequest,HttpServletResponse) +=== HttpServletRequest.authenticate(HttpServletRequest,HttpServletResponse) The http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#authenticate%28javax.servlet.http.HttpServletResponse%29[HttpServletRequest.authenticate(HttpServletRequest,HttpServletResponse)] method can be used to ensure that a user is authenticated. If they are not authenticated, the configured AuthenticationEntryPoint will be used to request the user to authenticate (i.e. redirect to the login page). [[servletapi-login]] -==== HttpServletRequest.login(String,String) +=== HttpServletRequest.login(String,String) The http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login%28java.lang.String,%20java.lang.String%29[HttpServletRequest.login(String,String)] method can be used to authenticate the user with the current `AuthenticationManager`. For example, the following would attempt to authenticate with the username "user" and password "password": @@ -87,7 +86,7 @@ It is not necessary to catch the ServletException if you want Spring Security to ==== [[servletapi-logout]] -==== HttpServletRequest.logout() +=== HttpServletRequest.logout() The http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout%28%29[HttpServletRequest.logout()] method can be used to log the current user out. Typically this means that the SecurityContextHolder will be cleared out, the HttpSession will be invalidated, any "Remember Me" authentication will be cleaned up, etc. @@ -96,7 +95,7 @@ It is important to note that after HttpServletRequest.logout() has been invoked, Typically this would involve a redirect to the welcome page. [[servletapi-start-runnable]] -==== AsyncContext.start(Runnable) +=== AsyncContext.start(Runnable) The http://docs.oracle.com/javaee/6/api/javax/servlet/AsyncContext.html#start%28java.lang.Runnable%29[AsynchContext.start(Runnable)] method that ensures your credentials will be propagated to the new Thread. Using Spring Security's concurrency support, Spring Security overrides the AsyncContext.start(Runnable) to ensure that the current SecurityContext is used when processing the Runnable. For example, the following would output the current user's Authentication: @@ -120,7 +119,7 @@ async.start(new Runnable() { ---- [[servletapi-async]] -==== Async Servlet Support +=== Async Servlet Support If you are using Java Based configuration, you are ready to go. If you are using XML configuration, there are a few updates that are necessary. The first step is to ensure you have updated your web.xml to use at least the 3.0 schema as shown below: @@ -189,9 +188,9 @@ When Spring Security automatically saved the SecurityContext on committing the H Since version 3.2, Spring Security is smart enough to no longer automatically save the SecurityContext on commiting the HttpServletResponse as soon as HttpServletRequest.startAsync() is invoked. [[servletapi-31]] -=== Servlet 3.1+ Integration +== Servlet 3.1+ Integration The following section describes the Servlet 3.1 methods that Spring Security integrates with. [[servletapi-change-session-id]] -==== HttpServletRequest#changeSessionId() +=== HttpServletRequest#changeSessionId() The http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#changeSessionId()[HttpServletRequest.changeSessionId()] is the default method for protecting against <> attacks in Servlet 3.1 and higher. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/data/index.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/spring-data.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/data/index.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/integrations/spring-data.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/web/websocket.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/websocket.adoc similarity index 97% rename from docs/manual/src/docs/asciidoc/_includes/servlet/web/websocket.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/integrations/websocket.adoc index d01a38fe228..d2796067fe3 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/web/websocket.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/websocket.adoc @@ -1,5 +1,5 @@ [[websocket]] -== WebSocket Security += WebSocket Security Spring Security 4 added support for securing http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html[Spring's WebSocket support]. This section describes how to use Spring Security's WebSocket support. @@ -14,7 +14,7 @@ Additionally, JSR-356 does not provide a way to intercept messages, so security **** [[websocket-configuration]] -=== WebSocket Configuration +== WebSocket Configuration Spring Security 4.0 has introduced authorization support for WebSockets through the Spring Messaging abstraction. To configure authorization using Java Configuration, simply extend the `AbstractSecurityWebSocketMessageBrokerConfigurer` and configure the `MessageSecurityMetadataSourceRegistry`. @@ -57,7 +57,7 @@ This will ensure that: <3> Our messages require the proper authorization. Specifically, any inbound message that starts with "/user/" will require ROLE_USER. Additional details on authorization can be found in <> [[websocket-authentication]] -=== WebSocket Authentication +== WebSocket Authentication WebSockets reuse the same authentication information that is found in the HTTP request when the WebSocket connection was made. This means that the `Principal` on the `HttpServletRequest` will be handed off to WebSockets. @@ -66,7 +66,7 @@ If you are using Spring Security, the `Principal` on the `HttpServletRequest` is More concretely, to ensure a user has authenticated to your WebSocket application, all that is necessary is to ensure that you setup Spring Security to authenticate your HTTP based web application. [[websocket-authorization]] -=== WebSocket Authorization +== WebSocket Authorization Spring Security 4.0 has introduced authorization support for WebSockets through the Spring Messaging abstraction. To configure authorization using Java Configuration, simply extend the `AbstractSecurityWebSocketMessageBrokerConfigurer` and configure the `MessageSecurityMetadataSourceRegistry`. @@ -136,12 +136,12 @@ This will ensure that: <6> Any other message with a destination is rejected. This is a good idea to ensure that you do not miss any messages. [[websocket-authorization-notes]] -==== WebSocket Authorization Notes +=== WebSocket Authorization Notes In order to properly secure your application it is important to understand Spring's WebSocket support. [[websocket-authorization-notes-messagetypes]] -===== WebSocket Authorization on Message Types +==== WebSocket Authorization on Message Types It is important to understand the distinction between SUBSCRIBE and MESSAGE types of messages and how it works within Spring. @@ -156,7 +156,7 @@ If we allowed sending a MESSAGE to "/topic/system/notifications", then clients c In general, it is common for applications to deny any MESSAGE sent to a message that starts with the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html#websocket-stomp[broker prefix] (i.e. "/topic/" or "/queue/"). [[websocket-authorization-notes-destinations]] -===== WebSocket Authorization on Destinations +==== WebSocket Authorization on Destinations It is also is important to understand how destinations are transformed. @@ -174,7 +174,7 @@ In general, it is common for applications to deny any SUBSCRIBE sent to a messag Of course we may provide exceptions to account for things like [[websocket-authorization-notes-outbound]] -==== Outbound Messages +=== Outbound Messages Spring contains a section titled http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html#websocket-stomp-message-flow[Flow of Messages] that describes how messages flow through the system. It is important to note that Spring Security only secures the `clientInboundChannel`. @@ -185,13 +185,13 @@ For every message that goes in, there are typically many more that go out. Instead of securing the outbound messages, we encourage securing the subscription to the endpoints. [[websocket-sameorigin]] -=== Enforcing Same Origin Policy +== Enforcing Same Origin Policy It is important to emphasize that the browser does not enforce the http://en.wikipedia.org/wiki/Same-origin_policy[Same Origin Policy] for WebSocket connections. This is an extremely important consideration. [[websocket-sameorigin-why]] -==== Why Same Origin? +=== Why Same Origin? Consider the following scenario. A user visits bank.com and authenticates to their account. @@ -206,13 +206,13 @@ Since SockJS tries to emulate WebSockets it also bypasses the Same Origin Policy This means developers need to explicitly protect their applications from external domains when using SockJS. [[websocket-sameorigin-spring]] -==== Spring WebSocket Allowed Origin +=== Spring WebSocket Allowed Origin Fortunately, since Spring 4.1.5 Spring's WebSocket and SockJS support restricts access to the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html#websocket-server-allowed-origins[current domain]. Spring Security adds an additional layer of protection to provide http://en.wikipedia.org/wiki/Defense_in_depth_%28computing%29[defence in depth]. [[websocket-sameorigin-csrf]] -==== Adding CSRF to Stomp Headers +=== Adding CSRF to Stomp Headers By default Spring Security requires the <> in any CONNECT message type. This ensures that only a site that has access to the CSRF token can connect. @@ -263,7 +263,7 @@ stompClient.connect(headers, function(frame) { ---- [[websocket-sameorigin-disable]] -==== Disable CSRF within WebSockets +=== Disable CSRF within WebSockets If you want to allow other domains to access your site, you can disable Spring Security's protection. For example, in Java Configuration you can use the following: @@ -284,13 +284,13 @@ public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBro [[websocket-sockjs]] -=== Working with SockJS +== Working with SockJS http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html#websocket-fallback[SockJS] provides fallback transports to support older browsers. When using the fallback options we need to relax a few security constraints to allow SockJS to work with Spring Security. [[websocket-sockjs-sameorigin]] -==== SockJS & frame-options +=== SockJS & frame-options SockJS may use an https://github.com/sockjs/sockjs-client/tree/v0.3.4[transport that leverages an iframe]. By default Spring Security will <> the site from being framed to prevent Clickjacking attacks. @@ -331,7 +331,7 @@ public class WebSecurityConfig extends ---- [[websocket-sockjs-csrf]] -==== SockJS & Relaxing CSRF +=== SockJS & Relaxing CSRF SockJS uses a POST on the CONNECT messages for any HTTP based transport. Typically we need to include the CSRF token in an HTTP header or an HTTP parameter. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/index.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/index.adoc new file mode 100644 index 00000000000..609f13d6d3b --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/index.adoc @@ -0,0 +1,5 @@ += OAuth2 + +include::login.adoc[leveloffset=+1] + +include::webclient.adoc[leveloffset=+1] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/login.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/login.adoc new file mode 100644 index 00000000000..1352944f0c4 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/login.adoc @@ -0,0 +1,1132 @@ +[[jc-oauth2login]] += OAuth 2.0 Login + +The OAuth 2.0 Login feature provides an application with the capability to have users log in to the application by using their existing account at an OAuth 2.0 Provider (e.g. +GitHub) or OpenID Connect 1.0 Provider (such as Google). +OAuth 2.0 Login implements the use cases: "Login with Google" or "Login with GitHub". + +NOTE: OAuth 2.0 Login is implemented by using the *Authorization Code Grant*, as specified in the https://tools.ietf.org/html/rfc6749#section-4.1[OAuth 2.0 Authorization Framework] and http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth[OpenID Connect Core 1.0]. + +[[jc-oauth2login-sample-boot]] +== Spring Boot 2.0 Sample + +Spring Boot 2.0 brings full auto-configuration capabilities for OAuth 2.0 Login. + +This section shows how to configure the {gh-samples-url}/boot/oauth2login[*OAuth 2.0 Login sample*] using _Google_ as the _Authentication Provider_ and covers the following topics: + +* <> +* <> +* <> +* <> + + +[[jc-oauth2login-sample-initial-setup]] +=== Initial setup + +To use Google's OAuth 2.0 authentication system for login, you must set up a project in the Google API Console to obtain OAuth 2.0 credentials. + +NOTE: https://developers.google.com/identity/protocols/OpenIDConnect[Google's OAuth 2.0 implementation] for authentication conforms to the http://openid.net/connect/[OpenID Connect 1.0] specification and is http://openid.net/certification/[OpenID Certified]. + +Follow the instructions on the https://developers.google.com/identity/protocols/OpenIDConnect[OpenID Connect] page, starting in the section, "Setting up OAuth 2.0". + +After completing the "Obtain OAuth 2.0 credentials" instructions, you should have a new OAuth Client with credentials consisting of a Client ID and a Client Secret. + +[[jc-oauth2login-sample-redirect-uri]] +=== Setting the redirect URI + +The redirect URI is the path in the application that the end-user's user-agent is redirected back to after they have authenticated with Google and have granted access to the OAuth Client _(<>)_ on the Consent page. + +In the "Set a redirect URI" sub-section, ensure that the *Authorized redirect URIs* field is set to `http://localhost:8080/login/oauth2/code/google`. + +TIP: The default redirect URI template is `{baseUrl}/login/oauth2/code/{registrationId}`. +The *_registrationId_* is a unique identifier for the <>. + +[[jc-oauth2login-sample-application-config]] +=== Configure `application.yml` + +Now that you have a new OAuth Client with Google, you need to configure the application to use the OAuth Client for the _authentication flow_. +To do so: + +. Go to `application.yml` and set the following configuration: ++ +[source,yaml] +---- +spring: + security: + oauth2: + client: + registration: <1> + google: <2> + client-id: google-client-id + client-secret: google-client-secret +---- ++ +.OAuth Client properties +=== +<1> `spring.security.oauth2.client.registration` is the base property prefix for OAuth Client properties. +<2> Following the base property prefix is the ID for the <>, such as google. +=== + +. Replace the values in the `client-id` and `client-secret` property with the OAuth 2.0 credentials you created earlier. + + +[[jc-oauth2login-sample-boot-application]] +=== Boot up the application + +Launch the Spring Boot 2.0 sample and go to `http://localhost:8080`. +You are then redirected to the default _auto-generated_ login page, which displays a link for Google. + +Click on the Google link, and you are then redirected to Google for authentication. + +After authenticating with your Google account credentials, the next page presented to you is the Consent screen. +The Consent screen asks you to either allow or deny access to the OAuth Client you created earlier. +Click *Allow* to authorize the OAuth Client to access your email address and basic profile information. + +At this point, the OAuth Client retrieves your email address and basic profile information from the http://openid.net/specs/openid-connect-core-1_0.html#UserInfo[UserInfo Endpoint] and establishes an authenticated session. + +[[jc-oauth2login-client-registration]] +== ClientRegistration + +`ClientRegistration` is a representation of a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider. + +A client registration holds information, such as client id, client secret, +authorization grant type, redirect URI, scope(s), authorization URI, token URI, and other details. + +`ClientRegistration` and its properties are defined as follows: + +[source,java] +---- +public final class ClientRegistration { + private String registrationId; <1> + private String clientId; <2> + private String clientSecret; <3> + private ClientAuthenticationMethod clientAuthenticationMethod; <4> + private AuthorizationGrantType authorizationGrantType; <5> + private String redirectUriTemplate; <6> + private Set scopes; <7> + private ProviderDetails providerDetails; + private String clientName; <8> + + public class ProviderDetails { + private String authorizationUri; <9> + private String tokenUri; <10> + private UserInfoEndpoint userInfoEndpoint; + private String jwkSetUri; <11> + + public class UserInfoEndpoint { + private String uri; <12> + private String userNameAttributeName; <13> + + } + } +} +---- +<1> `registrationId`: The ID that uniquely identifies the `ClientRegistration`. +<2> `clientId`: The client identifier. +<3> `clientSecret`: The client secret. +<4> `clientAuthenticationMethod`: The method used to authenticate the Client with the Provider. +The supported values are *basic* and *post*. +<5> `authorizationGrantType`: The OAuth 2.0 Authorization Framework defines four https://tools.ietf.org/html/rfc6749#section-1.3[Authorization Grant] types. + The supported values are authorization_code and implicit. +<6> `redirectUriTemplate`: The client's registered redirect URI that the _Authorization Server_ redirects the end-user's user-agent + to after the end-user has authenticated and authorized access to the client. + The default redirect URI template is `{baseUrl}/login/oauth2/code/{registrationId}`, which supports URI template variables. +<7> `scopes`: The scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile. +<8> `clientName`: A descriptive name used for the client. +The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page. +<9> `authorizationUri`: The Authorization Endpoint URI for the Authorization Server. +<10> `tokenUri`: The Token Endpoint URI for the Authorization Server. +<11> `jwkSetUri`: The URI used to retrieve the https://tools.ietf.org/html/rfc7517[JSON Web Key (JWK)] Set from the Authorization Server, + which contains the cryptographic key(s) used to verify the https://tools.ietf.org/html/rfc7515[JSON Web Signature (JWS)] of the ID Token and optionally the UserInfo Response. +<12> `(userInfoEndpoint)uri`: The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user. +<13> `userNameAttributeName`: The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user. + +[[jc-oauth2login-boot-property-mappings]] +== Spring Boot 2.0 Property Mappings + +The following table outlines the mapping of the Spring Boot 2.0 OAuth Client properties to the `ClientRegistration` properties. + +|=== +|Spring Boot 2.0 |ClientRegistration + +|`spring.security.oauth2.client.registration._[registrationId]_` +|`registrationId` + +|`spring.security.oauth2.client.registration._[registrationId]_.client-id` +|`clientId` + +|`spring.security.oauth2.client.registration._[registrationId]_.client-secret` +|`clientSecret` + +|`spring.security.oauth2.client.registration._[registrationId]_.client-authentication-method` +|`clientAuthenticationMethod` + +|`spring.security.oauth2.client.registration._[registrationId]_.authorization-grant-type` +|`authorizationGrantType` + +|`spring.security.oauth2.client.registration._[registrationId]_.redirect-uri-template` +|`redirectUriTemplate` + +|`spring.security.oauth2.client.registration._[registrationId]_.scope` +|`scopes` + +|`spring.security.oauth2.client.registration._[registrationId]_.client-name` +|`clientName` + +|`spring.security.oauth2.client.provider._[providerId]_.authorization-uri` +|`providerDetails.authorizationUri` + +|`spring.security.oauth2.client.provider._[providerId]_.token-uri` +|`providerDetails.tokenUri` + +|`spring.security.oauth2.client.provider._[providerId]_.jwk-set-uri` +|`providerDetails.jwkSetUri` + +|`spring.security.oauth2.client.provider._[providerId]_.user-info-uri` +|`providerDetails.userInfoEndpoint.uri` + +|`spring.security.oauth2.client.provider._[providerId]_.userNameAttribute` +|`providerDetails.userInfoEndpoint.userNameAttributeName` +|=== + +[[jc-oauth2login-client-registration-repo]] +== ClientRegistrationRepository + +The `ClientRegistrationRepository` serves as a repository for OAuth 2.0 / OpenID Connect 1.0 `ClientRegistration`(s). + +[NOTE] +Client registration information is ultimately stored and owned by the associated Authorization Server. +This repository provides the ability to retrieve a sub-set of the primary client registration information, +which is stored with the Authorization Server. + +Spring Boot 2.0 auto-configuration binds each of the properties under `spring.security.oauth2.client.registration._[registrationId]_` +to an instance of `ClientRegistration` and then composes each of the `ClientRegistration` instance(s) within a `ClientRegistrationRepository`. + +[NOTE] +The default implementation of `ClientRegistrationRepository` is `InMemoryClientRegistrationRepository`. + +The auto-configuration also registers the `ClientRegistrationRepository` as a `@Bean` in the `ApplicationContext` +so that it is available for dependency-injection, if needed by the application. + +The following listing shows an example: + +[source,java] +---- +@Controller +public class OAuth2LoginController { + + @Autowired + private ClientRegistrationRepository clientRegistrationRepository; + + @RequestMapping("/") + public String index() { + ClientRegistration googleRegistration = + this.clientRegistrationRepository.findByRegistrationId("google"); + + ... + + return "index"; + } +} +---- + +[[jc-oauth2login-common-oauth2-provider]] +== CommonOAuth2Provider + +`CommonOAuth2Provider` pre-defines a set of default client properties for a number of well known providers: Google, GitHub, Facebook, and Okta. + +For example, the `authorization-uri`, `token-uri`, and `user-info-uri` do not change often for a Provider. +Therefore, it makes sense to provide default values in order to reduce the required configuration. + +As demonstrated previously, when we <>, only the `client-id` and `client-secret` properties are required. + +The following listing shows an example: + +[source,yaml] +---- +spring: + security: + oauth2: + client: + registration: + google: + client-id: google-client-id + client-secret: google-client-secret +---- + +[TIP] +The auto-defaulting of client properties works seamlessly here because the `registrationId` (`google`) matches the `GOOGLE` `enum` (case-insensitive) in `CommonOAuth2Provider`. + +For cases where you may want to specify a different `registrationId`, such as `google-login`, +you can still leverage auto-defaulting of client properties by configuring the `provider` property. + +The following listing shows an example: + +[source,yaml] +---- +spring: + security: + oauth2: + client: + registration: + google-login: <1> + provider: google <2> + client-id: google-client-id + client-secret: google-client-secret +---- +<1> The `registrationId` is set to `google-login`. +<2> The `provider` property is set to `google`, which will leverage the auto-defaulting of client properties set in `CommonOAuth2Provider.GOOGLE.getBuilder()`. + +[[jc-oauth2login-custom-provider-properties]] +== Configuring Custom Provider Properties + +There are some OAuth 2.0 Providers that support multi-tenancy, which results in different protocol endpoints for each tenant (or sub-domain). + +For example, an OAuth Client registered with Okta is assigned to a specific sub-domain and have their own protocol endpoints. + +For these cases, Spring Boot 2.0 provides the following base property for configuring custom provider properties: `spring.security.oauth2.client.provider._[providerId]_`. + +The following listing shows an example: + +[source,yaml] +---- +spring: + security: + oauth2: + client: + registration: + okta: + client-id: okta-client-id + client-secret: okta-client-secret + provider: + okta: <1> + authorization-uri: https://your-subdomain.oktapreview.com/oauth2/v1/authorize + token-uri: https://your-subdomain.oktapreview.com/oauth2/v1/token + user-info-uri: https://your-subdomain.oktapreview.com/oauth2/v1/userinfo + user-name-attribute: sub + jwk-set-uri: https://your-subdomain.oktapreview.com/oauth2/v1/keys +---- + +<1> The base property (`spring.security.oauth2.client.provider.okta`) allows for custom configuration of protocol endpoint locations. + +[[jc-oauth2login-override-boot-autoconfig]] +== Overriding Spring Boot 2.0 Auto-configuration + +The Spring Boot 2.0 Auto-configuration class for OAuth Client support is `OAuth2ClientAutoConfiguration`. + +It performs the following tasks: + +* Registers a `ClientRegistrationRepository` `@Bean` composed of `ClientRegistration`(s) from the configured OAuth Client properties. +* Provides a `WebSecurityConfigurerAdapter` `@Configuration` and enables OAuth 2.0 Login through `httpSecurity.oauth2Login()`. + +If you need to override the auto-configuration based on your specific requirements, you may do so in the following ways: + +* <> +* <> +* <> + + +[[jc-oauth2login-register-clientregistrationrepository-bean]] +=== Register a `ClientRegistrationRepository` `@Bean` + +The following example shows how to register a `ClientRegistrationRepository` `@Bean`: + +[source,java] +---- +@Configuration +public class OAuth2LoginConfig { + + @Bean + public ClientRegistrationRepository clientRegistrationRepository() { + return new InMemoryClientRegistrationRepository(this.googleClientRegistration()); + } + + private ClientRegistration googleClientRegistration() { + return ClientRegistration.withRegistrationId("google") + .clientId("google-client-id") + .clientSecret("google-client-secret") + .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .redirectUriTemplate("{baseUrl}/login/oauth2/code/{registrationId}") + .scope("openid", "profile", "email", "address", "phone") + .authorizationUri("https://accounts.google.com/o/oauth2/v2/auth") + .tokenUri("https://www.googleapis.com/oauth2/v4/token") + .userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo") + .userNameAttributeName(IdTokenClaimNames.SUB) + .jwkSetUri("https://www.googleapis.com/oauth2/v3/certs") + .clientName("Google") + .build(); + } +} +---- + + +[[jc-oauth2login-provide-websecurityconfigureradapter]] +=== Provide a `WebSecurityConfigurerAdapter` + +The following example shows how to provide a `WebSecurityConfigurerAdapter` with `@EnableWebSecurity` and enable OAuth 2.0 login through `httpSecurity.oauth2Login()`: + +[source,java] +---- +@EnableWebSecurity +public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().authenticated() + .and() + .oauth2Login(); + } +} +---- + + +[[jc-oauth2login-completely-override-autoconfiguration]] +=== Completely Override the Auto-configuration + +The following example shows how to completely override the auto-configuration by both registering a `ClientRegistrationRepository` `@Bean` and providing a `WebSecurityConfigurerAdapter`, both of which were described in the two preceding sections. + +[source,java] +---- +@Configuration +public class OAuth2LoginConfig { + + @EnableWebSecurity + public static class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().authenticated() + .and() + .oauth2Login(); + } + } + + @Bean + public ClientRegistrationRepository clientRegistrationRepository() { + return new InMemoryClientRegistrationRepository(this.googleClientRegistration()); + } + + private ClientRegistration googleClientRegistration() { + return ClientRegistration.withRegistrationId("google") + .clientId("google-client-id") + .clientSecret("google-client-secret") + .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .redirectUriTemplate("{baseUrl}/login/oauth2/code/{registrationId}") + .scope("openid", "profile", "email", "address", "phone") + .authorizationUri("https://accounts.google.com/o/oauth2/v2/auth") + .tokenUri("https://www.googleapis.com/oauth2/v4/token") + .userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo") + .userNameAttributeName(IdTokenClaimNames.SUB) + .jwkSetUri("https://www.googleapis.com/oauth2/v3/certs") + .clientName("Google") + .build(); + } +} +---- + +[[jc-oauth2login-javaconfig-wo-boot]] +== Java Configuration without Spring Boot 2.0 + +If you are not able to use Spring Boot 2.0 and would like to configure one of the pre-defined providers in `CommonOAuth2Provider` (for example, Google), apply the following configuration: + +[source,java] +---- +@Configuration +public class OAuth2LoginConfig { + + @EnableWebSecurity + public static class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().authenticated() + .and() + .oauth2Login(); + } + } + + @Bean + public ClientRegistrationRepository clientRegistrationRepository() { + return new InMemoryClientRegistrationRepository(this.googleClientRegistration()); + } + + @Bean + public OAuth2AuthorizedClientService authorizedClientService() { + return new InMemoryOAuth2AuthorizedClientService(this.clientRegistrationRepository()); + } + + private ClientRegistration googleClientRegistration() { + return CommonOAuth2Provider.GOOGLE.getBuilder("google") + .clientId("google-client-id") + .clientSecret("google-client-secret") + .build(); + } +} +---- + +[[jc-oauth2login-authorized-client]] +== OAuth2AuthorizedClient / OAuth2AuthorizedClientService + +`OAuth2AuthorizedClient` is a representation of an Authorized Client. +A client is considered to be authorized when the end-user (Resource Owner) has granted authorization to the client to access its protected resources. + +`OAuth2AuthorizedClient` serves the purpose of associating an `OAuth2AccessToken` to a `ClientRegistration` (client) and resource owner, who is the `Principal` end-user that granted the authorization. + +The primary role of the `OAuth2AuthorizedClientService` is to manage `OAuth2AuthorizedClient` instances. +From a developer perspective, it provides the capability to lookup an `OAuth2AccessToken` associated with a client so that it may be used to initiate a request to a resource server. + +[NOTE] +Spring Boot 2.0 Auto-configuration registers an `OAuth2AuthorizedClientService` `@Bean` in the `ApplicationContext`. + +The developer may also register an `OAuth2AuthorizedClientService` `@Bean` in the `ApplicationContext` (overriding Spring Boot 2.0 Auto-configuration) in order to have the ability to lookup an `OAuth2AccessToken` associated with a specific `ClientRegistration` (client). + +The following listing shows an example: + +[source,java] +---- +@Controller +public class OAuth2LoginController { + + @Autowired + private OAuth2AuthorizedClientService authorizedClientService; + + @RequestMapping("/userinfo") + public String userinfo(OAuth2AuthenticationToken authentication) { + // authentication.getAuthorizedClientRegistrationId() returns the + // registrationId of the Client that was authorized during the Login flow + OAuth2AuthorizedClient authorizedClient = + this.authorizedClientService.loadAuthorizedClient( + authentication.getAuthorizedClientRegistrationId(), + authentication.getName()); + + OAuth2AccessToken accessToken = authorizedClient.getAccessToken(); + + ... + + return "userinfo"; + } +} +---- + +[[oauth2login-advanced]] +== OAuth 2.0 Login -- Advanced Configuration + +`HttpSecurity.oauth2Login()` provides a number of configuration options for customizing OAuth 2.0 Login. +The main configuration options are grouped into their protocol endpoint counterparts. + +For example, `oauth2Login().authorizationEndpoint()` allows configuring the _Authorization Endpoint_, +whereas `oauth2Login().tokenEndpoint()` allows configuring the _Token Endpoint_. + +The following code shows an example: + +[source,java] +---- +@EnableWebSecurity +public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .oauth2Login() + .authorizationEndpoint() + ... + .redirectionEndpoint() + ... + .tokenEndpoint() + ... + .userInfoEndpoint() + ... + } +} +---- + +The main goal of the `oauth2Login()` DSL was to closely align with the naming, as defined in the specifications. + +The OAuth 2.0 Authorization Framework defines the https://tools.ietf.org/html/rfc6749#section-3[Protocol Endpoints] as follows: + +The authorization process utilizes two authorization server endpoints (HTTP resources): + +* Authorization Endpoint: Used by the client to obtain authorization from the resource owner via user-agent redirection. +* Token Endpoint: Used by the client to exchange an authorization grant for an access token, typically with client authentication. + +As well as one client endpoint: + +* Redirection Endpoint: Used by the authorization server to return responses +containing authorization credentials to the client via the resource owner user-agent. + +The OpenID Connect Core 1.0 specification defines the http://openid.net/specs/openid-connect-core-1_0.html#UserInfo[UserInfo Endpoint] as follows: + +The UserInfo Endpoint is an OAuth 2.0 Protected Resource that returns claims about the authenticated end-user. +To obtain the requested claims about the end-user, the client makes a request to the UserInfo Endpoint +by using an access token obtained through OpenID Connect Authentication. +These claims are normally represented by a JSON object that contains a collection of name-value pairs for the claims. + +The following code shows the complete configuration options available for the `oauth2Login()` DSL: + +[source,java] +---- +@EnableWebSecurity +public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .oauth2Login() + .clientRegistrationRepository(this.clientRegistrationRepository()) + .authorizedClientService(this.authorizedClientService()) + .loginPage("/login") + .authorizationEndpoint() + .baseUri(this.authorizationRequestBaseUri()) + .authorizationRequestRepository(this.authorizationRequestRepository()) + .and() + .redirectionEndpoint() + .baseUri(this.authorizationResponseBaseUri()) + .and() + .tokenEndpoint() + .accessTokenResponseClient(this.accessTokenResponseClient()) + .and() + .userInfoEndpoint() + .userAuthoritiesMapper(this.userAuthoritiesMapper()) + .userService(this.oauth2UserService()) + .oidcUserService(this.oidcUserService()) + .customUserType(GitHubOAuth2User.class, "github"); + } +} +---- + +The sections to follow go into more detail on each of the configuration options available: + +* <> +* <> +* <> +* <> +* <> + + +[[oauth2login-advanced-login-page]] +=== OAuth 2.0 Login Page + +By default, the OAuth 2.0 Login Page is auto-generated by the `DefaultLoginPageGeneratingFilter`. +The default login page shows each configured OAuth Client with its `ClientRegistration.clientName` +as a link, which is capable of initiating the Authorization Request (or OAuth 2.0 Login). + +The link's destination for each OAuth Client defaults to the following: + +`OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI` + "/{registrationId}" + +The following line shows an example: + +[source,html] +---- +Google +---- + +To override the default login page, +configure `oauth2Login().loginPage()` and (optionally) `oauth2Login().authorizationEndpoint().baseUri()`. + +The following listing shows an example: + +[source,java] +---- +@EnableWebSecurity +public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .oauth2Login() + .loginPage("/login/oauth2") + ... + .authorizationEndpoint() + .baseUri("/login/oauth2/authorization") + .... + } +} +---- + +[IMPORTANT] +You need to provide a `@Controller` with a `@RequestMapping("/login/oauth2")` that is capable of rendering the custom login page. + +[TIP] +==== +As noted earlier, configuring `oauth2Login().authorizationEndpoint().baseUri()` is optional. +However, if you choose to customize it, ensure the link to each OAuth Client matches the `authorizationEndpoint().baseUri()`. + +The following line shows an example: + +[source,html] +---- +Google +---- +==== + +[[oauth2login-advanced-authorization-endpoint]] +=== Authorization Endpoint + + +[[oauth2login-advanced-authorization-request-repository]] +==== `AuthorizationRequestRepository` + +`AuthorizationRequestRepository` is responsible for the persistence of the `OAuth2AuthorizationRequest` +from the time the Authorization Request is initiated to the time the Authorization Response +is received (the callback). + +[TIP] +The `OAuth2AuthorizationRequest` is used to correlate and validate the Authorization Response. + +The default implementation of `AuthorizationRequestRepository` is `HttpSessionOAuth2AuthorizationRequestRepository`, +which stores the `OAuth2AuthorizationRequest` in the `HttpSession`. + +If you would like to provide a custom implementation of `AuthorizationRequestRepository` +that stores the attributes of `OAuth2AuthorizationRequest` in a `Cookie`, +configure it as shown in the following example: + +[source,java] +---- +@EnableWebSecurity +public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .oauth2Login() + .authorizationEndpoint() + .authorizationRequestRepository(this.cookieAuthorizationRequestRepository()) + ... + } + + private AuthorizationRequestRepository cookieAuthorizationRequestRepository() { + return new HttpCookieOAuth2AuthorizationRequestRepository(); + } +} +---- + +[[oauth2login-advanced-redirection-endpoint]] +=== Redirection Endpoint + +The Redirection Endpoint is used by the Authorization Server for returning the Authorization Response +(which contains the authorization credentials) to the client via the Resource Owner user-agent. + +[TIP] +OAuth 2.0 Login leverages the Authorization Code Grant. +Therefore, the authorization credential is the authorization code. + +The default Authorization Response `baseUri` (redirection endpoint) is `*/login/oauth2/code/**`, which is defined in `OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI`. + +If you would like to customize the Authorization Response `baseUri`, configure it as shown in the following example: + +[source,java] +---- +@EnableWebSecurity +public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .oauth2Login() + .redirectionEndpoint() + .baseUri("/login/oauth2/callback/*") + .... + } +} +---- + +[IMPORTANT] +==== +You also need to ensure the `ClientRegistration.redirectUriTemplate` matches the custom Authorization Response `baseUri`. + +The following listing shows an example: + +[source,java] +---- +return CommonOAuth2Provider.GOOGLE.getBuilder("google") + .clientId("google-client-id") + .clientSecret("google-client-secret") + .redirectUriTemplate("{baseUrl}/login/oauth2/callback/{registrationId}") + .build(); +---- +==== + +[[oauth2login-advanced-token-endpoint]] +=== Token Endpoint + + +[[oauth2login-advanced-token-client]] +==== OAuth2AccessTokenResponseClient + +`OAuth2AccessTokenResponseClient` is responsible for exchanging an authorization grant credential +for an access token credential at the Authorization Server's Token Endpoint. + +The default implementation of `OAuth2AccessTokenResponseClient` is `NimbusAuthorizationCodeTokenResponseClient`, +which exchanges an authorization code for an access token at the Token Endpoint. + +[NOTE] +`NimbusAuthorizationCodeTokenResponseClient` uses the https://connect2id.com/products/nimbus-oauth-openid-connect-sdk[Nimbus OAuth 2.0 SDK] internally. + +If you would like to provide a custom implementation of `OAuth2AccessTokenResponseClient` +that uses Spring Framework 5 reactive `WebClient` for initiating requests to the Token Endpoint, +configure it as shown in the following example: + +[source,java] +---- +@EnableWebSecurity +public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .oauth2Login() + .tokenEndpoint() + .accessTokenResponseClient(this.accessTokenResponseClient()) + ... + } + + private OAuth2AccessTokenResponseClient accessTokenResponseClient() { + return new SpringWebClientAuthorizationCodeTokenResponseClient(); + } +} +---- + +[[oauth2login-advanced-userinfo-endpoint]] +=== UserInfo Endpoint + +The UserInfo Endpoint includes a number of configuration options, as described in the following sub-sections: + +* <> +* <> +* <> +* <> + + +[[oauth2login-advanced-map-authorities]] +==== Mapping User Authorities + +After the user successfully authenticates with the OAuth 2.0 Provider, +the `OAuth2User.getAuthorities()` (or `OidcUser.getAuthorities()`) may be mapped to a new set of `GrantedAuthority` instances, +which will be supplied to `OAuth2AuthenticationToken` when completing the authentication. + +[TIP] +`OAuth2AuthenticationToken.getAuthorities()` is used for authorizing requests, such as in `hasRole('USER')` or `hasRole('ADMIN')`. + +There are a couple of options to choose from when mapping user authorities: + +* <> +* <> + +[[oauth2login-advanced-map-authorities-grantedauthoritiesmapper]] +===== Using a `GrantedAuthoritiesMapper` + +Provide an implementation of `GrantedAuthoritiesMapper` and configure it as shown in the following example: + +[source,java] +---- +@EnableWebSecurity +public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .oauth2Login() + .userInfoEndpoint() + .userAuthoritiesMapper(this.userAuthoritiesMapper()) + ... + } + + private GrantedAuthoritiesMapper userAuthoritiesMapper() { + return (authorities) -> { + Set mappedAuthorities = new HashSet<>(); + + authorities.forEach(authority -> { + if (OidcUserAuthority.class.isInstance(authority)) { + OidcUserAuthority oidcUserAuthority = (OidcUserAuthority)authority; + + OidcIdToken idToken = oidcUserAuthority.getIdToken(); + OidcUserInfo userInfo = oidcUserAuthority.getUserInfo(); + + // Map the claims found in idToken and/or userInfo + // to one or more GrantedAuthority's and add it to mappedAuthorities + + } else if (OAuth2UserAuthority.class.isInstance(authority)) { + OAuth2UserAuthority oauth2UserAuthority = (OAuth2UserAuthority)authority; + + Map userAttributes = oauth2UserAuthority.getAttributes(); + + // Map the attributes found in userAttributes + // to one or more GrantedAuthority's and add it to mappedAuthorities + + } + }); + + return mappedAuthorities; + }; + } +} +---- + +Alternatively, you may register a `GrantedAuthoritiesMapper` `@Bean` to have it automatically applied to the configuration, as shown in the following example: + +[source,java] +---- +@EnableWebSecurity +public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.oauth2Login(); + } + + @Bean + public GrantedAuthoritiesMapper userAuthoritiesMapper() { + ... + } +} +---- + +[[oauth2login-advanced-map-authorities-oauth2userservice]] +===== Delegation-based strategy with `OAuth2UserService` + +This strategy is advanced compared to using a `GrantedAuthoritiesMapper`, however, it's also more flexible +as it gives you access to the `OAuth2UserRequest` and `OAuth2User` (when using an OAuth 2.0 UserService) +or `OidcUserRequest` and `OidcUser` (when using an OpenID Connect 1.0 UserService). + +The `OAuth2UserRequest` (and `OidcUserRequest`) provides you access to the associated `OAuth2AccessToken`, +which is very useful in the cases where the _delegator_ needs to fetch authority information +from a protected resource before it can map the custom authorities for the user. + +The following example shows how to implement and configure a delegation-based strategy using an OpenID Connect 1.0 UserService: + +[source,java] +---- +@EnableWebSecurity +public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .oauth2Login() + .userInfoEndpoint() + .oidcUserService(this.oidcUserService()) + ... + } + + private OAuth2UserService oidcUserService() { + final OidcUserService delegate = new OidcUserService(); + + return (userRequest) -> { + // Delegate to the default implementation for loading a user + OidcUser oidcUser = delegate.loadUser(userRequest); + + OAuth2AccessToken accessToken = userRequest.getAccessToken(); + Set mappedAuthorities = new HashSet<>(); + + // TODO + // 1) Fetch the authority information from the protected resource using accessToken + // 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities + + // 3) Create a copy of oidcUser but use the mappedAuthorities instead + oidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo()); + + return oidcUser; + }; + } +} +---- + +[[oauth2login-advanced-custom-user]] +==== Configuring a Custom OAuth2User + +`CustomUserTypesOAuth2UserService` is an implementation of an `OAuth2UserService` +that provides support for custom `OAuth2User` types. + +If the default implementation (`DefaultOAuth2User`) does not suit your needs, +you can define your own implementation of `OAuth2User`. + +The following code demonstrates how you would register a custom `OAuth2User` type for GitHub: + +[source,java] +---- +@EnableWebSecurity +public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .oauth2Login() + .userInfoEndpoint() + .customUserType(GitHubOAuth2User.class, "github") + ... + } +} +---- + +The following code shows an example of a custom `OAuth2User` type for GitHub: + +[source,java] +---- +public class GitHubOAuth2User implements OAuth2User { + private List authorities = + AuthorityUtils.createAuthorityList("ROLE_USER"); + private Map attributes; + private String id; + private String name; + private String login; + private String email; + + @Override + public Collection getAuthorities() { + return this.authorities; + } + + @Override + public Map getAttributes() { + if (this.attributes == null) { + this.attributes = new HashMap<>(); + this.attributes.put("id", this.getId()); + this.attributes.put("name", this.getName()); + this.attributes.put("login", this.getLogin()); + this.attributes.put("email", this.getEmail()); + } + return attributes; + } + + public String getId() { + return this.id; + } + + public void setId(String id) { + this.id = id; + } + + @Override + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLogin() { + return this.login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getEmail() { + return this.email; + } + + public void setEmail(String email) { + this.email = email; + } +} +---- + +[TIP] +`id`, `name`, `login`, and `email` are attributes returned in GitHub's UserInfo Response. +For detailed information returned from the UserInfo Endpoint, see the API documentation +for https://developer.github.com/v3/users/#get-the-authenticated-user["Get the authenticated user"]. + +[[oauth2login-advanced-oauth2-user-service]] +==== OAuth 2.0 UserService + +`DefaultOAuth2UserService` is an implementation of an `OAuth2UserService` +that supports standard OAuth 2.0 Provider's. + +[NOTE] +`OAuth2UserService` obtains the user attributes +of the end-user (the resource owner) from the UserInfo Endpoint (by using the +access token granted to the client during the authorization flow) +and returns an `AuthenticatedPrincipal` in the form of an `OAuth2User`. + +If the default implementation does not suit your needs, you can define your own implementation of `OAuth2UserService` +for standard OAuth 2.0 Provider's. + +The following configuration demonstrates how to configure a custom `OAuth2UserService`: + +[source,java] +---- +@EnableWebSecurity +public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .oauth2Login() + .userInfoEndpoint() + .userService(this.oauth2UserService()) + ... + } + + private OAuth2UserService oauth2UserService() { + return new CustomOAuth2UserService(); + } +} +---- + +[[oauth2login-advanced-oidc-user-service]] +==== OpenID Connect 1.0 UserService + +`OidcUserService` is an implementation of an `OAuth2UserService` +that supports OpenID Connect 1.0 Provider's. + +[NOTE] +`OAuth2UserService` is responsible for obtaining the user attributes +of the end user (the resource owner) from the UserInfo Endpoint (by using the +access token granted to the client during the authorization flow) +and return an `AuthenticatedPrincipal` in the form of an `OidcUser`. + +If the default implementation does not suit your needs, you can define your own implementation of `OAuth2UserService` +for OpenID Connect 1.0 Provider's. + +The following configuration demonstrates how to configure a custom OpenID Connect 1.0 `OAuth2UserService`: + +[source,java] +---- +@EnableWebSecurity +public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .oauth2Login() + .userInfoEndpoint() + .oidcUserService(this.oidcUserService()) + ... + } + + private OAuth2UserService oidcUserService() { + return new CustomOidcUserService(); + } +} +---- + + + +[[jc-oauth2login-resources]] +== Additional Resources + +The following additional resources describe advanced configuration options: + +* <> +* Authorization Endpoint: +** <> +* <> +* Token Endpoint: +** <> +* UserInfo Endpoint: +** <> +** <> +** <> +** <> + diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/webclient.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/webclient.adoc similarity index 98% rename from docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/webclient.adoc rename to docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/webclient.adoc index 939b432b74b..ca7cd05edd6 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/webclient.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/webclient.adoc @@ -1,5 +1,5 @@ [[servlet-webclient]] -= WebClient for Servlet Environments += WebClient OAuth2 for Servlet Environments [NOTE] ==== diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/preface/java-configuration.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/preface/java-configuration.adoc index 9b300d95706..9ebbf425811 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/preface/java-configuration.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/preface/java-configuration.adoc @@ -9,123 +9,6 @@ If you are familiar with the <> then you should find quite a few simi NOTE: Spring Security provides https://github.com/spring-projects/spring-security/tree/master/samples/javaconfig[lots of sample applications] which demonstrate the use of Spring Security Java Configuration. -== Hello Web Security Java Configuration - -The first step is to create our Spring Security Java Configuration. -The configuration creates a Servlet Filter known as the `springSecurityFilterChain` which is responsible for all the security (protecting the application URLs, validating submitted username and passwords, redirecting to the log in form, etc) within your application. -You can find the most basic example of a Spring Security Java Configuration below: - -[[jc-hello-wsca]] -[source,java] ----- -import org.springframework.beans.factory.annotation.Autowired; - -import org.springframework.context.annotation.*; -import org.springframework.security.config.annotation.authentication.builders.*; -import org.springframework.security.config.annotation.web.configuration.*; - -@EnableWebSecurity -public class WebSecurityConfig implements WebMvcConfigurer { - - @Bean - public UserDetailsService userDetailsService() throws Exception { - InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); - manager.createUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build()); - return manager; - } -} ----- - -There really isn't much to this configuration, but it does a lot. -You can find a summary of the features below: - -* Require authentication to every URL in your application -* Generate a login form for you -* Allow the user with the *Username* _user_ and the *Password* _password_ to authenticate with form based authentication -* Allow the user to logout -* http://en.wikipedia.org/wiki/Cross-site_request_forgery[CSRF attack] prevention -* http://en.wikipedia.org/wiki/Session_fixation[Session Fixation] protection -* Security Header integration -** http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security[HTTP Strict Transport Security] for secure requests -** http://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx[X-Content-Type-Options] integration -** Cache Control (can be overridden later by your application to allow caching of your static resources) -** http://msdn.microsoft.com/en-us/library/dd565647(v=vs.85).aspx[X-XSS-Protection] integration -** X-Frame-Options integration to help prevent http://en.wikipedia.org/wiki/Clickjacking[Clickjacking] -* Integrate with the following Servlet API methods -** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[HttpServletRequest#getRemoteUser()] -** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[HttpServletRequest.html#getUserPrincipal()] -** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[HttpServletRequest.html#isUserInRole(java.lang.String)] -** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login(java.lang.String,%20java.lang.String)[HttpServletRequest.html#login(java.lang.String, java.lang.String)] -** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout()[HttpServletRequest.html#logout()] - -=== AbstractSecurityWebApplicationInitializer - -The next step is to register the `springSecurityFilterChain` with the war. -This can be done in Java Configuration with http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-container-config[Spring's WebApplicationInitializer support] in a Servlet 3.0+ environment. -Not suprisingly, Spring Security provides a base class `AbstractSecurityWebApplicationInitializer` that will ensure the `springSecurityFilterChain` gets registered for you. -The way in which we use `AbstractSecurityWebApplicationInitializer` differs depending on if we are already using Spring or if Spring Security is the only Spring component in our application. - -* <> - Use these instructions if you are not using Spring already -* <> - Use these instructions if you are already using Spring - -=== AbstractSecurityWebApplicationInitializer without Existing Spring - -If you are not using Spring or Spring MVC, you will need to pass in the `WebSecurityConfig` into the superclass to ensure the configuration is picked up. -You can find an example below: - -[source,java] ----- -import org.springframework.security.web.context.*; - -public class SecurityWebApplicationInitializer - extends AbstractSecurityWebApplicationInitializer { - - public SecurityWebApplicationInitializer() { - super(WebSecurityConfig.class); - } -} ----- - -The `SecurityWebApplicationInitializer` will do the following things: - -* Automatically register the springSecurityFilterChain Filter for every URL in your application -* Add a ContextLoaderListener that loads the <>. - -=== AbstractSecurityWebApplicationInitializer with Spring MVC - -If we were using Spring elsewhere in our application we probably already had a `WebApplicationInitializer` that is loading our Spring Configuration. -If we use the previous configuration we would get an error. -Instead, we should register Spring Security with the existing `ApplicationContext`. -For example, if we were using Spring MVC our `SecurityWebApplicationInitializer` would look something like the following: - -[source,java] ----- -import org.springframework.security.web.context.*; - -public class SecurityWebApplicationInitializer - extends AbstractSecurityWebApplicationInitializer { - -} ----- - -This would simply only register the springSecurityFilterChain Filter for every URL in your application. -After that we would ensure that `WebSecurityConfig` was loaded in our existing ApplicationInitializer. -For example, if we were using Spring MVC it would be added in the `getRootConfigClasses()` - -[[message-web-application-inititializer-java]] -[source,java] ----- -public class MvcWebApplicationInitializer extends - AbstractAnnotationConfigDispatcherServletInitializer { - - @Override - protected Class[] getRootConfigClasses() { - return new Class[] { WebSecurityConfig.class }; - } - - // ... other overrides ... -} ----- [[jc-httpsecurity]] == HttpSecurity @@ -167,890 +50,8 @@ The Java Configuration equivalent of closing an XML tag is expressed using the ` If you read the code it also makes sense. I want to configure authorized requests __and__ configure form login __and__ configure HTTP Basic authentication. -[[jc-form]] -== Java Configuration and Form Login -You might be wondering where the login form came from when you were prompted to log in, since we made no mention of any HTML files or JSPs. -Since Spring Security's default configuration does not explicitly set a URL for the login page, Spring Security generates one automatically, based on the features that are enabled and using standard values for the URL which processes the submitted login, the default target URL the user will be sent to after logging in and so on. - -While the automatically generated log in page is convenient to get up and running quickly, most applications will want to provide their own log in page. -To do so we can update our configuration as seen below: - - -[source,java] ----- -protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest().authenticated() - .and() - .formLogin() - .loginPage("/login") // <1> - .permitAll(); // <2> -} ----- - -<1> The updated configuration specifies the location of the log in page. -<2> We must grant all users (i.e. unauthenticated users) access to our log in page. -The `formLogin().permitAll()` method allows granting access to all users for all URLs associated with form based log in. - -An example log in page implemented with JSPs for our current configuration can be seen below: - -NOTE: The login page below represents our current configuration. -We could easily update our configuration if some of the defaults do not meet our needs. - -[source,html] ----- - -
<1> - <2> -

- Invalid username and password. -

-
- <3> -

- You have been logged out. -

-
-

- - <4> -

-

- - <5> -

- - name="${_csrf.parameterName}" - value="${_csrf.token}"/> - -
----- - -<1> A POST to the `/login` URL will attempt to authenticate the user -<2> If the query parameter `error` exists, authentication was attempted and failed -<3> If the query parameter `logout` exists, the user was successfully logged out -<4> The username must be present as the HTTP parameter named __username__ -<5> The password must be present as the HTTP parameter named __password__ -<6> We must <> To learn more read the <> section of the reference - -[[jc-authorize-requests]] -== Authorize Requests -Our examples have only required users to be authenticated and have done so for every URL in our application. -We can specify custom requirements for our URLs by adding multiple children to our `http.authorizeRequests()` method. -For example: - - -[source,java] ----- -protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() <1> - .antMatchers("/resources/**", "/signup", "/about").permitAll() <2> - .antMatchers("/admin/**").hasRole("ADMIN") <3> - .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") <4> - .anyRequest().authenticated() <5> - .and() - // ... - .formLogin(); -} ----- - -<1> There are multiple children to the `http.authorizeRequests()` method each matcher is considered in the order they were declared. -<2> We specified multiple URL patterns that any user can access. -Specifically, any user can access a request if the URL starts with "/resources/", equals "/signup", or equals "/about". -<3> Any URL that starts with "/admin/" will be restricted to users who have the role "ROLE_ADMIN". -You will notice that since we are invoking the `hasRole` method we do not need to specify the "ROLE_" prefix. -<4> Any URL that starts with "/db/" requires the user to have both "ROLE_ADMIN" and "ROLE_DBA". -You will notice that since we are using the `hasRole` expression we do not need to specify the "ROLE_" prefix. -<5> Any URL that has not already been matched on only requires that the user be authenticated - -[[jc-logout]] -== Handling Logouts - -When using the `{security-api-url}org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.html[WebSecurityConfigurerAdapter]`, logout capabilities are automatically applied. -The default is that accessing the URL `/logout` will log the user out by: - -- Invalidating the HTTP Session -- Cleaning up any RememberMe authentication that was configured -- Clearing the `SecurityContextHolder` -- Redirect to `/login?logout` - -Similar to configuring login capabilities, however, you also have various options to further customize your logout requirements: - -[source,java] ----- -protected void configure(HttpSecurity http) throws Exception { - http - .logout() <1> - .logoutUrl("/my/logout") <2> - .logoutSuccessUrl("/my/index") <3> - .logoutSuccessHandler(logoutSuccessHandler) <4> - .invalidateHttpSession(true) <5> - .addLogoutHandler(logoutHandler) <6> - .deleteCookies(cookieNamesToClear) <7> - .and() - ... -} ----- - -<1> Provides logout support. -This is automatically applied when using `WebSecurityConfigurerAdapter`. -<2> The URL that triggers log out to occur (default is `/logout`). -If CSRF protection is enabled (default), then the request must also be a POST. -For more information, please consult the {security-api-url}org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.html#logoutUrl-java.lang.String-[JavaDoc]. -<3> The URL to redirect to after logout has occurred. -The default is `/login?logout`. -For more information, please consult the {security-api-url}org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.html#logoutSuccessUrl-java.lang.String-[JavaDoc]. -<4> Let's you specify a custom `LogoutSuccessHandler`. -If this is specified, `logoutSuccessUrl()` is ignored. -For more information, please consult the {security-api-url}org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.html#logoutSuccessHandler-org.springframework.security.web.authentication.logout.LogoutSuccessHandler-[JavaDoc]. -<5> Specify whether to invalidate the `HttpSession` at the time of logout. -This is *true* by default. -Configures the `SecurityContextLogoutHandler` under the covers. -For more information, please consult the {security-api-url}org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.html#invalidateHttpSession-boolean-[JavaDoc]. -<6> Adds a `LogoutHandler`. -`SecurityContextLogoutHandler` is added as the last `LogoutHandler` by default. -<7> Allows specifying the names of cookies to be removed on logout success. -This is a shortcut for adding a `CookieClearingLogoutHandler` explicitly. - -[NOTE] -=== -Logouts can of course also be configured using the XML Namespace notation. -Please see the documentation for the <> in the Spring Security XML Namespace section for further details. -=== - -Generally, in order to customize logout functionality, you can add -`{security-api-url}org/springframework/security/web/authentication/logout/LogoutHandler.html[LogoutHandler]` -and/or -`{security-api-url}org/springframework/security/web/authentication/logout/LogoutSuccessHandler.html[LogoutSuccessHandler]` -implementations. -For many common scenarios, these handlers are applied under the -covers when using the fluent API. - -[[jc-logout-handler]] -=== LogoutHandler - -Generally, `{security-api-url}org/springframework/security/web/authentication/logout/LogoutHandler.html[LogoutHandler]` -implementations indicate classes that are able to participate in logout handling. -They are expected to be invoked to perform necessary clean-up. -As such they should -not throw exceptions. -Various implementations are provided: - -- {security-api-url}org/springframework/security/web/authentication/rememberme/PersistentTokenBasedRememberMeServices.html[PersistentTokenBasedRememberMeServices] -- {security-api-url}org/springframework/security/web/authentication/rememberme/TokenBasedRememberMeServices.html[TokenBasedRememberMeServices] -- {security-api-url}org/springframework/security/web/authentication/logout/CookieClearingLogoutHandler.html[CookieClearingLogoutHandler] -- {security-api-url}org/springframework/security/web/csrf/CsrfLogoutHandler.html[CsrfLogoutHandler] -- {security-api-url}org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html[SecurityContextLogoutHandler] -- {security-api-url}org/springframework/security/web/authentication/logout/HeaderWriterLogoutHandler.html[HeaderWriterLogoutHandler] - -Please see <> for details. - -Instead of providing `LogoutHandler` implementations directly, the fluent API also provides shortcuts that provide the respective `LogoutHandler` implementations under the covers. -E.g. `deleteCookies()` allows specifying the names of one or more cookies to be removed on logout success. -This is a shortcut compared to adding a `CookieClearingLogoutHandler`. - -[[jc-logout-success-handler]] -=== LogoutSuccessHandler - -The `LogoutSuccessHandler` is called after a successful logout by the `LogoutFilter`, to handle e.g. -redirection or forwarding to the appropriate destination. -Note that the interface is almost the same as the `LogoutHandler` but may raise an exception. - -The following implementations are provided: - -- {security-api-url}org/springframework/security/web/authentication/logout/SimpleUrlLogoutSuccessHandler.html[SimpleUrlLogoutSuccessHandler] -- HttpStatusReturningLogoutSuccessHandler - -As mentioned above, you don't need to specify the `SimpleUrlLogoutSuccessHandler` directly. -Instead, the fluent API provides a shortcut by setting the `logoutSuccessUrl()`. -This will setup the `SimpleUrlLogoutSuccessHandler` under the covers. -The provided URL will be redirected to after a logout has occurred. -The default is `/login?logout`. - -The `HttpStatusReturningLogoutSuccessHandler` can be interesting in REST API type scenarios. -Instead of redirecting to a URL upon the successful logout, this `LogoutSuccessHandler` allows you to provide a plain HTTP status code to be returned. -If not configured a status code 200 will be returned by default. - -[[jc-logout-references]] -=== Further Logout-Related References - -- <> -- <> -- <> -- <> -- <> in section CSRF Caveats -- Section <> (CAS protocol) -- Documentation for the <> in the Spring Security XML Namespace section - - -include::oauth2-client.adoc[] - -include::oauth2-login.adoc[] - - -[[oauth2resourceserver]] -== OAuth 2.0 Resource Server - -Spring Security supports protecting endpoints using https://tools.ietf.org/html/rfc7519[JWT]-encoded OAuth 2.0 https://tools.ietf.org/html/rfc6750.html[Bearer Tokens]. - -This is handy in circumstances where an application has federated its authority management out to an https://tools.ietf.org/html/rfc6749[authorization server] (for example, Okta or Ping Identity). -This authorization server can be consulted by Resource Servers to validate authority when serving requests. - -[NOTE] -==== -A complete working example can be found in {gh-samples-url}/boot/oauth2resourceserver[*OAuth 2.0 Resource Server Servlet sample*]. -==== - -=== Dependencies - -Most Resource Server support is collected into `spring-security-oauth2-resource-server`. -However, the support for decoding and verifying JWTs is in `spring-security-oauth2-jose`, meaning that both are necessary in order to have a working resource server that supports JWT-encoded Bearer Tokens. - -[[oauth2resourceserver-minimalconfiguration]] -=== Minimal Configuration - -When using https://spring.io/projects/spring-boot[Spring Boot], configuring an application as a resource server consists of two basic steps. -First, include the needed dependencies and second, indicate the location of the authorization server. - -==== Specifying the Authorization Server - -To specify which authorization server to use, simply do: - -```yaml -security: - oauth2: - resourceserver: - jwt: - issuer-uri: https://idp.example.com -``` - -Where `https://idp.example.com` is the value contained in the `iss` claim for JWT tokens that the authorization server will issue. -Resource Server will use this property to further self-configure, discover the authorization server's public keys, and subsequently validate incoming JWTs. - -[NOTE] -To use the `issuer-uri` property, it must also be true that `https://idp.example.com/.well-known/openid-configuration` is a supported endpoint for the authorization server. -This endpoint is referred to as a https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[Provider Configuration] endpoint. - -And that's it! - -==== Startup Expectations - -When this property and these dependencies are used, Resource Server will automatically configure itself to validate JWT-encoded Bearer Tokens. - -It achieves this through a deterministic startup process: - -1. Hit the Provider Configuration endpoint, `https://idp.example.com/.well-known/openid-configuration`, processing the response for the `jwks_url` property -2. Configure the validation strategy to query `jwks_url` for valid public keys -3. Configure the validation strategy to validate each JWTs `iss` claim against `https://idp.example.com`. - -A consequence of this process is that the authorization server must be up and receiving requests in order for Resource Server to successfully start up. - -[NOTE] -If the authorization server is down when Resource Server queries it (given appropriate timeouts), then startup will fail. - -==== Runtime Expectations - -Once the application is started up, Resource Server will attempt to process any request containing an `Authorization: Bearer` header: - -```http -GET / HTTP/1.1 -Authorization: Bearer some-token-value # Resource Server will process this -``` - -So long as this scheme is indicated, Resource Server will attempt to process the request according to the Bearer Token specification. - -Given a well-formed JWT token, Resource Server will - -1. Validate its signature against a public key obtained from the `jwks_url` endpoint during startup and matched against the JWTs header -2. Validate the JWTs `exp` and `nbf` timestamps and the JWTs `iss` claim, and -3. Map each scope to an authority with the prefix `SCOPE_`. - -[NOTE] -As the authorization server makes available new keys, Spring Security will automatically rotate the keys used to validate the JWT tokens. - -The resulting `Authentication#getPrincipal`, by default, is a Spring Security `Jwt` object, and `Authentication#getName` maps to the JWT's `sub` property, if one is present. - -From here, consider jumping to: - -<> - -<> - -[[oauth2resourceserver-jwkseturi]] -=== Specifying the Authorization Server JWK Set Uri Directly - -If the authorization server doesn't support the Provider Configuration endpoint, or if Resource Server must be able to start up independently from the authorization server, then `issuer-uri` can be exchanged for `jwk-set-uri`: - -```yaml -security: - oauth2: - resourceserver: - jwt: - jwk-set-uri: https://idp.example.com/.well-known/jwks.json -``` - -[NOTE] -The JWK Set uri is not standardized, but can typically be found in the authorization server's documentation - -Consequently, Resource Server will not ping the authorization server at startup. -However, it will also no longer validate the `iss` claim in the JWT (since Resource Server no longer knows what the issuer value should be). - -[NOTE] -This property can also be supplied directly on the <>. - -[[oauth2resourceserver-sansboot]] -=== Overriding or Replacing Boot Auto Configuration - -There are two `@Bean` s that Spring Boot generates on Resource Server's behalf. - -The first is a `WebSecurityConfigurerAdapter` that configures the app as a resource server: - -```java -protected void configure(HttpSecurity http) { - http - .authorizeRequests() - .anyRequest().authenticated() - .and() - .oauth2ResourceServer() - .jwt(); -} -``` - -If the application doesn't expose a `WebSecurityConfigurerAdapter` bean, then Spring Boot will expose the above default one. - -Replacing this is as simple as exposing the bean within the application: - -```java -@EnableWebSecurity -public class MyCustomSecurityConfiguration extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) { - http - .authorizeRequests() - .mvcMatchers("/messages/**").hasAuthority("SCOPE_message:read") - .anyRequest().authenticated() - .and() - .oauth2ResourceServer() - .jwt() - .jwtAuthenticationConverter(myConverter()); - } -} -``` - -The above requires the scope of `message:read` for any URL that starts with `/messages/`. - -Methods on the `oauth2ResourceServer` DSL will also override or replace auto configuration. - -For example, the second `@Bean` Spring Boot creates is a `JwtDecoder`, which decodes `String` tokens into validated instances of `Jwt`: - -```java -@Bean -public JwtDecoder jwtDecoder() { - return JwtDecoders.fromOidcIssuerLocation(issuerUri); -} -``` - -If the application doesn't expose a `JwtDecoder` bean, then Spring Boot will expose the above default one. - -And its configuration can be overridden using `jwkSetUri()` or replaced using `decoder()`. - -[[oauth2resourceserver-jwkseturi-dsl]] -==== Using `jwkSetUri()` - -An authorization server's JWK Set Uri can be configured <> or it can be supplied in the DSL: - -```java -@EnableWebSecurity -public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) { - http - .authorizeRequests() - .anyRequest().authenticated() - .and() - .oauth2ResourceServer() - .jwt() - .jwkSetUri("https://idp.example.com/.well-known/jwks.json"); - } -} -``` - -Using `jwkSetUri()` takes precedence over any configuration property. - -[[oauth2resourceserver-decoder-dsl]] -==== Using `decoder()` - -More powerful than `jwkSetUri()` is `decoder()`, which will completely replace any Boot auto configuration of `JwtDecoder`: - -```java -@EnableWebSecurity -public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) { - http - .authorizeRequests() - .anyRequest().authenticated() - .and() - .oauth2ResourceServer() - .jwt() - .decoder(myCustomDecoder()); - } -} -``` - -This is handy when deeper configuration, like <>, <>, or <>, is necessary. - -[[oauth2resourceserver-decoder-bean]] -==== Exposing a `JwtDecoder` `@Bean` - -Or, exposing a `JwtDecoder` `@Bean` has the same effect as `decoder()`: - -```java -@Bean -public JwtDecoder jwtDecoder() { - return new NimbusJwtDecoder(JwtProcessors.withJwkSetUri(jwkSetUri).build()); -} -``` - -[[oauth2resourceserver-authorization]] -=== Configuring Authorization - -A JWT that is issued from an OAuth 2.0 Authorization Server will typically either have a `scope` or `scp` attribute, indicating the scopes (or authorities) it's been granted, for example: - -`{ ..., "scope" : "messages contacts"}` - -When this is the case, Resource Server will attempt to coerce these scopes into a list of granted authorities, prefixing each scope with the string "SCOPE_". - -This means that to protect an endpoint or method with a scope derived from a JWT, the corresponding expressions should include this prefix: - -```java -@EnableWebSecurity -public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) { - http - .authorizeRequests() - .mvcMatchers("/contacts/**").hasAuthority("SCOPE_contacts") - .mvcMatchers("/messages/**").hasAuthority("SCOPE_messages") - .anyRequest().authenticated() - .and() - .oauth2ResourceServer() - .jwt(); - } -} -``` - -Or similarly with method security: - -```java -@PreAuthorize("hasAuthority('SCOPE_messages')") -public List getMessages(...) {} -``` - -[[oauth2resourceserver-authorization-extraction]] -==== Extracting Authorities Manually - -However, there are a number of circumstances where this default is insufficient. -For example, some authorization servers don't use the `scope` attribute, but instead have their own custom attribute. -Or, at other times, the resource server may need to adapt the attribute or a composition of attributes into internalized authorities. - -To this end, the DSL exposes `jwtAuthenticationConverter()`: - -```java -@EnableWebSecurity -public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) { - http - .authorizeRequests() - .anyRequest().authenticated() - .and() - .oauth2ResourceServer() - .jwt() - .jwtAuthenticationConverter(grantedAuthoritiesExtractor()); - } -} - -Converter grantedAuthoritiesExtractor() { - return new GrantedAuthoritiesExtractor(); -} -``` - -which is responsible for converting a `Jwt` into an `Authentication`. - -We can override this quite simply to alter the way granted authorities are derived: - -```java -static class GrantedAuthoritiesExtractor extends JwtAuthenticationConverter { - protected Collection extractAuthorities(Jwt jwt) { - Collection authorities = (Collection) - jwt.getClaims().get("mycustomclaim"); - - return authorities.stream() - .map(SimpleGrantedAuthority::new) - .collect(Collectors.toList()); - } -} -``` - -For more flexibility, the DSL supports entirely replacing the converter with any class that implements `Converter`: - -```java -static class CustomAuthenticationConverter implements Converter { - public AbstractAuthenticationToken convert(Jwt jwt) { - return new CustomAuthenticationToken(jwt); - } -} -``` - -[[oauth2resourceserver-validation]] -=== Configuring Validation - -Using <>, indicating the authorization server's issuer uri, Resource Server will default to verifying the `iss` claim as well as the `exp` and `nbf` timestamp claims. - -In circumstances where validation needs to be customized, Resource Server ships with two standard validators and also accepts custom `OAuth2TokenValidator` instances. - -[[oauth2resourceserver-validation-clockskew]] -==== Customizing Timestamp Validation - -JWT's typically have a window of validity, with the start of the window indicated in the `nbf` claim and the end indicated in the `exp` claim. -However, every server can experience clock drift, which can cause tokens to appear expired to one server, but not to another. -This can cause some implementation heartburn as the number of collaborating servers increases in a distributed system. -Resource Server uses `JwtTimestampValidator` to verify a token's validity window, and it can be configured with a `clockSkew` to alleviate the above problem: - -```java -@Bean -JwtDecoder jwtDecoder() { - NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder) - JwtDecoders.withOidcIssuerLocation(issuerUri); - - OAuth2TokenValidator withClockSkew = new DelegatingOAuth2TokenValidator<>( - new JwtTimestampValidator(Duration.ofSeconds(60)), - new IssuerValidator(issuerUri)); - - jwtDecoder.setJwtValidator(withClockSkew); - - return jwtDecoder; -} -``` - -[NOTE] -By default, Resource Server configures a clock skew of 30 seconds. - -[[oauth2resourceserver-validation-custom]] -==== Configuring a Custom Validator - -Adding a check for the `aud` claim is simple with the `OAuth2TokenValidator` API: - -```java -public class AudienceValidator implements OAuth2TokenValidator { - OAuth2Error error = new OAuth2Error("invalid_token", "The required audience is missing", null); - - public OAuth2TokenValidatorResult validate(Jwt jwt) { - if (jwt.getAudience().contains("messaging")) { - return OAuth2TokenValidatorResult.success(); - } else { - return OAuth2TokenValidatorResult.failure(error); - } - } -} -``` - -Then, to add into a resource server, it's a matter of specifying the `JwtDecoder` instance: - -```java -@Bean -JwtDecoder jwtDecoder() { - NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder) - JwtDecoders.withOidcIssuerLocation(issuerUri); - - OAuth2TokenValidator audienceValidator = new AudienceValidator(); - OAuth2TokenValidator withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri); - OAuth2TokenValidator withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator); - - jwtDecoder.setJwtValidator(withAudience); - - return jwtDecoder; -} -``` - -[[oauth2resourceserver-claimsetmapping]] -=== Configuring Claim Set Mapping - -Spring Security uses the https://bitbucket.org/connect2id/nimbus-jose-jwt/wiki/Home[Nimbus] library for parsing JWTs and validating their signatures. -Consequently, Spring Security is subject to Nimbus's interpretation of each field value and how to coerce each into a Java type. - -For example, because Nimbus remains Java 7 compatible, it doesn't use `Instant` to represent timestamp fields. - -And it's entirely possible to use a different library or for JWT processing, which may make its own coercion decisions that need adjustment. - -Or, quite simply, a resource server may want to add or remove claims from a JWT for domain-specific reasons. - -For these purposes, Resource Server supports mapping the JWT claim set with `MappedJwtClaimSetConverter`. - -[[oauth2resourceserver-claimsetmapping-singleclaim]] -==== Customizing the Conversion of a Single Claim - -By default, `MappedJwtClaimSetConverter` will attempt to coerce claims into the following types: - -|============ -| Claim | Java Type -| `aud` | `Collection` -| `exp` | `Instant` -| `iat` | `Instant` -| `iss` | `String` -| `jti` | `String` -| `nbf` | `Instant` -| `sub` | `String` -|============ - -An individual claim's conversion strategy can be configured using `MappedJwtClaimSetConverter.withDefaults`: - -```java -@Bean -JwtDecoder jwtDecoder() { - NimbusJwtDecoder jwtDecoder = new NimbusJwtDecoder(JwtProcessors.withJwkSetUri(jwkSetUri).build()); - - MappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter - .withDefaults(Collections.singletonMap("sub", this::lookupUserIdBySub)); - jwtDecoder.setClaimSetConverter(converter); - - return jwtDecoder; -} -``` -This will keep all the defaults, except it will override the default claim converter for `sub`. - -[[oauth2resourceserver-claimsetmapping-add]] -==== Adding a Claim - -`MappedJwtClaimSetConverter` can also be used to add a custom claim, for example, to adapt to an existing system: - -```java -MappedJwtClaimSetConverter.withDefaults(Collections.singletonMap("custom", custom -> "value")); -``` - -[[oauth2resourceserver-claimsetmapping-remove]] -==== Removing a Claim - -And removing a claim is also simple, using the same API: - -```java -MappedJwtClaimSetConverter.withDefaults(Collections.singletonMap("legacyclaim", legacy -> null)); -``` - -[[oauth2resourceserver-claimsetmapping-rename]] -==== Renaming a Claim - -In more sophisticated scenarios, like consulting multiple claims at once or renaming a claim, Resource Server accepts any class that implements `Converter, Map>`: - -```java -public class UsernameSubClaimAdapter implements Converter, Map> { - private final MappedJwtClaimSetConverter delegate = - MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap()); - - public Map convert(Map claims) { - Map convertedClaims = this.delegate.convert(claims); - - String username = (String) convertedClaims.get("user_name"); - convertedClaims.put("sub", username); - - return convertedClaims; - } -} -``` - -And then, the instance can be supplied like normal: - -```java -@Bean -JwtDecoder jwtDecoder() { - NimbusJwtDecoder jwtDecoder = new NimbusJwtDecoder(JwtProcessors.withJwkSetUri(jwkSetUri).build()); - jwtDecoder.setClaimSetConverter(new UsernameSubClaimAdapter()); - return jwtDecoder; -} -``` - -[[oauth2resourceserver-timeouts]] -=== Configuring Timeouts - -By default, Resource Server uses connection and socket timeouts of 30 seconds each for coordinating with the authorization server. - -This may be too short in some scenarios. -Further, it doesn't take into account more sophisticated patterns like back-off and discovery. - -To adjust the way in which Resource Server connects to the authorization server, `NimbusJwtDecoder` accepts an instance of `RestOperations`: - -```java -@Bean -public JwtDecoder jwtDecoder(RestTemplateBuilder builder) { - RestOperations rest = builder - .setConnectionTimeout(60000) - .setReadTimeout(60000) - .build(); - - NimbusJwtDecoder jwtDecoder = new NimbusJwtDecoder(JwtProcessors.withJwkSetUri(jwkSetUri).restOperations(rest).build()); - return jwtDecoder; -} -``` - -[[jc-authentication]] -== Authentication - -Thus far we have only taken a look at the most basic authentication configuration. -Let's take a look at a few slightly more advanced options for configuring authentication. - -[[jc-authentication-inmemory]] -=== In-Memory Authentication - -We have already seen an example of configuring in-memory authentication for a single user. -Below is an example to configure multiple users: - -[source,java] ----- -@Bean -public UserDetailsService userDetailsService() throws Exception { - // ensure the passwords are encoded properly - UserBuilder users = User.withDefaultPasswordEncoder(); - InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); - manager.createUser(users.username("user").password("password").roles("USER").build()); - manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build()); - return manager; -} ----- - -[[jc-authentication-jdbc]] -=== JDBC Authentication - -You can find the updates to support JDBC based authentication. -The example below assumes that you have already defined a `DataSource` within your application. -The https://github.com/spring-projects/spring-security/tree/master/samples/javaconfig/jdbc[jdbc-javaconfig] sample provides a complete example of using JDBC based authentication. - -[source,java] ----- -@Autowired -private DataSource dataSource; - -@Autowired -public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - // ensure the passwords are encoded properly - UserBuilder users = User.withDefaultPasswordEncoder(); - auth - .jdbcAuthentication() - .dataSource(dataSource) - .withDefaultSchema() - .withUser(users.username("user").password("password").roles("USER")) - .withUser(users.username("admin").password("password").roles("USER","ADMIN")); -} ----- - -=== LDAP Authentication - -You can find the updates to support LDAP based authentication. -The https://github.com/spring-projects/spring-security/tree/master/samples/javaconfig/ldap[ldap-javaconfig] sample provides a complete example of using LDAP based authentication. - -[source,java] ----- -@Autowired -private DataSource dataSource; - -@Autowired -public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth - .ldapAuthentication() - .userDnPatterns("uid={0},ou=people") - .groupSearchBase("ou=groups"); -} ----- - -The example above uses the following LDIF and an embedded Apache DS LDAP instance. - -.users.ldif ----- -dn: ou=groups,dc=springframework,dc=org -objectclass: top -objectclass: organizationalUnit -ou: groups - -dn: ou=people,dc=springframework,dc=org -objectclass: top -objectclass: organizationalUnit -ou: people - -dn: uid=admin,ou=people,dc=springframework,dc=org -objectclass: top -objectclass: person -objectclass: organizationalPerson -objectclass: inetOrgPerson -cn: Rod Johnson -sn: Johnson -uid: admin -userPassword: password - -dn: uid=user,ou=people,dc=springframework,dc=org -objectclass: top -objectclass: person -objectclass: organizationalPerson -objectclass: inetOrgPerson -cn: Dianne Emu -sn: Emu -uid: user -userPassword: password - -dn: cn=user,ou=groups,dc=springframework,dc=org -objectclass: top -objectclass: groupOfNames -cn: user -uniqueMember: uid=admin,ou=people,dc=springframework,dc=org -uniqueMember: uid=user,ou=people,dc=springframework,dc=org - -dn: cn=admin,ou=groups,dc=springframework,dc=org -objectclass: top -objectclass: groupOfNames -cn: admin -uniqueMember: uid=admin,ou=people,dc=springframework,dc=org ----- - -[[jc-authentication-authenticationprovider]] -=== AuthenticationProvider - -You can define custom authentication by exposing a custom `AuthenticationProvider` as a bean. -For example, the following will customize authentication assuming that `SpringAuthenticationProvider` implements `AuthenticationProvider`: - -NOTE: This is only used if the `AuthenticationManagerBuilder` has not been populated - -[source,java] ----- -@Bean -public SpringAuthenticationProvider springAuthenticationProvider() { - return new SpringAuthenticationProvider(); -} ----- - -[[jc-authentication-userdetailsservice]] -=== UserDetailsService - -You can define custom authentication by exposing a custom `UserDetailsService` as a bean. -For example, the following will customize authentication assuming that `SpringDataUserDetailsService` implements `UserDetailsService`: - -NOTE: This is only used if the `AuthenticationManagerBuilder` has not been populated and no `AuthenticationProviderBean` is defined. - -[source,java] ----- -@Bean -public SpringDataUserDetailsService springDataUserDetailsService() { - return new SpringDataUserDetailsService(); -} ----- - -You can also customize how passwords are encoded by exposing a `PasswordEncoder` as a bean. -For example, if you use bcrypt you can add a bean definition as shown below: - -[source,java] ----- -@Bean -public BCryptPasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); -} ----- == Multiple HttpSecurity @@ -1108,130 +109,6 @@ If the URL does not start with `/api/` this configuration will be used. This configuration is considered after `ApiWebSecurityConfigurationAdapter` since it has an `@Order` value after `1` (no `@Order` defaults to last). -[[jc-method]] -== Method Security - -From version 2.0 onwards Spring Security has improved support substantially for adding security to your service layer methods. -It provides support for JSR-250 annotation security as well as the framework's original `@Secured` annotation. -From 3.0 you can also make use of new <>. -You can apply security to a single bean, using the `intercept-methods` element to decorate the bean declaration, or you can secure multiple beans across the entire service layer using the AspectJ style pointcuts. - -=== EnableGlobalMethodSecurity - -We can enable annotation-based security using the `@EnableGlobalMethodSecurity` annotation on any `@Configuration` instance. -For example, the following would enable Spring Security's `@Secured` annotation. - -[source,java] ----- -@EnableGlobalMethodSecurity(securedEnabled = true) -public class MethodSecurityConfig { -// ... -} ----- - -Adding an annotation to a method (on a class or interface) would then limit the access to that method accordingly. -Spring Security's native annotation support defines a set of attributes for the method. -These will be passed to the AccessDecisionManager for it to make the actual decision: - -[source,java] ----- -public interface BankService { - -@Secured("IS_AUTHENTICATED_ANONYMOUSLY") -public Account readAccount(Long id); - -@Secured("IS_AUTHENTICATED_ANONYMOUSLY") -public Account[] findAccounts(); - -@Secured("ROLE_TELLER") -public Account post(Account account, double amount); -} ----- - -Support for JSR-250 annotations can be enabled using - -[source,java] ----- -@EnableGlobalMethodSecurity(jsr250Enabled = true) -public class MethodSecurityConfig { -// ... -} ----- - -These are standards-based and allow simple role-based constraints to be applied but do not have the power Spring Security's native annotations. -To use the new expression-based syntax, you would use - -[source,java] ----- -@EnableGlobalMethodSecurity(prePostEnabled = true) -public class MethodSecurityConfig { -// ... -} ----- - -and the equivalent Java code would be - -[source,java] ----- -public interface BankService { - -@PreAuthorize("isAnonymous()") -public Account readAccount(Long id); - -@PreAuthorize("isAnonymous()") -public Account[] findAccounts(); - -@PreAuthorize("hasAuthority('ROLE_TELLER')") -public Account post(Account account, double amount); -} ----- - -=== GlobalMethodSecurityConfiguration - -Sometimes you may need to perform operations that are more complicated than are possible with the `@EnableGlobalMethodSecurity` annotation allow. -For these instances, you can extend the `GlobalMethodSecurityConfiguration` ensuring that the `@EnableGlobalMethodSecurity` annotation is present on your subclass. -For example, if you wanted to provide a custom `MethodSecurityExpressionHandler`, you could use the following configuration: - -[source,java] ----- -@EnableGlobalMethodSecurity(prePostEnabled = true) -public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration { - @Override - protected MethodSecurityExpressionHandler createExpressionHandler() { - // ... create and return custom MethodSecurityExpressionHandler ... - return expressionHandler; - } -} ----- - -For additional information about methods that can be overridden, refer to the `GlobalMethodSecurityConfiguration` Javadoc. - -== Post Processing Configured Objects - -Spring Security's Java Configuration does not expose every property of every object that it configures. -This simplifies the configuration for a majority of users. -Afterall, if every property was exposed, users could use standard bean configuration. - -While there are good reasons to not directly expose every property, users may still need more advanced configuration options. -To address this Spring Security introduces the concept of an `ObjectPostProcessor` which can be used to modify or replace many of the Object instances created by the Java Configuration. -For example, if you wanted to configure the `filterSecurityPublishAuthorizationSuccess` property on `FilterSecurityInterceptor` you could use the following: - -[source,java] ----- -@Override -protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest().authenticated() - .withObjectPostProcessor(new ObjectPostProcessor() { - public O postProcess( - O fsi) { - fsi.setPublishAuthorizationSuccess(true); - return fsi; - } - }); -} ----- [[jc-custom-dsls]] == Custom DSLs @@ -1319,3 +196,31 @@ public class Config extends WebSecurityConfigurerAdapter { } } ---- + + +== Post Processing Configured Objects + +Spring Security's Java Configuration does not expose every property of every object that it configures. +This simplifies the configuration for a majority of users. +Afterall, if every property was exposed, users could use standard bean configuration. + +While there are good reasons to not directly expose every property, users may still need more advanced configuration options. +To address this Spring Security introduces the concept of an `ObjectPostProcessor` which can be used to modify or replace many of the Object instances created by the Java Configuration. +For example, if you wanted to configure the `filterSecurityPublishAuthorizationSuccess` property on `FilterSecurityInterceptor` you could use the following: + +[source,java] +---- +@Override +protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().authenticated() + .withObjectPostProcessor(new ObjectPostProcessor() { + public O postProcess( + O fsi) { + fsi.setPublishAuthorizationSuccess(true); + return fsi; + } + }); +} +---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/preface/namespace.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/preface/namespace.adoc index 020a06842e1..fa62b5a29cb 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/preface/namespace.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/preface/namespace.adoc @@ -1,599 +1,9 @@ -[[ns-config]] -= Security Namespace Configuration -== Introduction -Namespace configuration has been available since version 2.0 of the Spring Framework. -It allows you to supplement the traditional Spring beans application context syntax with elements from additional XML schema. -You can find more information in the Spring http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/[Reference Documentation]. -A namespace element can be used simply to allow a more concise way of configuring an individual bean or, more powerfully, to define an alternative configuration syntax which more closely matches the problem domain and hides the underlying complexity from the user. -A simple element may conceal the fact that multiple beans and processing steps are being added to the application context. -For example, adding the following element from the security namespace to an application context will start up an embedded LDAP server for testing use within the application: - -[source,xml] ----- - ----- - -This is much simpler than wiring up the equivalent Apache Directory Server beans. -The most common alternative configuration requirements are supported by attributes on the `ldap-server` element and the user is isolated from worrying about which beans they need to create and what the bean property names are. -footnote:[You can find out more about the use of the `ldap-server` element in the chapter on pass:specialcharacters,macros[<>].]. -Use of a good XML editor while editing the application context file should provide information on the attributes and elements that are available. -We would recommend that you try out the http://spring.io/tools/sts[Spring Tool Suite] as it has special features for working with standard Spring namespaces. - - -To start using the security namespace in your application context, you need to have the `spring-security-config` jar on your classpath. -Then all you need to do is add the schema declaration to your application context file: - -[source,xml] ----- - - ... - ----- - -In many of the examples you will see (and in the sample applications), we will often use "security" as the default namespace rather than "beans", which means we can omit the prefix on all the security namespace elements, making the content easier to read. -You may also want to do this if you have your application context divided up into separate files and have most of your security configuration in one of them. -Your security application context file would then start like this - -[source,xml] ----- - - ... - ----- - -We'll assume this syntax is being used from now on in this chapter. - - -=== Design of the Namespace -The namespace is designed to capture the most common uses of the framework and provide a simplified and concise syntax for enabling them within an application. -The design is based around the large-scale dependencies within the framework, and can be divided up into the following areas: - -* __Web/HTTP Security__ - the most complex part. -Sets up the filters and related service beans used to apply the framework authentication mechanisms, to secure URLs, render login and error pages and much more. - -* __Business Object (Method) Security__ - options for securing the service layer. - -* __AuthenticationManager__ - handles authentication requests from other parts of the framework. - -* __AccessDecisionManager__ - provides access decisions for web and method security. -A default one will be registered, but you can also choose to use a custom one, declared using normal Spring bean syntax. - -* __AuthenticationProvider__s - mechanisms against which the authentication manager authenticates users. -The namespace provides supports for several standard options and also a means of adding custom beans declared using a traditional syntax. - -* __UserDetailsService__ - closely related to authentication providers, but often also required by other beans. - -We'll see how to configure these in the following sections. - -[[ns-getting-started]] -== Getting Started with Security Namespace Configuration -In this section, we'll look at how you can build up a namespace configuration to use some of the main features of the framework. -Let's assume you initially want to get up and running as quickly as possible and add authentication support and access control to an existing web application, with a few test logins. -Then we'll look at how to change over to authenticating against a database or other security repository. -In later sections we'll introduce more advanced namespace configuration options. - -[[ns-web-xml]] -=== web.xml Configuration -The first thing you need to do is add the following filter declaration to your `web.xml` file: - -[source,xml] ----- - -springSecurityFilterChain -org.springframework.web.filter.DelegatingFilterProxy - - - -springSecurityFilterChain -/* - ----- - -This provides a hook into the Spring Security web infrastructure. -`DelegatingFilterProxy` is a Spring Framework class which delegates to a filter implementation which is defined as a Spring bean in your application context. -In this case, the bean is named "springSecurityFilterChain", which is an internal infrastructure bean created by the namespace to handle web security. -Note that you should not use this bean name yourself. -Once you've added this to your `web.xml`, you're ready to start editing your application context file. -Web security services are configured using the `` element. - -[[ns-minimal]] -=== A Minimal Configuration -All you need to enable web security to begin with is - -[source,xml] ----- - - - - - ----- - -Which says that we want all URLs within our application to be secured, requiring the role `ROLE_USER` to access them, we want to log in to the application using a form with username and password, and that we want a logout URL registered which will allow us to log out of the application. -`` element is the parent for all web-related namespace functionality. -The `` element defines a `pattern` which is matched against the URLs of incoming requests using an ant path style syntax footnote:[See the section on pass:specialcharacters,macros[<>] in the Web Application Infrastructure chapter for more details on how matches are actually performed.]. -You can also use regular-expression matching as an alternative (see the namespace appendix for more details). -The `access` attribute defines the access requirements for requests matching the given pattern. -With the default configuration, this is typically a comma-separated list of roles, one of which a user must have to be allowed to make the request. -The prefix "ROLE_" is a marker which indicates that a simple comparison with the user's authorities should be made. -In other words, a normal role-based check should be used. -Access-control in Spring Security is not limited to the use of simple roles (hence the use of the prefix to differentiate between different types of security attributes). -We'll see later how the interpretation can vary footnote:[The interpretation of the comma-separated values in the `access` attribute depends on the implementation of the pass:specialcharacters,macros[<>] which is used. -In Spring Security 3.0, the attribute can also be populated with an pass:specialcharacters,macros[<>]. - - -[NOTE] -=== - -You can use multiple `` elements to define different access requirements for different sets of URLs, but they will be evaluated in the order listed and the first match will be used. -So you must put the most specific matches at the top. -You can also add a `method` attribute to limit the match to a particular HTTP method (`GET`, `POST`, `PUT` etc.). - -=== - -To add some users, you can define a set of test data directly in the namespace: - -[source,xml] ----- - - - - - - - - - ----- - -This is an example of a secure way of storing the same passwords. -The password is prefixed with `{bcrypt}` to instruct `DelegatingPasswordEncoder`, which supports any configured `PasswordEncoder` for matching, that the passwords are hashed using BCrypt: - -[source,xml] ----- - - - - - - - - - - ----- - - - -[subs="quotes"] -**** -If you are familiar with pre-namespace versions of the framework, you can probably already guess roughly what's going on here. -The `` element is responsible for creating a `FilterChainProxy` and the filter beans which it uses. -Common problems like incorrect filter ordering are no longer an issue as the filter positions are predefined. - -The `` element creates a `DaoAuthenticationProvider` bean and the `` element creates an `InMemoryDaoImpl`. -All `authentication-provider` elements must be children of the `` element, which creates a `ProviderManager` and registers the authentication providers with it. -You can find more detailed information on the beans that are created in the <>. -It's worth cross-checking this if you want to start understanding what the important classes in the framework are and how they are used, particularly if you want to customise things later. -**** - -The configuration above defines two users, their passwords and their roles within the application (which will be used for access control). -It is also possible to load user information from a standard properties file using the `properties` attribute on `user-service`. -See the section on <> for more details on the file format. -Using the `` element means that the user information will be used by the authentication manager to process authentication requests. -You can have multiple `` elements to define different authentication sources and each will be consulted in turn. - -At this point you should be able to start up your application and you will be required to log in to proceed. -Try it out, or try experimenting with the "tutorial" sample application that comes with the project. - - -[[ns-form-and-basic]] -=== Form and Basic Login Options -You might be wondering where the login form came from when you were prompted to log in, since we made no mention of any HTML files or JSPs. -In fact, since we didn't explicitly set a URL for the login page, Spring Security generates one automatically, based on the features that are enabled and using standard values for the URL which processes the submitted login, the default target URL the user will be sent to after logging in and so on. -However, the namespace offers plenty of support to allow you to customize these options. -For example, if you want to supply your own login page, you could use: - -[source,xml] ----- - - - - - ----- - -Also note that we've added an extra `intercept-url` element to say that any requests for the login page should be available to anonymous users footnote:[See the chapter on pass:specialcharacters,macros[<>]] and also the <> class for more details on how the value `IS_AUTHENTICATED_ANONYMOUSLY` is processed.]. -Otherwise the request would be matched by the pattern /** and it wouldn't be possible to access the login page itself! -This is a common configuration error and will result in an infinite loop in the application. -Spring Security will emit a warning in the log if your login page appears to be secured. -It is also possible to have all requests matching a particular pattern bypass the security filter chain completely, by defining a separate `http` element for the pattern like this: - - -[source,xml] ----- - - - - - - - ----- - -From Spring Security 3.1 it is now possible to use multiple `http` elements to define separate security filter chain configurations for different request patterns. -If the `pattern` attribute is omitted from an `http` element, it matches all requests. -Creating an unsecured pattern is a simple example of this syntax, where the pattern is mapped to an empty filter chain footnote:[The use of multiple `` elements is an important feature, allowing the namespace to simultaneously support both stateful and stateless paths within the same application, for example. -The previous syntax, using the attribute `filters="none"` on an `intercept-url` element is incompatible with this change and is no longer supported in 3.1.]. -We'll look at this new syntax in more detail in the chapter on the <>. - -It's important to realise that these unsecured requests will be completely oblivious to any Spring Security web-related configuration or additional attributes such as `requires-channel`, so you will not be able to access information on the current user or call secured methods during the request. -Use `access='IS_AUTHENTICATED_ANONYMOUSLY'` as an alternative if you still want the security filter chain to be applied. - -If you want to use basic authentication instead of form login, then change the configuration to - -[source,xml] ----- - - - - ----- - -Basic authentication will then take precedence and will be used to prompt for a login when a user attempts to access a protected resource. -Form login is still available in this configuration if you wish to use it, for example through a login form embedded in another web page. - -[[ns-form-target]] -==== Setting a Default Post-Login Destination -If a form login isn't prompted by an attempt to access a protected resource, the `default-target-url` option comes into play. -This is the URL the user will be taken to after successfully logging in, and defaults to "/". -You can also configure things so that the user __always__ ends up at this page (regardless of whether the login was "on-demand" or they explicitly chose to log in) by setting the `always-use-default-target` attribute to "true". -This is useful if your application always requires that the user starts at a "home" page, for example: - -[source,xml] ----- - - - - - ----- - -For even more control over the destination, you can use the `authentication-success-handler-ref` attribute as an alternative to `default-target-url`. -The referenced bean should be an instance of `AuthenticationSuccessHandler`. -You'll find more on this in the <> chapter and also in the namespace appendix, as well as information on how to customize the flow when authentication fails. - -[[ns-logout]] -=== Logout Handling -The `logout` element adds support for logging out by navigating to a particular URL. -The default logout URL is `/logout`, but you can set it to something else using the `logout-url` attribute. -More information on other available attributes may be found in the namespace appendix. - -[[ns-auth-providers]] -=== Using other Authentication Providers -In practice you will need a more scalable source of user information than a few names added to the application context file. -Most likely you will want to store your user information in something like a database or an LDAP server. -LDAP namespace configuration is dealt with in the <>, so we won't cover it here. -If you have a custom implementation of Spring Security's `UserDetailsService`, called "myUserDetailsService" in your application context, then you can authenticate against this using - -[source,xml] ----- - - - - - ----- - -If you want to use a database, then you can use - -[source,xml] ----- - - - - - ----- - -Where "securityDataSource" is the name of a `DataSource` bean in the application context, pointing at a database containing the standard Spring Security <>. -Alternatively, you could configure a Spring Security `JdbcDaoImpl` bean and point at that using the `user-service-ref` attribute: - -[source,xml] ----- - - - - - - - ----- - -You can also use standard `AuthenticationProvider` beans as follows - -[source,xml] ----- - - - - - ----- - -where `myAuthenticationProvider` is the name of a bean in your application context which implements `AuthenticationProvider`. -You can use multiple `authentication-provider` elements, in which case the providers will be queried in the order they are declared. -See <> for more information on how the Spring Security `AuthenticationManager` is configured using the namespace. - -[[ns-password-encoder]] -==== Adding a Password Encoder -Passwords should always be encoded using a secure hashing algorithm designed for the purpose (not a standard algorithm like SHA or MD5). -This is supported by the `` element. -With bcrypt encoded passwords, the original authentication provider configuration would look like this: - -[source,xml] ----- - - - - - - - - - - - - ----- - - - -bcrypt is a good choice for most cases, unless you have a legacy system which forces you to use a different algorithm. -If you are using a simple hashing algorithm or, even worse, storing plain text passwords, then you should consider migrating to a more secure option like bcrypt. - [[ns-web-advanced]] == Advanced Web Features -[[ns-remember-me]] -=== Remember-Me Authentication -See the separate <> for information on remember-me namespace configuration. - -[[ns-requires-channel]] -=== Adding HTTP/HTTPS Channel Security -If your application supports both HTTP and HTTPS, and you require that particular URLs can only be accessed over HTTPS, then this is directly supported using the `requires-channel` attribute on ``: - -[source,xml] ----- - - - -... - ----- - -With this configuration in place, if a user attempts to access anything matching the "/secure/**" pattern using HTTP, they will first be redirected to an HTTPS URL footnote:[For more details on how channel-processing is implemented, see the Javadoc for `ChannelProcessingFilter` and related classes.]. -The available options are "http", "https" or "any". -Using the value "any" means that either HTTP or HTTPS can be used. - -If your application uses non-standard ports for HTTP and/or HTTPS, you can specify a list of port mappings as follows: - -[source,xml] ----- - -... - - - - ----- - -Note that in order to be truly secure, an application should not use HTTP at all or switch between HTTP and HTTPS. -It should start in HTTPS (with the user entering an HTTPS URL) and use a secure connection throughout to avoid any possibility of man-in-the-middle attacks. - -[[ns-session-mgmt]] -=== Session Management - -==== Detecting Timeouts -You can configure Spring Security to detect the submission of an invalid session ID and redirect the user to an appropriate URL. -This is achieved through the `session-management` element: - -[source,xml] ----- - -... - - ----- - -Note that if you use this mechanism to detect session timeouts, it may falsely report an error if the user logs out and then logs back in without closing the browser. -This is because the session cookie is not cleared when you invalidate the session and will be resubmitted even if the user has logged out. -You may be able to explicitly delete the JSESSIONID cookie on logging out, for example by using the following syntax in the logout handler: - -[source,xml] ----- - - - ----- - -Unfortunately this can't be guaranteed to work with every servlet container, so you will need to test it in your environment - -[NOTE] -=== -If you are running your application behind a proxy, you may also be able to remove the session cookie by configuring the proxy server. -For example, using Apache HTTPD's mod_headers, the following directive would delete the `JSESSIONID` cookie by expiring it in the response to a logout request (assuming the application is deployed under the path `/tutorial`): - -[source,xml] ----- - -Header always set Set-Cookie "JSESSIONID=;Path=/tutorial;Expires=Thu, 01 Jan 1970 00:00:00 GMT" - ----- -=== - - -[[ns-concurrent-sessions]] -==== Concurrent Session Control -If you wish to place constraints on a single user's ability to log in to your application, Spring Security supports this out of the box with the following simple additions. -First you need to add the following listener to your `web.xml` file to keep Spring Security updated about session lifecycle events: - -[source,xml] ----- - - - org.springframework.security.web.session.HttpSessionEventPublisher - - ----- - -Then add the following lines to your application context: - -[source,xml] ----- - -... - - - - ----- - -This will prevent a user from logging in multiple times - a second login will cause the first to be invalidated. -Often you would prefer to prevent a second login, in which case you can use - -[source,xml] ----- - -... - - - - ----- - -The second login will then be rejected. -By "rejected", we mean that the user will be sent to the `authentication-failure-url` if form-based login is being used. -If the second authentication takes place through another non-interactive mechanism, such as "remember-me", an "unauthorized" (401) error will be sent to the client. -If instead you want to use an error page, you can add the attribute `session-authentication-error-url` to the `session-management` element. - -If you are using a customized authentication filter for form-based login, then you have to configure concurrent session control support explicitly. -More details can be found in the <>. - -[[ns-session-fixation]] -==== Session Fixation Attack Protection -http://en.wikipedia.org/wiki/Session_fixation[Session fixation] attacks are a potential risk where it is possible for a malicious attacker to create a session by accessing a site, then persuade another user to log in with the same session (by sending them a link containing the session identifier as a parameter, for example). -Spring Security protects against this automatically by creating a new session or otherwise changing the session ID when a user logs in. -If you don't require this protection, or it conflicts with some other requirement, you can control the behavior using the `session-fixation-protection` attribute on ``, which has four options - -* `none` - Don't do anything. -The original session will be retained. - -* `newSession` - Create a new "clean" session, without copying the existing session data (Spring Security-related attributes will still be copied). - -* `migrateSession` - Create a new session and copy all existing session attributes to the new session. -This is the default in Servlet 3.0 or older containers. - -* `changeSessionId` - Do not create a new session. -Instead, use the session fixation protection provided by the Servlet container (`HttpServletRequest#changeSessionId()`). -This option is only available in Servlet 3.1 (Java EE 7) and newer containers. -Specifying it in older containers will result in an exception. -This is the default in Servlet 3.1 and newer containers. - - -When session fixation protection occurs, it results in a `SessionFixationProtectionEvent` being published in the application context. -If you use `changeSessionId`, this protection will __also__ result in any `javax.servlet.http.HttpSessionIdListener` s being notified, so use caution if your code listens for both events. -See the <> chapter for additional information. - - -[[ns-openid]] -=== OpenID Support -The namespace supports http://openid.net/[OpenID] login either instead of, or in addition to normal form-based login, with a simple change: - -[source,xml] ----- - - - - ----- - -You should then register yourself with an OpenID provider (such as myopenid.com), and add the user information to your in-memory ``: - -[source,xml] ----- - ----- - -You should be able to login using the `myopenid.com` site to authenticate. -It is also possible to select a specific `UserDetailsService` bean for use OpenID by setting the `user-service-ref` attribute on the `openid-login` element. -See the previous section on <> for more information. -Note that we have omitted the password attribute from the above user configuration, since this set of user data is only being used to load the authorities for the user. -A random password will be generated internally, preventing you from accidentally using this user data as an authentication source elsewhere in your configuration. - - -==== Attribute Exchange -Support for OpenID http://openid.net/specs/openid-attribute-exchange-1_0.html[attribute exchange]. -As an example, the following configuration would attempt to retrieve the email and full name from the OpenID provider, for use by the application: - -[source,xml] ----- - - - - - - ----- - -The "type" of each OpenID attribute is a URI, determined by a particular schema, in this case http://axschema.org/[http://axschema.org/]. -If an attribute must be retrieved for successful authentication, the `required` attribute can be set. -The exact schema and attributes supported will depend on your OpenID provider. -The attribute values are returned as part of the authentication process and can be accessed afterwards using the following code: - -[source,java] ----- -OpenIDAuthenticationToken token = - (OpenIDAuthenticationToken)SecurityContextHolder.getContext().getAuthentication(); -List attributes = token.getAttributes(); ----- - -The `OpenIDAttribute` contains the attribute type and the retrieved value (or values in the case of multi-valued attributes). -We'll see more about how the `SecurityContextHolder` class is used when we look at core Spring Security components in the <> chapter. -Multiple attribute exchange configurations are also be supported, if you wish to use multiple identity providers. -You can supply multiple `attribute-exchange` elements, using an `identifier-matcher` attribute on each. -This contains a regular expression which will be matched against the OpenID identifier supplied by the user. -See the OpenID sample application in the codebase for an example configuration, providing different attribute lists for the Google, Yahoo and MyOpenID providers. - - -[[ns-headers]] -=== Response Headers -For additional information on how to customize the headers element refer to the <> section of the reference. - [[ns-custom-filters]] === Adding in Your Own Filters @@ -713,7 +123,7 @@ The names "FIRST" and "LAST" can be used with the `position` attribute to indica .Avoiding filter position conflicts [TIP] -=== +==== If you are inserting a custom filter which may occupy the same position as one of the standard filters created by the namespace then it's important that you don't include the namespace versions by mistake. Remove any elements which create filters whose functionality you want to replace. @@ -722,191 +132,7 @@ Note that you can't replace filters which are created by the use of the `` Some other filters are added by default, but you can disable them. An `AnonymousAuthenticationFilter` is added by default and unless you have <> disabled, a `SessionManagementFilter` will also be added to the filter chain. -=== +==== If you're replacing a namespace filter which requires an authentication entry point (i.e. where the authentication process is triggered by an attempt by an unauthenticated user to access to a secured resource), you will need to add a custom entry point bean too. - -[[ns-entry-point-ref]] -==== Setting a Custom AuthenticationEntryPoint -If you aren't using form login, OpenID or basic authentication through the namespace, you may want to define an authentication filter and entry point using a traditional bean syntax and link them into the namespace, as we've just seen. -The corresponding `AuthenticationEntryPoint` can be set using the `entry-point-ref` attribute on the `` element. - -The CAS sample application is a good example of the use of custom beans with the namespace, including this syntax. -If you aren't familiar with authentication entry points, they are discussed in the <> chapter. - - -[[ns-method-security]] -== Method Security -From version 2.0 onwards Spring Security has improved support substantially for adding security to your service layer methods. -It provides support for JSR-250 annotation security as well as the framework's original `@Secured` annotation. -From 3.0 you can also make use of new <>. -You can apply security to a single bean, using the `intercept-methods` element to decorate the bean declaration, or you can secure multiple beans across the entire service layer using the AspectJ style pointcuts. - - -[[ns-global-method]] -=== The Element -This element is used to enable annotation-based security in your application (by setting the appropriate attributes on the element), and also to group together security pointcut declarations which will be applied across your entire application context. -You should only declare one `` element. -The following declaration would enable support for Spring Security's `@Secured`: - -[source,xml] ----- - ----- - -Adding an annotation to a method (on an class or interface) would then limit the access to that method accordingly. -Spring Security's native annotation support defines a set of attributes for the method. -These will be passed to the `AccessDecisionManager` for it to make the actual decision: - -[source,java] ----- -public interface BankService { - -@Secured("IS_AUTHENTICATED_ANONYMOUSLY") -public Account readAccount(Long id); - -@Secured("IS_AUTHENTICATED_ANONYMOUSLY") -public Account[] findAccounts(); - -@Secured("ROLE_TELLER") -public Account post(Account account, double amount); -} ----- - -Support for JSR-250 annotations can be enabled using - -[source,xml] ----- - ----- - -These are standards-based and allow simple role-based constraints to be applied but do not have the power Spring Security's native annotations. -To use the new expression-based syntax, you would use - -[source,xml] ----- - ----- - -and the equivalent Java code would be - -[source,java] ----- -public interface BankService { - -@PreAuthorize("isAnonymous()") -public Account readAccount(Long id); - -@PreAuthorize("isAnonymous()") -public Account[] findAccounts(); - -@PreAuthorize("hasAuthority('ROLE_TELLER')") -public Account post(Account account, double amount); -} ----- - -Expression-based annotations are a good choice if you need to define simple rules that go beyond checking the role names against the user's list of authorities. - -[NOTE] -=== -The annotated methods will only be secured for instances which are defined as Spring beans (in the same application context in which method-security is enabled). -If you want to secure instances which are not created by Spring (using the `new` operator, for example) then you need to use AspectJ. -=== - -[NOTE] -=== -You can enable more than one type of annotation in the same application, but only one type should be used for any interface or class as the behaviour will not be well-defined otherwise. -If two annotations are found which apply to a particular method, then only one of them will be applied. -=== - -[[ns-protect-pointcut]] -==== Adding Security Pointcuts using protect-pointcut - -The use of `protect-pointcut` is particularly powerful, as it allows you to apply security to many beans with only a simple declaration. -Consider the following example: - -[source,xml] ----- - - - ----- - -This will protect all methods on beans declared in the application context whose classes are in the `com.mycompany` package and whose class names end in "Service". -Only users with the `ROLE_USER` role will be able to invoke these methods. -As with URL matching, the most specific matches must come first in the list of pointcuts, as the first matching expression will be used. -Security annotations take precedence over pointcuts. - -[[ns-access-manager]] -== The Default AccessDecisionManager -This section assumes you have some knowledge of the underlying architecture for access-control within Spring Security. -If you don't you can skip it and come back to it later, as this section is only really relevant for people who need to do some customization in order to use more than simple role-based security. - -When you use a namespace configuration, a default instance of `AccessDecisionManager` is automatically registered for you and will be used for making access decisions for method invocations and web URL access, based on the access attributes you specify in your `intercept-url` and `protect-pointcut` declarations (and in annotations if you are using annotation secured methods). - -The default strategy is to use an `AffirmativeBased` `AccessDecisionManager` with a `RoleVoter` and an `AuthenticatedVoter`. -You can find out more about these in the chapter on <>. - - -[[ns-custom-access-mgr]] -=== Customizing the AccessDecisionManager -If you need to use a more complicated access control strategy then it is easy to set an alternative for both method and web security. - -For method security, you do this by setting the `access-decision-manager-ref` attribute on `global-method-security` to the `id` of the appropriate `AccessDecisionManager` bean in the application context: - -[source,xml] ----- - -... - ----- - -The syntax for web security is the same, but on the `http` element: - -[source,xml] ----- - -... - ----- - -[[ns-auth-manager]] -== The Authentication Manager and the Namespace -The main interface which provides authentication services in Spring Security is the `AuthenticationManager`. -This is usually an instance of Spring Security's `ProviderManager` class, which you may already be familiar with if you've used the framework before. -If not, it will be covered later, in the <>. -The bean instance is registered using the `authentication-manager` namespace element. -You can't use a custom `AuthenticationManager` if you are using either HTTP or method security through the namespace, but this should not be a problem as you have full control over the `AuthenticationProvider` s that are used. - -You may want to register additional `AuthenticationProvider` beans with the `ProviderManager` and you can do this using the `` element with the `ref` attribute, where the value of the attribute is the name of the provider bean you want to add. -For example: - -[source,xml] ----- - - - - - -... - ----- - -Another common requirement is that another bean in the context may require a reference to the `AuthenticationManager`. -You can easily register an alias for the `AuthenticationManager` and use this name elsewhere in your application context. - -[source,xml] ----- - -... - - - - -... - ----- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/web/csrf.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/web/csrf.adoc deleted file mode 100644 index 5e8f3068c67..00000000000 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/web/csrf.adoc +++ /dev/null @@ -1,485 +0,0 @@ - -[[csrf]] -== Cross Site Request Forgery (CSRF) -This section discusses Spring Security's http://en.wikipedia.org/wiki/Cross-site_request_forgery[ Cross Site Request Forgery (CSRF)] support. - - -=== CSRF Attacks -Before we discuss how Spring Security can protect applications from CSRF attacks, we will explain what a CSRF attack is. -Let's take a look at a concrete example to get a better understanding. - -Assume that your bank's website provides a form that allows transferring money from the currently logged in user to another bank account. -For example, the HTTP request might look like: - - -[source] ----- -POST /transfer HTTP/1.1 -Host: bank.example.com -Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly -Content-Type: application/x-www-form-urlencoded - -amount=100.00&routingNumber=1234&account=9876 ----- - -Now pretend you authenticate to your bank's website and then, without logging out, visit an evil website. -The evil website contains an HTML page with the following form: - -[source,xml] ----- -
- - - - -
----- - -You like to win money, so you click on the submit button. -In the process, you have unintentionally transferred $100 to a malicious user. -This happens because, while the evil website cannot see your cookies, the cookies associated with your bank are still sent along with the request. - -Worst yet, this whole process could have been automated using JavaScript. -This means you didn't even need to click on the button. -So how do we protect ourselves from such attacks? - -=== Synchronizer Token Pattern -The issue is that the HTTP request from the bank's website and the request from the evil website are exactly the same. -This means there is no way to reject requests coming from the evil website and allow requests coming from the bank's website. -To protect against CSRF attacks we need to ensure there is something in the request that the evil site is unable to provide. - -One solution is to use the https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#General_Recommendation:_Synchronizer_Token_Pattern[Synchronizer Token Pattern]. -This solution is to ensure that each request requires, in addition to our session cookie, a randomly generated token as an HTTP parameter. -When a request is submitted, the server must look up the expected value for the parameter and compare it against the actual value in the request. -If the values do not match, the request should fail. - -We can relax the expectations to only require the token for each HTTP request that updates state. -This can be safely done since the same origin policy ensures the evil site cannot read the response. -Additionally, we do not want to include the random token in HTTP GET as this can cause the tokens to be leaked. - -Let's take a look at how our example would change. -Assume the randomly generated token is present in an HTTP parameter named _csrf. -For example, the request to transfer money would look like this: - - -[source] ----- -POST /transfer HTTP/1.1 -Host: bank.example.com -Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly -Content-Type: application/x-www-form-urlencoded - -amount=100.00&routingNumber=1234&account=9876&_csrf= ----- - - -You will notice that we added the _csrf parameter with a random value. -Now the evil website will not be able to guess the correct value for the _csrf parameter (which must be explicitly provided on the evil website) and the transfer will fail when the server compares the actual token to the expected token. - - -=== When to use CSRF protection -When should you use CSRF protection? Our recommendation is to use CSRF protection for any request that could be processed by a browser by normal users. -If you are only creating a service that is used by non-browser clients, you will likely want to disable CSRF protection. - - -==== CSRF protection and JSON -A common question is "do I need to protect JSON requests made by javascript?" The short answer is, it depends. -However, you must be very careful as there are CSRF exploits that can impact JSON requests. -For example, a malicious user can create a http://blog.opensecurityresearch.com/2012/02/json-csrf-with-parameter-padding.html[CSRF with JSON using the following form]: - - -[source,xml] ----- -
- - -
----- - - -This will produce the following JSON structure - - -[source,javascript] ----- -{ "amount": 100, -"routingNumber": "evilsRoutingNumber", -"account": "evilsAccountNumber", -"ignore_me": "=test" -} ----- - -If an application were not validating the Content-Type, then it would be exposed to this exploit. -Depending on the setup, a Spring MVC application that validates the Content-Type could still be exploited by updating the URL suffix to end with ".json" as shown below: - -[source,xml] ----- -
- - -
----- - -==== CSRF and Stateless Browser Applications -What if my application is stateless? That doesn't necessarily mean you are protected. -In fact, if a user does not need to perform any actions in the web browser for a given request, they are likely still vulnerable to CSRF attacks. - -For example, consider an application uses a custom cookie that contains all the state within it for authentication instead of the JSESSIONID. -When the CSRF attack is made the custom cookie will be sent with the request in the same manner that the JSESSIONID cookie was sent in our previous example. - -Users using basic authentication are also vulnerable to CSRF attacks since the browser will automatically include the username password in any requests in the same manner that the JSESSIONID cookie was sent in our previous example. - -[[csrf-using]] -=== Using Spring Security CSRF Protection -So what are the steps necessary to use Spring Security's to protect our site against CSRF attacks? The steps to using Spring Security's CSRF protection are outlined below: - -* <> -* <> -* <> - -[[csrf-use-proper-verbs]] -==== Use proper HTTP verbs -The first step to protecting against CSRF attacks is to ensure your website uses proper HTTP verbs. -Specifically, before Spring Security's CSRF support can be of use, you need to be certain that your application is using PATCH, POST, PUT, and/or DELETE for anything that modifies state. - -This is not a limitation of Spring Security's support, but instead a general requirement for proper CSRF prevention. -The reason is that including private information in an HTTP GET can cause the information to be leaked. -See http://www.w3.org/Protocols/rfc2616/rfc2616-sec15.html#sec15.1.3[RFC 2616 Section 15.1.3 Encoding Sensitive Information in URI's] for general guidance on using POST instead of GET for sensitive information. - - -[[csrf-configure]] -==== Configure CSRF Protection -The next step is to include Spring Security's CSRF protection within your application. -Some frameworks handle invalid CSRF tokens by invaliding the user's session, but this causes <>. -Instead by default Spring Security's CSRF protection will produce an HTTP 403 access denied. -This can be customized by configuring the <> to process `InvalidCsrfTokenException` differently. - -As of Spring Security 4.0, CSRF protection is enabled by default with XML configuration. -If you would like to disable CSRF protection, the corresponding XML configuration can be seen below. - -[source,xml] ----- - - - - ----- - -CSRF protection is enabled by default with Java Configuration. -If you would like to disable CSRF, the corresponding Java configuration can be seen below. -Refer to the Javadoc of csrf() for additional customizations in how CSRF protection is configured. - -[source,java] ----- -@EnableWebSecurity -public class WebSecurityConfig extends -WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .csrf().disable(); - } -} ----- - -[[csrf-include-csrf-token]] -==== Include the CSRF Token - -[[csrf-include-csrf-token-form]] -===== Form Submissions -The last step is to ensure that you include the CSRF token in all PATCH, POST, PUT, and DELETE methods. -One way to approach this is to use the `_csrf` request attribute to obtain the current `CsrfToken`. -An example of doing this with a JSP is shown below: - -[source,xml] ----- - -
- - -
----- - -An easier approach is to use <> from the Spring Security JSP tag library. - -[NOTE] -==== -If you are using Spring MVC `` tag or http://www.thymeleaf.org/whatsnew21.html#reqdata[Thymeleaf 2.1+] and are using `@EnableWebSecurity`, the `CsrfToken` is automatically included for you (using the `CsrfRequestDataValueProcessor`). -==== - -[[csrf-include-csrf-token-ajax]] -===== Ajax and JSON Requests -If you are using JSON, then it is not possible to submit the CSRF token within an HTTP parameter. -Instead you can submit the token within a HTTP header. -A typical pattern would be to include the CSRF token within your meta tags. -An example with a JSP is shown below: - - -[source,xml] ----- - - - - - - - - ----- - -Instead of manually creating the meta tags, you can use the simpler <> from the Spring Security JSP tag library. - -You can then include the token within all your Ajax requests. -If you were using jQuery, this could be done with the following: - -[source,javascript] ----- -$(function () { -var token = $("meta[name='_csrf']").attr("content"); -var header = $("meta[name='_csrf_header']").attr("content"); -$(document).ajaxSend(function(e, xhr, options) { - xhr.setRequestHeader(header, token); -}); -}); ----- - -As an alternative to jQuery, we recommend using http://cujojs.com/[cujoJS's] rest.js. -The https://github.com/cujojs/rest[rest.js] module provides advanced support for working with HTTP requests and responses in RESTful ways. -A core capability is the ability to contextualize the HTTP client adding behavior as needed by chaining interceptors on to the client. - -[source,javascript] ----- -var client = rest.chain(csrf, { -token: $("meta[name='_csrf']").attr("content"), -name: $("meta[name='_csrf_header']").attr("content") -}); ----- - - -The configured client can be shared with any component of the application that needs to make a request to the CSRF protected resource. -One significant difference between rest.js and jQuery is that only requests made with the configured client will contain the CSRF token, vs jQuery where __all__ requests will include the token. -The ability to scope which requests receive the token helps guard against leaking the CSRF token to a third party. -Please refer to the https://github.com/cujojs/rest/tree/master/docs[rest.js reference documentation] for more information on rest.js. - -[[csrf-cookie]] -===== CookieCsrfTokenRepository - -There can be cases where users will want to persist the `CsrfToken` in a cookie. -By default the `CookieCsrfTokenRepository` will write to a cookie named `XSRF-TOKEN` and read it from a header named `X-XSRF-TOKEN` or the HTTP parameter `_csrf`. -These defaults come from https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection[AngularJS] - -You can configure `CookieCsrfTokenRepository` in XML using the following: - -[source,xml] ----- - - - - - ----- - -[NOTE] -==== -The sample explicitly sets `cookieHttpOnly=false`. -This is necessary to allow JavaScript (i.e. AngularJS) to read it. -If you do not need the ability to read the cookie with JavaScript directly, it is recommended to omit `cookieHttpOnly=false` to improve security. -==== - - -You can configure `CookieCsrfTokenRepository` in Java Configuration using: - -[source,java] ----- -@EnableWebSecurity -public class WebSecurityConfig extends - WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .csrf() - .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); - } -} ----- - -[NOTE] -==== -The sample explicitly sets `cookieHttpOnly=false`. -This is necessary to allow JavaScript (i.e. AngularJS) to read it. -If you do not need the ability to read the cookie with JavaScript directly, it is recommended to omit `cookieHttpOnly=false` (by using `new CookieCsrfTokenRepository()` instead) to improve security. -==== - - -[[csrf-caveats]] -=== CSRF Caveats -There are a few caveats when implementing CSRF. - - -[[csrf-timeouts]] -==== Timeouts -One issue is that the expected CSRF token is stored in the HttpSession, so as soon as the HttpSession expires your configured `AccessDeniedHandler` will receive a InvalidCsrfTokenException. -If you are using the default `AccessDeniedHandler`, the browser will get an HTTP 403 and display a poor error message. - -[NOTE] -==== -One might ask why the expected `CsrfToken` isn't stored in a cookie by default. -This is because there are known exploits in which headers (i.e. specify the cookies) can be set by another domain. -This is the same reason Ruby on Rails http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails/[no longer skips CSRF checks when the header X-Requested-With is present]. -See http://lists.webappsec.org/pipermail/websecurity_lists.webappsec.org/2011-February/007533.html[this webappsec.org thread] for details on how to perform the exploit. -Another disadvantage is that by removing the state (i.e. the timeout) you lose the ability to forcibly terminate the token if it is compromised. -==== - -A simple way to mitigate an active user experiencing a timeout is to have some JavaScript that lets the user know their session is about to expire. -The user can click a button to continue and refresh the session. - -Alternatively, specifying a custom `AccessDeniedHandler` allows you to process the `InvalidCsrfTokenException` any way you like. -For an example of how to customize the `AccessDeniedHandler` refer to the provided links for both <> and https://github.com/spring-projects/spring-security/blob/3.2.0.RC1/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpAccessDeniedHandlerTests.groovy#L64[Java configuration]. - -Finally, the application can be configured to use <> which will not expire. -As previously mentioned, this is not as secure as using a session, but in many cases can be good enough. - - -[[csrf-login]] -==== Logging In -In order to protect against http://en.wikipedia.org/wiki/Cross-site_request_forgery#Forging_login_requests[forging log in requests] the log in form should be protected against CSRF attacks too. -Since the `CsrfToken` is stored in HttpSession, this means an HttpSession will be created as soon as `CsrfToken` token attribute is accessed. -While this sounds bad in a RESTful / stateless architecture the reality is that state is necessary to implement practical security. -Without state, we have nothing we can do if a token is compromised. -Practically speaking, the CSRF token is quite small in size and should have a negligible impact on our architecture. - -A common technique to protect the log in form is by using a JavaScript function to obtain a valid CSRF token before the form submission. -By doing this, there is no need to think about session timeouts (discussed in the previous section) because the session is created right before the form submission (assuming that <> isn't configured instead), so the user can stay on the login page and submit the username/password when he wants. -In order to achieve this, you can take advantage of the `CsrfTokenArgumentResolver` provided by Spring Security and expose an endpoint like it's described on <>. - - -[[csrf-logout]] -==== Logging Out -Adding CSRF will update the LogoutFilter to only use HTTP POST. -This ensures that log out requires a CSRF token and that a malicious user cannot forcibly log out your users. - -One approach is to use a form for log out. -If you really want a link, you can use JavaScript to have the link perform a POST (i.e. maybe on a hidden form). -For browsers with JavaScript that is disabled, you can optionally have the link take the user to a log out confirmation page that will perform the POST. - -If you really want to use HTTP GET with logout you can do so, but remember this is generally not recommended. -For example, the following Java Configuration will perform logout with the URL /logout is requested with any HTTP method: - -[source,java] ----- -@EnableWebSecurity -public class WebSecurityConfig extends -WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .logout() - .logoutRequestMatcher(new AntPathRequestMatcher("/logout")); - } -} ----- - -[[csrf-multipart]] -==== Multipart (file upload) -There are two options to using CSRF protection with multipart/form-data. -Each option has its tradeoffs. - -* <> -* <> - -[NOTE] -==== -Before you integrate Spring Security's CSRF protection with multipart file upload, ensure that you can upload without the CSRF protection first. -More information about using multipart forms with Spring can be found within the http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-multipart[17.10 Spring's multipart (file upload) support] section of the Spring reference and the http://docs.spring.io/spring/docs/3.2.x/javadoc-api/org/springframework/web/multipart/support/MultipartFilter.html[MultipartFilter javadoc]. -==== - -[[csrf-multipartfilter]] -===== Placing MultipartFilter before Spring Security -The first option is to ensure that the `MultipartFilter` is specified before the Spring Security filter. -Specifying the `MultipartFilter` before the Spring Security filter means that there is no authorization for invoking the `MultipartFilter` which means anyone can place temporary files on your server. -However, only authorized users will be able to submit a File that is processed by your application. -In general, this is the recommended approach because the temporary file upload should have a negligble impact on most servers. - -To ensure `MultipartFilter` is specified before the Spring Security filter with java configuration, users can override beforeSpringSecurityFilterChain as shown below: - -[source,java] ----- -public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer { - - @Override - protected void beforeSpringSecurityFilterChain(ServletContext servletContext) { - insertFilters(servletContext, new MultipartFilter()); - } -} ----- - -To ensure `MultipartFilter` is specified before the Spring Security filter with XML configuration, users can ensure the element of the `MultipartFilter` is placed before the springSecurityFilterChain within the web.xml as shown below: - -[source,xml] ----- - - MultipartFilter - org.springframework.web.multipart.support.MultipartFilter - - - springSecurityFilterChain - org.springframework.web.filter.DelegatingFilterProxy - - - MultipartFilter - /* - - - springSecurityFilterChain - /* - ----- - -[[csrf-include-csrf-token-in-action]] -===== Include CSRF token in action -If allowing unauthorized users to upload temporariy files is not acceptable, an alternative is to place the `MultipartFilter` after the Spring Security filter and include the CSRF as a query parameter in the action attribute of the form. -An example with a jsp is shown below - -[source,xml] ----- -
----- - -The disadvantage to this approach is that query parameters can be leaked. -More genearlly, it is considered best practice to place sensitive data within the body or headers to ensure it is not leaked. -Additional information can be found in http://www.w3.org/Protocols/rfc2616/rfc2616-sec15.html#sec15.1.3[RFC 2616 Section 15.1.3 Encoding Sensitive Information in URI's]. - -==== HiddenHttpMethodFilter -The HiddenHttpMethodFilter should be placed before the Spring Security filter. -In general this is true, but it could have additional implications when protecting against CSRF attacks. - -Note that the HiddenHttpMethodFilter only overrides the HTTP method on a POST, so this is actually unlikely to cause any real problems. -However, it is still best practice to ensure it is placed before Spring Security's filters. - -=== Overriding Defaults -Spring Security's goal is to provide defaults that protect your users from exploits. -This does not mean that you are forced to accept all of its defaults. - -For example, you can provide a custom CsrfTokenRepository to override the way in which the `CsrfToken` is stored. - -You can also specify a custom RequestMatcher to determine which requests are protected by CSRF (i.e. perhaps you don't care if log out is exploited). -In short, if Spring Security's CSRF protection doesn't behave exactly as you want it, you are able to customize the behavior. -Refer to the <> documentation for details on how to make these customizations with XML and the `CsrfConfigurer` javadoc for details on how to make these customizations when using Java configuration. diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/web/index.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/web/index.adoc deleted file mode 100644 index 7a587870bef..00000000000 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/web/index.adoc +++ /dev/null @@ -1,28 +0,0 @@ -[[web-app-security]] -= Web Application Security -Most Spring Security users will be using the framework in applications which make user of HTTP and the Servlet API. -In this part, we'll take a look at how Spring Security provides authentication and access-control features for the web layer of an application. -We'll look behind the facade of the namespace and see which classes and interfaces are actually assembled to provide web-layer security. -In some situations it is necessary to use traditional bean configuration to provide full control over the configuration, so we'll also see how to configure these classes directly without the namespace. - -include::security-filter-chain.adoc[] - -include::core-filters.adoc[] - -include::servlet-api.adoc[] - -include::basic.adoc[] - -include::rememberme.adoc[] - -include::csrf.adoc[] - -include::cors.adoc[] - -include::headers.adoc[] - -include::session-management.adoc[] - -include::anonymous.adoc[] - -include::websocket.adoc[] diff --git a/docs/manual/src/docs/asciidoc/index.adoc b/docs/manual/src/docs/asciidoc/index.adoc index f8708396023..420bb3008c3 100644 --- a/docs/manual/src/docs/asciidoc/index.adoc +++ b/docs/manual/src/docs/asciidoc/index.adoc @@ -5,12 +5,9 @@ Ben Alex; Luke Taylor; Rob Winch; Gunnar Hillert; Joe Grandja; Jay Bryant :source-indent: 0 :tabsize: 4 -// FIXME: Add links for authentication, authorization, common attacks -Spring Security is a framework that provides authentication, authorization, and protection against common attacks. -// FIXME: Add links for imperative and reactive applications -With first class support for both imperative and reactive applications, it is the de-facto standard for securing Spring-based applications. +Spring Security provides first class authentication, authorization, and protection against common attacks for web applications deployed in a <> and <>. -include::{include-dir}/preface/index.adoc[] +include::{include-dir}/about/index.adoc[] include::{include-dir}/servlet/index.adoc[] diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserServiceTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserServiceTests.java index a43da286bd6..4b8e6db1657 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserServiceTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserServiceTests.java @@ -16,15 +16,12 @@ package org.springframework.security.oauth2.client.userinfo; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import java.time.Duration; +import java.time.Instant; + import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; -import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.TestClientRegistrations; import org.springframework.security.oauth2.core.AuthenticationMethod; @@ -33,13 +30,16 @@ import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.oauth2.core.user.OAuth2UserAuthority; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; import reactor.test.StepVerifier; -import java.time.Duration; -import java.time.Instant; - -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * @author Rob Winch @@ -208,7 +208,7 @@ public void loadUserWhenUserInfoErrorResponseThenThrowOAuth2AuthenticationExcept public void loadUserWhenUserInfoUriInvalidThenThrowAuthenticationServiceException() throws Exception { this.clientRegistration.userInfoUri("http://invalid-provider.com/user"); assertThatThrownBy(() -> this.userService.loadUser(oauth2UserRequest()).block()) - .isInstanceOf(AuthenticationServiceException.class); + .isInstanceOf(OAuth2AuthenticationException.class); } private OAuth2UserRequest oauth2UserRequest() { diff --git a/samples/javaconfig/hellomvc/spring-security-samples-javaconfig-hellomvc.gradle b/samples/javaconfig/hellomvc/spring-security-samples-javaconfig-hellomvc.gradle index 3074d45935e..a0f34dccff8 100644 --- a/samples/javaconfig/hellomvc/spring-security-samples-javaconfig-hellomvc.gradle +++ b/samples/javaconfig/hellomvc/spring-security-samples-javaconfig-hellomvc.gradle @@ -3,7 +3,6 @@ apply plugin: 'io.spring.convention.spring-sample-war' dependencies { compile project(':spring-security-config') compile project(':spring-security-core') - compile project(':spring-security-samples-javaconfig-messages') compile project(':spring-security-web') compile 'javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api' compile 'javax.validation:validation-api' @@ -11,6 +10,8 @@ dependencies { compile 'org.hibernate:hibernate-validator' compile 'org.springframework:spring-jdbc' compile 'org.springframework:spring-webmvc' + compile 'org.thymeleaf:thymeleaf' + compile 'org.thymeleaf:thymeleaf-spring5' compile slf4jDependencies providedCompile 'javax.servlet.jsp:javax.servlet.jsp-api' diff --git a/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/pages/IndexController.java b/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/pages/IndexController.java new file mode 100644 index 00000000000..3ceff3d682c --- /dev/null +++ b/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/pages/IndexController.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.samples.pages; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +/** + * @author Rob Winch + */ +@Controller +public class IndexController { + @GetMapping("/") + String index() { + return "index"; + } +} diff --git a/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/pages/MvcConfiguration.java b/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/pages/MvcConfiguration.java new file mode 100644 index 00000000000..9fe74b0cedc --- /dev/null +++ b/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/pages/MvcConfiguration.java @@ -0,0 +1,60 @@ +/* + * Copyright 2002-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.security.samples.pages; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.ViewResolver; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.thymeleaf.spring5.SpringTemplateEngine; +import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver; +import org.thymeleaf.spring5.view.ThymeleafViewResolver; +import org.thymeleaf.templatemode.TemplateMode; + +@EnableWebMvc +@Configuration +@ComponentScan +public class MvcConfiguration implements WebMvcConfigurer { + + @Bean + public ViewResolver viewResolver(SpringTemplateEngine templateEngine) { + ThymeleafViewResolver resolver = new ThymeleafViewResolver(); + resolver.setTemplateEngine(templateEngine); + resolver.setCharacterEncoding("UTF-8"); + return resolver; + } + + @Bean + public SpringTemplateEngine templateEngine(SpringResourceTemplateResolver templateResolver) { + SpringTemplateEngine engine = new SpringTemplateEngine(); + engine.setEnableSpringELCompiler(true); + engine.setTemplateResolver(templateResolver); + return engine; + } + + @Bean + public SpringResourceTemplateResolver templateResolver(ApplicationContext applicationContext) { + SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver(); + resolver.setPrefix("classpath:/templates/"); + resolver.setSuffix(".html"); + resolver.setTemplateMode(TemplateMode.HTML); + resolver.setApplicationContext(applicationContext); + return resolver; + } +} diff --git a/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/pages/MvcInitializer.java b/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/pages/MvcInitializer.java new file mode 100644 index 00000000000..7549dd03629 --- /dev/null +++ b/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/pages/MvcInitializer.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.security.samples.pages; + +import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; + +public class MvcInitializer extends + AbstractAnnotationConfigDispatcherServletInitializer { + + @Override + protected Class[] getRootConfigClasses() { + return null; + } + + @Override + protected Class[] getServletConfigClasses() { + return null; + } + + @Override + protected String[] getServletMappings() { + return new String[] { "/" }; + } + +} diff --git a/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java b/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/pages/SecurityInitializer.java similarity index 66% rename from samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java rename to samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/pages/SecurityInitializer.java index a55447386ca..4d337a74047 100644 --- a/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java +++ b/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/pages/SecurityInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,17 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.security.samples.config; +package org.springframework.security.samples.pages; -import org.springframework.core.annotation.Order; import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; -/** - * No customizations of {@link AbstractSecurityWebApplicationInitializer} are necessary. - * - * @author Rob Winch - */ -@Order(2) -public class MessageSecurityWebApplicationInitializer extends +public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer { + public SecurityInitializer() { + super(MvcConfiguration.class, WebSecurityConfig.class); + } } diff --git a/samples/javaconfig/helloworld/src/main/java/org/springframework/security/samples/config/SecurityConfig.java b/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/pages/WebSecurityConfig.java similarity index 74% rename from samples/javaconfig/helloworld/src/main/java/org/springframework/security/samples/config/SecurityConfig.java rename to samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/pages/WebSecurityConfig.java index c5dfc89a2a3..a206b8af611 100644 --- a/samples/javaconfig/helloworld/src/main/java/org/springframework/security/samples/config/SecurityConfig.java +++ b/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/pages/WebSecurityConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.security.samples.config; +package org.springframework.security.samples.pages; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -23,13 +23,17 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager; @EnableWebSecurity -public class SecurityConfig { +public class WebSecurityConfig { // @formatter:off @Bean - public UserDetailsService userDetailsService() throws Exception { - UserDetails user = User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build(); - return new InMemoryUserDetailsManager(user); + public UserDetailsService userDetailsService() { + UserDetails user = User.withDefaultPasswordEncoder() + .username("user") + .password("password") + .roles("USER") + .build(); + return new InMemoryUserDetailsManager(user); } // @formatter:on } diff --git a/samples/javaconfig/hellomvc/src/main/resources/templates/index.html b/samples/javaconfig/hellomvc/src/main/resources/templates/index.html new file mode 100644 index 00000000000..7e7d282fd90 --- /dev/null +++ b/samples/javaconfig/hellomvc/src/main/resources/templates/index.html @@ -0,0 +1,32 @@ + + + + + + + + + Hello Security + + +
+
+

+ Log Out +

+ + diff --git a/samples/javaconfig/hellomvc/src/main/resources/views/README.adoc b/samples/javaconfig/hellomvc/src/main/resources/views/README.adoc deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/samples/javaconfig/hellomvc/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java b/samples/javaconfig/hellomvc/src/test/java/org/springframework/security/samples/pages/WebSecurityConfigTests.java similarity index 68% rename from samples/javaconfig/hellomvc/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java rename to samples/javaconfig/hellomvc/src/test/java/org/springframework/security/samples/pages/WebSecurityConfigTests.java index f798b89f3df..31051ec9589 100644 --- a/samples/javaconfig/hellomvc/src/test/java/org/springframework/security/samples/config/SecurityConfigTests.java +++ b/samples/javaconfig/hellomvc/src/test/java/org/springframework/security/samples/pages/WebSecurityConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,38 +13,38 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.security.samples.config; +package org.springframework.security.samples.pages; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; -import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; -import org.springframework.security.samples.mvc.config.WebMvcConfiguration; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.logout; +import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + /** * @author Rob Winch * */ @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = { RootConfiguration.class, WebMvcConfiguration.class }) +@ContextConfiguration(classes = { MvcConfiguration.class, WebSecurityConfig.class }) @WebAppConfiguration -public class SecurityConfigTests { +public class WebSecurityConfigTests { private MockMvc mvc; @Autowired @@ -80,31 +80,6 @@ public void requestProtectedResourceWithUser() throws Exception { mvc.perform(get("/")).andExpect(status().isOk()); } - @Test - @WithMockUser - public void composeMessageRequiresCsrfToken() throws Exception { - MockHttpServletRequestBuilder composeMessage = post("/").param("summary", - "New Message").param("text", "This is a new message"); - - mvc.perform(composeMessage).andExpect(status().isForbidden()); - } - - @Test - @WithMockUser - public void composeMessage() throws Exception { - MockHttpServletRequestBuilder composeMessage = post("/") - .param("summary", "New Message").param("text", "This is a new message") - .with(csrf()); - - mvc.perform(composeMessage).andExpect(redirectedUrlPattern("/*")); - } - - @Test - @WithMockUser - public void logoutRequiresCsrfToken() throws Exception { - mvc.perform(post("/logout")).andExpect(status().isForbidden()); - } - @Test @WithMockUser public void logoutSuccess() throws Exception { diff --git a/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/HelloWorldJcTests.java b/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/HelloWorldJcTests.java similarity index 84% rename from samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/HelloWorldJcTests.java rename to samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/HelloWorldJcTests.java index 5f4ebec2101..32338f6cf57 100644 --- a/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/HelloWorldJcTests.java +++ b/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/HelloWorldJcTests.java @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.security.samples; +package org.springframework.security.samples.pages; + +import org.springframework.security.samples.pages.pages.HomePage; +import org.springframework.security.samples.pages.pages.LoginPage; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.openqa.selenium.WebDriver; import org.openqa.selenium.htmlunit.HtmlUnitDriver; -import org.springframework.security.samples.pages.HomePage; -import org.springframework.security.samples.pages.LoginPage; /** * @author Michael Simons @@ -52,23 +53,23 @@ public void accessHomePageWithUnauthenticatedUserSendsToLoginPage() { @Test public void authenticatedUserIsSentToOriginalPage() { final HomePage homePage = HomePage.to(this.driver, this.port) - .loginForm() + .loginForm() .username("user") .password("password") - .submit(); + .submit(); homePage - .assertAt() - .andTheUserNameIsDisplayed(); + .assertAt() + .andTheUserNameIsDisplayed(); } @Test public void authenticatedUserLogsOut() { LoginPage loginPage = HomePage.to(this.driver, this.port) - .loginForm() + .loginForm() .username("user") .password("password") - .submit() - .logout(); + .submit() + .logout(); loginPage.assertAt(); loginPage = HomePage.to(this.driver, this.port); diff --git a/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java b/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/pages/HomePage.java similarity index 96% rename from samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java rename to samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/pages/HomePage.java index 76d033b4361..639ab02284a 100644 --- a/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/HomePage.java +++ b/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/pages/HomePage.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.security.samples.pages; +package org.springframework.security.samples.pages.pages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; diff --git a/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java b/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/pages/LoginPage.java similarity index 97% rename from samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java rename to samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/pages/LoginPage.java index 6d72ee7c03f..1d0aeea312a 100644 --- a/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/LoginPage.java +++ b/samples/javaconfig/helloworld/src/integration-test/java/org/springframework/security/samples/pages/pages/LoginPage.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.security.samples.pages; +package org.springframework.security.samples.pages.pages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; diff --git a/samples/javaconfig/helloworld/src/main/java/org/springframework/security/samples/config/SecurityWebApplicationInitializer.java b/samples/javaconfig/helloworld/src/main/java/org/springframework/security/samples/pages/SecurityWebApplicationInitializer.java similarity index 86% rename from samples/javaconfig/helloworld/src/main/java/org/springframework/security/samples/config/SecurityWebApplicationInitializer.java rename to samples/javaconfig/helloworld/src/main/java/org/springframework/security/samples/pages/SecurityWebApplicationInitializer.java index 478d905de76..f0b6fd44f18 100644 --- a/samples/javaconfig/helloworld/src/main/java/org/springframework/security/samples/config/SecurityWebApplicationInitializer.java +++ b/samples/javaconfig/helloworld/src/main/java/org/springframework/security/samples/pages/SecurityWebApplicationInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.security.samples.config; +package org.springframework.security.samples.pages; import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; @@ -26,6 +26,6 @@ public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer { public SecurityWebApplicationInitializer() { - super(SecurityConfig.class); + super(WebSecurityConfig.class); } } diff --git a/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/config/SecurityConfig.java b/samples/javaconfig/helloworld/src/main/java/org/springframework/security/samples/pages/WebSecurityConfig.java similarity index 54% rename from samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/config/SecurityConfig.java rename to samples/javaconfig/helloworld/src/main/java/org/springframework/security/samples/pages/WebSecurityConfig.java index 8b3d394dbb7..a206b8af611 100644 --- a/samples/javaconfig/hellomvc/src/main/java/org/springframework/security/samples/config/SecurityConfig.java +++ b/samples/javaconfig/helloworld/src/main/java/org/springframework/security/samples/pages/WebSecurityConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,23 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.security.samples.config; +package org.springframework.security.samples.pages; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; @EnableWebSecurity -public class SecurityConfig { +public class WebSecurityConfig { // @formatter:off - @Autowired - public void configureGlobal( - AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER")); + @Bean + public UserDetailsService userDetailsService() { + UserDetails user = User.withDefaultPasswordEncoder() + .username("user") + .password("password") + .roles("USER") + .build(); + return new InMemoryUserDetailsManager(user); } // @formatter:on } diff --git a/samples/xml/helloworld/src/main/webapp/WEB-INF/web.xml b/samples/xml/helloworld/src/main/webapp/WEB-INF/web.xml index 68563b6eea4..8ab31011575 100644 --- a/samples/xml/helloworld/src/main/webapp/WEB-INF/web.xml +++ b/samples/xml/helloworld/src/main/webapp/WEB-INF/web.xml @@ -5,9 +5,16 @@ http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> + Loads the Spring configurations from contextConfigLocation + --> + + org.springframework.web.context.ContextLoaderListener + + + contextConfigLocation @@ -15,7 +22,10 @@ - + springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy @@ -26,11 +36,20 @@ - - org.springframework.web.context.ContextLoaderListener -