From f01a13aa52600843df49cfc5837564a1e484f6dd Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Mon, 26 Jul 2021 15:48:55 -0500 Subject: [PATCH 01/34] Antora mkdir -p docs/modules/ROOT/ mkdir -p docs/modules/ROOT/pages/ git checkout antora-2.x docs/antora.yml git checkout antora-2.x docs/modules/ROOT/nav.adoc mv docs/manual/src/docs/asciidoc/images docs/modules/ROOT/ mv docs/manual/src/docs/asciidoc/_includes/* docs/modules/ROOT/pages/ cp ~/code/rwinch/spring-reference/*antora* ~/code/spring-projects/spring-security/ mv docs/modules/ROOT/pages/about docs/modules/ROOT/pages/overview --- docs/antora.yml | 24 ++ .../guides/spring-security-docs-guides.gradle | 40 --- docs/guides/src/docs/asciidoc/Guardfile | 15 -- .../asciidoc/_form-includes/verify-app.asc | 6 - ...exploring-the-secured-application-boot.asc | 21 -- ...ing-the-secured-application-javaconfig.asc | 19 -- .../exploring-the-secured-application-xml.asc | 19 -- .../secure-the-application-boot.asc | 118 --------- .../secure-the-application-javaconfig.asc | 92 ------- .../secure-the-application-xml.asc | 89 ------- .../_includes/maven-repository-milestone.asc | 14 - .../_includes/maven-repository-release.asc | 1 - .../_includes/maven-repository-snapshot.asc | 14 - .../_includes/setting-up-the-sample-boot.asc | 46 ---- .../_includes/setting-up-the-sample.asc | 48 ---- .../src/docs/asciidoc/form-javaconfig.asc | 249 ------------------ .../src/docs/asciidoc/hellomvc-javaconfig.asc | 127 --------- .../src/docs/asciidoc/helloworld-boot.asc | 97 ------- .../docs/asciidoc/helloworld-javaconfig.asc | 113 -------- .../src/docs/asciidoc/helloworld-xml.asc | 136 ---------- docs/guides/src/docs/asciidoc/index.asc | 17 -- .../manual/spring-security-docs-manual.gradle | 62 ----- docs/manual/src/docs/asciidoc/Guardfile | 12 - .../docs/asciidoc/_includes/about/index.adoc | 17 -- .../src/docs/asciidoc/_includes/content.adoc | 9 - docs/manual/src/docs/asciidoc/css/style.css | 5 - .../src/docs/asciidoc/index-docinfo.xml | 11 - docs/manual/src/docs/asciidoc/index.adoc | 19 -- .../ROOT}/images/Authentication.gif | Bin .../images/access-decision-voting.graffle | Bin .../ROOT}/images/access-decision-voting.png | Bin .../ROOT}/images/after-invocation.graffle | Bin .../ROOT}/images/after-invocation.png | Bin .../ROOT}/images/icons/number_1.png | Bin .../ROOT}/images/icons/number_10.png | Bin .../ROOT}/images/icons/number_2.png | Bin .../ROOT}/images/icons/number_3.png | Bin .../ROOT}/images/icons/number_4.png | Bin .../ROOT}/images/icons/number_5.png | Bin .../ROOT}/images/icons/number_6.png | Bin .../ROOT}/images/icons/number_7.png | Bin .../ROOT}/images/icons/number_8.png | Bin .../ROOT}/images/icons/number_9.png | Bin .../ROOT}/images/icons/numbers.odg | Bin .../asciidoc => modules/ROOT}/images/note.png | Bin .../ROOT}/images/palette.otg | Bin .../ROOT}/images/s2-banner-rhs.png | Bin .../ROOT}/images/s2_box_logo.png | Bin .../images/security-interception.graffle | Bin .../ROOT}/images/security-interception.png | Bin .../architecture/delegatingfilterproxy.odg | Bin .../architecture/delegatingfilterproxy.png | Bin .../exceptiontranslationfilter.odg | Bin .../exceptiontranslationfilter.png | Bin .../servlet/architecture/filterchain.gif | Bin .../servlet/architecture/filterchain.odg | Bin .../servlet/architecture/filterchain.png | Bin .../servlet/architecture/filterchainproxy.odg | Bin .../servlet/architecture/filterchainproxy.png | Bin .../multi-securityfilterchain.odg | Bin .../multi-securityfilterchain.png | Bin .../architecture/securityfilterchain.odg | Bin .../architecture/securityfilterchain.png | Bin ...abstractauthenticationprocessingfilter.odg | Bin ...abstractauthenticationprocessingfilter.png | Bin .../architecture/providermanager-parent.odg | Bin .../architecture/providermanager-parent.png | Bin .../architecture/providermanager.odg | Bin .../architecture/providermanager.png | Bin .../architecture/providermanagers-parent.odg | Bin .../architecture/providermanagers-parent.png | Bin .../architecture/securitycontextholder.odg | Bin .../architecture/securitycontextholder.png | Bin .../unpwd/basicauthenticationentrypoint.odg | Bin .../unpwd/basicauthenticationentrypoint.png | Bin .../unpwd/basicauthenticationfilter.odg | Bin .../unpwd/basicauthenticationfilter.png | Bin .../unpwd/daoauthenticationprovider.odg | Bin .../unpwd/daoauthenticationprovider.png | Bin .../loginurlauthenticationentrypoint.odg | Bin .../loginurlauthenticationentrypoint.png | Bin .../usernamepasswordauthenticationfilter.odg | Bin .../usernamepasswordauthenticationfilter.png | Bin .../filtersecurityinterceptor.odg | Bin .../filtersecurityinterceptor.png | Bin .../oauth2/beareraccessdeniedhandler.odg | Bin .../oauth2/bearerauthenticationentrypoint.odg | Bin .../oauth2/bearerauthenticationentrypoint.png | Bin .../bearertokenauthenticationfilter.odg | Bin .../bearertokenauthenticationfilter.png | Bin .../oauth2/jwtauthenticationprovider.odg | Bin .../oauth2/jwtauthenticationprovider.png | Bin .../opaquetokenauthenticationprovider.odg | Bin .../opaquetokenauthenticationprovider.png | Bin .../saml2/opensamlauthenticationprovider.odg | Bin .../saml2/opensamlauthenticationprovider.png | Bin .../saml2/saml2webssoauthenticationfilter.odg | Bin .../saml2/saml2webssoauthenticationfilter.png | Bin ...saml2webssoauthenticationrequestfilter.odg | Bin ...saml2webssoauthenticationrequestfilter.png | Bin .../asciidoc => modules/ROOT}/images/tip.png | Bin docs/modules/ROOT/nav.adoc | 72 +++++ .../ROOT/pages/overview}/community.adoc | 0 .../features}/authentication/index.adoc | 0 .../authentication/password-storage.adoc | 0 .../features}/authentication/supported.adoc | 0 .../overview/features}/exploits/csrf.adoc | 0 .../overview/features}/exploits/headers.adoc | 0 .../overview/features}/exploits/http.adoc | 0 .../overview/features}/exploits/index.adoc | 0 .../ROOT/pages/overview/features/index.adoc} | 3 - .../overview}/getting-spring-security.adoc | 0 docs/modules/ROOT/pages/overview/index.adoc | 5 + .../ROOT/pages/overview}/modules.adoc | 0 .../ROOT/pages/overview}/prerequisites.adoc | 0 .../ROOT/pages/overview}/samples.adoc | 0 .../ROOT/pages/overview}/whats-new.adoc | 0 .../ROOT/pages}/reactive/cors.adoc | 0 .../ROOT/pages}/reactive/exploits/csrf.adoc | 0 .../pages}/reactive/exploits/headers.adoc | 0 .../ROOT/pages}/reactive/exploits/http.adoc | 0 .../ROOT/pages}/reactive/exploits/index.adoc | 0 .../ROOT/pages}/reactive/index.adoc | 0 .../ROOT/pages}/reactive/logout.adoc | 0 .../ROOT/pages}/reactive/method.adoc | 0 .../pages}/reactive/oauth2/access-token.adoc | 0 .../ROOT/pages}/reactive/oauth2/index.adoc | 0 .../ROOT/pages}/reactive/oauth2/login.adoc | 0 .../reactive/oauth2/resource-server.adoc | 0 .../registered-oauth2-authorized-client.adoc | 0 .../ROOT/pages}/reactive/rsocket.adoc | 0 .../ROOT/pages}/reactive/test.adoc | 0 .../ROOT/pages}/reactive/webclient.adoc | 0 .../ROOT/pages}/reactive/webflux.adoc | 0 .../ROOT/pages}/reactive/x509.adoc | 0 .../servlet/appendix/database-schema.adoc | 0 .../ROOT/pages}/servlet/appendix/faq.adoc | 0 .../ROOT/pages}/servlet/appendix/index.adoc | 0 .../pages}/servlet/appendix/namespace.adoc | 0 .../pages}/servlet/appendix/proxy-server.adoc | 0 .../architecture/delegating-filter-proxy.adoc | 0 .../exception-translation-filter.adoc | 0 .../architecture/filter-chain-proxy.adoc | 0 .../pages}/servlet/architecture/filters.adoc | 0 .../pages}/servlet/architecture/index.adoc | 0 .../architecture/security-filter-chain.adoc | 0 .../architecture/security-filters.adoc | 0 .../servlet/authentication/anonymous.adoc | 0 ...ract-authentication-processing-filter.adoc | 0 .../authentication-entry-point.adoc | 0 .../architecture/authentication-manager.adoc | 0 .../architecture/authentication-provider.adoc | 0 .../architecture/authentication.adoc | 0 .../architecture/granted-authority.adoc | 0 .../authentication/architecture/index.adoc | 0 .../architecture/provider-manager.adoc | 0 .../architecture/security-context-holder.adoc | 0 .../architecture/security-context.adoc | 0 .../pages}/servlet/authentication/cas.adoc | 0 .../pages}/servlet/authentication/events.adoc | 0 .../pages}/servlet/authentication/index.adoc | 0 .../pages}/servlet/authentication/jaas.adoc | 0 .../pages}/servlet/authentication/logout.adoc | 0 .../pages}/servlet/authentication/openid.adoc | 0 .../servlet/authentication/preauth.adoc | 0 .../servlet/authentication/rememberme.adoc | 0 .../pages}/servlet/authentication/runas.adoc | 0 .../authentication/session-management.adoc | 0 .../servlet/authentication/unpwd/basic.adoc | 0 .../unpwd/dao-authentication-provider.adoc | 0 .../servlet/authentication/unpwd/digest.adoc | 0 .../servlet/authentication/unpwd/form.adoc | 0 .../authentication/unpwd/in-memory.adoc | 0 .../servlet/authentication/unpwd/index.adoc | 23 +- .../servlet/authentication/unpwd/jdbc.adoc | 0 .../servlet/authentication/unpwd/ldap.adoc | 0 .../unpwd/password-encoder.adoc | 0 .../unpwd/user-details-service.adoc | 0 .../authentication/unpwd/user-details.adoc | 0 .../pages}/servlet/authentication/x509.adoc | 0 .../pages}/servlet/authorization/acls.adoc | 0 .../servlet/authorization/architecture.adoc | 0 .../authorization/authorize-requests.adoc | 0 .../authorization/expression-based.adoc | 0 .../pages}/servlet/authorization/index.adoc | 0 .../authorization/method-security.adoc | 0 .../servlet/authorization/secure-objects.adoc | 0 .../ROOT/pages}/servlet/crypto/index.adoc | 0 .../ROOT/pages}/servlet/exploits/csrf.adoc | 0 .../pages}/servlet/exploits/firewall.adoc | 0 .../ROOT/pages}/servlet/exploits/headers.adoc | 0 .../ROOT/pages}/servlet/exploits/http.adoc | 0 .../ROOT/pages}/servlet/exploits/index.adoc | 0 .../ROOT/pages}/servlet/hello/index.adoc | 0 .../ROOT/pages}/servlet/index.adoc | 0 .../servlet/integrations/concurrency.adoc | 0 .../pages}/servlet/integrations/cors.adoc | 0 .../pages}/servlet/integrations/data.adoc | 0 .../pages}/servlet/integrations/index.adoc | 0 .../pages}/servlet/integrations/jackson.adoc | 0 .../servlet/integrations/jsp-taglibs.adoc | 0 .../servlet/integrations/localization.adoc | 0 .../ROOT/pages}/servlet/integrations/mvc.adoc | 0 .../servlet/integrations/servlet-api.adoc | 0 .../servlet/integrations/websocket.adoc | 0 .../servlet/java-configuration/index.adoc | 2 +- .../servlet/kotlin-configuration/index.adoc | 0 .../ROOT/pages}/servlet/namespace/index.adoc | 0 .../ROOT/pages}/servlet/oauth2/index.adoc | 0 .../pages}/servlet/oauth2/oauth2-client.adoc | 0 .../pages}/servlet/oauth2/oauth2-login.adoc | 0 .../servlet/oauth2/oauth2-resourceserver.adoc | 0 .../ROOT/pages}/servlet/saml2/index.adoc | 0 .../pages}/servlet/saml2/saml2-login.adoc | 0 .../ROOT/pages}/servlet/test/index.adoc | 0 .../ROOT/pages}/servlet/test/method.adoc | 0 .../ROOT/pages}/servlet/test/mockmvc.adoc | 0 local-antora-playbook.yml | 15 ++ 218 files changed, 119 insertions(+), 1441 deletions(-) create mode 100644 docs/antora.yml delete mode 100644 docs/guides/spring-security-docs-guides.gradle delete mode 100644 docs/guides/src/docs/asciidoc/Guardfile delete mode 100644 docs/guides/src/docs/asciidoc/_form-includes/verify-app.asc delete mode 100644 docs/guides/src/docs/asciidoc/_hello-includes/exploring-the-secured-application-boot.asc delete mode 100644 docs/guides/src/docs/asciidoc/_hello-includes/exploring-the-secured-application-javaconfig.asc delete mode 100644 docs/guides/src/docs/asciidoc/_hello-includes/exploring-the-secured-application-xml.asc delete mode 100644 docs/guides/src/docs/asciidoc/_hello-includes/secure-the-application-boot.asc delete mode 100644 docs/guides/src/docs/asciidoc/_hello-includes/secure-the-application-javaconfig.asc delete mode 100644 docs/guides/src/docs/asciidoc/_hello-includes/secure-the-application-xml.asc delete mode 100644 docs/guides/src/docs/asciidoc/_includes/maven-repository-milestone.asc delete mode 100644 docs/guides/src/docs/asciidoc/_includes/maven-repository-release.asc delete mode 100644 docs/guides/src/docs/asciidoc/_includes/maven-repository-snapshot.asc delete mode 100644 docs/guides/src/docs/asciidoc/_includes/setting-up-the-sample-boot.asc delete mode 100644 docs/guides/src/docs/asciidoc/_includes/setting-up-the-sample.asc delete mode 100644 docs/guides/src/docs/asciidoc/form-javaconfig.asc delete mode 100644 docs/guides/src/docs/asciidoc/hellomvc-javaconfig.asc delete mode 100644 docs/guides/src/docs/asciidoc/helloworld-boot.asc delete mode 100644 docs/guides/src/docs/asciidoc/helloworld-javaconfig.asc delete mode 100644 docs/guides/src/docs/asciidoc/helloworld-xml.asc delete mode 100644 docs/guides/src/docs/asciidoc/index.asc delete mode 100644 docs/manual/spring-security-docs-manual.gradle delete mode 100644 docs/manual/src/docs/asciidoc/Guardfile delete mode 100644 docs/manual/src/docs/asciidoc/_includes/about/index.adoc delete mode 100644 docs/manual/src/docs/asciidoc/_includes/content.adoc delete mode 100644 docs/manual/src/docs/asciidoc/css/style.css delete mode 100644 docs/manual/src/docs/asciidoc/index-docinfo.xml delete mode 100644 docs/manual/src/docs/asciidoc/index.adoc rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/Authentication.gif (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/access-decision-voting.graffle (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/access-decision-voting.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/after-invocation.graffle (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/after-invocation.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/icons/number_1.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/icons/number_10.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/icons/number_2.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/icons/number_3.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/icons/number_4.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/icons/number_5.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/icons/number_6.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/icons/number_7.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/icons/number_8.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/icons/number_9.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/icons/numbers.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/note.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/palette.otg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/s2-banner-rhs.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/s2_box_logo.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/security-interception.graffle (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/security-interception.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/architecture/delegatingfilterproxy.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/architecture/delegatingfilterproxy.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/architecture/exceptiontranslationfilter.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/architecture/exceptiontranslationfilter.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/architecture/filterchain.gif (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/architecture/filterchain.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/architecture/filterchain.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/architecture/filterchainproxy.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/architecture/filterchainproxy.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/architecture/multi-securityfilterchain.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/architecture/multi-securityfilterchain.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/architecture/securityfilterchain.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/architecture/securityfilterchain.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/architecture/providermanager-parent.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/architecture/providermanager-parent.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/architecture/providermanager.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/architecture/providermanager.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/architecture/providermanagers-parent.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/architecture/providermanagers-parent.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/architecture/securitycontextholder.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/architecture/securitycontextholder.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/unpwd/basicauthenticationentrypoint.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/unpwd/basicauthenticationentrypoint.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/unpwd/basicauthenticationfilter.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/unpwd/basicauthenticationfilter.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/unpwd/daoauthenticationprovider.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/unpwd/daoauthenticationprovider.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authorization/filtersecurityinterceptor.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/authorization/filtersecurityinterceptor.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/oauth2/beareraccessdeniedhandler.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/oauth2/bearerauthenticationentrypoint.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/oauth2/bearerauthenticationentrypoint.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/oauth2/bearertokenauthenticationfilter.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/oauth2/bearertokenauthenticationfilter.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/oauth2/jwtauthenticationprovider.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/oauth2/jwtauthenticationprovider.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/oauth2/opaquetokenauthenticationprovider.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/oauth2/opaquetokenauthenticationprovider.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/saml2/opensamlauthenticationprovider.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/saml2/opensamlauthenticationprovider.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/saml2/saml2webssoauthenticationfilter.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/saml2/saml2webssoauthenticationfilter.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/saml2/saml2webssoauthenticationrequestfilter.odg (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/servlet/saml2/saml2webssoauthenticationrequestfilter.png (100%) rename docs/{manual/src/docs/asciidoc => modules/ROOT}/images/tip.png (100%) create mode 100644 docs/modules/ROOT/nav.adoc rename docs/{manual/src/docs/asciidoc/_includes/about => modules/ROOT/pages/overview}/community.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes/about => modules/ROOT/pages/overview/features}/authentication/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes/about => modules/ROOT/pages/overview/features}/authentication/password-storage.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes/about => modules/ROOT/pages/overview/features}/authentication/supported.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes/about => modules/ROOT/pages/overview/features}/exploits/csrf.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes/about => modules/ROOT/pages/overview/features}/exploits/headers.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes/about => modules/ROOT/pages/overview/features}/exploits/http.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes/about => modules/ROOT/pages/overview/features}/exploits/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes/about/features.adoc => modules/ROOT/pages/overview/features/index.adoc} (72%) rename docs/{manual/src/docs/asciidoc/_includes/about => modules/ROOT/pages/overview}/getting-spring-security.adoc (100%) create mode 100644 docs/modules/ROOT/pages/overview/index.adoc rename docs/{manual/src/docs/asciidoc/_includes/about => modules/ROOT/pages/overview}/modules.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes/about => modules/ROOT/pages/overview}/prerequisites.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes/about => modules/ROOT/pages/overview}/samples.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes/about => modules/ROOT/pages/overview}/whats-new.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/reactive/cors.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/reactive/exploits/csrf.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/reactive/exploits/headers.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/reactive/exploits/http.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/reactive/exploits/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/reactive/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/reactive/logout.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/reactive/method.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/reactive/oauth2/access-token.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/reactive/oauth2/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/reactive/oauth2/login.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/reactive/oauth2/resource-server.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/reactive/registered-oauth2-authorized-client.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/reactive/rsocket.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/reactive/test.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/reactive/webclient.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/reactive/webflux.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/reactive/x509.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/appendix/database-schema.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/appendix/faq.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/appendix/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/appendix/namespace.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/appendix/proxy-server.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/architecture/delegating-filter-proxy.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/architecture/exception-translation-filter.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/architecture/filter-chain-proxy.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/architecture/filters.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/architecture/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/architecture/security-filter-chain.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/architecture/security-filters.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/anonymous.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/architecture/abstract-authentication-processing-filter.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/architecture/authentication-entry-point.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/architecture/authentication-manager.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/architecture/authentication-provider.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/architecture/authentication.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/architecture/granted-authority.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/architecture/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/architecture/provider-manager.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/architecture/security-context-holder.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/architecture/security-context.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/cas.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/events.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/jaas.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/logout.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/openid.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/preauth.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/rememberme.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/runas.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/session-management.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/unpwd/basic.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/unpwd/dao-authentication-provider.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/unpwd/digest.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/unpwd/form.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/unpwd/in-memory.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/unpwd/index.adoc (69%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/unpwd/jdbc.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/unpwd/ldap.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/unpwd/password-encoder.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/unpwd/user-details-service.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/unpwd/user-details.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authentication/x509.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authorization/acls.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authorization/architecture.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authorization/authorize-requests.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authorization/expression-based.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authorization/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authorization/method-security.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/authorization/secure-objects.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/crypto/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/exploits/csrf.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/exploits/firewall.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/exploits/headers.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/exploits/http.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/exploits/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/hello/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/integrations/concurrency.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/integrations/cors.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/integrations/data.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/integrations/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/integrations/jackson.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/integrations/jsp-taglibs.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/integrations/localization.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/integrations/mvc.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/integrations/servlet-api.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/integrations/websocket.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/java-configuration/index.adoc (98%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/kotlin-configuration/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/namespace/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/oauth2/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/oauth2/oauth2-client.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/oauth2/oauth2-login.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/oauth2/oauth2-resourceserver.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/saml2/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/saml2/saml2-login.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/test/index.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/test/method.adoc (100%) rename docs/{manual/src/docs/asciidoc/_includes => modules/ROOT/pages}/servlet/test/mockmvc.adoc (100%) create mode 100644 local-antora-playbook.yml diff --git a/docs/antora.yml b/docs/antora.yml new file mode 100644 index 0000000000..4bb53235cd --- /dev/null +++ b/docs/antora.yml @@ -0,0 +1,24 @@ +name: security +title: Spring Security +version: 5.6 +start_page: ROOT:overview/index.adoc +asciidoc: + attributes: + page-pagination: true + idprefix: '' + idseparator: '-' + apacheds-core-version: 1.5.5 + figures: servlet/architecture + gh-old-samples-url: https://github.com/spring-projects/spring-security/tree/5.4.x/samples + gh-samples-url: https://github.com/spring-projects/spring-security-samples/tree/main + gh-url: https://github.com/spring-projects/spring-security/tree/main + icondir: icons + security-api-url: https://docs.spring.io/spring-security/site/docs/current/api/ + security-reference-url: https://docs.spring.io/spring-security/site/docs/reference/html5/ + spring-core-version: 5.3.8 + spring-framework-api-url: https://docs.spring.io/spring-framework/docs/current/javadoc-api/ + spring-framework-reference-url: https://docs.spring.io/spring-framework/docs/current/reference/html/ + spring-security-version: 5.6.0 + unboundid-ldapsdk-version: 4.1 +nav: +- modules/ROOT/nav.adoc diff --git a/docs/guides/spring-security-docs-guides.gradle b/docs/guides/spring-security-docs-guides.gradle deleted file mode 100644 index bce20f0081..0000000000 --- a/docs/guides/spring-security-docs-guides.gradle +++ /dev/null @@ -1,40 +0,0 @@ -apply plugin: 'org.asciidoctor.jvm.convert' -apply plugin: 'io.spring.convention.repository' - -asciidoctor { - baseDir = file('src/docs/asciidoc') - options eruby: 'erubis' - - def ghSamplesUrl = "https://github.com/spring-projects/spring-security-samples/tree/$samplesBranch" - attributes copycss : '', - icons : 'font', - 'source-highlighter': 'prettify', - sectanchors : '', - toc: '', - 'toc-placement' : 'preamble', - idprefix: '', - idseparator: '-', - doctype: 'book', - 'spring-security-version' : project.version, - 'include-maven-repository' : getMavenRepositoryInclude(), - revnumber : project.version, - 'gh-samples-url': ghSamplesUrl, - 'gh-old-samples-url': "https://github.com/spring-projects/spring-security/tree/5.4.x/samples" -} - -ext.spec = copySpec { - into ('guides') { - from(asciidoctor.outputDir) - exclude 'build', 'Guardfile' - } -} - -def getMavenRepositoryInclude() { - if(snapshotBuild) { - return "_includes/maven-repository-snapshot.asc" - } else if(releaseBuild) { - return "_includes/maven-repository-release.asc" - } else { - return "_includes/maven-repository-milestone.asc" - } -} diff --git a/docs/guides/src/docs/asciidoc/Guardfile b/docs/guides/src/docs/asciidoc/Guardfile deleted file mode 100644 index 1075c0114c..0000000000 --- a/docs/guides/src/docs/asciidoc/Guardfile +++ /dev/null @@ -1,15 +0,0 @@ -require 'asciidoctor' -require 'erb' - -guard 'shell' do - watch(/^.*\.asc$/) {|m| - Asciidoctor.render_file(m[0], :to_dir => "build/", :safe => Asciidoctor::SafeMode::UNSAFE, :attributes=> {'toc' => '', 'idprefix' => '', 'idseparator' => '-', 'copycss' => '', 'icons' => 'font', 'source-highlighter' => 'prettify', 'sectanchors' => '', 'toc-placement' => 'preamble', 'revnumber' => '3.2.0.CI-SNAPSHOT', - 'spring-security-version' => '3.2.0.CI-SNAPSHOT', - 'download-url' => 'https://github.com/spring-projects/spring-security/archive/main.zip', - 'include-maven-repository' => '_includes/maven-repository-snapshot.asc' }) - } -end - -guard 'livereload' do - watch(%r{build/.+\.(css|js|html)$}) -end diff --git a/docs/guides/src/docs/asciidoc/_form-includes/verify-app.asc b/docs/guides/src/docs/asciidoc/_form-includes/verify-app.asc deleted file mode 100644 index c8127c2c7a..0000000000 --- a/docs/guides/src/docs/asciidoc/_form-includes/verify-app.asc +++ /dev/null @@ -1,6 +0,0 @@ -Verify the application is working: - -* Navigate to http://localhost:8080/sample/ You should see a login form -* Enter the *username* _user_, the *password* _password_, and click the *Login* button. You should now see the main application. -* Try clicking on the Compose link and creating a message. The message details should be displayed. -* Now click on the Inbox link and see the message listed. You can click on the summary link to see the details displayed again. diff --git a/docs/guides/src/docs/asciidoc/_hello-includes/exploring-the-secured-application-boot.asc b/docs/guides/src/docs/asciidoc/_hello-includes/exploring-the-secured-application-boot.asc deleted file mode 100644 index b786261c20..0000000000 --- a/docs/guides/src/docs/asciidoc/_hello-includes/exploring-the-secured-application-boot.asc +++ /dev/null @@ -1,21 +0,0 @@ -=== Exploring the secured application - -Start the application as we did in <> - -Navigate to http://localhost:8080/ and click on the *_secured pages_* link and you will be prompted to login. - -==== Authenticating to the secured application - -Try entering an invalid username and password: - -* *Username* _invalid_ -* *Password* _invalid_ - -You should see an error message stating that authentication failed. Now try entering a valid username and password: - -* *Username* _user_ -* *Password* _password_ - -You should now see the page that we wanted to secure. - -NOTE: The reason we can successfully authenticate with *Username* _user_ and *Password* _password_ is because that is what we configured in our <>. diff --git a/docs/guides/src/docs/asciidoc/_hello-includes/exploring-the-secured-application-javaconfig.asc b/docs/guides/src/docs/asciidoc/_hello-includes/exploring-the-secured-application-javaconfig.asc deleted file mode 100644 index fbdaf4b272..0000000000 --- a/docs/guides/src/docs/asciidoc/_hello-includes/exploring-the-secured-application-javaconfig.asc +++ /dev/null @@ -1,19 +0,0 @@ -=== Exploring the secured application - -Start the server as we did in <> Now when you visit http://localhost:8080/sample/ you will be prompted with a login page that is automatically generated by Spring Security. - -==== Authenticating to the secured application - -Try entering an invalid username and password: - -* *Username* _invalid_ -* *Password* _invalid_ - -You should see an error message stating that authentication failed. Now try entering a valid username and password: - -* *Username* _user_ -* *Password* _password_ - -You should now see the page that we wanted to secure. - -NOTE: The reason we can successfully authenticate with *Username* _user_ and *Password* _password_ is because that is what we configured in our <>. diff --git a/docs/guides/src/docs/asciidoc/_hello-includes/exploring-the-secured-application-xml.asc b/docs/guides/src/docs/asciidoc/_hello-includes/exploring-the-secured-application-xml.asc deleted file mode 100644 index b8c6d971fe..0000000000 --- a/docs/guides/src/docs/asciidoc/_hello-includes/exploring-the-secured-application-xml.asc +++ /dev/null @@ -1,19 +0,0 @@ -=== Exploring the secured application - -Start the server as we did in <> Now when you visit http://localhost:8080/sample/ you will be prompted with a login page that is automatically generated by Spring Security. - -==== Authenticating to the secured application - -Try entering an invalid username and password: - -* *Username* _invalid_ -* *Password* _invalid_ - -You should see an error message stating that authentication failed. Now try entering a valid username and password: - -* *Username* _user_ -* *Password* _password_ - -You should now see the page that we wanted to secure. - -NOTE: The reason we can successfully authenticate with *Username* _user_ and *Password* _password_ is because that is what we configured in our <>. diff --git a/docs/guides/src/docs/asciidoc/_hello-includes/secure-the-application-boot.asc b/docs/guides/src/docs/asciidoc/_hello-includes/secure-the-application-boot.asc deleted file mode 100644 index 5d2c63dcbe..0000000000 --- a/docs/guides/src/docs/asciidoc/_hello-includes/secure-the-application-boot.asc +++ /dev/null @@ -1,118 +0,0 @@ -== Securing the application - -Before securing the application, it is important to ensure that the existing application works as we did in <>. Now that the application runs without security, we are ready to add security to our application. This section demonstrates the minimal steps to add Spring Security to our application. - -=== Updating your dependencies - -include::../{include-maven-repository}[] - -In order to use Spring Security you must add the necessary dependencies. For the sample we will add the following Spring Security dependencies: - -.pom.xml -[source,xml] -[subs="verbatim,attributes"] ----- - - - - org.springframework.security - spring-security-web - {spring-security-version} - - - org.springframework.security - spring-security-config - {spring-security-version} - - - org.thymeleaf.extras - thymeleaf-extras-springsecurity5 <1> - 2.1.2.RELEASE - - ----- - -<1> We are using https://www.thymeleaf.org/[Thymeleaf] for our view template engine -and need to add an additional dependency for the https://github.com/thymeleaf/thymeleaf-extras-springsecurity[Thymeleaf - Spring Security integration module]. - -After you have completed this, you need to ensure that STS knows about the updated dependencies by: - -* Right click on the _spring-security-samples-{starter-config-type}-{starter-appname}_ application -* Select *Maven->Update project...* -* Ensure the project is selected, and click *OK* - -=== Creating your Spring Security configuration - -The next step is to create a Spring Security configuration. - -* Right click the _spring-security-samples-{starter-config-type}-{starter-appname}_ project in the Package Explorer view -* Select *New->Class* -* Enter _org.springframework.security.samples.config_ for the *Package* -* Enter _SecurityConfig_ for the *Name* -* Click *Finish* -* Replace the file with the following contents: - -[[security-config-java]] -.src/main/java/org/springframework/security/samples/config/SecurityConfig.java -[source,java] ----- -package org.springframework.security.samples.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; - -@EnableWebSecurity -public class SecurityConfig extends WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .antMatchers("/css/**", "/index").permitAll() <1> - .antMatchers("/user/**").hasRole("USER") <2> - .and() - .formLogin() - .loginPage("/login").failureUrl("/login-error"); <3> - } - - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser("user").password("password").roles("USER"); - } -} ----- - -<1> requests matched against _/css/**_ and _/index_ are fully accessible -<2> requests matched against _/user/**_ require a user to be authenticated and must be associated to the _USER_ role -<3> form-based authentication is enabled with a custom login page and failure url - - -NOTE: The name of the configureGlobal method is not important. However, it is important to only configure AuthenticationManagerBuilder in a class annotated with either `@EnableWebSecurity`, `@EnableGlobalMethodSecurity`, or `@EnableGlobalAuthentication`. Doing otherwise has unpredictable results. - -[[servlet-api-integration]] -The <> will: - -* Require authentication to requests matched against _/user/**_ -* Specifies the URL to send users to for form-based login -* Allow the user with the *Username* _user_ and the *Password* _password_ to authenticate with form based authentication -* Allow the user to logout -* https://en.wikipedia.org/wiki/Cross-site_request_forgery[CSRF attack] prevention -* https://en.wikipedia.org/wiki/Session_fixation[Session Fixation] protection -* Security Header integration -** https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security[HTTP Strict Transport Security] for secure requests -** https://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) -** https://msdn.microsoft.com/en-us/library/dd565647(v=vs.85).aspx[X-XSS-Protection] integration -** X-Frame-Options integration to help prevent https://en.wikipedia.org/wiki/Clickjacking[Clickjacking] -* Integrate with the following Servlet API methods -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[HttpServletRequest#getRemoteUser()] -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[HttpServletRequest.html#getUserPrincipal()] -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[HttpServletRequest.html#isUserInRole(java.lang.String)] -** https://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)] -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout()[HttpServletRequest.html#logout()] - diff --git a/docs/guides/src/docs/asciidoc/_hello-includes/secure-the-application-javaconfig.asc b/docs/guides/src/docs/asciidoc/_hello-includes/secure-the-application-javaconfig.asc deleted file mode 100644 index 7dc183a393..0000000000 --- a/docs/guides/src/docs/asciidoc/_hello-includes/secure-the-application-javaconfig.asc +++ /dev/null @@ -1,92 +0,0 @@ -== Securing the application - -Before securing your application, it is important to ensure that the existing application works as we did in <>. Now that the application runs without security, we are ready to add security to our application. This section demonstrates the minimal steps to add Spring Security to our application. - -=== Updating your dependencies - -include::../{include-maven-repository}[] - -In order to use Spring Security you must add the necessary dependencies. For the sample we will add the following Spring Security dependencies: - -.pom.xml -[source,xml] -[subs="verbatim,attributes"] ----- - - - - org.springframework.security - spring-security-web - {spring-security-version} - - - org.springframework.security - spring-security-config - {spring-security-version} - - ----- - -After you have completed this, you need to ensure that STS knows about the updated dependencies by: - -* Right click on the _spring-security-samples-{starter-config-type}-{starter-appname}_ application -* Select *Maven->Update project...* -* Ensure the project is selected, and click *OK* - -=== Creating your Spring Security configuration - -The next step is to create a Spring Security configuration. - -* Right click the _spring-security-samples-{starter-config-type}-{starter-appname}_ project in the Package Explorer view -* Select *New->Class* -* Enter _org.springframework.security.samples.config_ for the *Package* -* Enter _SecurityConfig_ for the *Name* -* Click *Finish* -* Replace the file with the following contents: - -[[security-config-java]] -.src/main/java/org/springframework/security/samples/config/SecurityConfig.java -[source,java] ----- -package org.springframework.security.samples.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.configuration.*; - -@EnableWebSecurity -public class SecurityConfig { - - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser("user").password("password").roles("USER"); - } -} ----- - -NOTE: The name of the configureGlobal method is not important. However, it is important to only configure AuthenticationManagerBuilder in a class annotated with either `@EnableWebSecurity`, `@EnableGlobalMethodSecurity`, or `@EnableGlobalAuthentication`. Doing otherwise has unpredictable results. - -[[servlet-api-integration]] -The <> will: - -* 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 -* https://en.wikipedia.org/wiki/Cross-site_request_forgery[CSRF attack] prevention -* https://en.wikipedia.org/wiki/Session_fixation[Session Fixation] protection -* Security Header integration -** https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security[HTTP Strict Transport Security] for secure requests -** https://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) -** https://msdn.microsoft.com/en-us/library/dd565647(v=vs.85).aspx[X-XSS-Protection] integration -** X-Frame-Options integration to help prevent https://en.wikipedia.org/wiki/Clickjacking[Clickjacking] -* Integrate with the following Servlet API methods -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[HttpServletRequest#getRemoteUser()] -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[HttpServletRequest.html#getUserPrincipal()] -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[HttpServletRequest.html#isUserInRole(java.lang.String)] -** https://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)] -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout()[HttpServletRequest.html#logout()] - diff --git a/docs/guides/src/docs/asciidoc/_hello-includes/secure-the-application-xml.asc b/docs/guides/src/docs/asciidoc/_hello-includes/secure-the-application-xml.asc deleted file mode 100644 index 0d47daf040..0000000000 --- a/docs/guides/src/docs/asciidoc/_hello-includes/secure-the-application-xml.asc +++ /dev/null @@ -1,89 +0,0 @@ -== Securing the application - -Before securing your application, it is important to ensure that the existing application works as we did in <>. Now that the application runs without security, we are ready to add security to our application. This section demonstrates the minimal steps to add Spring Security to our application. - -=== Updating your dependencies - -include::../{include-maven-repository}[] - -In order to use Spring Security you must add the necessary dependencies. For the sample we will add the following Spring Security dependencies: - -.pom.xml -[source,xml] -[subs="verbatim,attributes"] ----- - - - - org.springframework.security - spring-security-web - {spring-security-version} - - - org.springframework.security - spring-security-config - {spring-security-version} - - ----- - -After you have completed this, you need to ensure that STS knows about the updated dependencies by: - -* Right click on the _spring-security-samples-{starter-config-type}-{starter-appname}_ application -* Select *Maven->Update project...* -* Ensure the project is selected, and click *OK* - -=== Creating your Spring Security configuration - -The next step is to create a Spring Security configuration. - -* In the Package Explorer view, right click on the folder _src/main/webapp_ -* Select *New->Folder* -* Enter _WEB-INF/spring_ for the *Folder name* -* Then right click on the new folder _WEB-INF/spring_ -* Select *New->File* -* Enter _security.xml_ for the *File name* -* Click *Finish* -* Replace the contents of the file with the following: - -[[security-config-xml]] -.src/main/webapp/WEB-INF/spring/security.xml -[source,xml] ----- - - - - - - - - - ----- - -[[servlet-api-integration]] -The <> will: - -* 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 -* https://en.wikipedia.org/wiki/Cross-site_request_forgery[CSRF attack] prevention -* https://en.wikipedia.org/wiki/Session_fixation[Session Fixation] protection -* Security Header integration -** https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security[HTTP Strict Transport Security] for secure requests -** https://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) -** https://msdn.microsoft.com/en-us/library/dd565647(v=vs.85).aspx[X-XSS-Protection] integration -** X-Frame-Options integration to help prevent https://en.wikipedia.org/wiki/Clickjacking[Clickjacking] -* Integrate with the following Servlet API methods -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[HttpServletRequest#getRemoteUser()] -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[HttpServletRequest.html#getUserPrincipal()] -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[HttpServletRequest.html#isUserInRole(java.lang.String)] -** https://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)] -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout()[HttpServletRequest.html#logout()] - diff --git a/docs/guides/src/docs/asciidoc/_includes/maven-repository-milestone.asc b/docs/guides/src/docs/asciidoc/_includes/maven-repository-milestone.asc deleted file mode 100644 index 9414018722..0000000000 --- a/docs/guides/src/docs/asciidoc/_includes/maven-repository-milestone.asc +++ /dev/null @@ -1,14 +0,0 @@ -In order to resolve Spring Security milestones and release candidates add the Spring Milestone repository. For our example, the repository has already been added for you. In the event you were working on another application, you would need to ensure you add the following to your pom: - -.pom.xml -[source,xml] ----- - - - - spring-milestone - Spring Milestone Repository - https://repo.spring.io/milestone - - ----- \ No newline at end of file diff --git a/docs/guides/src/docs/asciidoc/_includes/maven-repository-release.asc b/docs/guides/src/docs/asciidoc/_includes/maven-repository-release.asc deleted file mode 100644 index a61938c847..0000000000 --- a/docs/guides/src/docs/asciidoc/_includes/maven-repository-release.asc +++ /dev/null @@ -1 +0,0 @@ -Spring Security GA releases are included within Maven Central, so no additional Maven repositories are necessary. \ No newline at end of file diff --git a/docs/guides/src/docs/asciidoc/_includes/maven-repository-snapshot.asc b/docs/guides/src/docs/asciidoc/_includes/maven-repository-snapshot.asc deleted file mode 100644 index 8a1c04f964..0000000000 --- a/docs/guides/src/docs/asciidoc/_includes/maven-repository-snapshot.asc +++ /dev/null @@ -1,14 +0,0 @@ -In order to resolve Spring Security SNAPSHOT dependencies add the Spring Snapshot repository. For our example, the repository has already been added for you. In the event you were working on another application, you would need to ensure you add the following to your pom: - -.pom.xml -[source,xml] ----- - - - - spring-snapshot - Spring Snapshot Repository - https://repo.spring.io/snapshot - - ----- \ No newline at end of file diff --git a/docs/guides/src/docs/asciidoc/_includes/setting-up-the-sample-boot.asc b/docs/guides/src/docs/asciidoc/_includes/setting-up-the-sample-boot.asc deleted file mode 100644 index d73db251f2..0000000000 --- a/docs/guides/src/docs/asciidoc/_includes/setting-up-the-sample-boot.asc +++ /dev/null @@ -1,46 +0,0 @@ -//// - -Variables: - -starter-appname: the name of the module users should start with to complete the exercise -starter-config-type: the type of configuration the starter sample uses: javaconfig, xml, boot -completed-appname: the name of the module that contains the completed application -completed-config-type: the type of configuration the completed sample uses: javaconfig, xml, boot -download-url: the URL to download the Spring Security distribution - -//// - -== Setting up the sample - -This section outlines how to setup a workspace within https://spring.io/tools[Eclipse IDE with Spring Tools] so that you can follow along with this guide. The next section outlines generic steps for how to apply Spring Security to your existing application. While you could simply apply the steps to your existing application, we encourage you to follow along with this guide in order to reduce the complexity. - -=== Obtaining the sample project - -Clone the {gh-samples-url}[Spring Security Samples project] to a known location and remember it as _SPRING_SECURITY_HOME_. -[NOTE] -==== -These samples are being migrated to a separate project, however, you can still find -the not migrated samples in an older branch of the {gh-old-samples-url}[Spring Security repository]. -==== - -=== Import the {starter-appname} sample application - -In order to follow along, we encourage you to import the {starter-appname} sample application into your IDE. You may use any IDE you prefer, but the instructions in this guide will assume you are using Spring Tool Suite (STS). - -TIP: The completed sample application can be found at _SPRING_SECURITY_HOME_/samples/{completed-config-type}/{completed-appname} - -* If you do not have STS installed, download STS from https://spring.io/tools -* Start STS and import the sample application into STS using the following steps: -** *File->Import* -** *Existing Maven Projects* -** Click *Next >* -** Click *Browse...* -** Navigate to the samples (i.e. _SPRING_SECURITY_HOME_/samples/{starter-config-type}/{starter-appname}) and click *OK* -** Click *Finish* - -=== Running the {starter-appname} application - -In the following exercise we will be modifying the _spring-security-samples-{starter-config-type}-{starter-appname}_ application. Before we make any changes, it is best to verify that the sample works properly. Perform the following steps to ensure that _spring-security-samples-{starter-config-type}-{starter-appname}_ works. - -* Right click on the _spring-security-samples-{starter-config-type}-{starter-appname}_ application -* Select *Run As->Spring Boot App* diff --git a/docs/guides/src/docs/asciidoc/_includes/setting-up-the-sample.asc b/docs/guides/src/docs/asciidoc/_includes/setting-up-the-sample.asc deleted file mode 100644 index c0586953f2..0000000000 --- a/docs/guides/src/docs/asciidoc/_includes/setting-up-the-sample.asc +++ /dev/null @@ -1,48 +0,0 @@ -//// - -Variables: - -starter-appname: the name of the module users should start with to complete the exercise -starter-config-type: the type of configuration the starter sample uses: javaconfig, xml, boot -completed-appname: the name of the module that contains the completed application -completed-config-type: the type of configuration the completed sample uses: javaconfig, xml, boot -download-url: the URL to download the Spring Security distribution - -//// - -== Setting up the sample - -This section outlines how to setup a workspace within https://spring.io/tools[Eclipse IDE with Spring Tools] so that you can follow along with this guide. The next section outlines generic steps for how to apply Spring Security to your existing application. While you could simply apply the steps to your existing application, we encourage you to follow along with this guide in order to reduce the complexity. - -=== Obtaining the sample project - -Clone the {gh-samples-url}[Spring Security Samples project] to a known location and remember it as _SPRING_SECURITY_HOME_. -[NOTE] -==== -These samples are being migrated to a separate project, however, you can still find -the not migrated samples in an older branch of the {gh-old-samples-url}[Spring Security repository]. -==== - -=== Import the {starter-appname} sample application - -In order to follow along, we encourage you to import the {starter-appname} sample application into your IDE. You may use any IDE you prefer, but the instructions in this guide will assume you are using Spring Tool Suite (STS). - -TIP: The completed sample application can be found at _SPRING_SECURITY_HOME_/samples/{completed-config-type}/{completed-appname} - -* If you do not have STS installed, download STS from https://spring.io/tools -* Start STS and import the sample application into STS using the following steps: -** *File->Import* -** *Existing Maven Projects* -** Click *Next >* -** Click *Browse...* -** Navigate to the samples (i.e. _SPRING_SECURITY_HOME_/samples/{starter-config-type}/{starter-appname}) and click *OK* -** Click *Finish* - -=== Running the {starter-appname} application - -In the following exercise we will be modifying the _spring-security-samples-{starter-config-type}-{starter-appname}_ application. Before we make any changes, it is best to verify that the sample works properly. Perform the following steps to ensure that _spring-security-samples-{starter-config-type}-{starter-appname}_ works. - -* Right click on the _spring-security-samples-{starter-config-type}-{starter-appname}_ application -* Select *Run As->Run on Server* -* Select the latest tc Server -* Click *Finish* diff --git a/docs/guides/src/docs/asciidoc/form-javaconfig.asc b/docs/guides/src/docs/asciidoc/form-javaconfig.asc deleted file mode 100644 index c8281e9b86..0000000000 --- a/docs/guides/src/docs/asciidoc/form-javaconfig.asc +++ /dev/null @@ -1,249 +0,0 @@ -= Creating a Custom Login Form -:author: Rob Winch -:starter-appname: hellomvc -:starter-config-type: javaconfig -:completed-appname: form -:completed-config-type: javaconfig -:include-dir: _includes - -This guide builds off of link:hellomvc-javaconfig.html[Hello Spring MVC Security Java Config] to explain how to configure and use a custom login form with Spring Security Java Configuration. - -include::{include-dir}/setting-up-the-sample.asc[] - -Verify the application is working: - -* A page displaying a user's inbox can be seen at http://localhost:8080/sample/ after authenticating with the *username* _user_ and the *password* _password_. -* Try clicking on the Compose link and creating a message. The message details should be displayed. -* Now click on the Inbox link and see the message listed. You can click on the summary link to see the details displayed again. - - -= Overriding the default configure(HttpSecurity) method - -As we saw in link:hellomvc-javaconfig.html[Hello Spring MVC Security Java Config], Spring Security's `WebSecurityConfigurerAdapter` provides some convenient defaults to get our application -up and running quickly. However, our login form does not look like the rest of our application. Let's see how we can update our configuration to use a custom form. - -== Default configure(HttpSecurity) - -The default configuration for the configure(HttpSecurity) method can be seen below: - -[source,java] ----- -protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest().authenticated() <1> - .and() - .formLogin() <2> - .and() - .httpBasic(); <3> -} ----- - -The configuration ensures that: - -<1> every request requires the user to be authenticated -<2> form based authentication is supported -<3> HTTP Basic Authentication is supported - -== Configuring a custom login page - -We will want to ensure we compensate for overriding these defaults in our updates. Open up the `SecurityConfig` and insert the configure method as shown below: - -.src/main/java/org/springframework/security/samples/config/SecurityConfig.java -[source,java] ----- -// ... - -import org.springframework.security.config.annotation.web.builders.HttpSecurity; - -@EnableWebSecurity -public class SecurityConfig extends WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest().authenticated() - .and() - .formLogin() - .loginPage("/login"); - } - - // ... -} ----- - -The line `loginPage("/login")` instructs Spring Security - -* when authentication is required, redirect the browser to */login* -* we are in charge of rendering the login page when */login* is requested -* when authentication attempt fails, redirect the browser to */login?error* (since we have not specified otherwise) -* we are in charge of rendering a failure page when */login?error* is requested -* when we successfully logout, redirect the browser to */login?logout* (since we have not specified otherwise) -* we are in charge of rendering a logout confirmation page when */login?logout* is requested - -Go ahead and start up the server and try visiting http://localhost:8080/sample/ to see the updates to our configuration. In many browsers you will see an error similar to *This webpage has a redirect loop*. What is happening? - -== Granting access to unauthenticated users - -The issue is that Spring Security is protecting access to our custom login page. In particular the following is happening: - -* We make a request to our web application -* Spring Security sees that we are not authenticated -* We are redirected to */login* -* The browser requests */login* -* Spring Security sees that we are not authenticated -* We are redirected to */login* ... - -To fix this we need to instruct Spring Security to allow anyone to access the */login* URL. We can easily do this with the following updates: - -.src/main/java/org/springframework/security/samples/config/SecurityConfig.java -[source,java] ----- -// ... - -@EnableWebSecurity -public class SecurityConfig extends WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest().authenticated() - .and() - .formLogin() - .loginPage("/login") - .permitAll(); - } - - // ... -} ----- - -The method `formLogin().permitAll()` statement instructs Spring Security to allow any access to any URL (i.e. */login* and */login?error*) associated to `formLogin()`. - -NOTE: Granting access to the `formLogin()` URLs is not done by default since Spring Security needs to make certain assumptions about what is allowed and what is not. To be secure, it is best to ensure granting access to resources is explicit. - -Start up the server and try visiting http://localhost:8080/sample/ to see the updates to our configuration. You should now get a 500 error stating **Error resolving template "login"**. - -= Creating a login page - -Within Spring Web MVC there are two steps to creating our login page: - -* Creating a controller -* Creating a view - -== Configuring a login view controller - -Within Spring Web MVC, the first step is to ensure that we have a controller that can point to our view. Since our project adds the *javaconfig/messages* project as a dependency and it contains a view controller for */login* we do not need to create a controller within our application. For reference, you can see the configuration below: - -[source,java] ----- -// ... - -@EnableWebMvc -@ComponentScan("org.springframework.security.samples.mvc") -public class WebMvcConfiguration implements WebMvcConfigurer { - - // ... - - @Override - public void addViewControllers(ViewControllerRegistry registry) { - registry.addViewController("/login").setViewName("login"); - registry.setOrder(Ordered.HIGHEST_PRECEDENCE); - } -} ----- - -== Creating a login view - -Our existing configuration means that all we need to do is create a *login.html* file with the following contents: - -.src/main/resources/views/login.html -[source,xml] ----- - - - Please Login - - -
-
<1> -
- Please Login -
<2> - Invalid username and password. -
-
<3> - You have been logged out. -
- - <4> - - <5> -
- -
-
-
-
- - ----- - -<1> The URL we submit our username and password to is the same URL as our login form (i.e. */login*), but a *POST* instead of a *GET*. -<2> When authentication fails, the browser is redirected to */login?error* so we can display an error message by detecting if the parameter *error* is non-null. -<3> When we are successfully logged out, the browser is redirected to */login?logout* so we can display an logout success message by detecting if the parameter *logout* is non-null. -<4> The username should be present on the HTTP parameter username -<5> The password should be present on the HTTP parameter password - -IMPORTANT: Do not display details about why authentication failed. For example, we do not want to display that the user does not exist as this will tell an attacker that they should try a different username. - -TIP: We use Thymeleaf to automatically add the CSRF token to our form. If we were not using Thymeleaf or Spring MVCs taglib we could also manually add the CSRF token using ``. - -Start up the server and try visiting http://localhost:8080/sample/ to see the updates to our configuration. We now see our login page, but it does not look very pretty. The issue is that we have not granted access to the css files. - -== Grant access to remaining resources - -We need to update our configuration to allow anyone to access our resources and our logout pages. Update the configuration as shown below: - -.src/main/java/org/springframework/security/samples/config/SecurityConfig.java -[source,java] ----- -// ... - -@EnableWebSecurity -public class SecurityConfig extends WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .antMatchers("/resources/**").permitAll() <1> - .anyRequest().authenticated() - .and() - .formLogin() - .loginPage("/login") - .permitAll() - .and() - .logout() <2> - .permitAll(); - } - - // ... -} ----- - -<1> This allows anyone to access a URL that begins with */resources/*. Since this is where our css, javascript, and images are stored all our static resources are viewable by anyone. -<2> As you might expect, `logout().permitAll()` allows any user to request logout and view logout success URL. - - -Restart the server and try visiting http://localhost:8080/sample/ to see the updates to our configuration. We now see a custom login page that looks like the rest of our application. - -* Try entering an invalid username and password. You will see our error message is displayed. -* Try entering a valid username (user) and password (password). You will be authenticated successfully. -* Try clicking the Log Out button. You will see our logout success message - -== Conclusion - -You should now know how to add a custom login form using Spring Security's Java Configuration. To learn more refer to the link:index.html[Spring Security Guides index page]. diff --git a/docs/guides/src/docs/asciidoc/hellomvc-javaconfig.asc b/docs/guides/src/docs/asciidoc/hellomvc-javaconfig.asc deleted file mode 100644 index c913e2c4ad..0000000000 --- a/docs/guides/src/docs/asciidoc/hellomvc-javaconfig.asc +++ /dev/null @@ -1,127 +0,0 @@ -= Hello Spring MVC Security Java Config -:author: Rob Winch -:starter-appname: insecuremvc -:starter-config-type: xml -:completed-appname: hellomvc -:completed-config-type: javaconfig -:include-dir: _includes -:hello-include-dir: _hello-includes - -This guide provides instructions on how to add Spring Security to an existing Spring MVC application without the use of XML. - -include::{include-dir}/setting-up-the-sample.asc[] - -Verify the application is working: - -* A page displaying a user's inbox can be seen at http://localhost:8080/sample/ -* Try clicking on the Compose link and creating a message. The message details should be displayed. -* Now click on the Inbox link and see the message listed. You can click on the summary link to see the details displayed again. - - -include::{hello-include-dir}/secure-the-application-javaconfig.asc[] - -=== Registering Spring Security with the war - -We have created the Spring Security configuration, but we still need to register it with the war. This can be done using the following steps: - -* Right click the _spring-security-samples-{starter-config-type}-{starter-appname}_ project the Package Explorer view -* Select *New->Class* -* Enter _org.springframework.security.samples.config_ for the *Package* -* Enter MessageSecurityWebApplicationInitializer for the *Name* -* Click *Finish* -* Replace the file with the following contents: - -.src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java -[source,java] ----- -package org.springframework.security.samples.config; - -import org.springframework.security.web.context.*; - -public class MessageSecurityWebApplicationInitializer - extends AbstractSecurityWebApplicationInitializer { -} ----- - -The `MessageSecurityWebApplicationInitializer` will automatically register the springSecurityFilterChain Filter for every URL in your application. If Filters are added within other `WebApplicationInitializer` instances we can use `@Order` to control the ordering of the Filter instances. - -=== Verify SecurityConfig is loaded - -Just because <> exists, does not mean that our Spring application knows about it. In this instance, our Spring root application context is initialized using MessageWebApplicationInitializer which is included with our spring-security-samples-javaconfig-messages project. You can find a snippet of it below: - -[[message-web-application-inititializer-java]] -.MessageWebApplicationInitializer.java -[source,java] ----- -public class MessageWebApplicationInitializer extends - AbstractAnnotationConfigDispatcherServletInitializer { - - @Override - protected Class[] getRootConfigClasses() { - return new Class[] { RootConfiguration.class }; - } - - // ... other overrides ... -} ----- - -You will notice it is loading the `RootConfiguration` class which is also included in our spring-security-samples-javaconfig-messages project. - -[[root-configuration-java]] -.RootConfiguration.java -[source,java] ----- -@Configuration -@ComponentScan -public class RootConfiguration { -} ----- - -The `@ComponentScan` is loading all configuration within the same package (and child packages) as `RootConfiguration`. Since <> is in this package, it will be loaded with our existing setup and there is nothing more to do. - -NOTE: Had <> not been loaded, we could have used an `@Import(SecurityConfig.class)` above the class definition of <> or added <> as one of the results for `getRootConfigClasses()`. - -include::{hello-include-dir}/exploring-the-secured-application-javaconfig.asc[] - -==== Displaying the user name - -Now that we have authenticated, let's see how our application is displaying the username if the user is authenticated. - -.messages/src/main/resources/views/layout.html -[source,html] ----- -
-

- sample_user -

-
----- - -In our samples we use https://www.thymeleaf.org/[Thymeleaf], but any view technology will work. Any technology can inspect the `HttpServletRequest#getRemoteUser()` to view the current user since Spring Security integrates with the <>. - -WARNING: The Thymeleaf ensures the username is escaped to avoid https://en.wikipedia.org/wiki/Cross-site_scripting[XSS vulnerabilities] Regardless of how an application renders user inputed values, it should ensure that the values are properly escaped. - -==== Logging out - -We can view the user name, but how are we able to log out? Below you can see how we are able to log out. - -.messages/src/main/resources/views/layout.html -[source,html] ----- -
- -
----- - -In order to help protect against https://en.wikipedia.org/wiki/Cross-site_request_forgery[CSRF attacks], by default, Spring Security Java Configuration log out requires: - -* the HTTP method must be a POST -* the CSRF token must be added to the request. Since we have used `@EnableWebSecurity` and are using Thymeleaf, the CSRF token is automatically added as a hidden input for you (view the source to see it). - -NOTE: If you were not using Spring MVC taglibs or Thymeleaf, you can access the CsrfToken on the ServletRequest using the attribute _csrf. You can find an example of including the CSRF token in a JSP within the link:helloworld-javaconfig.html[Hello Spring Security Java Config]. - -Restart the application server and click the Log out button and see that the application logs you out successfully. - -== Conclusion - -You should now know how to secure your application using Spring Security without using any XML. Next, we will see how to link:form-javaconfig.html[customize our login form]. diff --git a/docs/guides/src/docs/asciidoc/helloworld-boot.asc b/docs/guides/src/docs/asciidoc/helloworld-boot.asc deleted file mode 100644 index cc75981d58..0000000000 --- a/docs/guides/src/docs/asciidoc/helloworld-boot.asc +++ /dev/null @@ -1,97 +0,0 @@ -= Hello Spring Security with Boot -:author: Joe Grandja -:starter-appname: insecure -:starter-config-type: boot -:completed-appname: helloworld -:completed-config-type: boot -:include-dir: _includes -:hello-include-dir: _hello-includes - -This guide provides instructions on how to add Spring Security to an existing Spring Boot application. - -include::{include-dir}/setting-up-the-sample-boot.asc[] - -Verify the application is working by navigating to http://localhost:8080/ - -Click on the *_secured pages_* link and verify the page states *TODO Secure this* - -Once you have verified the application runs, stop the application server using the following steps: - -* In the _Boot Dashboard_ view select the running application -* Click the stop button (a red square) to stop the application - -include::{hello-include-dir}/secure-the-application-boot.asc[] - -include::{hello-include-dir}/exploring-the-secured-application-boot.asc[] - -==== Displaying the user name - -Now that we have authenticated, let's update the application to display the username. Update the complete content of */index.html* with the following: - -.src/main/resources/templates/index.html -[source,html] ----- - - - - Hello Spring Security - - - - -
<1> - Logged in user: | <2> - Roles: <3> -
-
<4> - -
-
-
-

Hello Spring Security

-

This is an unsecured page, but you can access the secured pages after authenticating.

- - - ----- - -NOTE: We are using https://www.thymeleaf.org/[Thymeleaf] for our view template engine and -https://github.com/thymeleaf/thymeleaf-extras-springsecurity[Thymeleaf - Spring Security integration modules] -in order to utilize the _sec:authentication_ and _sec:authorize_ attributes. - -<1> Displays the Thymeleaf fragment (DOM Node) if the current user has been authenticated. -<2> Displays the name of the currently authenticated principal. -<3> Displays the authorities of the currently authenticated principal. -<4> The logout form. - -TIP: Thymeleaf will automatically add the CSRF token to our logout form. If we were not using Thymeleaf or Spring MVCs taglib we could also manually add the CSRF token using ``. - -==== Update the _secured_ page - -The last step is to update the _secured_ page to also display the currently authenticated principal. Update the complete content of */user/index.html* with the following: - -.src/main/resources/templates/user/index.html -[source,html] ----- - - - - Hello Spring Security - - - - -
-

This is a secured page!

-

Back to home page

- - ----- - -Start up the server and try visiting http://localhost:8080/ to see the updates to our application. - -== Conclusion - -You should now know how to secure your application using Spring Security with an existing Spring Boot application . To learn more refer to the link:index.html[Spring Security Guides index page]. diff --git a/docs/guides/src/docs/asciidoc/helloworld-javaconfig.asc b/docs/guides/src/docs/asciidoc/helloworld-javaconfig.asc deleted file mode 100644 index d418e918a2..0000000000 --- a/docs/guides/src/docs/asciidoc/helloworld-javaconfig.asc +++ /dev/null @@ -1,113 +0,0 @@ -= Hello Spring Security Java Config -:author: Rob Winch -:starter-appname: insecure -:starter-config-type: xml -:completed-appname: helloworld -:completed-config-type: javaconfig -:include-dir: _includes -:hello-include-dir: _hello-includes - -This guide provides instructions on how to add Spring Security to an existing application without the use of XML. - -include::{include-dir}/setting-up-the-sample.asc[] - -Verify the application is working by ensuring a page stating *TODO Secure this* is displayed at http://localhost:8080/sample/ - -Once you have verified the application runs, stop the application server using the following steps: - -* In the Servers view select the latest tc Server -* Click the stop button (a red square) to stop the application server - -include::{hello-include-dir}/secure-the-application-javaconfig.asc[] - -=== Registering Spring Security with the war - -We have created the Spring Security configuration, but we still need to register it with the war. This can be done using the following steps: - -* Navigate to the *Package Explorer* view -* Right click the *org.springframework.security.samples.config* package within the *spring-security-samples-{starter-config-type}-{starter-appname}* project -* Select *New->Class* -* Enter _SecurityWebApplicationInitializer_ for the *Name* -* Click *Finish* -* Replace the file with the following contents: - -.src/main/java/org/springframework/security/samples/config/SecurityWebApplicationInitializer.java -[source,java] ----- -package org.springframework.security.samples.config; - -import org.springframework.security.web.context.*; - -public class SecurityWebApplicationInitializer - extends AbstractSecurityWebApplicationInitializer { - - public SecurityWebApplicationInitializer() { - super(SecurityConfig.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 <>. - -NOTE: Since we were not already using Spring, this is a simple way to add our <>. If we were already using Spring, then we should add our <> with the reset of our Spring configuration (i.e. a subclass of AbstractContextLoaderInitializer or AbstractDispatcherServletInitializer) and use the default constructor instead. - -include::{hello-include-dir}/exploring-the-secured-application-javaconfig.asc[] - -==== Displaying the user name - -Now that we have authenticated, let's update the application to display the username. Update the body of index.jsp to be the following: - -.src/main/webapp/index.jsp -[source,html] ----- - -
-

This is secured!

-

- Hello -

-
- ----- - -WARNING: The `` tag ensures the username is escaped to avoid https://en.wikipedia.org/wiki/Cross-site_scripting[XSS vulnerabilities] Regardless of how an application renders user inputed values, it should ensure that the values are properly escaped. - -Refresh the page at http://localhost:8080/sample/ and you will see the user name displayed. This works because Spring Security integrates with the <> - -==== Logging out - -Now that we can view the user name, let's update the application to allow logging out. Update the body of index.jsp to contain a log out form as shown below: - -.src/main/webapp/index.jsp -[source,html] ----- - -
-

This is secured!

-

- Hello -

- -
- - -
-
- ----- - -In order to help protect against https://en.wikipedia.org/wiki/Cross-site_request_forgery[CSRF attacks], by default, Spring Security Java Configuration log out requires: - -* the HTTP method must be a POST -* the CSRF token must be added to the request. You can access it on the ServletRequest using the attribute _csrf as illustrated above. - -NOTE: If you were using Spring MVC's tag library or Thymeleaf, the CSRF token is automatically added as a hidden input for you. - -Refresh the page at http://localhost:8080/sample/ and you will see the log out button. Click the logout button and see that the application logs you out successfully. - -== Conclusion - -You should now know how to secure your application using Spring Security without using any XML. To learn more refer to the link:index.html[Spring Security Guides index page]. diff --git a/docs/guides/src/docs/asciidoc/helloworld-xml.asc b/docs/guides/src/docs/asciidoc/helloworld-xml.asc deleted file mode 100644 index c04f05a5fe..0000000000 --- a/docs/guides/src/docs/asciidoc/helloworld-xml.asc +++ /dev/null @@ -1,136 +0,0 @@ -= Hello Spring Security Xml Config -:author: Joe Grandja -:starter-appname: insecure -:starter-config-type: xml -:completed-appname: helloworld -:completed-config-type: xml -:include-dir: _includes -:hello-include-dir: _hello-includes - -This guide provides instructions on how to add Spring Security to an existing application using XML configuration. - -include::{include-dir}/setting-up-the-sample.asc[] - -Verify the application is working by ensuring a page stating *TODO Secure this* is displayed at http://localhost:8080/sample/ - -Once you have verified the application runs, stop the application server using the following steps: - -* In the Servers view select the latest tc Server -* Click the stop button (a red square) to stop the application server - -include::{hello-include-dir}/secure-the-application-xml.asc[] - -=== Registering Spring Security with the war - -We have created the Spring Security configuration, but we still need to register it with the war. This can be done using the following steps: - -* In the Package Explorer view, right click on the folder _src/main/webapp/WEB-INF_ -* Select *New->File* -* Enter _web.xml_ for the *File name* -* Click *Finish* -* Replace the contents of the file with the following: - -.src/main/webapp/WEB-INF/web.xml -[source,xml] ----- - - - - - - contextConfigLocation - - /WEB-INF/spring/*.xml - - - - - - springSecurityFilterChain - org.springframework.web.filter.DelegatingFilterProxy - - - springSecurityFilterChain - /* - - - - - org.springframework.web.context.ContextLoaderListener - - - ----- - -The _web.xml_ will do the following things: - -* Registers the `springSecurityFilterChain` Filter for every URL in your application -* Adds a `ContextLoaderListener` that loads the <>. - -include::{hello-include-dir}/exploring-the-secured-application-xml.asc[] - -==== Displaying the user name - -Now that we have authenticated, let's update the application to display the username. Update the body of index.jsp to be the following: - -.src/main/webapp/index.jsp -[source,html] ----- - -
-

This is secured!

-

- Hello -

-
- ----- - -WARNING: The `` tag ensures the username is escaped to avoid https://en.wikipedia.org/wiki/Cross-site_scripting[XSS vulnerabilities] Regardless of how an application renders user inputed values, it should ensure that the values are properly escaped. - -Refresh the page at http://localhost:8080/sample/ and you will see the user name displayed. This works because Spring Security integrates with the <> - -==== Logging out - -Now that we can view the user name, let's update the application to allow logging out. Update the body of index.jsp to contain a log out form as shown below: - -.src/main/webapp/index.jsp -[source,html] ----- - -
-

This is secured!

-

- Hello -

- -
- - -
-
- ----- - -In order to help protect against https://en.wikipedia.org/wiki/Cross-site_request_forgery[CSRF attacks], by default, Spring Security Xml Configuration log out requires: - -* the HTTP method must be a POST -* the CSRF token must be added to the request. You can access it on the ServletRequest using the attribute _csrf as illustrated above. - -NOTE: If you were using Spring MVC's tag library or Thymeleaf, the CSRF token is automatically added as a hidden input for you. - -Refresh the page at http://localhost:8080/sample/ and you will see the log out button. Click the logout button and see that the application logs you out successfully. - -== Conclusion - -You should now know how to secure your application using Spring Security with XML. To learn more refer to the link:index.html[Spring Security Guides index page]. diff --git a/docs/guides/src/docs/asciidoc/index.asc b/docs/guides/src/docs/asciidoc/index.asc deleted file mode 100644 index bf721d86a3..0000000000 --- a/docs/guides/src/docs/asciidoc/index.asc +++ /dev/null @@ -1,17 +0,0 @@ -= Spring Security Guides -Rob Winch, Joe Grandja - -These are step by step guides on how to use Spring Security. - -== Hello World - -These are the most basic starting points for using a web based application. - -* link:helloworld-javaconfig.html[Hello Spring Security Java Config] - demonstrates how to integrate Spring Security with an existing application using Java-based configuration -* link:helloworld-boot.html[Hello Spring Security with Boot] - demonstrates how to integrate Spring Security with an existing Spring Boot application -* link:helloworld-xml.html[Hello Spring Security Xml Config] - demonstrates how to integrate Spring Security with an existing application using Xml-based configuration -* link:hellomvc-javaconfig.html[Hello Spring MVC Security Java Config] - demonstrates how to integrate Spring Security with an existing Spring MVC application - -== Simple Customization - -* link:form-javaconfig.html[Creating a custom login form] - demonstrates how to create a custom login form diff --git a/docs/manual/spring-security-docs-manual.gradle b/docs/manual/spring-security-docs-manual.gradle deleted file mode 100644 index 6b0647cb9c..0000000000 --- a/docs/manual/spring-security-docs-manual.gradle +++ /dev/null @@ -1,62 +0,0 @@ -apply plugin: 'io.spring.convention.docs' -apply plugin: 'io.spring.convention.management-configuration' -apply plugin: 'io.spring.convention.repository' -apply plugin: 'java' - -asciidoctor { - attributes([stylesheet: 'css/style.css']) - resources { - from(sourceDir) { - include "css/**" - } - } -} - -asciidoctorj { - def docsTag = snapshotBuild ? 'current' : project.version - def ghTag = snapshotBuild ? 'main' : project.version - def ghUrl = "https://github.com/spring-projects/spring-security/tree/$ghTag" - def ghOldSamplesUrl = "https://github.com/spring-projects/spring-security/tree/5.4.x/samples" - def ghSamplesUrl = "https://github.com/spring-projects/spring-security-samples/tree/$samplesBranch" - def securityDocsUrl = "https://docs.spring.io/spring-security/site/docs/$docsTag" - def securityApiUrl = "$securityDocsUrl/api/" - def securityReferenceUrl = "$securityDocsUrl/reference/html5/" - def springFrameworkApiUrl = "https://docs.spring.io/spring-framework/docs/$springFrameworkVersion/javadoc-api/" - def springFrameworkReferenceUrl = "https://docs.spring.io/spring-framework/docs/$springFrameworkVersion/reference/html/" - attributes 'spring-security-version' : project.version, - 'spring-boot-version' : springBootVersion, - revnumber : project.version, - 'gh-url': ghUrl, - 'gh-samples-url': ghSamplesUrl, - 'gh-old-samples-url': ghOldSamplesUrl, - 'security-api-url': securityApiUrl, - 'security-reference-url': securityReferenceUrl, - 'spring-framework-api-url': springFrameworkApiUrl, - 'spring-framework-reference-url': springFrameworkReferenceUrl - attributeProvider resolvedVersions(project.configurations.testRuntimeClasspath) -} - -docsZip { - from(project(':spring-security-docs-guides').asciidoctor) { - into 'guides' - } -} - -dependencies { - management platform(project(":spring-security-dependencies")) - testImplementation "com.unboundid:unboundid-ldapsdk" - testImplementation "org.apache.directory.server:apacheds-core" - testImplementation "org.springframework:spring-core" -} - -def resolvedVersions(Configuration configuration) { - return { - configuration.resolvedConfiguration - .resolvedArtifacts - .collectEntries { [(it.name + "-version"): it.moduleVersion.id.version] } - } -} - -repositories { - maven { url "https://repo.spring.io/release" } -} diff --git a/docs/manual/src/docs/asciidoc/Guardfile b/docs/manual/src/docs/asciidoc/Guardfile deleted file mode 100644 index 076f37d0f4..0000000000 --- a/docs/manual/src/docs/asciidoc/Guardfile +++ /dev/null @@ -1,12 +0,0 @@ -require 'asciidoctor' -require 'erb' - -guard 'shell' do - watch(/^.*\.adoc$/) {|m| - Asciidoctor.render_file(m[0], :to_dir => "build/", :safe => Asciidoctor::SafeMode::UNSAFE, :attributes=> {'idprefix' => '', 'idseparator' => '-', 'copycss' => '', 'icons' => 'font', 'source-highlighter' => 'prettify', 'sectanchors' => '', 'doctype' => 'book','toc2' => '', 'spring-security-version' => '3.2.0.CI-SNAPSHOT','spring-version' => '3.2.0.RELEASE','spring4-version' => '4.0.0.RELEASE', 'revnumber' => '3.2.0.CI-SNAPSHOT', 'numbered'=>'' }) - } -end - -guard 'livereload' do - watch(%r{build/.+\.(css|js|html)$}) -end diff --git a/docs/manual/src/docs/asciidoc/_includes/about/index.adoc b/docs/manual/src/docs/asciidoc/_includes/about/index.adoc deleted file mode 100644 index 9345d31731..0000000000 --- a/docs/manual/src/docs/asciidoc/_includes/about/index.adoc +++ /dev/null @@ -1,17 +0,0 @@ -= Introduction - -This section discusses the logistics of Spring Security. - -include::prerequisites.adoc[leveloffset=+1] - -include::community.adoc[leveloffset=+1] - -include::whats-new.adoc[] - -include::getting-spring-security.adoc[leveloffset=+1] - -include::features.adoc[leveloffset=+1] - -include::modules.adoc[leveloffset=+1] - -include::samples.adoc[leveloffset=+1] diff --git a/docs/manual/src/docs/asciidoc/_includes/content.adoc b/docs/manual/src/docs/asciidoc/_includes/content.adoc deleted file mode 100644 index 2cabae6ea5..0000000000 --- a/docs/manual/src/docs/asciidoc/_includes/content.adoc +++ /dev/null @@ -1,9 +0,0 @@ -= Book Title -Rob Winch - -[abstract] -This is the actual content. - -== First Section - -And off we go. \ No newline at end of file diff --git a/docs/manual/src/docs/asciidoc/css/style.css b/docs/manual/src/docs/asciidoc/css/style.css deleted file mode 100644 index 45650c46df..0000000000 --- a/docs/manual/src/docs/asciidoc/css/style.css +++ /dev/null @@ -1,5 +0,0 @@ -@import 'spring.css'; - -a code { - color: #097dff; -} diff --git a/docs/manual/src/docs/asciidoc/index-docinfo.xml b/docs/manual/src/docs/asciidoc/index-docinfo.xml deleted file mode 100644 index 9a041505ba..0000000000 --- a/docs/manual/src/docs/asciidoc/index-docinfo.xml +++ /dev/null @@ -1,11 +0,0 @@ -Spring Security -{spring-security-version} - - 2004-2019 - - - Copies of this document may be made for your own use and for distribution to - others, provided that you do not charge any fee for such copies and further provided - that each copy contains this Copyright Notice, whether distributed in print or - electronically. - diff --git a/docs/manual/src/docs/asciidoc/index.adoc b/docs/manual/src/docs/asciidoc/index.adoc deleted file mode 100644 index 5536b762e4..0000000000 --- a/docs/manual/src/docs/asciidoc/index.adoc +++ /dev/null @@ -1,19 +0,0 @@ -= Spring Security Reference -Ben Alex; Luke Taylor; Rob Winch; Gunnar Hillert; Joe Grandja; Jay Bryant; Eddú Meléndez; Josh Cummings; Dave Syer; Eleftheria Stein -:include-dir: _includes -:source-indent: 0 -:tabsize: 4 -:toc: left - -// 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. - -include::{include-dir}/about/index.adoc[] - -include::{include-dir}/servlet/index.adoc[] - -include::{include-dir}/reactive/index.adoc[] - - diff --git a/docs/manual/src/docs/asciidoc/images/Authentication.gif b/docs/modules/ROOT/images/Authentication.gif similarity index 100% rename from docs/manual/src/docs/asciidoc/images/Authentication.gif rename to docs/modules/ROOT/images/Authentication.gif diff --git a/docs/manual/src/docs/asciidoc/images/access-decision-voting.graffle b/docs/modules/ROOT/images/access-decision-voting.graffle similarity index 100% rename from docs/manual/src/docs/asciidoc/images/access-decision-voting.graffle rename to docs/modules/ROOT/images/access-decision-voting.graffle diff --git a/docs/manual/src/docs/asciidoc/images/access-decision-voting.png b/docs/modules/ROOT/images/access-decision-voting.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/access-decision-voting.png rename to docs/modules/ROOT/images/access-decision-voting.png diff --git a/docs/manual/src/docs/asciidoc/images/after-invocation.graffle b/docs/modules/ROOT/images/after-invocation.graffle similarity index 100% rename from docs/manual/src/docs/asciidoc/images/after-invocation.graffle rename to docs/modules/ROOT/images/after-invocation.graffle diff --git a/docs/manual/src/docs/asciidoc/images/after-invocation.png b/docs/modules/ROOT/images/after-invocation.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/after-invocation.png rename to docs/modules/ROOT/images/after-invocation.png diff --git a/docs/manual/src/docs/asciidoc/images/icons/number_1.png b/docs/modules/ROOT/images/icons/number_1.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/icons/number_1.png rename to docs/modules/ROOT/images/icons/number_1.png diff --git a/docs/manual/src/docs/asciidoc/images/icons/number_10.png b/docs/modules/ROOT/images/icons/number_10.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/icons/number_10.png rename to docs/modules/ROOT/images/icons/number_10.png diff --git a/docs/manual/src/docs/asciidoc/images/icons/number_2.png b/docs/modules/ROOT/images/icons/number_2.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/icons/number_2.png rename to docs/modules/ROOT/images/icons/number_2.png diff --git a/docs/manual/src/docs/asciidoc/images/icons/number_3.png b/docs/modules/ROOT/images/icons/number_3.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/icons/number_3.png rename to docs/modules/ROOT/images/icons/number_3.png diff --git a/docs/manual/src/docs/asciidoc/images/icons/number_4.png b/docs/modules/ROOT/images/icons/number_4.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/icons/number_4.png rename to docs/modules/ROOT/images/icons/number_4.png diff --git a/docs/manual/src/docs/asciidoc/images/icons/number_5.png b/docs/modules/ROOT/images/icons/number_5.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/icons/number_5.png rename to docs/modules/ROOT/images/icons/number_5.png diff --git a/docs/manual/src/docs/asciidoc/images/icons/number_6.png b/docs/modules/ROOT/images/icons/number_6.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/icons/number_6.png rename to docs/modules/ROOT/images/icons/number_6.png diff --git a/docs/manual/src/docs/asciidoc/images/icons/number_7.png b/docs/modules/ROOT/images/icons/number_7.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/icons/number_7.png rename to docs/modules/ROOT/images/icons/number_7.png diff --git a/docs/manual/src/docs/asciidoc/images/icons/number_8.png b/docs/modules/ROOT/images/icons/number_8.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/icons/number_8.png rename to docs/modules/ROOT/images/icons/number_8.png diff --git a/docs/manual/src/docs/asciidoc/images/icons/number_9.png b/docs/modules/ROOT/images/icons/number_9.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/icons/number_9.png rename to docs/modules/ROOT/images/icons/number_9.png diff --git a/docs/manual/src/docs/asciidoc/images/icons/numbers.odg b/docs/modules/ROOT/images/icons/numbers.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/icons/numbers.odg rename to docs/modules/ROOT/images/icons/numbers.odg diff --git a/docs/manual/src/docs/asciidoc/images/note.png b/docs/modules/ROOT/images/note.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/note.png rename to docs/modules/ROOT/images/note.png diff --git a/docs/manual/src/docs/asciidoc/images/palette.otg b/docs/modules/ROOT/images/palette.otg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/palette.otg rename to docs/modules/ROOT/images/palette.otg diff --git a/docs/manual/src/docs/asciidoc/images/s2-banner-rhs.png b/docs/modules/ROOT/images/s2-banner-rhs.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/s2-banner-rhs.png rename to docs/modules/ROOT/images/s2-banner-rhs.png diff --git a/docs/manual/src/docs/asciidoc/images/s2_box_logo.png b/docs/modules/ROOT/images/s2_box_logo.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/s2_box_logo.png rename to docs/modules/ROOT/images/s2_box_logo.png diff --git a/docs/manual/src/docs/asciidoc/images/security-interception.graffle b/docs/modules/ROOT/images/security-interception.graffle similarity index 100% rename from docs/manual/src/docs/asciidoc/images/security-interception.graffle rename to docs/modules/ROOT/images/security-interception.graffle diff --git a/docs/manual/src/docs/asciidoc/images/security-interception.png b/docs/modules/ROOT/images/security-interception.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/security-interception.png rename to docs/modules/ROOT/images/security-interception.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/architecture/delegatingfilterproxy.odg b/docs/modules/ROOT/images/servlet/architecture/delegatingfilterproxy.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/architecture/delegatingfilterproxy.odg rename to docs/modules/ROOT/images/servlet/architecture/delegatingfilterproxy.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/architecture/delegatingfilterproxy.png b/docs/modules/ROOT/images/servlet/architecture/delegatingfilterproxy.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/architecture/delegatingfilterproxy.png rename to docs/modules/ROOT/images/servlet/architecture/delegatingfilterproxy.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/architecture/exceptiontranslationfilter.odg b/docs/modules/ROOT/images/servlet/architecture/exceptiontranslationfilter.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/architecture/exceptiontranslationfilter.odg rename to docs/modules/ROOT/images/servlet/architecture/exceptiontranslationfilter.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/architecture/exceptiontranslationfilter.png b/docs/modules/ROOT/images/servlet/architecture/exceptiontranslationfilter.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/architecture/exceptiontranslationfilter.png rename to docs/modules/ROOT/images/servlet/architecture/exceptiontranslationfilter.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/architecture/filterchain.gif b/docs/modules/ROOT/images/servlet/architecture/filterchain.gif similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/architecture/filterchain.gif rename to docs/modules/ROOT/images/servlet/architecture/filterchain.gif diff --git a/docs/manual/src/docs/asciidoc/images/servlet/architecture/filterchain.odg b/docs/modules/ROOT/images/servlet/architecture/filterchain.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/architecture/filterchain.odg rename to docs/modules/ROOT/images/servlet/architecture/filterchain.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/architecture/filterchain.png b/docs/modules/ROOT/images/servlet/architecture/filterchain.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/architecture/filterchain.png rename to docs/modules/ROOT/images/servlet/architecture/filterchain.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/architecture/filterchainproxy.odg b/docs/modules/ROOT/images/servlet/architecture/filterchainproxy.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/architecture/filterchainproxy.odg rename to docs/modules/ROOT/images/servlet/architecture/filterchainproxy.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/architecture/filterchainproxy.png b/docs/modules/ROOT/images/servlet/architecture/filterchainproxy.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/architecture/filterchainproxy.png rename to docs/modules/ROOT/images/servlet/architecture/filterchainproxy.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/architecture/multi-securityfilterchain.odg b/docs/modules/ROOT/images/servlet/architecture/multi-securityfilterchain.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/architecture/multi-securityfilterchain.odg rename to docs/modules/ROOT/images/servlet/architecture/multi-securityfilterchain.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/architecture/multi-securityfilterchain.png b/docs/modules/ROOT/images/servlet/architecture/multi-securityfilterchain.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/architecture/multi-securityfilterchain.png rename to docs/modules/ROOT/images/servlet/architecture/multi-securityfilterchain.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/architecture/securityfilterchain.odg b/docs/modules/ROOT/images/servlet/architecture/securityfilterchain.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/architecture/securityfilterchain.odg rename to docs/modules/ROOT/images/servlet/architecture/securityfilterchain.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/architecture/securityfilterchain.png b/docs/modules/ROOT/images/servlet/architecture/securityfilterchain.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/architecture/securityfilterchain.png rename to docs/modules/ROOT/images/servlet/architecture/securityfilterchain.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.odg b/docs/modules/ROOT/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.odg rename to docs/modules/ROOT/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.png b/docs/modules/ROOT/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.png rename to docs/modules/ROOT/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/providermanager-parent.odg b/docs/modules/ROOT/images/servlet/authentication/architecture/providermanager-parent.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/providermanager-parent.odg rename to docs/modules/ROOT/images/servlet/authentication/architecture/providermanager-parent.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/providermanager-parent.png b/docs/modules/ROOT/images/servlet/authentication/architecture/providermanager-parent.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/providermanager-parent.png rename to docs/modules/ROOT/images/servlet/authentication/architecture/providermanager-parent.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/providermanager.odg b/docs/modules/ROOT/images/servlet/authentication/architecture/providermanager.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/providermanager.odg rename to docs/modules/ROOT/images/servlet/authentication/architecture/providermanager.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/providermanager.png b/docs/modules/ROOT/images/servlet/authentication/architecture/providermanager.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/providermanager.png rename to docs/modules/ROOT/images/servlet/authentication/architecture/providermanager.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/providermanagers-parent.odg b/docs/modules/ROOT/images/servlet/authentication/architecture/providermanagers-parent.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/providermanagers-parent.odg rename to docs/modules/ROOT/images/servlet/authentication/architecture/providermanagers-parent.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/providermanagers-parent.png b/docs/modules/ROOT/images/servlet/authentication/architecture/providermanagers-parent.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/providermanagers-parent.png rename to docs/modules/ROOT/images/servlet/authentication/architecture/providermanagers-parent.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/securitycontextholder.odg b/docs/modules/ROOT/images/servlet/authentication/architecture/securitycontextholder.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/securitycontextholder.odg rename to docs/modules/ROOT/images/servlet/authentication/architecture/securitycontextholder.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/securitycontextholder.png b/docs/modules/ROOT/images/servlet/authentication/architecture/securitycontextholder.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/architecture/securitycontextholder.png rename to docs/modules/ROOT/images/servlet/authentication/architecture/securitycontextholder.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/basicauthenticationentrypoint.odg b/docs/modules/ROOT/images/servlet/authentication/unpwd/basicauthenticationentrypoint.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/basicauthenticationentrypoint.odg rename to docs/modules/ROOT/images/servlet/authentication/unpwd/basicauthenticationentrypoint.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/basicauthenticationentrypoint.png b/docs/modules/ROOT/images/servlet/authentication/unpwd/basicauthenticationentrypoint.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/basicauthenticationentrypoint.png rename to docs/modules/ROOT/images/servlet/authentication/unpwd/basicauthenticationentrypoint.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/basicauthenticationfilter.odg b/docs/modules/ROOT/images/servlet/authentication/unpwd/basicauthenticationfilter.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/basicauthenticationfilter.odg rename to docs/modules/ROOT/images/servlet/authentication/unpwd/basicauthenticationfilter.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/basicauthenticationfilter.png b/docs/modules/ROOT/images/servlet/authentication/unpwd/basicauthenticationfilter.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/basicauthenticationfilter.png rename to docs/modules/ROOT/images/servlet/authentication/unpwd/basicauthenticationfilter.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/daoauthenticationprovider.odg b/docs/modules/ROOT/images/servlet/authentication/unpwd/daoauthenticationprovider.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/daoauthenticationprovider.odg rename to docs/modules/ROOT/images/servlet/authentication/unpwd/daoauthenticationprovider.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/daoauthenticationprovider.png b/docs/modules/ROOT/images/servlet/authentication/unpwd/daoauthenticationprovider.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/daoauthenticationprovider.png rename to docs/modules/ROOT/images/servlet/authentication/unpwd/daoauthenticationprovider.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.odg b/docs/modules/ROOT/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.odg rename to docs/modules/ROOT/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.png b/docs/modules/ROOT/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.png rename to docs/modules/ROOT/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.odg b/docs/modules/ROOT/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.odg rename to docs/modules/ROOT/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.png b/docs/modules/ROOT/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.png rename to docs/modules/ROOT/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authorization/filtersecurityinterceptor.odg b/docs/modules/ROOT/images/servlet/authorization/filtersecurityinterceptor.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authorization/filtersecurityinterceptor.odg rename to docs/modules/ROOT/images/servlet/authorization/filtersecurityinterceptor.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/authorization/filtersecurityinterceptor.png b/docs/modules/ROOT/images/servlet/authorization/filtersecurityinterceptor.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/authorization/filtersecurityinterceptor.png rename to docs/modules/ROOT/images/servlet/authorization/filtersecurityinterceptor.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/oauth2/beareraccessdeniedhandler.odg b/docs/modules/ROOT/images/servlet/oauth2/beareraccessdeniedhandler.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/oauth2/beareraccessdeniedhandler.odg rename to docs/modules/ROOT/images/servlet/oauth2/beareraccessdeniedhandler.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearerauthenticationentrypoint.odg b/docs/modules/ROOT/images/servlet/oauth2/bearerauthenticationentrypoint.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearerauthenticationentrypoint.odg rename to docs/modules/ROOT/images/servlet/oauth2/bearerauthenticationentrypoint.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearerauthenticationentrypoint.png b/docs/modules/ROOT/images/servlet/oauth2/bearerauthenticationentrypoint.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearerauthenticationentrypoint.png rename to docs/modules/ROOT/images/servlet/oauth2/bearerauthenticationentrypoint.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearertokenauthenticationfilter.odg b/docs/modules/ROOT/images/servlet/oauth2/bearertokenauthenticationfilter.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearertokenauthenticationfilter.odg rename to docs/modules/ROOT/images/servlet/oauth2/bearertokenauthenticationfilter.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearertokenauthenticationfilter.png b/docs/modules/ROOT/images/servlet/oauth2/bearertokenauthenticationfilter.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/oauth2/bearertokenauthenticationfilter.png rename to docs/modules/ROOT/images/servlet/oauth2/bearertokenauthenticationfilter.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/oauth2/jwtauthenticationprovider.odg b/docs/modules/ROOT/images/servlet/oauth2/jwtauthenticationprovider.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/oauth2/jwtauthenticationprovider.odg rename to docs/modules/ROOT/images/servlet/oauth2/jwtauthenticationprovider.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/oauth2/jwtauthenticationprovider.png b/docs/modules/ROOT/images/servlet/oauth2/jwtauthenticationprovider.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/oauth2/jwtauthenticationprovider.png rename to docs/modules/ROOT/images/servlet/oauth2/jwtauthenticationprovider.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/oauth2/opaquetokenauthenticationprovider.odg b/docs/modules/ROOT/images/servlet/oauth2/opaquetokenauthenticationprovider.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/oauth2/opaquetokenauthenticationprovider.odg rename to docs/modules/ROOT/images/servlet/oauth2/opaquetokenauthenticationprovider.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/oauth2/opaquetokenauthenticationprovider.png b/docs/modules/ROOT/images/servlet/oauth2/opaquetokenauthenticationprovider.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/oauth2/opaquetokenauthenticationprovider.png rename to docs/modules/ROOT/images/servlet/oauth2/opaquetokenauthenticationprovider.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/saml2/opensamlauthenticationprovider.odg b/docs/modules/ROOT/images/servlet/saml2/opensamlauthenticationprovider.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/saml2/opensamlauthenticationprovider.odg rename to docs/modules/ROOT/images/servlet/saml2/opensamlauthenticationprovider.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/saml2/opensamlauthenticationprovider.png b/docs/modules/ROOT/images/servlet/saml2/opensamlauthenticationprovider.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/saml2/opensamlauthenticationprovider.png rename to docs/modules/ROOT/images/servlet/saml2/opensamlauthenticationprovider.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/saml2/saml2webssoauthenticationfilter.odg b/docs/modules/ROOT/images/servlet/saml2/saml2webssoauthenticationfilter.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/saml2/saml2webssoauthenticationfilter.odg rename to docs/modules/ROOT/images/servlet/saml2/saml2webssoauthenticationfilter.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/saml2/saml2webssoauthenticationfilter.png b/docs/modules/ROOT/images/servlet/saml2/saml2webssoauthenticationfilter.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/saml2/saml2webssoauthenticationfilter.png rename to docs/modules/ROOT/images/servlet/saml2/saml2webssoauthenticationfilter.png diff --git a/docs/manual/src/docs/asciidoc/images/servlet/saml2/saml2webssoauthenticationrequestfilter.odg b/docs/modules/ROOT/images/servlet/saml2/saml2webssoauthenticationrequestfilter.odg similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/saml2/saml2webssoauthenticationrequestfilter.odg rename to docs/modules/ROOT/images/servlet/saml2/saml2webssoauthenticationrequestfilter.odg diff --git a/docs/manual/src/docs/asciidoc/images/servlet/saml2/saml2webssoauthenticationrequestfilter.png b/docs/modules/ROOT/images/servlet/saml2/saml2webssoauthenticationrequestfilter.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/servlet/saml2/saml2webssoauthenticationrequestfilter.png rename to docs/modules/ROOT/images/servlet/saml2/saml2webssoauthenticationrequestfilter.png diff --git a/docs/manual/src/docs/asciidoc/images/tip.png b/docs/modules/ROOT/images/tip.png similarity index 100% rename from docs/manual/src/docs/asciidoc/images/tip.png rename to docs/modules/ROOT/images/tip.png diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc new file mode 100644 index 0000000000..9b378b7b6f --- /dev/null +++ b/docs/modules/ROOT/nav.adoc @@ -0,0 +1,72 @@ +* Overview +** xref:overview/prerequisites.adoc[Prerequisites] +** xref:overview/community.adoc[Community] +** xref:overview/whats-new.adoc[What's New] +** xref:overview/getting-spring-security.adoc[Getting Spring Security] +** xref:overview/features/index.adoc[Features] +*** xref:overview/features/authentication/index.adoc[Authentication] +*** xref:overview/features/exploits/index.adoc[Protection Against Exploits] +** xref:overview/modules.adoc[Project Modules & Dependencies] +** xref:overview/samples.adoc[Samples] +* Servlet Applications +** xref:servlet/hello/index.adoc[Hello Spring Security] +** xref:servlet/architecture/index.adoc[The Big Picture] +** Authentication +*** xref:servlet/authentication/architecture/index.adoc[Authentication Architecture] +*** xref:servlet/authentication/unpwd/index.adoc[Username/Password] +**** Reading Username/Password +***** xref:servlet/authentication/unpwd/form.adoc[Form] +***** xref:servlet/authentication/unpwd/basic.adoc[Basic] +***** xref:servlet/authentication/unpwd/digest.adoc[Digest] +**** Password Storage +***** xref:servlet/authentication/unpwd/in-memory.adoc[In Memory] +***** xref:servlet/authentication/unpwd/jdbc.adoc[JDBC] +***** xref:servlet/authentication/unpwd/user-details.adoc[UserDetails] +***** xref:servlet/authentication/unpwd/user-details-service.adoc[UserDetailsService] +***** xref:servlet/authentication/unpwd/password-encoder.adoc[PasswordEncoder] +***** xref:servlet/authentication/unpwd/dao-authentication-provider.adoc[DaoAuthenticationProvider] +***** xref:servlet/authentication/unpwd/ldap.adoc[LDAP] +*** xref:servlet/authentication/session-management.adoc[Session Management] +*** xref:servlet/authentication/rememberme.adoc[Remember Me] +*** xref:servlet/authentication/openid.adoc[OpenID] +*** xref:servlet/authentication/anonymous.adoc[Anonymous] +*** xref:servlet/authentication/preauth.adoc[Pre-Authentication] +*** xref:servlet/authentication/jaas.adoc[JAAS] +*** xref:servlet/authentication/cas.adoc[CAS] +*** xref:servlet/authentication/x509.adoc[X509] +*** xref:servlet/authentication/runas.adoc[Run-As] +*** xref:servlet/authentication/logout.adoc[Logout] +*** xref:servlet/authentication/events.adoc[Authentication Events] +** Authorization +*** xref:servlet/authorization/architecture.adoc[Authorization Architecture] +*** xref:servlet/authorization/authorize-requests.adoc[Authorize HTTP Requests] +*** xref:servlet/authorization/expression-based.adoc[Expression-Based Access Control] +*** xref:servlet/authorization/secure-objects.adoc[Secure Object Implementations] +*** xref:servlet/authorization/method-security.adoc[Method Security] +*** xref:servlet/authorization/acls.adoc[Domain Object Security ACLs] +** OAuth2 +*** xref:servlet/oauth2/oauth2-login.adoc[OAuth2 Log In] +*** xref:servlet/oauth2/oauth2-client.adoc[OAuth2 Client] +*** xref:servlet/oauth2/oauth2-resourceserver.adoc[OAuth2 Resource Server] +** xref:servlet/saml2/index.adoc[SAML2] +** xref:servlet/exploits/index.adoc[Protection Against Exploits] +** xref:servlet/integrations/index.adoc[Integrations] +** Configuration +*** xref:servlet/java-configuration/index.adoc[Java Configuration] +*** xref:servlet/kotlin-configuration/index.adoc[Kotlin Configuration] +*** xref:servlet/namespace/index.adoc[Namespace Configuration] +** xref:servlet/test/index.adoc[Testing] +** xref:servlet/crypto/index.adoc[Cryptography] +** xref:servlet/appendix/index.adoc[Appendix] +* Reactive Applications +** xref:reactive/webflux.adoc[WebFlux Security] +** xref:reactive/exploits/index.adoc[Protection Against Exploits] +** xref:reactive/oauth2/index.adoc[OAuth2] +** xref:reactive/registered-oauth2-authorized-client.adoc[@RegisteredOAuth2AuthorizedClient] +** xref:reactive/x509.adoc[X.509 Authentication] +** xref:reactive/logout.adoc[Logout] +** xref:reactive/webclient.adoc[WebClient] +** xref:reactive/method.adoc[EnableReactiveMethodSecurity] +** xref:reactive/cors.adoc[CORS] +** xref:reactive/test.adoc[Testing] +** xref:reactive/rsocket.adoc[RSocket] diff --git a/docs/manual/src/docs/asciidoc/_includes/about/community.adoc b/docs/modules/ROOT/pages/overview/community.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/about/community.adoc rename to docs/modules/ROOT/pages/overview/community.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/about/authentication/index.adoc b/docs/modules/ROOT/pages/overview/features/authentication/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/about/authentication/index.adoc rename to docs/modules/ROOT/pages/overview/features/authentication/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/about/authentication/password-storage.adoc b/docs/modules/ROOT/pages/overview/features/authentication/password-storage.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/about/authentication/password-storage.adoc rename to docs/modules/ROOT/pages/overview/features/authentication/password-storage.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/about/authentication/supported.adoc b/docs/modules/ROOT/pages/overview/features/authentication/supported.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/about/authentication/supported.adoc rename to docs/modules/ROOT/pages/overview/features/authentication/supported.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/about/exploits/csrf.adoc b/docs/modules/ROOT/pages/overview/features/exploits/csrf.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/about/exploits/csrf.adoc rename to docs/modules/ROOT/pages/overview/features/exploits/csrf.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/about/exploits/headers.adoc b/docs/modules/ROOT/pages/overview/features/exploits/headers.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/about/exploits/headers.adoc rename to docs/modules/ROOT/pages/overview/features/exploits/headers.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/about/exploits/http.adoc b/docs/modules/ROOT/pages/overview/features/exploits/http.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/about/exploits/http.adoc rename to docs/modules/ROOT/pages/overview/features/exploits/http.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/about/exploits/index.adoc b/docs/modules/ROOT/pages/overview/features/exploits/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/about/exploits/index.adoc rename to docs/modules/ROOT/pages/overview/features/exploits/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/about/features.adoc b/docs/modules/ROOT/pages/overview/features/index.adoc similarity index 72% rename from docs/manual/src/docs/asciidoc/_includes/about/features.adoc rename to docs/modules/ROOT/pages/overview/features/index.adoc index c08113ae5e..8ae9eb3feb 100644 --- a/docs/manual/src/docs/asciidoc/_includes/about/features.adoc +++ b/docs/modules/ROOT/pages/overview/features/index.adoc @@ -4,6 +4,3 @@ Spring Security provides comprehensive support for <>, authorization, and protection against <>. It also provides integration with other libraries to simplify its usage. -include::authentication/index.adoc[leveloffset=+1] - -include::exploits/index.adoc[leveloffset=+1] diff --git a/docs/manual/src/docs/asciidoc/_includes/about/getting-spring-security.adoc b/docs/modules/ROOT/pages/overview/getting-spring-security.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/about/getting-spring-security.adoc rename to docs/modules/ROOT/pages/overview/getting-spring-security.adoc diff --git a/docs/modules/ROOT/pages/overview/index.adoc b/docs/modules/ROOT/pages/overview/index.adoc new file mode 100644 index 0000000000..7441dadf05 --- /dev/null +++ b/docs/modules/ROOT/pages/overview/index.adoc @@ -0,0 +1,5 @@ += Overview + +Spring Security is a framework that provides authentication, authorization, and protection against common attacks. +With first class support for both imperative and reactive applications, it is the de-facto standard for securing Spring-based applications. + diff --git a/docs/manual/src/docs/asciidoc/_includes/about/modules.adoc b/docs/modules/ROOT/pages/overview/modules.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/about/modules.adoc rename to docs/modules/ROOT/pages/overview/modules.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/about/prerequisites.adoc b/docs/modules/ROOT/pages/overview/prerequisites.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/about/prerequisites.adoc rename to docs/modules/ROOT/pages/overview/prerequisites.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/about/samples.adoc b/docs/modules/ROOT/pages/overview/samples.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/about/samples.adoc rename to docs/modules/ROOT/pages/overview/samples.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/about/whats-new.adoc b/docs/modules/ROOT/pages/overview/whats-new.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/about/whats-new.adoc rename to docs/modules/ROOT/pages/overview/whats-new.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/cors.adoc b/docs/modules/ROOT/pages/reactive/cors.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/reactive/cors.adoc rename to docs/modules/ROOT/pages/reactive/cors.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/exploits/csrf.adoc b/docs/modules/ROOT/pages/reactive/exploits/csrf.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/reactive/exploits/csrf.adoc rename to docs/modules/ROOT/pages/reactive/exploits/csrf.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/exploits/headers.adoc b/docs/modules/ROOT/pages/reactive/exploits/headers.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/reactive/exploits/headers.adoc rename to docs/modules/ROOT/pages/reactive/exploits/headers.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/exploits/http.adoc b/docs/modules/ROOT/pages/reactive/exploits/http.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/reactive/exploits/http.adoc rename to docs/modules/ROOT/pages/reactive/exploits/http.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/exploits/index.adoc b/docs/modules/ROOT/pages/reactive/exploits/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/reactive/exploits/index.adoc rename to docs/modules/ROOT/pages/reactive/exploits/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/index.adoc b/docs/modules/ROOT/pages/reactive/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/reactive/index.adoc rename to docs/modules/ROOT/pages/reactive/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/logout.adoc b/docs/modules/ROOT/pages/reactive/logout.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/reactive/logout.adoc rename to docs/modules/ROOT/pages/reactive/logout.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/method.adoc b/docs/modules/ROOT/pages/reactive/method.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/reactive/method.adoc rename to docs/modules/ROOT/pages/reactive/method.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/access-token.adoc b/docs/modules/ROOT/pages/reactive/oauth2/access-token.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/access-token.adoc rename to docs/modules/ROOT/pages/reactive/oauth2/access-token.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/index.adoc b/docs/modules/ROOT/pages/reactive/oauth2/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/index.adoc rename to docs/modules/ROOT/pages/reactive/oauth2/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/login.adoc b/docs/modules/ROOT/pages/reactive/oauth2/login.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/login.adoc rename to docs/modules/ROOT/pages/reactive/oauth2/login.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/resource-server.adoc b/docs/modules/ROOT/pages/reactive/oauth2/resource-server.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/resource-server.adoc rename to docs/modules/ROOT/pages/reactive/oauth2/resource-server.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/registered-oauth2-authorized-client.adoc b/docs/modules/ROOT/pages/reactive/registered-oauth2-authorized-client.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/reactive/registered-oauth2-authorized-client.adoc rename to docs/modules/ROOT/pages/reactive/registered-oauth2-authorized-client.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/rsocket.adoc b/docs/modules/ROOT/pages/reactive/rsocket.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/reactive/rsocket.adoc rename to docs/modules/ROOT/pages/reactive/rsocket.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/test.adoc b/docs/modules/ROOT/pages/reactive/test.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/reactive/test.adoc rename to docs/modules/ROOT/pages/reactive/test.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/webclient.adoc b/docs/modules/ROOT/pages/reactive/webclient.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/reactive/webclient.adoc rename to docs/modules/ROOT/pages/reactive/webclient.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/webflux.adoc b/docs/modules/ROOT/pages/reactive/webflux.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/reactive/webflux.adoc rename to docs/modules/ROOT/pages/reactive/webflux.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/x509.adoc b/docs/modules/ROOT/pages/reactive/x509.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/reactive/x509.adoc rename to docs/modules/ROOT/pages/reactive/x509.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/appendix/database-schema.adoc b/docs/modules/ROOT/pages/servlet/appendix/database-schema.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/appendix/database-schema.adoc rename to docs/modules/ROOT/pages/servlet/appendix/database-schema.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/appendix/faq.adoc b/docs/modules/ROOT/pages/servlet/appendix/faq.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/appendix/faq.adoc rename to docs/modules/ROOT/pages/servlet/appendix/faq.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/appendix/index.adoc b/docs/modules/ROOT/pages/servlet/appendix/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/appendix/index.adoc rename to docs/modules/ROOT/pages/servlet/appendix/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/appendix/namespace.adoc b/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/appendix/namespace.adoc rename to docs/modules/ROOT/pages/servlet/appendix/namespace.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/appendix/proxy-server.adoc b/docs/modules/ROOT/pages/servlet/appendix/proxy-server.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/appendix/proxy-server.adoc rename to docs/modules/ROOT/pages/servlet/appendix/proxy-server.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/delegating-filter-proxy.adoc b/docs/modules/ROOT/pages/servlet/architecture/delegating-filter-proxy.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/architecture/delegating-filter-proxy.adoc rename to docs/modules/ROOT/pages/servlet/architecture/delegating-filter-proxy.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/exception-translation-filter.adoc b/docs/modules/ROOT/pages/servlet/architecture/exception-translation-filter.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/architecture/exception-translation-filter.adoc rename to docs/modules/ROOT/pages/servlet/architecture/exception-translation-filter.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/filter-chain-proxy.adoc b/docs/modules/ROOT/pages/servlet/architecture/filter-chain-proxy.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/architecture/filter-chain-proxy.adoc rename to docs/modules/ROOT/pages/servlet/architecture/filter-chain-proxy.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/filters.adoc b/docs/modules/ROOT/pages/servlet/architecture/filters.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/architecture/filters.adoc rename to docs/modules/ROOT/pages/servlet/architecture/filters.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/architecture/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/architecture/index.adoc rename to docs/modules/ROOT/pages/servlet/architecture/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/security-filter-chain.adoc b/docs/modules/ROOT/pages/servlet/architecture/security-filter-chain.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/architecture/security-filter-chain.adoc rename to docs/modules/ROOT/pages/servlet/architecture/security-filter-chain.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/architecture/security-filters.adoc b/docs/modules/ROOT/pages/servlet/architecture/security-filters.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/architecture/security-filters.adoc rename to docs/modules/ROOT/pages/servlet/architecture/security-filters.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/anonymous.adoc b/docs/modules/ROOT/pages/servlet/authentication/anonymous.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/anonymous.adoc rename to docs/modules/ROOT/pages/servlet/authentication/anonymous.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/abstract-authentication-processing-filter.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/abstract-authentication-processing-filter.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/abstract-authentication-processing-filter.adoc rename to docs/modules/ROOT/pages/servlet/authentication/architecture/abstract-authentication-processing-filter.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/authentication-entry-point.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/authentication-entry-point.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/authentication-entry-point.adoc rename to docs/modules/ROOT/pages/servlet/authentication/architecture/authentication-entry-point.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/authentication-manager.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/authentication-manager.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/authentication-manager.adoc rename to docs/modules/ROOT/pages/servlet/authentication/architecture/authentication-manager.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/authentication-provider.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/authentication-provider.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/authentication-provider.adoc rename to docs/modules/ROOT/pages/servlet/authentication/architecture/authentication-provider.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/authentication.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/authentication.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/authentication.adoc rename to docs/modules/ROOT/pages/servlet/authentication/architecture/authentication.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/granted-authority.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/granted-authority.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/granted-authority.adoc rename to docs/modules/ROOT/pages/servlet/authentication/architecture/granted-authority.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/index.adoc rename to docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/provider-manager.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/provider-manager.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/provider-manager.adoc rename to docs/modules/ROOT/pages/servlet/authentication/architecture/provider-manager.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/security-context-holder.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/security-context-holder.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/security-context-holder.adoc rename to docs/modules/ROOT/pages/servlet/authentication/architecture/security-context-holder.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/security-context.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/security-context.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/architecture/security-context.adoc rename to docs/modules/ROOT/pages/servlet/authentication/architecture/security-context.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/cas.adoc b/docs/modules/ROOT/pages/servlet/authentication/cas.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/cas.adoc rename to docs/modules/ROOT/pages/servlet/authentication/cas.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/events.adoc b/docs/modules/ROOT/pages/servlet/authentication/events.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/events.adoc rename to docs/modules/ROOT/pages/servlet/authentication/events.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/index.adoc rename to docs/modules/ROOT/pages/servlet/authentication/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/jaas.adoc b/docs/modules/ROOT/pages/servlet/authentication/jaas.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/jaas.adoc rename to docs/modules/ROOT/pages/servlet/authentication/jaas.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/logout.adoc b/docs/modules/ROOT/pages/servlet/authentication/logout.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/logout.adoc rename to docs/modules/ROOT/pages/servlet/authentication/logout.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/openid.adoc b/docs/modules/ROOT/pages/servlet/authentication/openid.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/openid.adoc rename to docs/modules/ROOT/pages/servlet/authentication/openid.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/preauth.adoc b/docs/modules/ROOT/pages/servlet/authentication/preauth.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/preauth.adoc rename to docs/modules/ROOT/pages/servlet/authentication/preauth.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/rememberme.adoc b/docs/modules/ROOT/pages/servlet/authentication/rememberme.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/rememberme.adoc rename to docs/modules/ROOT/pages/servlet/authentication/rememberme.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/runas.adoc b/docs/modules/ROOT/pages/servlet/authentication/runas.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/runas.adoc rename to docs/modules/ROOT/pages/servlet/authentication/runas.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/session-management.adoc b/docs/modules/ROOT/pages/servlet/authentication/session-management.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/session-management.adoc rename to docs/modules/ROOT/pages/servlet/authentication/session-management.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/basic.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/basic.adoc rename to docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/dao-authentication-provider.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/dao-authentication-provider.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/dao-authentication-provider.adoc rename to docs/modules/ROOT/pages/servlet/authentication/unpwd/dao-authentication-provider.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/digest.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/digest.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/digest.adoc rename to docs/modules/ROOT/pages/servlet/authentication/unpwd/digest.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/form.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/form.adoc rename to docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/in-memory.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/in-memory.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/in-memory.adoc rename to docs/modules/ROOT/pages/servlet/authentication/unpwd/in-memory.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/index.adoc similarity index 69% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/index.adoc rename to docs/modules/ROOT/pages/servlet/authentication/unpwd/index.adoc index 415d828118..e59dedf3da 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/index.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/index.adoc @@ -7,7 +7,7 @@ One of the most common ways to authenticate a user is by validating a username a As such, Spring Security provides comprehensive support for authenticating with a username and password. [[servlet-authentication-unpwd-input]] -*Reading the Username & Password* +== Reading the Username & Password Spring Security provides the following built in mechanisms for reading a username and password from the `HttpServletRequest`: @@ -16,7 +16,7 @@ Spring Security provides the following built in mechanisms for reading a usernam * <> [[servlet-authentication-unpwd-storage]] -*Storage Mechanisms* +== Storage Mechanisms Each of the supported mechanisms for reading a username and password can leverage any of the supported storage mechanisms: @@ -25,22 +25,3 @@ Each of the supported mechanisms for reading a username and password can leverag * Custom data stores with <> * LDAP storage with <> -include::form.adoc[leveloffset=+1] - -include::basic.adoc[leveloffset=+1] - -include::digest.adoc[leveloffset=+1] - -include::in-memory.adoc[leveloffset=+1] - -include::jdbc.adoc[leveloffset=+1] - -include::user-details.adoc[leveloffset=+1] - -include::user-details-service.adoc[leveloffset=+1] - -include::password-encoder.adoc[leveloffset=+1] - -include::dao-authentication-provider.adoc[leveloffset=+1] - -include::ldap.adoc[leveloffset=+1] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/jdbc.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/jdbc.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/jdbc.adoc rename to docs/modules/ROOT/pages/servlet/authentication/unpwd/jdbc.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/ldap.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/ldap.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/ldap.adoc rename to docs/modules/ROOT/pages/servlet/authentication/unpwd/ldap.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/password-encoder.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/password-encoder.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/password-encoder.adoc rename to docs/modules/ROOT/pages/servlet/authentication/unpwd/password-encoder.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/user-details-service.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details-service.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/user-details-service.adoc rename to docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details-service.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/user-details.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/unpwd/user-details.adoc rename to docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authentication/x509.adoc b/docs/modules/ROOT/pages/servlet/authentication/x509.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authentication/x509.adoc rename to docs/modules/ROOT/pages/servlet/authentication/x509.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/acls.adoc b/docs/modules/ROOT/pages/servlet/authorization/acls.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authorization/acls.adoc rename to docs/modules/ROOT/pages/servlet/authorization/acls.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/architecture.adoc b/docs/modules/ROOT/pages/servlet/authorization/architecture.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authorization/architecture.adoc rename to docs/modules/ROOT/pages/servlet/authorization/architecture.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/authorize-requests.adoc b/docs/modules/ROOT/pages/servlet/authorization/authorize-requests.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authorization/authorize-requests.adoc rename to docs/modules/ROOT/pages/servlet/authorization/authorize-requests.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/expression-based.adoc b/docs/modules/ROOT/pages/servlet/authorization/expression-based.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authorization/expression-based.adoc rename to docs/modules/ROOT/pages/servlet/authorization/expression-based.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/index.adoc b/docs/modules/ROOT/pages/servlet/authorization/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authorization/index.adoc rename to docs/modules/ROOT/pages/servlet/authorization/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/method-security.adoc b/docs/modules/ROOT/pages/servlet/authorization/method-security.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authorization/method-security.adoc rename to docs/modules/ROOT/pages/servlet/authorization/method-security.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/authorization/secure-objects.adoc b/docs/modules/ROOT/pages/servlet/authorization/secure-objects.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/authorization/secure-objects.adoc rename to docs/modules/ROOT/pages/servlet/authorization/secure-objects.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/crypto/index.adoc b/docs/modules/ROOT/pages/servlet/crypto/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/crypto/index.adoc rename to docs/modules/ROOT/pages/servlet/crypto/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf.adoc b/docs/modules/ROOT/pages/servlet/exploits/csrf.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf.adoc rename to docs/modules/ROOT/pages/servlet/exploits/csrf.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/firewall.adoc b/docs/modules/ROOT/pages/servlet/exploits/firewall.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/exploits/firewall.adoc rename to docs/modules/ROOT/pages/servlet/exploits/firewall.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers.adoc b/docs/modules/ROOT/pages/servlet/exploits/headers.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/exploits/headers.adoc rename to docs/modules/ROOT/pages/servlet/exploits/headers.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/http.adoc b/docs/modules/ROOT/pages/servlet/exploits/http.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/exploits/http.adoc rename to docs/modules/ROOT/pages/servlet/exploits/http.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/index.adoc b/docs/modules/ROOT/pages/servlet/exploits/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/exploits/index.adoc rename to docs/modules/ROOT/pages/servlet/exploits/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/hello/index.adoc b/docs/modules/ROOT/pages/servlet/hello/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/hello/index.adoc rename to docs/modules/ROOT/pages/servlet/hello/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/index.adoc b/docs/modules/ROOT/pages/servlet/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/index.adoc rename to docs/modules/ROOT/pages/servlet/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/concurrency.adoc b/docs/modules/ROOT/pages/servlet/integrations/concurrency.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/integrations/concurrency.adoc rename to docs/modules/ROOT/pages/servlet/integrations/concurrency.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/cors.adoc b/docs/modules/ROOT/pages/servlet/integrations/cors.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/integrations/cors.adoc rename to docs/modules/ROOT/pages/servlet/integrations/cors.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/data.adoc b/docs/modules/ROOT/pages/servlet/integrations/data.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/integrations/data.adoc rename to docs/modules/ROOT/pages/servlet/integrations/data.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/index.adoc b/docs/modules/ROOT/pages/servlet/integrations/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/integrations/index.adoc rename to docs/modules/ROOT/pages/servlet/integrations/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/jackson.adoc b/docs/modules/ROOT/pages/servlet/integrations/jackson.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/integrations/jackson.adoc rename to docs/modules/ROOT/pages/servlet/integrations/jackson.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/jsp-taglibs.adoc b/docs/modules/ROOT/pages/servlet/integrations/jsp-taglibs.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/integrations/jsp-taglibs.adoc rename to docs/modules/ROOT/pages/servlet/integrations/jsp-taglibs.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/localization.adoc b/docs/modules/ROOT/pages/servlet/integrations/localization.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/integrations/localization.adoc rename to docs/modules/ROOT/pages/servlet/integrations/localization.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/mvc.adoc b/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/integrations/mvc.adoc rename to docs/modules/ROOT/pages/servlet/integrations/mvc.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/servlet-api.adoc b/docs/modules/ROOT/pages/servlet/integrations/servlet-api.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/integrations/servlet-api.adoc rename to docs/modules/ROOT/pages/servlet/integrations/servlet-api.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/integrations/websocket.adoc b/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/integrations/websocket.adoc rename to docs/modules/ROOT/pages/servlet/integrations/websocket.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/java-configuration/index.adoc b/docs/modules/ROOT/pages/servlet/java-configuration/index.adoc similarity index 98% rename from docs/manual/src/docs/asciidoc/_includes/servlet/java-configuration/index.adoc rename to docs/modules/ROOT/pages/servlet/java-configuration/index.adoc index 48205a64a6..e54df9bd7f 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/java-configuration/index.adoc +++ b/docs/modules/ROOT/pages/servlet/java-configuration/index.adoc @@ -65,7 +65,7 @@ This can be done in Java Configuration with https://docs.spring.io/spring/docs/3 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 +* xref:servlet/java-configuration/index.adoc#abstractsecuritywebapplicationinitializer-without-existing-spring[] - Use these instructions if you are not using Spring already * <> - Use these instructions if you are already using Spring === AbstractSecurityWebApplicationInitializer without Existing Spring diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/kotlin-configuration/index.adoc b/docs/modules/ROOT/pages/servlet/kotlin-configuration/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/kotlin-configuration/index.adoc rename to docs/modules/ROOT/pages/servlet/kotlin-configuration/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/namespace/index.adoc b/docs/modules/ROOT/pages/servlet/namespace/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/namespace/index.adoc rename to docs/modules/ROOT/pages/servlet/namespace/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/index.adoc b/docs/modules/ROOT/pages/servlet/oauth2/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/index.adoc rename to docs/modules/ROOT/pages/servlet/oauth2/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-client.adoc b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-client.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-client.adoc rename to docs/modules/ROOT/pages/servlet/oauth2/oauth2-client.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-login.adoc b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-login.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-login.adoc rename to docs/modules/ROOT/pages/servlet/oauth2/oauth2-login.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-resourceserver.adoc b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-resourceserver.adoc rename to docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/saml2/index.adoc b/docs/modules/ROOT/pages/servlet/saml2/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/saml2/index.adoc rename to docs/modules/ROOT/pages/servlet/saml2/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/saml2/saml2-login.adoc b/docs/modules/ROOT/pages/servlet/saml2/saml2-login.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/saml2/saml2-login.adoc rename to docs/modules/ROOT/pages/servlet/saml2/saml2-login.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/test/index.adoc b/docs/modules/ROOT/pages/servlet/test/index.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/test/index.adoc rename to docs/modules/ROOT/pages/servlet/test/index.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/test/method.adoc b/docs/modules/ROOT/pages/servlet/test/method.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/test/method.adoc rename to docs/modules/ROOT/pages/servlet/test/method.adoc diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/test/mockmvc.adoc b/docs/modules/ROOT/pages/servlet/test/mockmvc.adoc similarity index 100% rename from docs/manual/src/docs/asciidoc/_includes/servlet/test/mockmvc.adoc rename to docs/modules/ROOT/pages/servlet/test/mockmvc.adoc diff --git a/local-antora-playbook.yml b/local-antora-playbook.yml new file mode 100644 index 0000000000..cdcb52c474 --- /dev/null +++ b/local-antora-playbook.yml @@ -0,0 +1,15 @@ +site: + title: Spring Security + start_page: security::index.adoc +asciidoc: + attributes: + page-pagination: true +content: + sources: + - url: ./ + branches: [HEAD] + start_path: docs +ui: + bundle: + url: https://github.com/rwinch/antora-ui-spring/releases/download/latest/ui-bundle.zip + snapshot: true From 59e7a10732fe33c4f1b86e90eb2b9c7a61d16810 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Tue, 27 Jul 2021 13:02:21 -0500 Subject: [PATCH 02/34] Add dispatch --- .github/actions/dispatch.sh | 5 +++++ .github/workflows/build-reference.yml | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100755 .github/actions/dispatch.sh create mode 100644 .github/workflows/build-reference.yml diff --git a/.github/actions/dispatch.sh b/.github/actions/dispatch.sh new file mode 100755 index 0000000000..d6c2a37794 --- /dev/null +++ b/.github/actions/dispatch.sh @@ -0,0 +1,5 @@ +REPOSITORY_REF="$1" +TOKEN="$2" + +curl -H "Accept: application/vnd.github.everest-preview+json" -H "Authorization: token ${TOKEN}" --request POST --data '{"event_type": "request-build"}' https://api.github.com/repos/${REPOSITORY_REF}/dispatches +echo "Requested Build for $REPOSITORY_REF" \ No newline at end of file diff --git a/.github/workflows/build-reference.yml b/.github/workflows/build-reference.yml new file mode 100644 index 0000000000..3e3f714e06 --- /dev/null +++ b/.github/workflows/build-reference.yml @@ -0,0 +1,18 @@ +name: reference + +on: + push: + branches-ignore: + - 'gh-pages' + +env: + GH_TOKEN_DISPATCH: ${{ secrets.GH_TOKEN_DISPATCH }} + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout Source + uses: actions/checkout@v2 + - name: Dispatch Build Request + run: ${GITHUB_WORKSPACE}/.github/actions/dispatch.sh 'rwinch/spring-reference' "$GH_TOKEN_DISPATCH" \ No newline at end of file From c3dfb1711d70e59d3a4b17a2ee7bb13739749b93 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Thu, 29 Jul 2021 17:12:34 -0500 Subject: [PATCH 03/34] Remove includes --- docs/modules/ROOT/nav.adoc | 36 ++- .../features/authentication/index.adoc | 7 +- .../features/authentication/supported.adoc | 5 - .../overview/features/exploits/index.adoc | 8 +- .../ROOT/pages/reactive/exploits/index.adoc | 9 +- docs/modules/ROOT/pages/reactive/index.adoc | 23 +- .../ROOT/pages/reactive/oauth2/index.adoc | 8 +- .../ROOT/pages/servlet/appendix/index.adoc | 10 +- .../architecture/delegating-filter-proxy.adoc | 46 ---- .../exception-translation-filter.adoc | 50 ---- .../architecture/filter-chain-proxy.adoc | 10 - .../pages/servlet/architecture/filters.adoc | 45 ---- .../pages/servlet/architecture/index.adoc | 242 ++++++++++++++++- .../architecture/security-filter-chain.adoc | 43 --- .../architecture/security-filters.adoc | 42 --- ...ract-authentication-processing-filter.adoc | 41 --- .../authentication-entry-point.adoc | 14 - .../architecture/authentication-manager.adoc | 9 - .../architecture/authentication-provider.adoc | 6 - .../architecture/authentication.adoc | 18 -- .../architecture/granted-authority.adoc | 16 -- .../authentication/architecture/index.adoc | 245 +++++++++++++++++- .../architecture/provider-manager.adoc | 36 --- .../architecture/security-context-holder.adoc | 89 ------- .../architecture/security-context.adoc | 5 - .../pages/servlet/authentication/index.adoc | 52 +--- .../pages/servlet/authorization/index.adoc | 17 +- .../ROOT/pages/servlet/exploits/index.adoc | 11 +- docs/modules/ROOT/pages/servlet/index.adoc | 29 +-- .../pages/servlet/integrations/index.adoc | 27 +- .../ROOT/pages/servlet/oauth2/index.adoc | 9 +- .../pages/servlet/oauth2/oauth2-client.adoc | 88 +++---- .../pages/servlet/oauth2/oauth2-login.adoc | 50 ++-- .../servlet/oauth2/oauth2-resourceserver.adoc | 118 ++++----- .../ROOT/pages/servlet/test/index.adoc | 8 +- 35 files changed, 690 insertions(+), 782 deletions(-) delete mode 100644 docs/modules/ROOT/pages/overview/features/authentication/supported.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/architecture/delegating-filter-proxy.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/architecture/exception-translation-filter.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/architecture/filter-chain-proxy.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/architecture/filters.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/architecture/security-filter-chain.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/architecture/security-filters.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/authentication/architecture/abstract-authentication-processing-filter.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/authentication/architecture/authentication-entry-point.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/authentication/architecture/authentication-manager.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/authentication/architecture/authentication-provider.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/authentication/architecture/authentication.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/authentication/architecture/granted-authority.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/authentication/architecture/provider-manager.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/authentication/architecture/security-context-holder.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/authentication/architecture/security-context.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 9b378b7b6f..7e28eb55af 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -5,10 +5,14 @@ ** xref:overview/getting-spring-security.adoc[Getting Spring Security] ** xref:overview/features/index.adoc[Features] *** xref:overview/features/authentication/index.adoc[Authentication] +**** xref:overview/features/authentication/password-storage.adoc[Password Storage] *** xref:overview/features/exploits/index.adoc[Protection Against Exploits] +**** xref:overview/features/exploits/csrf.adoc[CSRF] +**** xref:overview/features/exploits/headers.adoc[HTTP Headers] +**** xref:overview/features/exploits/http.adoc[HTTP Requests] ** xref:overview/modules.adoc[Project Modules & Dependencies] ** xref:overview/samples.adoc[Samples] -* Servlet Applications +* xref:servlet/index.adoc[Servlet Applications] ** xref:servlet/hello/index.adoc[Hello Spring Security] ** xref:servlet/architecture/index.adoc[The Big Picture] ** Authentication @@ -37,31 +41,55 @@ *** xref:servlet/authentication/runas.adoc[Run-As] *** xref:servlet/authentication/logout.adoc[Logout] *** xref:servlet/authentication/events.adoc[Authentication Events] -** Authorization +** xref:servlet/authorization/index.adoc[Authorization] *** xref:servlet/authorization/architecture.adoc[Authorization Architecture] *** xref:servlet/authorization/authorize-requests.adoc[Authorize HTTP Requests] *** xref:servlet/authorization/expression-based.adoc[Expression-Based Access Control] *** xref:servlet/authorization/secure-objects.adoc[Secure Object Implementations] *** xref:servlet/authorization/method-security.adoc[Method Security] *** xref:servlet/authorization/acls.adoc[Domain Object Security ACLs] -** OAuth2 +** xref:servlet/oauth2/index.adoc[OAuth2] *** xref:servlet/oauth2/oauth2-login.adoc[OAuth2 Log In] *** xref:servlet/oauth2/oauth2-client.adoc[OAuth2 Client] *** xref:servlet/oauth2/oauth2-resourceserver.adoc[OAuth2 Resource Server] ** xref:servlet/saml2/index.adoc[SAML2] ** xref:servlet/exploits/index.adoc[Protection Against Exploits] +*** xref:servlet/exploits/csrf.adoc[] +*** xref:servlet/exploits/headers.adoc[] +*** xref:servlet/exploits/http.adoc[] +*** xref:servlet/exploits/firewall.adoc[ ** xref:servlet/integrations/index.adoc[Integrations] +*** xref:servlet/integrations/servlet-api.adoc[Servlet APIs] +*** xref:servlet/integrations/data.adoc[Spring Data] +*** xref:servlet/integrations/concurrency.adoc[Java's Concurrency APIs] +*** xref:servlet/integrations/jackson.adoc[Jackson] +*** xref:servlet/integrations/localization.adoc[Localization] +*** xref:servlet/integrations/mvc.adoc[Spring MVC] +*** xref:servlet/integrations/websocket.adoc[WebSocket] +*** xref:servlet/integrations/cors.adoc[Spring's CORS Support] +*** xref:servlet/integrations/jsp-taglibs.adoc[JSP Taglib] ** Configuration *** xref:servlet/java-configuration/index.adoc[Java Configuration] *** xref:servlet/kotlin-configuration/index.adoc[Kotlin Configuration] *** xref:servlet/namespace/index.adoc[Namespace Configuration] ** xref:servlet/test/index.adoc[Testing] +** xref:servlet/test/method.adoc[Method Security] +** xref:servlet/test/mockmvc.adoc[MockMvc Support] ** xref:servlet/crypto/index.adoc[Cryptography] ** xref:servlet/appendix/index.adoc[Appendix] -* Reactive Applications +*** xref:servlet/appendix/database-schema.adoc[Database Schemas] +*** xref:servlet/appendix/namespace.adoc[XML Namespace] +*** xref:servlet/appendix/faq.adoc[FAQ] +* xref:reactive/index.adoc[Reactive Applications] ** xref:reactive/webflux.adoc[WebFlux Security] ** xref:reactive/exploits/index.adoc[Protection Against Exploits] +*** xref:reactive/exploits/csrf.adoc[CSRF] +*** xref:reactive/exploits/headers.adoc[Headers] +*** xref:reactive/exploits/http.adoc[HTTP Requests] ** xref:reactive/oauth2/index.adoc[OAuth2] +*** xref:reactive/oauth2/login.adoc[OAuth 2.0 Login] +*** xref:reactive/oauth2/access-token.adoc[OAuth2 Client] +*** xref:reactive/oauth2/resource-server.adoc[OAuth 2.0 Resource Server] ** xref:reactive/registered-oauth2-authorized-client.adoc[@RegisteredOAuth2AuthorizedClient] ** xref:reactive/x509.adoc[X.509 Authentication] ** xref:reactive/logout.adoc[Logout] diff --git a/docs/modules/ROOT/pages/overview/features/authentication/index.adoc b/docs/modules/ROOT/pages/overview/features/authentication/index.adoc index 2d7cf93798..2dbe3ae377 100644 --- a/docs/modules/ROOT/pages/overview/features/authentication/index.adoc +++ b/docs/modules/ROOT/pages/overview/features/authentication/index.adoc @@ -5,8 +5,9 @@ Spring Security provides comprehensive support for https://en.wikipedia.org/wiki Authentication is how we verify the identity of who is trying to access a particular resource. A common way to authenticate users is by requiring the user to enter a username and password. Once authentication is performed we know the identity and can perform authorization. -// FIXME: Link authorization to authorization -include::supported.adoc[leveloffset=+1] +== Authentication Support -include::password-storage.adoc[leveloffset=+1] +Spring Security provides built in support for authenticating users. +This section is dedicated to generic authentication support that applies in both Servlet and WebFlux environments. +Refer to the sections on authentication for <> and WebFlux for details on what is supported for each stack. diff --git a/docs/modules/ROOT/pages/overview/features/authentication/supported.adoc b/docs/modules/ROOT/pages/overview/features/authentication/supported.adoc deleted file mode 100644 index 85bd943456..0000000000 --- a/docs/modules/ROOT/pages/overview/features/authentication/supported.adoc +++ /dev/null @@ -1,5 +0,0 @@ -[[authentication-support]] -= Authentication Support - -Spring Security provides built in support for authenticating users. -Refer to the sections on authentication for <> and WebFlux for details on what is supported for each stack. diff --git a/docs/modules/ROOT/pages/overview/features/exploits/index.adoc b/docs/modules/ROOT/pages/overview/features/exploits/index.adoc index bb8abc73dc..b0e34907b8 100644 --- a/docs/modules/ROOT/pages/overview/features/exploits/index.adoc +++ b/docs/modules/ROOT/pages/overview/features/exploits/index.adoc @@ -5,8 +5,6 @@ Spring Security provides protection against common exploits. Whenever possible, the protection is enabled by default. Below you will find high level description of the various exploits that Spring Security protects against. -include::csrf.adoc[leveloffset=+1] - -include::headers.adoc[leveloffset=+1] - -include::http.adoc[leveloffset=+1] +* xref:overview/features/exploits/csrf.adoc[CSRF] +* xref:overview/features/exploits/headers.adoc[HTTP Headers] +* xref:overview/features/exploits/http.adoc[HTTP Requests] diff --git a/docs/modules/ROOT/pages/reactive/exploits/index.adoc b/docs/modules/ROOT/pages/reactive/exploits/index.adoc index 6d5c8c66a8..200d037f57 100644 --- a/docs/modules/ROOT/pages/reactive/exploits/index.adoc +++ b/docs/modules/ROOT/pages/reactive/exploits/index.adoc @@ -1,7 +1,8 @@ = Protection Against Exploits -include::csrf.adoc[leveloffset=+1] +Spring Security provides protection against numerous exploits. +This section discusses WebFlux specific support for: -include::headers.adoc[leveloffset=+1] - -include::http.adoc[leveloffset=+1] +* xref:reactive/exploits/csrf.adoc[CSRF] +* xref:reactive/exploits/headers.adoc[Headers] +* xref:reactive/exploits/http.adoc[HTTP Requests] diff --git a/docs/modules/ROOT/pages/reactive/index.adoc b/docs/modules/ROOT/pages/reactive/index.adoc index 497e239786..2e9d23500d 100644 --- a/docs/modules/ROOT/pages/reactive/index.adoc +++ b/docs/modules/ROOT/pages/reactive/index.adoc @@ -1,23 +1,4 @@ = Reactive Applications -include::webflux.adoc[leveloffset=+1] - -include::exploits/index.adoc[leveloffset=+1] - -include::oauth2/index.adoc[leveloffset=+1] - -include::registered-oauth2-authorized-client.adoc[leveloffset=+1] - -include::x509.adoc[leveloffset=+1] - -include::logout.adoc[leveloffset=+1] - -include::webclient.adoc[leveloffset=+1] - -include::method.adoc[leveloffset=+1] - -include::cors.adoc[leveloffset=+1] - -include::test.adoc[leveloffset=+1] - -include::rsocket.adoc[leveloffset=+1] +Reactive applications work very differently than <>. +This section discusses how Spring Security works with reactive applications which are typically written using Spring's WebFlux. diff --git a/docs/modules/ROOT/pages/reactive/oauth2/index.adoc b/docs/modules/ROOT/pages/reactive/oauth2/index.adoc index c58f3fb3ab..560181008c 100644 --- a/docs/modules/ROOT/pages/reactive/oauth2/index.adoc +++ b/docs/modules/ROOT/pages/reactive/oauth2/index.adoc @@ -3,8 +3,6 @@ Spring Security provides OAuth2 and WebFlux integration for reactive applications. -include::login.adoc[leveloffset=+1] - -include::access-token.adoc[leveloffset=+1] - -include::resource-server.adoc[leveloffset=+1] +* xref:reactive/oauth2/login.adoc[OAuth 2.0 Login] +* xref:reactive/oauth2/access-token.adoc[OAuth2 Client] +* xref:reactive/oauth2/resource-server.adoc[OAuth 2.0 Resource Server] diff --git a/docs/modules/ROOT/pages/servlet/appendix/index.adoc b/docs/modules/ROOT/pages/servlet/appendix/index.adoc index 5f01cc38d0..3fecd174b4 100644 --- a/docs/modules/ROOT/pages/servlet/appendix/index.adoc +++ b/docs/modules/ROOT/pages/servlet/appendix/index.adoc @@ -1,8 +1,8 @@ - = Appendix -include::database-schema.adoc[leveloffset=+1] +This is an appendix for Servlet based Spring Security. +It has the following sections: -include::namespace.adoc[] - -include::faq.adoc[] +* xref:servlet/appendix/database-schema.adoc[Database Schemas] +* xref:servlet/appendix/namespace.adoc[XML Namespace] +* xref:servlet/appendix/faq.adoc[FAQ] diff --git a/docs/modules/ROOT/pages/servlet/architecture/delegating-filter-proxy.adoc b/docs/modules/ROOT/pages/servlet/architecture/delegating-filter-proxy.adoc deleted file mode 100644 index 1ccac8c584..0000000000 --- a/docs/modules/ROOT/pages/servlet/architecture/delegating-filter-proxy.adoc +++ /dev/null @@ -1,46 +0,0 @@ -[[servlet-delegatingfilterproxy]] -= DelegatingFilterProxy - -Spring provides a `Filter` implementation named {spring-framework-api-url}org/springframework/web/filter/DelegatingFilterProxy.html[`DelegatingFilterProxy`] that allows bridging between the Servlet container's lifecycle and Spring's `ApplicationContext`. -The Servlet container allows registering ``Filter``s using its own standards, but it is not aware of Spring defined Beans. -`DelegatingFilterProxy` can be registered via standard Servlet container mechanisms, but delegate all the work to a Spring Bean that implements `Filter`. - -Here is a picture of how `DelegatingFilterProxy` fits into the <>. - -.DelegatingFilterProxy -[[servlet-delegatingfilterproxy-figure]] -image::{figures}/delegatingfilterproxy.png[] - -`DelegatingFilterProxy` looks up __Bean Filter~0~__ from the `ApplicationContext` and then invokes __Bean Filter~0~__. -The pseudo code of `DelegatingFilterProxy` can be seen below. - -.`DelegatingFilterProxy` Pseudo Code -==== -.Java -[source,java,role="primary",subs="+quotes,+macros"] ----- -public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { - // Lazily get Filter that was registered as a Spring Bean - // For the example in <> `delegate` is an instance of __Bean Filter~0~__ - Filter delegate = getFilterBean(someBeanName); - // delegate work to the Spring Bean - delegate.doFilter(request, response); -} ----- - -.Kotlin -[source,kotlin,role="secondary",subs="+quotes,+macros"] ----- -fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { - // Lazily get Filter that was registered as a Spring Bean - // For the example in <> `delegate` is an instance of __Bean Filter~0~__ - val delegate: Filter = getFilterBean(someBeanName) - // delegate work to the Spring Bean - delegate.doFilter(request, response) -} ----- -==== - -Another benefit of `DelegatingFilterProxy` is that it allows delaying looking `Filter` bean instances up. -This is important because the container needs to register the `Filter` instances before the container can startup. -However, Spring typically uses a `ContextLoaderListener` to load the Spring Beans which will not be done until after the `Filter` instances need to be registered. diff --git a/docs/modules/ROOT/pages/servlet/architecture/exception-translation-filter.adoc b/docs/modules/ROOT/pages/servlet/architecture/exception-translation-filter.adoc deleted file mode 100644 index 941c1fb122..0000000000 --- a/docs/modules/ROOT/pages/servlet/architecture/exception-translation-filter.adoc +++ /dev/null @@ -1,50 +0,0 @@ -[[servlet-exceptiontranslationfilter]] -= Handling Security Exceptions -:figures: images/servlet/architecture -:icondir: images/icons - - -The {security-api-url}org/springframework/security/web/access/ExceptionTranslationFilter.html[`ExceptionTranslationFilter`] allows translation of {security-api-url}org/springframework/security/access/AccessDeniedException.html[`AccessDeniedException`] and {security-api-url}/org/springframework/security/core/AuthenticationException.html[`AuthenticationException`] into HTTP responses. - -`ExceptionTranslationFilter` is inserted into the <> as one of the <>. - -image::{figures}/exceptiontranslationfilter.png[] - - -* image:{icondir}/number_1.png[] First, the `ExceptionTranslationFilter` invokes `FilterChain.doFilter(request, response)` to invoke the rest of the application. -* image:{icondir}/number_2.png[] If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__. -** The <> is cleared out. -** The `HttpServletRequest` is saved in the {security-api-url}org/springframework/security/web/savedrequest/RequestCache.html[`RequestCache`]. -When the user successfully authenticates, the `RequestCache` is used to replay the original request. -// FIXME: add link to authentication success -** The `AuthenticationEntryPoint` is used to request credentials from the client. -For example, it might redirect to a log in page or send a `WWW-Authenticate` header. -// FIXME: link to AuthenticationEntryPoint -* image:{icondir}/number_3.png[] Otherwise if it is an `AccessDeniedException`, then __Access Denied__. -The `AccessDeniedHandler` is invoked to handle access denied. -// FIXME: link to AccessDeniedHandler - -[NOTE] -==== -If the application does not throw an `AccessDeniedException` or an `AuthenticationException`, then `ExceptionTranslationFilter` does not do anything. -==== - -The pseudocode for `ExceptionTranslationFilter` looks something like this: - -.ExceptionTranslationFilter pseudocode -[source,java] ----- -try { - filterChain.doFilter(request, response); // <1> -} catch (AccessDeniedException | AuthenticationException ex) { - if (!authenticated || ex instanceof AuthenticationException) { - startAuthentication(); // <2> - } else { - accessDenied(); // <3> - } -} ----- -<1> You will recall from <> that invoking `FilterChain.doFilter(request, response)` is the equivalent of invoking the rest of the application. -This means that if another part of the application, (i.e. <> or method security) throws an `AuthenticationException` or `AccessDeniedException` it will be caught and handled here. -<2> If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__. -<3> Otherwise, __Access Denied__ diff --git a/docs/modules/ROOT/pages/servlet/architecture/filter-chain-proxy.adoc b/docs/modules/ROOT/pages/servlet/architecture/filter-chain-proxy.adoc deleted file mode 100644 index 0b15efeec9..0000000000 --- a/docs/modules/ROOT/pages/servlet/architecture/filter-chain-proxy.adoc +++ /dev/null @@ -1,10 +0,0 @@ -[[servlet-filterchainproxy]] -= FilterChainProxy - -Spring Security's Servlet support is contained within `FilterChainProxy`. -`FilterChainProxy` is a special `Filter` provided by Spring Security that allows delegating to many `Filter` instances through <>. -Since `FilterChainProxy` is a Bean, it is typically wrapped in a <>. - -.FilterChainProxy -[[servlet-filterchainproxy-figure]] -image::{figures}/filterchainproxy.png[] diff --git a/docs/modules/ROOT/pages/servlet/architecture/filters.adoc b/docs/modules/ROOT/pages/servlet/architecture/filters.adoc deleted file mode 100644 index 37509ac256..0000000000 --- a/docs/modules/ROOT/pages/servlet/architecture/filters.adoc +++ /dev/null @@ -1,45 +0,0 @@ -[[servlet-filters-review]] -= A Review of ``Filter``s - -Spring Security's Servlet support is based on Servlet ``Filter``s, so it is helpful to look at the role of ``Filter``s generally first. -The picture below shows the typical layering of the handlers for a single HTTP request. - -.FilterChain -[[servlet-filterchain-figure]] -image::{figures}/filterchain.png[] - -The client sends a request to the application, and the container creates a `FilterChain` which contains the ``Filter``s and `Servlet` that should process the `HttpServletRequest` based on the path of the request URI. -In a Spring MVC application the `Servlet` is an instance of {spring-framework-reference-url}web.html#mvc-servlet[`DispatcherServlet`]. -At most one `Servlet` can handle a single `HttpServletRequest` and `HttpServletResponse`. -However, more than one `Filter` can be used to: - -* Prevent downstream ``Filter``s or the `Servlet` from being invoked. -In this instance the `Filter` will typically write the `HttpServletResponse`. -* Modify the `HttpServletRequest` or `HttpServletResponse` used by the downstream ``Filter``s and `Servlet` - -The power of the `Filter` comes from the `FilterChain` that is passed into it. - -.`FilterChain` Usage Example -==== -.Java -[source,java,role="primary"] ----- -public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { - // do something before the rest of the application - chain.doFilter(request, response); // invoke the rest of the application - // do something after the rest of the application -} ----- - -.Kotlin -[source,kotlin,role="secondary"] ----- -fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { - // do something before the rest of the application - chain.doFilter(request, response) // invoke the rest of the application - // do something after the rest of the application -} ----- -==== - -Since a `Filter` only impacts downstream ``Filter``s and the `Servlet`, the order each `Filter` is invoked is extremely important. diff --git a/docs/modules/ROOT/pages/servlet/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/architecture/index.adoc index 38b639f9b2..ec5b4bc4d8 100644 --- a/docs/modules/ROOT/pages/servlet/architecture/index.adoc +++ b/docs/modules/ROOT/pages/servlet/architecture/index.adoc @@ -1,19 +1,249 @@ [[servlet-architecture]] = Servlet Security: The Big Picture :figures: images/servlet/architecture +:icondir: images/icons This section discusses Spring Security's high level architecture within Servlet based applications. We build on this high level understanding within <>, <>, <> sections of the reference. // FIXME: Add links to other sections of architecture -include::filters.adoc[leveloffset=+1] +[[servlet-filters-review]] +== A Review of ``Filter``s -include::delegating-filter-proxy.adoc[leveloffset=+1] +Spring Security's Servlet support is based on Servlet ``Filter``s, so it is helpful to look at the role of ``Filter``s generally first. +The picture below shows the typical layering of the handlers for a single HTTP request. -include::filter-chain-proxy.adoc[leveloffset=+1] +.FilterChain +[[servlet-filterchain-figure]] +image::{figures}/filterchain.png[] -include::security-filter-chain.adoc[leveloffset=+1] +The client sends a request to the application, and the container creates a `FilterChain` which contains the ``Filter``s and `Servlet` that should process the `HttpServletRequest` based on the path of the request URI. +In a Spring MVC application the `Servlet` is an instance of {spring-framework-reference-url}web.html#mvc-servlet[`DispatcherServlet`]. +At most one `Servlet` can handle a single `HttpServletRequest` and `HttpServletResponse`. +However, more than one `Filter` can be used to: -include::security-filters.adoc[leveloffset=+1] +* Prevent downstream ``Filter``s or the `Servlet` from being invoked. +In this instance the `Filter` will typically write the `HttpServletResponse`. +* Modify the `HttpServletRequest` or `HttpServletResponse` used by the downstream ``Filter``s and `Servlet` -include::exception-translation-filter.adoc[leveloffset=+1] +The power of the `Filter` comes from the `FilterChain` that is passed into it. + +.`FilterChain` Usage Example +==== +.Java +[source,java,role="primary"] +---- +public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { + // do something before the rest of the application + chain.doFilter(request, response); // invoke the rest of the application + // do something after the rest of the application +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { + // do something before the rest of the application + chain.doFilter(request, response) // invoke the rest of the application + // do something after the rest of the application +} +---- +==== + +Since a `Filter` only impacts downstream ``Filter``s and the `Servlet`, the order each `Filter` is invoked is extremely important. + + +[[servlet-delegatingfilterproxy]] +== DelegatingFilterProxy + +Spring provides a `Filter` implementation named {spring-framework-api-url}org/springframework/web/filter/DelegatingFilterProxy.html[`DelegatingFilterProxy`] that allows bridging between the Servlet container's lifecycle and Spring's `ApplicationContext`. +The Servlet container allows registering ``Filter``s using its own standards, but it is not aware of Spring defined Beans. +`DelegatingFilterProxy` can be registered via standard Servlet container mechanisms, but delegate all the work to a Spring Bean that implements `Filter`. + +Here is a picture of how `DelegatingFilterProxy` fits into the <>. + +.DelegatingFilterProxy +[[servlet-delegatingfilterproxy-figure]] +image::{figures}/delegatingfilterproxy.png[] + +`DelegatingFilterProxy` looks up __Bean Filter~0~__ from the `ApplicationContext` and then invokes __Bean Filter~0~__. +The pseudo code of `DelegatingFilterProxy` can be seen below. + +.`DelegatingFilterProxy` Pseudo Code +==== +.Java +[source,java,role="primary",subs="+quotes,+macros"] +---- +public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { + // Lazily get Filter that was registered as a Spring Bean + // For the example in <> `delegate` is an instance of __Bean Filter~0~__ + Filter delegate = getFilterBean(someBeanName); + // delegate work to the Spring Bean + delegate.doFilter(request, response); +} +---- + +.Kotlin +[source,kotlin,role="secondary",subs="+quotes,+macros"] +---- +fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { + // Lazily get Filter that was registered as a Spring Bean + // For the example in <> `delegate` is an instance of __Bean Filter~0~__ + val delegate: Filter = getFilterBean(someBeanName) + // delegate work to the Spring Bean + delegate.doFilter(request, response) +} +---- +==== + +Another benefit of `DelegatingFilterProxy` is that it allows delaying looking `Filter` bean instances up. +This is important because the container needs to register the `Filter` instances before the container can startup. +However, Spring typically uses a `ContextLoaderListener` to load the Spring Beans which will not be done until after the `Filter` instances need to be registered. + +[[servlet-filterchainproxy]] +== FilterChainProxy + +Spring Security's Servlet support is contained within `FilterChainProxy`. +`FilterChainProxy` is a special `Filter` provided by Spring Security that allows delegating to many `Filter` instances through <>. +Since `FilterChainProxy` is a Bean, it is typically wrapped in a <>. + +.FilterChainProxy +[[servlet-filterchainproxy-figure]] +image::{figures}/filterchainproxy.png[] + +[[servlet-securityfilterchain]] +== SecurityFilterChain + +{security-api-url}org/springframework/security/web/SecurityFilterChain.html[`SecurityFilterChain`] is used by <> to determine which Spring Security ``Filter``s should be invoked for this request. + +.SecurityFilterChain +[[servlet-securityfilterchain-figure]] +image::{figures}/securityfilterchain.png[] + +The <> in `SecurityFilterChain` are typically Beans, but they are registered with `FilterChainProxy` instead of <>. +`FilterChainProxy` provides a number of advantages to registering directly with the Servlet container or <>. +First, it provides a starting point for all of Spring Security's Servlet support. +For that reason, if you are attempting to troubleshoot Spring Security's Servlet support, adding a debug point in `FilterChainProxy` is a great place to start. + +Second, since `FilterChainProxy` is central to Spring Security usage it can perform tasks that are not viewed as optional. +// FIXME: Add a link to SecurityContext +For example, it clears out the `SecurityContext` to avoid memory leaks. +It also applies Spring Security's <> to protect applications against certain types of attacks. + +In addition, it provides more flexibility in determining when a `SecurityFilterChain` should be invoked. +In a Servlet container, ``Filter``s are invoked based upon the URL alone. +// FIXME: Link to RequestMatcher +However, `FilterChainProxy` can determine invocation based upon anything in the `HttpServletRequest` by leveraging the `RequestMatcher` interface. + +In fact, `FilterChainProxy` can be used to determine which `SecurityFilterChain` should be used. +This allows providing a totally separate configuration for different _slices_ of your application. + +.Multiple SecurityFilterChain +[[servlet-multi-securityfilterchain-figure]] +image::{figures}/multi-securityfilterchain.png[] + +In the <> Figure `FilterChainProxy` decides which `SecurityFilterChain` should be used. +Only the first `SecurityFilterChain` that matches will be invoked. +If a URL of `/api/messages/` is requested, it will first match on ``SecurityFilterChain~0~``'s pattern of `+/api/**+`, so only `SecurityFilterChain~0~` will be invoked even though it also matches on ``SecurityFilterChain~n~``. +If a URL of `/messages/` is requested, it will not match on ``SecurityFilterChain~0~``'s pattern of `+/api/**+`, so `FilterChainProxy` will continue trying each `SecurityFilterChain`. +Assuming that no other, `SecurityFilterChain` instances match `SecurityFilterChain~n~` will be invoked. +// FIXME add link to pattern matching + +Notice that `SecurityFilterChain~0~` has only three security ``Filter``s instances configured. +However, `SecurityFilterChain~n~` has four security ``Filter``s configured. +It is important to note that each `SecurityFilterChain` can be unique and configured in isolation. +In fact, a `SecurityFilterChain` might have zero security ``Filter``s if the application wants Spring Security to ignore certain requests. +// FIXME: add link to configuring multiple `SecurityFilterChain` instances + +[[servlet-security-filters]] +== Security Filters + +The Security Filters are inserted into the <> with the <> API. +The <>s matters. +It is typically not necessary to know the ordering of Spring Security's ``Filter``s. +However, there are times that it is beneficial to know the ordering + +Below is a comprehensive list of Spring Security Filter ordering: + +* ChannelProcessingFilter +* WebAsyncManagerIntegrationFilter +* SecurityContextPersistenceFilter +* HeaderWriterFilter +* CorsFilter +* CsrfFilter +* LogoutFilter +* OAuth2AuthorizationRequestRedirectFilter +* Saml2WebSsoAuthenticationRequestFilter +* X509AuthenticationFilter +* AbstractPreAuthenticatedProcessingFilter +* CasAuthenticationFilter +* OAuth2LoginAuthenticationFilter +* Saml2WebSsoAuthenticationFilter +* <> +* OpenIDAuthenticationFilter +* DefaultLoginPageGeneratingFilter +* DefaultLogoutPageGeneratingFilter +* ConcurrentSessionFilter +* <> +* BearerTokenAuthenticationFilter +* <> +* RequestCacheAwareFilter +* SecurityContextHolderAwareRequestFilter +* JaasApiIntegrationFilter +* RememberMeAuthenticationFilter +* AnonymousAuthenticationFilter +* OAuth2AuthorizationCodeGrantFilter +* SessionManagementFilter +* <> +* <> +* SwitchUserFilter + +[[servlet-exceptiontranslationfilter]] +== Handling Security Exceptions + + +The {security-api-url}org/springframework/security/web/access/ExceptionTranslationFilter.html[`ExceptionTranslationFilter`] allows translation of {security-api-url}org/springframework/security/access/AccessDeniedException.html[`AccessDeniedException`] and {security-api-url}/org/springframework/security/core/AuthenticationException.html[`AuthenticationException`] into HTTP responses. + +`ExceptionTranslationFilter` is inserted into the <> as one of the <>. + +image::{figures}/exceptiontranslationfilter.png[] + + +* image:{icondir}/number_1.png[] First, the `ExceptionTranslationFilter` invokes `FilterChain.doFilter(request, response)` to invoke the rest of the application. +* image:{icondir}/number_2.png[] If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__. +** The <> is cleared out. +** The `HttpServletRequest` is saved in the {security-api-url}org/springframework/security/web/savedrequest/RequestCache.html[`RequestCache`]. +When the user successfully authenticates, the `RequestCache` is used to replay the original request. +// FIXME: add link to authentication success +** The `AuthenticationEntryPoint` is used to request credentials from the client. +For example, it might redirect to a log in page or send a `WWW-Authenticate` header. +// FIXME: link to AuthenticationEntryPoint +* image:{icondir}/number_3.png[] Otherwise if it is an `AccessDeniedException`, then __Access Denied__. +The `AccessDeniedHandler` is invoked to handle access denied. +// FIXME: link to AccessDeniedHandler + +[NOTE] +==== +If the application does not throw an `AccessDeniedException` or an `AuthenticationException`, then `ExceptionTranslationFilter` does not do anything. +==== + +The pseudocode for `ExceptionTranslationFilter` looks something like this: + +.ExceptionTranslationFilter pseudocode +[source,java] +---- +try { + filterChain.doFilter(request, response); // <1> +} catch (AccessDeniedException | AuthenticationException ex) { + if (!authenticated || ex instanceof AuthenticationException) { + startAuthentication(); // <2> + } else { + accessDenied(); // <3> + } +} +---- +<1> You will recall from <> that invoking `FilterChain.doFilter(request, response)` is the equivalent of invoking the rest of the application. +This means that if another part of the application, (i.e. <> or method security) throws an `AuthenticationException` or `AccessDeniedException` it will be caught and handled here. +<2> If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__. +<3> Otherwise, __Access Denied__ diff --git a/docs/modules/ROOT/pages/servlet/architecture/security-filter-chain.adoc b/docs/modules/ROOT/pages/servlet/architecture/security-filter-chain.adoc deleted file mode 100644 index 508e9e3d21..0000000000 --- a/docs/modules/ROOT/pages/servlet/architecture/security-filter-chain.adoc +++ /dev/null @@ -1,43 +0,0 @@ -[[servlet-securityfilterchain]] -= SecurityFilterChain - -{security-api-url}org/springframework/security/web/SecurityFilterChain.html[`SecurityFilterChain`] is used by <> to determine which Spring Security ``Filter``s should be invoked for this request. - -.SecurityFilterChain -[[servlet-securityfilterchain-figure]] -image::{figures}/securityfilterchain.png[] - -The <> in `SecurityFilterChain` are typically Beans, but they are registered with `FilterChainProxy` instead of <>. -`FilterChainProxy` provides a number of advantages to registering directly with the Servlet container or <>. -First, it provides a starting point for all of Spring Security's Servlet support. -For that reason, if you are attempting to troubleshoot Spring Security's Servlet support, adding a debug point in `FilterChainProxy` is a great place to start. - -Second, since `FilterChainProxy` is central to Spring Security usage it can perform tasks that are not viewed as optional. -// FIXME: Add a link to SecurityContext -For example, it clears out the `SecurityContext` to avoid memory leaks. -It also applies Spring Security's <> to protect applications against certain types of attacks. - -In addition, it provides more flexibility in determining when a `SecurityFilterChain` should be invoked. -In a Servlet container, ``Filter``s are invoked based upon the URL alone. -// FIXME: Link to RequestMatcher -However, `FilterChainProxy` can determine invocation based upon anything in the `HttpServletRequest` by leveraging the `RequestMatcher` interface. - -In fact, `FilterChainProxy` can be used to determine which `SecurityFilterChain` should be used. -This allows providing a totally separate configuration for different _slices_ of your application. - -.Multiple SecurityFilterChain -[[servlet-multi-securityfilterchain-figure]] -image::{figures}/multi-securityfilterchain.png[] - -In the <> Figure `FilterChainProxy` decides which `SecurityFilterChain` should be used. -Only the first `SecurityFilterChain` that matches will be invoked. -If a URL of `/api/messages/` is requested, it will first match on ``SecurityFilterChain~0~``'s pattern of `+/api/**+`, so only `SecurityFilterChain~0~` will be invoked even though it also matches on ``SecurityFilterChain~n~``. -If a URL of `/messages/` is requested, it will not match on ``SecurityFilterChain~0~``'s pattern of `+/api/**+`, so `FilterChainProxy` will continue trying each `SecurityFilterChain`. -Assuming that no other, `SecurityFilterChain` instances match `SecurityFilterChain~n~` will be invoked. -// FIXME add link to pattern matching - -Notice that `SecurityFilterChain~0~` has only three security ``Filter``s instances configured. -However, `SecurityFilterChain~n~` has four security ``Filter``s configured. -It is important to note that each `SecurityFilterChain` can be unique and configured in isolation. -In fact, a `SecurityFilterChain` might have zero security ``Filter``s if the application wants Spring Security to ignore certain requests. -// FIXME: add link to configuring multiple `SecurityFilterChain` instances diff --git a/docs/modules/ROOT/pages/servlet/architecture/security-filters.adoc b/docs/modules/ROOT/pages/servlet/architecture/security-filters.adoc deleted file mode 100644 index 408ec40637..0000000000 --- a/docs/modules/ROOT/pages/servlet/architecture/security-filters.adoc +++ /dev/null @@ -1,42 +0,0 @@ -[[servlet-security-filters]] -= Security Filters - -The Security Filters are inserted into the <> with the <> API. -The <>s matters. -It is typically not necessary to know the ordering of Spring Security's ``Filter``s. -However, there are times that it is beneficial to know the ordering - -Below is a comprehensive list of Spring Security Filter ordering: - -* ChannelProcessingFilter -* WebAsyncManagerIntegrationFilter -* SecurityContextPersistenceFilter -* HeaderWriterFilter -* CorsFilter -* CsrfFilter -* LogoutFilter -* OAuth2AuthorizationRequestRedirectFilter -* Saml2WebSsoAuthenticationRequestFilter -* X509AuthenticationFilter -* AbstractPreAuthenticatedProcessingFilter -* CasAuthenticationFilter -* OAuth2LoginAuthenticationFilter -* Saml2WebSsoAuthenticationFilter -* <> -* OpenIDAuthenticationFilter -* DefaultLoginPageGeneratingFilter -* DefaultLogoutPageGeneratingFilter -* ConcurrentSessionFilter -* <> -* BearerTokenAuthenticationFilter -* <> -* RequestCacheAwareFilter -* SecurityContextHolderAwareRequestFilter -* JaasApiIntegrationFilter -* RememberMeAuthenticationFilter -* AnonymousAuthenticationFilter -* OAuth2AuthorizationCodeGrantFilter -* SessionManagementFilter -* <> -* <> -* SwitchUserFilter diff --git a/docs/modules/ROOT/pages/servlet/authentication/architecture/abstract-authentication-processing-filter.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/abstract-authentication-processing-filter.adoc deleted file mode 100644 index a3a8a48d18..0000000000 --- a/docs/modules/ROOT/pages/servlet/authentication/architecture/abstract-authentication-processing-filter.adoc +++ /dev/null @@ -1,41 +0,0 @@ -[[servlet-authentication-abstractprocessingfilter]] -= AbstractAuthenticationProcessingFilter -:figures: images/servlet/authentication/architecture -:icondir: images/icons - -{security-api-url}org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.html[`AbstractAuthenticationProcessingFilter`] is used as a base `Filter` for authenticating a user's credentials. -Before the credentials can be authenticated, Spring Security typically requests the credentials using <>. - -Next, the `AbstractAuthenticationProcessingFilter` can authenticate any authentication requests that are submitted to it. - -image::{figures}/abstractauthenticationprocessingfilter.png[] - -image:{icondir}/number_1.png[] When the user submits their credentials, the `AbstractAuthenticationProcessingFilter` creates an <> from the `HttpServletRequest` to be authenticated. -The type of `Authentication` created depends on the subclass of `AbstractAuthenticationProcessingFilter`. -For example, <> creates a `UsernamePasswordAuthenticationToken` from a __username__ and __password__ that are submitted in the `HttpServletRequest`. - -image:{icondir}/number_2.png[] Next, the <> is passed into the <> to be authenticated. - -image:{icondir}/number_3.png[] If authentication fails, then __Failure__ - -* The <> is cleared out. -* `RememberMeServices.loginFail` is invoked. -If remember me is not configured, this is a no-op. -// FIXME: link to rememberme -* `AuthenticationFailureHandler` is invoked. -// FIXME: link to AuthenticationFailureHandler - -image:{icondir}/number_4.png[] If authentication is successful, then __Success__. - -* `SessionAuthenticationStrategy` is notified of a new log in. -// FIXME: Add link to SessionAuthenticationStrategy -* The <> is set on the <>. -Later the `SecurityContextPersistenceFilter` saves the `SecurityContext` to the `HttpSession`. -// FIXME: link securitycontextpersistencefilter -* `RememberMeServices.loginSuccess` is invoked. -If remember me is not configured, this is a no-op. -// FIXME: link to rememberme -* `ApplicationEventPublisher` publishes an `InteractiveAuthenticationSuccessEvent`. -* `AuthenticationSuccessHandler` is invoked. -// FIXME: link to AuthenticationSuccessHandler - diff --git a/docs/modules/ROOT/pages/servlet/authentication/architecture/authentication-entry-point.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/authentication-entry-point.adoc deleted file mode 100644 index cdc00dddbc..0000000000 --- a/docs/modules/ROOT/pages/servlet/authentication/architecture/authentication-entry-point.adoc +++ /dev/null @@ -1,14 +0,0 @@ -[[servlet-authentication-authenticationentrypoint]] -= Request Credentials with `AuthenticationEntryPoint` - - -{security-api-url}org/springframework/security/web/AuthenticationEntryPoint.html[`AuthenticationEntryPoint`] is used to send an HTTP response that requests credentials from a client. - -Sometimes a client will proactively include credentials such as a username/password to request a resource. -In these cases, Spring Security does not need to provide an HTTP response that requests credentials from the client since they are already included. - -In other cases, a client will make an unauthenticated request to a resource that they are not authorized to access. -In this case, an implementation of `AuthenticationEntryPoint` is used to request credentials from the client. -The `AuthenticationEntryPoint` implementation might perform a <>, respond with an <> header, etc. - - diff --git a/docs/modules/ROOT/pages/servlet/authentication/architecture/authentication-manager.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/authentication-manager.adoc deleted file mode 100644 index 5c85382b71..0000000000 --- a/docs/modules/ROOT/pages/servlet/authentication/architecture/authentication-manager.adoc +++ /dev/null @@ -1,9 +0,0 @@ -[[servlet-authentication-authenticationmanager]] -= AuthenticationManager - -{security-api-url}org/springframework/security/authentication/AuthenticationManager.html[`AuthenticationManager`] is the API that defines how Spring Security's Filters perform <>. -The <> that is returned is then set on the <> by the controller (i.e. <>) that invoked the `AuthenticationManager`. -If you are not integrating with __Spring Security's ``Filters``s__ you can set the `SecurityContextHolder` directly and are not required to use an `AuthenticationManager`. - -While the implementation of `AuthenticationManager` could be anything, the most common implementation is <>. -// FIXME: add configuration diff --git a/docs/modules/ROOT/pages/servlet/authentication/architecture/authentication-provider.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/authentication-provider.adoc deleted file mode 100644 index c7cb0b7b6c..0000000000 --- a/docs/modules/ROOT/pages/servlet/authentication/architecture/authentication-provider.adoc +++ /dev/null @@ -1,6 +0,0 @@ -[[servlet-authentication-authenticationprovider]] -= AuthenticationProvider - -Multiple {security-api-url}org/springframework/security/authentication/AuthenticationProvider.html[``AuthenticationProvider``s] can be injected into <>. -Each `AuthenticationProvider` performs a specific type of authentication. -For example, <> supports username/password based authentication while `JwtAuthenticationProvider` supports authenticating a JWT token. diff --git a/docs/modules/ROOT/pages/servlet/authentication/architecture/authentication.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/authentication.adoc deleted file mode 100644 index 878ecc2481..0000000000 --- a/docs/modules/ROOT/pages/servlet/authentication/architecture/authentication.adoc +++ /dev/null @@ -1,18 +0,0 @@ -[[servlet-authentication-authentication]] -= Authentication - -The {security-api-url}org/springframework/security/core/Authentication.html[`Authentication`] serves two main purposes within Spring Security: - -* An input to <> to provide the credentials a user has provided to authenticate. -When used in this scenario, `isAuthenticated()` returns `false`. -* Represents the currently authenticated user. -The current `Authentication` can be obtained from the <>. - -The `Authentication` contains: - -* `principal` - identifies the user. -When authenticating with a username/password this is often an instance of <>. -* `credentials` - often a password. -In many cases this will be cleared after the user is authenticated to ensure it is not leaked. -* `authorities` - the <> are high level permissions the user is granted. -A few examples are roles or scopes. diff --git a/docs/modules/ROOT/pages/servlet/authentication/architecture/granted-authority.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/granted-authority.adoc deleted file mode 100644 index 19b45cd049..0000000000 --- a/docs/modules/ROOT/pages/servlet/authentication/architecture/granted-authority.adoc +++ /dev/null @@ -1,16 +0,0 @@ -[[servlet-authentication-granted-authority]] -= GrantedAuthority -{security-api-url}org/springframework/security/core/GrantedAuthority.html[``GrantedAuthority``s] are high level permissions the user is granted. A few examples are roles or scopes. - -``GrantedAuthority``s can be obtained from the <> method. -This method provides a `Collection` of `GrantedAuthority` objects. -A `GrantedAuthority` is, not surprisingly, an authority that is granted to the principal. -Such authorities are usually "roles", such as `ROLE_ADMINISTRATOR` or `ROLE_HR_SUPERVISOR`. -These roles are later on configured for web authorization, method authorization and domain object authorization. -Other parts of Spring Security are capable of interpreting these authorities, and expect them to be present. -When using username/password based authentication ``GrantedAuthority``s are usually loaded by the <>. - -Usually the `GrantedAuthority` objects are application-wide permissions. -They are not specific to a given domain object. -Thus, you wouldn't likely have a `GrantedAuthority` to represent a permission to `Employee` object number 54, because if there are thousands of such authorities you would quickly run out of memory (or, at the very least, cause the application to take a long time to authenticate a user). -Of course, Spring Security is expressly designed to handle this common requirement, but you'd instead use the project's domain object security capabilities for this purpose. diff --git a/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc index 4df295e609..a7f768cc1f 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc @@ -1,22 +1,249 @@ -include::security-context-holder.adoc[leveloffset=+1] +[[servlet-authentication-architecture]] += Servlet Authentication Architecture +:figures: images/servlet/authentication/architecture +:icondir: images/icons -include::security-context.adoc[leveloffset=+1] +This discussion expands on <> to discuss the Servlet Authentication architecture. -include::authentication.adoc[leveloffset=+1] +[[servlet-authentication-securitycontextholder]] +== SecurityContextHolder -include::granted-authority.adoc[leveloffset=+1] -include::authentication-manager.adoc[leveloffset=+1] +At the heart of Spring Security's authentication model is the `SecurityContextHolder`. +It contains the <>. -include::provider-manager.adoc[leveloffset=+1] +image::{figures}/securitycontextholder.png[] + +The `SecurityContextHolder` is where Spring Security stores the details of who is <>. +Spring Security does not care how the `SecurityContextHolder` is populated. +If it contains a value, then it is used as the currently authenticated user. + +The simplest way to indicate a user is authenticated is to set the `SecurityContextHolder` directly. + +.Setting `SecurityContextHolder` +==== +.Java +[source,java,role="primary"] +---- +SecurityContext context = SecurityContextHolder.createEmptyContext(); // <1> +Authentication authentication = + new TestingAuthenticationToken("username", "password", "ROLE_USER"); // <2> +context.setAuthentication(authentication); + +SecurityContextHolder.setContext(context); // <3> +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +val context: SecurityContext = SecurityContextHolder.createEmptyContext() // <1> +val authentication: Authentication = TestingAuthenticationToken("username", "password", "ROLE_USER") // <2> +context.authentication = authentication + +SecurityContextHolder.setContext(context) // <3> +---- +==== + +<1> We start by creating an empty `SecurityContext`. +It is important to create a new `SecurityContext` instance instead of using `SecurityContextHolder.getContext().setAuthentication(authentication)` to avoid race conditions across multiple threads. +<2> Next we create a new <> object. +Spring Security does not care what type of `Authentication` implementation is set on the `SecurityContext`. +Here we use `TestingAuthenticationToken` because it is very simple. +A more common production scenario is `UsernamePasswordAuthenticationToken(userDetails, password, authorities)`. +<3> Finally, we set the `SecurityContext` on the `SecurityContextHolder`. +Spring Security will use this information for <>. + +If you wish to obtain information about the authenticated principal, you can do so by accessing the `SecurityContextHolder`. + +.Access Currently Authenticated User +==== +.Java +[source,java,role="primary"] +---- +SecurityContext context = SecurityContextHolder.getContext(); +Authentication authentication = context.getAuthentication(); +String username = authentication.getName(); +Object principal = authentication.getPrincipal(); +Collection authorities = authentication.getAuthorities(); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +val context = SecurityContextHolder.getContext() +val authentication = context.authentication +val username = authentication.name +val principal = authentication.principal +val authorities = authentication.authorities +---- +==== + +// FIXME: add links to HttpServletRequest.getRemoteUser() and @CurrentSecurityContext @AuthenticationPrincipal + +By default the `SecurityContextHolder` uses a `ThreadLocal` to store these details, which means that the `SecurityContext` is always available to methods in the same thread, even if the `SecurityContext` is not explicitly passed around as an argument to those methods. +Using a `ThreadLocal` in this way is quite safe if care is taken to clear the thread after the present principal's request is processed. +Spring Security's <> ensures that the `SecurityContext` is always cleared. + +Some applications aren't entirely suitable for using a `ThreadLocal`, because of the specific way they work with threads. +For example, a Swing client might want all threads in a Java Virtual Machine to use the same security context. +`SecurityContextHolder` can be configured with a strategy on startup to specify how you would like the context to be stored. +For a standalone application you would use the `SecurityContextHolder.MODE_GLOBAL` strategy. +Other applications might want to have threads spawned by the secure thread also assume the same security identity. +This is achieved by using `SecurityContextHolder.MODE_INHERITABLETHREADLOCAL`. +You can change the mode from the default `SecurityContextHolder.MODE_THREADLOCAL` in two ways. +The first is to set a system property, the second is to call a static method on `SecurityContextHolder`. +Most applications won't need to change from the default, but if you do, take a look at the Javadoc for `SecurityContextHolder` to learn more. + +[[servlet-authentication-securitycontext]] +== SecurityContext + +The {security-api-url}org/springframework/security/core/context/SecurityContext.html[`SecurityContext`] is obtained from the <>. +The `SecurityContext` contains an <> object. + +[[servlet-authentication-authentication]] +== Authentication + +The {security-api-url}org/springframework/security/core/Authentication.html[`Authentication`] serves two main purposes within Spring Security: + +* An input to <> to provide the credentials a user has provided to authenticate. +When used in this scenario, `isAuthenticated()` returns `false`. +* Represents the currently authenticated user. +The current `Authentication` can be obtained from the <>. + +The `Authentication` contains: + +* `principal` - identifies the user. +When authenticating with a username/password this is often an instance of <>. +* `credentials` - often a password. +In many cases this will be cleared after the user is authenticated to ensure it is not leaked. +* `authorities` - the <> are high level permissions the user is granted. +A few examples are roles or scopes. + +[[servlet-authentication-granted-authority]] +== GrantedAuthority +{security-api-url}org/springframework/security/core/GrantedAuthority.html[``GrantedAuthority``s] are high level permissions the user is granted. A few examples are roles or scopes. + +``GrantedAuthority``s can be obtained from the <> method. +This method provides a `Collection` of `GrantedAuthority` objects. +A `GrantedAuthority` is, not surprisingly, an authority that is granted to the principal. +Such authorities are usually "roles", such as `ROLE_ADMINISTRATOR` or `ROLE_HR_SUPERVISOR`. +These roles are later on configured for web authorization, method authorization and domain object authorization. +Other parts of Spring Security are capable of interpreting these authorities, and expect them to be present. +When using username/password based authentication ``GrantedAuthority``s are usually loaded by the <>. + +Usually the `GrantedAuthority` objects are application-wide permissions. +They are not specific to a given domain object. +Thus, you wouldn't likely have a `GrantedAuthority` to represent a permission to `Employee` object number 54, because if there are thousands of such authorities you would quickly run out of memory (or, at the very least, cause the application to take a long time to authenticate a user). +Of course, Spring Security is expressly designed to handle this common requirement, but you'd instead use the project's domain object security capabilities for this purpose. + +[[servlet-authentication-authenticationmanager]] +== AuthenticationManager + +{security-api-url}org/springframework/security/authentication/AuthenticationManager.html[`AuthenticationManager`] is the API that defines how Spring Security's Filters perform <>. +The <> that is returned is then set on the <> by the controller (i.e. <>) that invoked the `AuthenticationManager`. +If you are not integrating with __Spring Security's ``Filters``s__ you can set the `SecurityContextHolder` directly and are not required to use an `AuthenticationManager`. + +While the implementation of `AuthenticationManager` could be anything, the most common implementation is <>. +// FIXME: add configuration + +[[servlet-authentication-providermanager]] +== ProviderManager + +{security-api-url}org/springframework/security/authentication/ProviderManager.html[`ProviderManager`] is the most commonly used implementation of <>. +`ProviderManager` delegates to a `List` of <>. +// FIXME: link to AuthenticationProvider +Each `AuthenticationProvider` has an opportunity to indicate that authentication should be successful, fail, or indicate it cannot make a decision and allow a downstream `AuthenticationProvider` to decide. +If none of the configured ``AuthenticationProvider``s can authenticate, then authentication will fail with a `ProviderNotFoundException` which is a special `AuthenticationException` that indicates the `ProviderManager` was not configured to support the type of `Authentication` that was passed into it. + +image::{figures}/providermanager.png[] + +In practice each `AuthenticationProvider` knows how to perform a specific type of authentication. +For example, one `AuthenticationProvider` might be able to validate a username/password, while another might be able to authenticate a SAML assertion. +This allows each `AuthenticationProvider` to do a very specific type of authentication, while supporting multiple types of authentication and only exposing a single `AuthenticationManager` bean. + +`ProviderManager` also allows configuring an optional parent `AuthenticationManager` which is consulted in the event that no `AuthenticationProvider` can perform authentication. +The parent can be any type of `AuthenticationManager`, but it is often an instance of `ProviderManager`. + +image::{figures}/providermanager-parent.png[] + +In fact, multiple `ProviderManager` instances might share the same parent `AuthenticationManager`. +This is somewhat common in scenarios where there are multiple <> instances that have some authentication in common (the shared parent `AuthenticationManager`), but also different authentication mechanisms (the different `ProviderManager` instances). + +image::{figures}/providermanagers-parent.png[] + +[[servlet-authentication-providermanager-erasing-credentials]] +By default `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 in the `HttpSession`. + +This may cause issues when you are using a cache of user objects, for example, to improve performance in a stateless application. +If the `Authentication` contains a reference to an object in the cache (such as a `UserDetails` instance) and this has its credentials removed, then it will no longer be possible to authenticate against the cached value. +You need to take this into account if you are using a cache. +An obvious solution is to make a copy of the object first, either in the cache implementation or in the `AuthenticationProvider` which creates the returned `Authentication` object. +Alternatively, you can disable the `eraseCredentialsAfterAuthentication` property on `ProviderManager`. +See the {security-api-url}org/springframework/security/authentication/ProviderManager.html[Javadoc] for more information. + +[[servlet-authentication-authenticationprovider]] +== AuthenticationProvider + +Multiple {security-api-url}org/springframework/security/authentication/AuthenticationProvider.html[``AuthenticationProvider``s] can be injected into <>. +Each `AuthenticationProvider` performs a specific type of authentication. +For example, <> supports username/password based authentication while `JwtAuthenticationProvider` supports authenticating a JWT token. + +[[servlet-authentication-authenticationentrypoint]] +== Request Credentials with `AuthenticationEntryPoint` + +{security-api-url}org/springframework/security/web/AuthenticationEntryPoint.html[`AuthenticationEntryPoint`] is used to send an HTTP response that requests credentials from a client. + +Sometimes a client will proactively include credentials such as a username/password to request a resource. +In these cases, Spring Security does not need to provide an HTTP response that requests credentials from the client since they are already included. + +In other cases, a client will make an unauthenticated request to a resource that they are not authorized to access. +In this case, an implementation of `AuthenticationEntryPoint` is used to request credentials from the client. +The `AuthenticationEntryPoint` implementation might perform a <>, respond with an <> header, etc. -include::authentication-provider.adoc[leveloffset=+1] -include::authentication-entry-point.adoc[leveloffset=+1] // FIXME: authenticationsuccesshandler // FIXME: authenticationfailurehandler -include::abstract-authentication-processing-filter.adoc[leveloffset=+1] +[[servlet-authentication-abstractprocessingfilter]] +== AbstractAuthenticationProcessingFilter + +{security-api-url}org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.html[`AbstractAuthenticationProcessingFilter`] is used as a base `Filter` for authenticating a user's credentials. +Before the credentials can be authenticated, Spring Security typically requests the credentials using <>. + +Next, the `AbstractAuthenticationProcessingFilter` can authenticate any authentication requests that are submitted to it. + +image::{figures}/abstractauthenticationprocessingfilter.png[] + +image:{icondir}/number_1.png[] When the user submits their credentials, the `AbstractAuthenticationProcessingFilter` creates an <> from the `HttpServletRequest` to be authenticated. +The type of `Authentication` created depends on the subclass of `AbstractAuthenticationProcessingFilter`. +For example, <> creates a `UsernamePasswordAuthenticationToken` from a __username__ and __password__ that are submitted in the `HttpServletRequest`. + +image:{icondir}/number_2.png[] Next, the <> is passed into the <> to be authenticated. + +image:{icondir}/number_3.png[] If authentication fails, then __Failure__ + +* The <> is cleared out. +* `RememberMeServices.loginFail` is invoked. +If remember me is not configured, this is a no-op. +// FIXME: link to rememberme +* `AuthenticationFailureHandler` is invoked. +// FIXME: link to AuthenticationFailureHandler + +image:{icondir}/number_4.png[] If authentication is successful, then __Success__. + +* `SessionAuthenticationStrategy` is notified of a new log in. +// FIXME: Add link to SessionAuthenticationStrategy +* The <> is set on the <>. +Later the `SecurityContextPersistenceFilter` saves the `SecurityContext` to the `HttpSession`. +// FIXME: link securitycontextpersistencefilter +* `RememberMeServices.loginSuccess` is invoked. +If remember me is not configured, this is a no-op. +// FIXME: link to rememberme +* `ApplicationEventPublisher` publishes an `InteractiveAuthenticationSuccessEvent`. +* `AuthenticationSuccessHandler` is invoked. +// FIXME: link to AuthenticationSuccessHandler + // daoauthenticationprovider (goes in username/password) diff --git a/docs/modules/ROOT/pages/servlet/authentication/architecture/provider-manager.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/provider-manager.adoc deleted file mode 100644 index 4ed8cb4615..0000000000 --- a/docs/modules/ROOT/pages/servlet/authentication/architecture/provider-manager.adoc +++ /dev/null @@ -1,36 +0,0 @@ -[[servlet-authentication-providermanager]] -= ProviderManager -:figures: images/servlet/authentication/architecture - -{security-api-url}org/springframework/security/authentication/ProviderManager.html[`ProviderManager`] is the most commonly used implementation of <>. -`ProviderManager` delegates to a `List` of <>. -// FIXME: link to AuthenticationProvider -Each `AuthenticationProvider` has an opportunity to indicate that authentication should be successful, fail, or indicate it cannot make a decision and allow a downstream `AuthenticationProvider` to decide. -If none of the configured ``AuthenticationProvider``s can authenticate, then authentication will fail with a `ProviderNotFoundException` which is a special `AuthenticationException` that indicates the `ProviderManager` was not configured to support the type of `Authentication` that was passed into it. - -image::{figures}/providermanager.png[] - -In practice each `AuthenticationProvider` knows how to perform a specific type of authentication. - For example, one `AuthenticationProvider` might be able to validate a username/password, while another might be able to authenticate a SAML assertion. -This allows each `AuthenticationProvider` to do a very specific type of authentication, while supporting multiple types of authentication and only exposing a single `AuthenticationManager` bean. - -`ProviderManager` also allows configuring an optional parent `AuthenticationManager` which is consulted in the event that no `AuthenticationProvider` can perform authentication. -The parent can be any type of `AuthenticationManager`, but it is often an instance of `ProviderManager`. - -image::{figures}/providermanager-parent.png[] - -In fact, multiple `ProviderManager` instances might share the same parent `AuthenticationManager`. -This is somewhat common in scenarios where there are multiple <> instances that have some authentication in common (the shared parent `AuthenticationManager`), but also different authentication mechanisms (the different `ProviderManager` instances). - -image::{figures}/providermanagers-parent.png[] - -[[servlet-authentication-providermanager-erasing-credentials]] -By default `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 in the `HttpSession`. - -This may cause issues when you are using a cache of user objects, for example, to improve performance in a stateless application. -If the `Authentication` contains a reference to an object in the cache (such as a `UserDetails` instance) and this has its credentials removed, then it will no longer be possible to authenticate against the cached value. -You need to take this into account if you are using a cache. -An obvious solution is to make a copy of the object first, either in the cache implementation or in the `AuthenticationProvider` which creates the returned `Authentication` object. -Alternatively, you can disable the `eraseCredentialsAfterAuthentication` property on `ProviderManager`. -See the {security-api-url}org/springframework/security/authentication/ProviderManager.html[Javadoc] for more information. diff --git a/docs/modules/ROOT/pages/servlet/authentication/architecture/security-context-holder.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/security-context-holder.adoc deleted file mode 100644 index 60198d48f6..0000000000 --- a/docs/modules/ROOT/pages/servlet/authentication/architecture/security-context-holder.adoc +++ /dev/null @@ -1,89 +0,0 @@ -[[servlet-authentication-securitycontextholder]] -= SecurityContextHolder - -:figures: images/servlet/authentication/architecture - -At the heart of Spring Security's authentication model is the `SecurityContextHolder`. -It contains the <>. - -image::{figures}/securitycontextholder.png[] - -The `SecurityContextHolder` is where Spring Security stores the details of who is <>. -Spring Security does not care how the `SecurityContextHolder` is populated. -If it contains a value, then it is used as the currently authenticated user. - -The simplest way to indicate a user is authenticated is to set the `SecurityContextHolder` directly. - -.Setting `SecurityContextHolder` -==== -.Java -[source,java,role="primary"] ----- -SecurityContext context = SecurityContextHolder.createEmptyContext(); // <1> -Authentication authentication = - new TestingAuthenticationToken("username", "password", "ROLE_USER"); // <2> -context.setAuthentication(authentication); - -SecurityContextHolder.setContext(context); // <3> ----- - -.Kotlin -[source,kotlin,role="secondary"] ----- -val context: SecurityContext = SecurityContextHolder.createEmptyContext() // <1> -val authentication: Authentication = TestingAuthenticationToken("username", "password", "ROLE_USER") // <2> -context.authentication = authentication - -SecurityContextHolder.setContext(context) // <3> ----- -==== - -<1> We start by creating an empty `SecurityContext`. -It is important to create a new `SecurityContext` instance instead of using `SecurityContextHolder.getContext().setAuthentication(authentication)` to avoid race conditions across multiple threads. -<2> Next we create a new <> object. -Spring Security does not care what type of `Authentication` implementation is set on the `SecurityContext`. -Here we use `TestingAuthenticationToken` because it is very simple. -A more common production scenario is `UsernamePasswordAuthenticationToken(userDetails, password, authorities)`. -<3> Finally, we set the `SecurityContext` on the `SecurityContextHolder`. -Spring Security will use this information for <>. - -If you wish to obtain information about the authenticated principal, you can do so by accessing the `SecurityContextHolder`. - -.Access Currently Authenticated User -==== -.Java -[source,java,role="primary"] ----- -SecurityContext context = SecurityContextHolder.getContext(); -Authentication authentication = context.getAuthentication(); -String username = authentication.getName(); -Object principal = authentication.getPrincipal(); -Collection authorities = authentication.getAuthorities(); ----- - -.Kotlin -[source,kotlin,role="secondary"] ----- -val context = SecurityContextHolder.getContext() -val authentication = context.authentication -val username = authentication.name -val principal = authentication.principal -val authorities = authentication.authorities ----- -==== - -// FIXME: add links to HttpServletRequest.getRemoteUser() and @CurrentSecurityContext @AuthenticationPrincipal - -By default the `SecurityContextHolder` uses a `ThreadLocal` to store these details, which means that the `SecurityContext` is always available to methods in the same thread, even if the `SecurityContext` is not explicitly passed around as an argument to those methods. -Using a `ThreadLocal` in this way is quite safe if care is taken to clear the thread after the present principal's request is processed. -Spring Security's <> ensures that the `SecurityContext` is always cleared. - -Some applications aren't entirely suitable for using a `ThreadLocal`, because of the specific way they work with threads. -For example, a Swing client might want all threads in a Java Virtual Machine to use the same security context. -`SecurityContextHolder` can be configured with a strategy on startup to specify how you would like the context to be stored. -For a standalone application you would use the `SecurityContextHolder.MODE_GLOBAL` strategy. -Other applications might want to have threads spawned by the secure thread also assume the same security identity. -This is achieved by using `SecurityContextHolder.MODE_INHERITABLETHREADLOCAL`. -You can change the mode from the default `SecurityContextHolder.MODE_THREADLOCAL` in two ways. -The first is to set a system property, the second is to call a static method on `SecurityContextHolder`. -Most applications won't need to change from the default, but if you do, take a look at the Javadoc for `SecurityContextHolder` to learn more. diff --git a/docs/modules/ROOT/pages/servlet/authentication/architecture/security-context.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/security-context.adoc deleted file mode 100644 index 955cf70d28..0000000000 --- a/docs/modules/ROOT/pages/servlet/authentication/architecture/security-context.adoc +++ /dev/null @@ -1,5 +0,0 @@ -[[servlet-authentication-securitycontext]] -= SecurityContext - -The {security-api-url}org/springframework/security/core/context/SecurityContext.html[`SecurityContext`] is obtained from the <>. -The `SecurityContext` contains an <> object. diff --git a/docs/modules/ROOT/pages/servlet/authentication/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/index.adoc index abd8e400c4..9e976aff2b 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/index.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/index.adoc @@ -2,27 +2,14 @@ = Authentication Spring Security provides comprehensive support for <>. -This section discusses: +We start by discussing the overall <>. +As you might expect, this section is more abstract describing the architecture without much discussion on how it applies to concrete flows. -[[servlet-authentication-architecture]] -*Architecture Components* - -This section describes the main architectural components of Spring Security's used in Servlet authentication. -If you need concrete flows that explain how these pieces fit together, look at the <> specific sections. - -* <> - the `SecurityContextHolder` is where Spring Security stores the details of who is <>. -* <> - is obtained from the `SecurityContextHolder` and contains the `Authentication` of the currently authenticated user. -* <> - can be the input to `AuthenticationManager` to provide the credentials a user has provided to authenticate or the current user from the `SecurityContext`. -* <> - an authority that is granted to the principal on the `Authentication` (i.e. roles, scopes, etc.) -* <> - the API that defines how Spring Security's Filters perform <>. -* <> - the most common implementation of `AuthenticationManager`. -* <> - used by `ProviderManager` to perform a specific type of authentication. -* <> - used for requesting credentials from a client (i.e. redirecting to a log in page, sending a `WWW-Authenticate` response, etc.) -* <> - a base `Filter` used for authentication. -This also gives a good idea of the high level flow of authentication and how pieces work together. +If you prefer, you can refer to <> for concrete ways in which users can authenticate. +These sections focus on specific ways you may want to authenticate and point back at the architecture sections to describe how the specific flows work. [[servlet-authentication-mechanisms]] -*Authentication Mechanisms* +== Authentication Mechanisms // FIXME: brief description @@ -35,32 +22,3 @@ This also gives a good idea of the high level flow of authentication and how pie * <> - OpenID Authentication (not to be confused with OpenID Connect) * <> - authenticate with an external mechanism such as https://www.siteminder.com/[SiteMinder] or Java EE security but still use Spring Security for authorization and protection against common exploits. * <> - X509 Authentication - -// FIXME: Add other mechanisms - -// We intentionally do not increase leveloffset, this is just for organization vs document structure -include::architecture/index.adoc[] - -include::unpwd/index.adoc[leveloffset=+1] - -include::session-management.adoc[] - -include::rememberme.adoc[] - -include::openid.adoc[] - -include::anonymous.adoc[] - -include::preauth.adoc[] - -include::jaas.adoc[] - -include::cas.adoc[] - -include::x509.adoc[] - -include::runas.adoc[] - -include::logout.adoc[] - -include::events.adoc[] diff --git a/docs/modules/ROOT/pages/servlet/authorization/index.adoc b/docs/modules/ROOT/pages/servlet/authorization/index.adoc index 2e4dfc59ac..fec5d6b31f 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/index.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/index.adoc @@ -6,15 +6,10 @@ Irrespective of how you choose to authenticate - whether using a Spring Security In this part we'll explore the different `AbstractSecurityInterceptor` implementations, which were introduced in Part I. We then move on to explore how to fine-tune authorization through use of domain access control lists. -include::architecture.adoc[leveloffset=+1] - -include::authorize-requests.adoc[leveloffset=+1] - -include::expression-based.adoc[] - -include::secure-objects.adoc[] - -include::method-security.adoc[] - -include::acls.adoc[] +* xref:servlet/authorization/architecture.adoc[leveloffset=+1] +* xref:servlet/authorization/authorize-requests.adoc[leveloffset=+1] +* xref:servlet/authorization/expression-based.adoc[] +* xref:servlet/authorization/secure-objects.adoc[] +* xref:servlet/authorization/method-security.adoc[] +* xref:servlet/authorization/acls.adoc[] diff --git a/docs/modules/ROOT/pages/servlet/exploits/index.adoc b/docs/modules/ROOT/pages/servlet/exploits/index.adoc index bc955238c2..b19171f9a6 100644 --- a/docs/modules/ROOT/pages/servlet/exploits/index.adoc +++ b/docs/modules/ROOT/pages/servlet/exploits/index.adoc @@ -1,10 +1,9 @@ [[servlet-exploits]] = Protection Against Exploits -include::csrf.adoc[leveloffset=+1] +This section discusses Servlet specific support for <>. -include::headers.adoc[leveloffset=+1] - -include::http.adoc[leveloffset=+1] - -include::firewall.adoc[leveloffset=+1] +* xref:servlet/exploits/csrf.adoc[] +* xref:servlet/exploits/headers.adoc[] +* xref:servlet/exploits/http.adoc[] +* xref:servlet/exploits/firewall.adoc[] diff --git a/docs/modules/ROOT/pages/servlet/index.adoc b/docs/modules/ROOT/pages/servlet/index.adoc index fa8d861b0b..5c8f216973 100644 --- a/docs/modules/ROOT/pages/servlet/index.adoc +++ b/docs/modules/ROOT/pages/servlet/index.adoc @@ -1,31 +1,4 @@ +[[servlet-applications]] = Servlet Applications 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::hello/index.adoc[leveloffset=+1] - -include::architecture/index.adoc[leveloffset=+1] - -include::authentication/index.adoc[leveloffset=+1] - -include::authorization/index.adoc[leveloffset=+1] - -include::oauth2/index.adoc[leveloffset=+1] - -include::saml2/index.adoc[leveloffset=+1] - -include::exploits/index.adoc[leveloffset=+1] - -include::integrations/index.adoc[leveloffset=+1] - -include::java-configuration/index.adoc[leveloffset=+1] - -include::kotlin-configuration/index.adoc[leveloffset=+1] - -include::namespace/index.adoc[leveloffset=+1] - -include::test/index.adoc[leveloffset=+1] - -include::crypto/index.adoc[leveloffset=+1] - -include::appendix/index.adoc[leveloffset=+1] diff --git a/docs/modules/ROOT/pages/servlet/integrations/index.adoc b/docs/modules/ROOT/pages/servlet/integrations/index.adoc index 90b9783a1f..5bfff0a49c 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/index.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/index.adoc @@ -1,19 +1,14 @@ = Integrations -include::servlet-api.adoc[] +Spring Security integrates with numerous frameworks and APIs. +In this section, we discuss Spring Security integration with: -include::data.adoc[] - -include::concurrency.adoc[] - -include::jackson.adoc[] - -include::localization.adoc[] - -include::mvc.adoc[] - -include::websocket.adoc[] - -include::cors.adoc[] - -include::jsp-taglibs.adoc[] +* xref:servlet/integrations/servlet-api.adoc[Servlet APIs] +* xref:servlet/integrations/data.adoc[Spring Data] +* xref:servlet/integrations/concurrency.adoc[Java's Concurrency APIs] +* xref:servlet/integrations/jackson.adoc[Jackson] +* xref:servlet/integrations/localization.adoc[Localization] +* xref:servlet/integrations/mvc.adoc[Spring MVC] +* xref:servlet/integrations/websocket.adoc[WebSocket] +* xref:servlet/integrations/cors.adoc[Spring's CORS Support] +* xref:servlet/integrations/jsp-taglibs.adoc[JSP Taglib] diff --git a/docs/modules/ROOT/pages/servlet/oauth2/index.adoc b/docs/modules/ROOT/pages/servlet/oauth2/index.adoc index 99ed0947d9..df50a690b6 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/index.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/index.adoc @@ -1,7 +1,8 @@ = OAuth2 -include::oauth2-login.adoc[] +Spring Security provides comprehensive OAuth 2 support. +This section discusses how to integrate OAuth 2 into your servlet based application. -include::oauth2-client.adoc[] - -include::oauth2-resourceserver.adoc[] +* xref:servlet/oauth2/oauth2-login.adoc[] +* xref:servlet/oauth2/oauth2-client.adoc[] +* xref:servlet/oauth2/oauth2-resourceserver.adoc[] diff --git a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-client.adoc b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-client.adoc index 9bae570fe7..31ac01c9f9 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-client.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-client.adoc @@ -1,5 +1,5 @@ [[oauth2client]] -== OAuth 2.0 Client += OAuth 2.0 Client The OAuth 2.0 Client features provide support for the Client role as defined in the https://tools.ietf.org/html/rfc6749#section-1.1[OAuth 2.0 Authorization Framework]. @@ -166,11 +166,11 @@ The following sections will go into more detail on the core components used by O [[oauth2Client-core-interface-class]] -=== Core Interfaces / Classes +== Core Interfaces / Classes [[oauth2Client-client-registration]] -==== ClientRegistration +=== ClientRegistration `ClientRegistration` is a representation of a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider. @@ -256,7 +256,7 @@ The above code will query in series `https://idp.example.com/issuer/.well-known/ As an alternative, you can use `ClientRegistrations.fromOidcIssuerLocation()` to only query the OpenID Connect Provider's Configuration endpoint. [[oauth2Client-client-registration-repo]] -==== ClientRegistrationRepository +=== ClientRegistrationRepository The `ClientRegistrationRepository` serves as a repository for OAuth 2.0 / OpenID Connect 1.0 `ClientRegistration`(s). @@ -318,7 +318,7 @@ class OAuth2ClientController { ==== [[oauth2Client-authorized-client]] -==== OAuth2AuthorizedClient +=== OAuth2AuthorizedClient `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. @@ -327,7 +327,7 @@ A client is considered to be authorized when the end-user (Resource Owner) has g [[oauth2Client-authorized-repo-service]] -==== OAuth2AuthorizedClientRepository / OAuth2AuthorizedClientService +=== OAuth2AuthorizedClientRepository / OAuth2AuthorizedClientService `OAuth2AuthorizedClientRepository` is responsible for persisting `OAuth2AuthorizedClient`(s) between web requests. Whereas, the primary role of `OAuth2AuthorizedClientService` is to manage `OAuth2AuthorizedClient`(s) at the application-level. @@ -396,7 +396,7 @@ Alternatively, the JDBC implementation `JdbcOAuth2AuthorizedClientService` may b [[oauth2Client-authorized-manager-provider]] -==== OAuth2AuthorizedClientManager / OAuth2AuthorizedClientProvider +=== OAuth2AuthorizedClientManager / OAuth2AuthorizedClientProvider The `OAuth2AuthorizedClientManager` is responsible for the overall management of `OAuth2AuthorizedClient`(s). @@ -608,23 +608,23 @@ fun authorizedClientManager( [[oauth2Client-auth-grant-support]] -=== Authorization Grant Support +== Authorization Grant Support [[oauth2Client-auth-code-grant]] -==== Authorization Code +=== Authorization Code [NOTE] Please refer to the OAuth 2.0 Authorization Framework for further details on the https://tools.ietf.org/html/rfc6749#section-1.3.1[Authorization Code] grant. -===== Obtaining Authorization +==== Obtaining Authorization [NOTE] Please refer to the https://tools.ietf.org/html/rfc6749#section-4.1.1[Authorization Request/Response] protocol flow for the Authorization Code grant. -===== Initiating the Authorization Request +==== Initiating the Authorization Request The `OAuth2AuthorizationRequestRedirectFilter` uses an `OAuth2AuthorizationRequestResolver` to resolve an `OAuth2AuthorizationRequest` and initiate the Authorization Code grant flow by redirecting the end-user's user-agent to the Authorization Server's Authorization Endpoint. @@ -705,7 +705,7 @@ spring: Configuring the `redirect-uri` with `URI` template variables is especially useful when the OAuth 2.0 Client is running behind a <>. This ensures that the `X-Forwarded-*` headers are used when expanding the `redirect-uri`. -===== Customizing the Authorization Request +==== Customizing the Authorization Request One of the primary use cases an `OAuth2AuthorizationRequestResolver` can realize is the ability to customize the Authorization Request with additional parameters above the standard parameters defined in the OAuth 2.0 Authorization Framework. @@ -852,7 +852,7 @@ private fun authorizationRequestCustomizer(): Consumer>`. The default implementation `OAuth2AuthorizationCodeGrantRequestEntityConverter` builds a `RequestEntity` representation of a standard https://tools.ietf.org/html/rfc6749#section-4.1.3[OAuth 2.0 Access Token Request]. @@ -932,7 +932,7 @@ However, providing a custom `Converter`, would allow you to extend the standard IMPORTANT: The custom `Converter` must return a valid `RequestEntity` representation of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider. -===== Customizing the Access Token Response +==== Customizing the Access Token Response On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultAuthorizationCodeTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`. The default `RestOperations` is configured as follows: @@ -1021,13 +1021,13 @@ class OAuth2ClientSecurityConfig : WebSecurityConfigurerAdapter() { [[oauth2Client-refresh-token-grant]] -==== Refresh Token +=== Refresh Token [NOTE] Please refer to the OAuth 2.0 Authorization Framework for further details on the https://tools.ietf.org/html/rfc6749#section-1.5[Refresh Token]. -===== Refreshing an Access Token +==== Refreshing an Access Token [NOTE] Please refer to the https://tools.ietf.org/html/rfc6749#section-6[Access Token Request/Response] protocol flow for the Refresh Token grant. @@ -1037,7 +1037,7 @@ The default implementation of `OAuth2AccessTokenResponseClient` for the Refresh The `DefaultRefreshTokenTokenResponseClient` is quite flexible as it allows you to customize the pre-processing of the Token Request and/or post-handling of the Token Response. -===== Customizing the Access Token Request +==== Customizing the Access Token Request If you need to customize the pre-processing of the Token Request, you can provide `DefaultRefreshTokenTokenResponseClient.setRequestEntityConverter()` with a custom `Converter>`. The default implementation `OAuth2RefreshTokenGrantRequestEntityConverter` builds a `RequestEntity` representation of a standard https://tools.ietf.org/html/rfc6749#section-6[OAuth 2.0 Access Token Request]. @@ -1046,7 +1046,7 @@ However, providing a custom `Converter`, would allow you to extend the standard IMPORTANT: The custom `Converter` must return a valid `RequestEntity` representation of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider. -===== Customizing the Access Token Response +==== Customizing the Access Token Response On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultRefreshTokenTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`. The default `RestOperations` is configured as follows: @@ -1127,13 +1127,13 @@ If the `OAuth2AuthorizedClient.getRefreshToken()` is available and the `OAuth2Au [[oauth2Client-client-creds-grant]] -==== Client Credentials +=== Client Credentials [NOTE] Please refer to the OAuth 2.0 Authorization Framework for further details on the https://tools.ietf.org/html/rfc6749#section-1.3.4[Client Credentials] grant. -===== Requesting an Access Token +==== Requesting an Access Token [NOTE] Please refer to the https://tools.ietf.org/html/rfc6749#section-4.4.2[Access Token Request/Response] protocol flow for the Client Credentials grant. @@ -1143,7 +1143,7 @@ The default implementation of `OAuth2AccessTokenResponseClient` for the Client C The `DefaultClientCredentialsTokenResponseClient` is quite flexible as it allows you to customize the pre-processing of the Token Request and/or post-handling of the Token Response. -===== Customizing the Access Token Request +==== Customizing the Access Token Request If you need to customize the pre-processing of the Token Request, you can provide `DefaultClientCredentialsTokenResponseClient.setRequestEntityConverter()` with a custom `Converter>`. The default implementation `OAuth2ClientCredentialsGrantRequestEntityConverter` builds a `RequestEntity` representation of a standard https://tools.ietf.org/html/rfc6749#section-4.4.2[OAuth 2.0 Access Token Request]. @@ -1152,7 +1152,7 @@ However, providing a custom `Converter`, would allow you to extend the standard IMPORTANT: The custom `Converter` must return a valid `RequestEntity` representation of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider. -===== Customizing the Access Token Response +==== Customizing the Access Token Response On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultClientCredentialsTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`. The default `RestOperations` is configured as follows: @@ -1226,7 +1226,7 @@ authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider) `OAuth2AuthorizedClientProviderBuilder.builder().clientCredentials()` configures a `ClientCredentialsOAuth2AuthorizedClientProvider`, which is an implementation of an `OAuth2AuthorizedClientProvider` for the Client Credentials grant. -===== Using the Access Token +==== Using the Access Token Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration: @@ -1361,13 +1361,13 @@ If not provided, it will default to `ServletRequestAttributes` using `RequestCon [[oauth2Client-password-grant]] -==== Resource Owner Password Credentials +=== Resource Owner Password Credentials [NOTE] Please refer to the OAuth 2.0 Authorization Framework for further details on the https://tools.ietf.org/html/rfc6749#section-1.3.3[Resource Owner Password Credentials] grant. -===== Requesting an Access Token +==== Requesting an Access Token [NOTE] Please refer to the https://tools.ietf.org/html/rfc6749#section-4.3.2[Access Token Request/Response] protocol flow for the Resource Owner Password Credentials grant. @@ -1377,7 +1377,7 @@ The default implementation of `OAuth2AccessTokenResponseClient` for the Resource The `DefaultPasswordTokenResponseClient` is quite flexible as it allows you to customize the pre-processing of the Token Request and/or post-handling of the Token Response. -===== Customizing the Access Token Request +==== Customizing the Access Token Request If you need to customize the pre-processing of the Token Request, you can provide `DefaultPasswordTokenResponseClient.setRequestEntityConverter()` with a custom `Converter>`. The default implementation `OAuth2PasswordGrantRequestEntityConverter` builds a `RequestEntity` representation of a standard https://tools.ietf.org/html/rfc6749#section-4.3.2[OAuth 2.0 Access Token Request]. @@ -1386,7 +1386,7 @@ However, providing a custom `Converter`, would allow you to extend the standard IMPORTANT: The custom `Converter` must return a valid `RequestEntity` representation of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider. -===== Customizing the Access Token Response +==== Customizing the Access Token Response On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultPasswordTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`. The default `RestOperations` is configured as follows: @@ -1461,7 +1461,7 @@ authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider) `OAuth2AuthorizedClientProviderBuilder.builder().password()` configures a `PasswordOAuth2AuthorizedClientProvider`, which is an implementation of an `OAuth2AuthorizedClientProvider` for the Resource Owner Password Credentials grant. -===== Using the Access Token +==== Using the Access Token Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration: @@ -1639,13 +1639,13 @@ If not provided, it will default to `ServletRequestAttributes` using `RequestCon [[oauth2Client-jwt-bearer-grant]] -==== JWT Bearer +=== JWT Bearer [NOTE] Please refer to JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants for further details on the https://datatracker.ietf.org/doc/html/rfc7523[JWT Bearer] grant. -===== Requesting an Access Token +==== Requesting an Access Token [NOTE] Please refer to the https://datatracker.ietf.org/doc/html/rfc7523#section-2.1[Access Token Request/Response] protocol flow for the JWT Bearer grant. @@ -1655,14 +1655,14 @@ The default implementation of `OAuth2AccessTokenResponseClient` for the JWT Bear The `DefaultJwtBearerTokenResponseClient` is quite flexible as it allows you to customize the pre-processing of the Token Request and/or post-handling of the Token Response. -===== Customizing the Access Token Request +==== Customizing the Access Token Request If you need to customize the pre-processing of the Token Request, you can provide `DefaultJwtBearerTokenResponseClient.setRequestEntityConverter()` with a custom `Converter>`. The default implementation `JwtBearerGrantRequestEntityConverter` builds a `RequestEntity` representation of a https://datatracker.ietf.org/doc/html/rfc7523#section-2.1[OAuth 2.0 Access Token Request]. However, providing a custom `Converter`, would allow you to extend the Token Request and add custom parameter(s). -===== Customizing the Access Token Response +==== Customizing the Access Token Response On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultJwtBearerTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`. The default `RestOperations` is configured as follows: @@ -1738,7 +1738,7 @@ authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider) ---- ==== -===== Using the Access Token +==== Using the Access Token Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration: @@ -1857,11 +1857,11 @@ class OAuth2ResourceServerController { [[oauth2Client-client-auth-support]] -=== Client Authentication Support +== Client Authentication Support [[oauth2Client-jwt-bearer-auth]] -==== JWT Bearer +=== JWT Bearer [NOTE] Please refer to JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants for further details on https://datatracker.ietf.org/doc/html/rfc7523#section-2.2[JWT Bearer] Client Authentication. @@ -1874,7 +1874,7 @@ The `java.security.PrivateKey` or `javax.crypto.SecretKey` used for signing the is supplied by the `com.nimbusds.jose.jwk.JWK` resolver associated with `NimbusJwtClientAuthenticationParametersConverter`. -===== Authenticate using `private_key_jwt` +==== Authenticate using `private_key_jwt` Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration: @@ -1949,7 +1949,7 @@ tokenResponseClient.setRequestEntityConverter(requestEntityConverter) ==== -===== Authenticate using `client_secret_jwt` +==== Authenticate using `client_secret_jwt` Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration: @@ -2024,11 +2024,11 @@ tokenResponseClient.setRequestEntityConverter(requestEntityConverter) [[oauth2Client-additional-features]] -=== Additional Features +== Additional Features [[oauth2Client-registered-authorized-client]] -==== Resolving an Authorized Client +=== Resolving an Authorized Client The `@RegisteredOAuth2AuthorizedClient` annotation provides the capability of resolving a method parameter to an argument value of type `OAuth2AuthorizedClient`. This is a convenient alternative compared to accessing the `OAuth2AuthorizedClient` using the `OAuth2AuthorizedClientManager` or `OAuth2AuthorizedClientService`. @@ -2072,7 +2072,7 @@ The `@RegisteredOAuth2AuthorizedClient` annotation is handled by `OAuth2Authoriz [[oauth2Client-webclient-servlet]] -=== WebClient integration for Servlet Environments +== WebClient integration for Servlet Environments The OAuth 2.0 Client support integrates with `WebClient` using an `ExchangeFilterFunction`. @@ -2114,7 +2114,7 @@ fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClien ---- ==== -==== Providing the Authorized Client +=== Providing the Authorized Client The `ServletOAuth2AuthorizedClientExchangeFilterFunction` determines the client to use (for a request) by resolving the `OAuth2AuthorizedClient` from the `ClientRequest.attributes()` (request attributes). @@ -2213,7 +2213,7 @@ fun index(): String { <1> `clientRegistrationId()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`. -==== Defaulting the Authorized Client +=== Defaulting the Authorized Client If neither `OAuth2AuthorizedClient` or `ClientRegistration.getRegistrationId()` is provided as a request attribute, the `ServletOAuth2AuthorizedClientExchangeFilterFunction` can determine the _default_ client to use depending on it's configuration. diff --git a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-login.adoc b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-login.adoc index 3981eda033..db121281b0 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-login.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-login.adoc @@ -1,5 +1,5 @@ [[oauth2login]] -== OAuth 2.0 Login += 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". @@ -8,7 +8,7 @@ NOTE: OAuth 2.0 Login is implemented by using the *Authorization Code Grant*, as [[oauth2login-sample-boot]] -=== Spring Boot 2.x Sample +== Spring Boot 2.x Sample Spring Boot 2.x brings full auto-configuration capabilities for OAuth 2.0 Login. @@ -21,7 +21,7 @@ This section shows how to configure the {gh-samples-url}/servlet/spring-boot/jav [[oauth2login-sample-initial-setup]] -==== 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. @@ -33,7 +33,7 @@ After completing the "Obtain OAuth 2.0 credentials" instructions, you should hav [[oauth2login-sample-redirect-uri]] -==== Setting the 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. @@ -47,7 +47,7 @@ Also, see the supported <> properties. @@ -149,7 +149,7 @@ A `ClientRegistration` can be initially configured using discovery of an OpenID [[oauth2login-common-oauth2-provider]] -=== CommonOAuth2Provider +== CommonOAuth2Provider `CommonOAuth2Provider` pre-defines a set of default client properties for a number of well known providers: Google, GitHub, Facebook, and Okta. @@ -196,7 +196,7 @@ spring: [[oauth2login-custom-provider-properties]] -=== Configuring 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). @@ -229,7 +229,7 @@ spring: [[oauth2login-override-boot-autoconfig]] -=== Overriding Spring Boot 2.x Auto-configuration +== Overriding Spring Boot 2.x Auto-configuration The Spring Boot 2.x auto-configuration class for OAuth Client support is `OAuth2ClientAutoConfiguration`. @@ -246,7 +246,7 @@ If you need to override the auto-configuration based on your specific requiremen [[oauth2login-register-clientregistrationrepository-bean]] -==== Register a ClientRegistrationRepository @Bean +=== Register a ClientRegistrationRepository @Bean The following example shows how to register a `ClientRegistrationRepository` `@Bean`: @@ -313,7 +313,7 @@ class OAuth2LoginConfig { [[oauth2login-provide-websecurityconfigureradapter]] -==== Provide a WebSecurityConfigurerAdapter +=== Provide a WebSecurityConfigurerAdapter The following example shows how to provide a `WebSecurityConfigurerAdapter` with `@EnableWebSecurity` and enable OAuth 2.0 login through `httpSecurity.oauth2Login()`: @@ -356,7 +356,7 @@ class OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() { [[oauth2login-completely-override-autoconfiguration]] -==== Completely Override the Auto-configuration +=== Completely Override the Auto-configuration The following example shows how to completely override the auto-configuration by registering a `ClientRegistrationRepository` `@Bean` and providing a `WebSecurityConfigurerAdapter`. @@ -451,7 +451,7 @@ class OAuth2LoginConfig { [[oauth2login-javaconfig-wo-boot]] -=== Java Configuration without Spring Boot 2.x +== Java Configuration without Spring Boot 2.x If you are not able to use Spring Boot 2.x and would like to configure one of the pre-defined providers in `CommonOAuth2Provider` (for example, Google), apply the following configuration: @@ -575,7 +575,7 @@ open class OAuth2LoginConfig { [[oauth2login-advanced]] -=== Advanced Configuration +== 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. @@ -770,7 +770,7 @@ The following sections go into more detail on each of the configuration options [[oauth2login-advanced-login-page]] -==== OAuth 2.0 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). @@ -865,7 +865,7 @@ The following line shows an example: [[oauth2login-advanced-redirection-endpoint]] -==== 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. @@ -956,7 +956,7 @@ return CommonOAuth2Provider.GOOGLE.getBuilder("google") [[oauth2login-advanced-userinfo-endpoint]] -==== UserInfo Endpoint +=== UserInfo Endpoint The UserInfo Endpoint includes a number of configuration options, as described in the following sub-sections: @@ -966,7 +966,7 @@ The UserInfo Endpoint includes a number of configuration options, as described i [[oauth2login-advanced-map-authorities]] -===== Mapping User 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. @@ -980,7 +980,7 @@ There are a couple of options to choose from when mapping user authorities: [[oauth2login-advanced-map-authorities-grantedauthoritiesmapper]] -====== Using a GrantedAuthoritiesMapper +===== Using a GrantedAuthoritiesMapper Provide an implementation of `GrantedAuthoritiesMapper` and configure it as shown in the following example: @@ -1126,7 +1126,7 @@ class OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() { ==== [[oauth2login-advanced-map-authorities-oauth2userservice]] -====== Delegation-based strategy with 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). @@ -1228,7 +1228,7 @@ class OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() { [[oauth2login-advanced-oauth2-user-service]] -===== OAuth 2.0 UserService +==== OAuth 2.0 UserService `DefaultOAuth2UserService` is an implementation of an `OAuth2UserService` that supports standard OAuth 2.0 Provider's. @@ -1304,7 +1304,7 @@ class OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() { [[oauth2login-advanced-oidc-user-service]] -===== OpenID Connect 1.0 UserService +==== OpenID Connect 1.0 UserService `OidcUserService` is an implementation of an `OAuth2UserService` that supports OpenID Connect 1.0 Provider's. @@ -1364,7 +1364,7 @@ class OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() { [[oauth2login-advanced-idtoken-verify]] -==== ID Token Signature Verification +=== ID Token Signature Verification OpenID Connect 1.0 Authentication introduces the https://openid.net/specs/openid-connect-core-1_0.html#IDToken[ID Token], which is a security token that contains Claims about the Authentication of an End-User by an Authorization Server when used by a Client. @@ -1409,7 +1409,7 @@ If more than one `ClientRegistration` is configured for OpenID Connect 1.0 Authe [[oauth2login-advanced-oidc-logout]] -==== OpenID Connect 1.0 Logout +=== OpenID Connect 1.0 Logout OpenID Connect Session Management 1.0 allows the ability to log out the End-User at the Provider using the Client. One of the strategies available is https://openid.net/specs/openid-connect-session-1_0.html#RPLogout[RP-Initiated Logout]. diff --git a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc index c57881a269..2919b251ba 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc @@ -1,5 +1,5 @@ [[oauth2resourceserver]] -== OAuth 2.0 Resource Server += OAuth 2.0 Resource Server :figures: images/servlet/oauth2 :icondir: images/icons @@ -59,18 +59,18 @@ image:{icondir}/number_4.png[] If authentication is successful, then __Success__ * The `BearerTokenAuthenticationFilter` invokes `FilterChain.doFilter(request,response)` to continue with the rest of the application logic. [[oauth2resourceserver-jwt-minimaldependencies]] -=== Minimal Dependencies for JWT +== Minimal Dependencies for JWT 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-jwt-minimalconfiguration]] -=== Minimal Configuration for JWTs +== Minimal Configuration for JWTs 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 +=== Specifying the Authorization Server In a Spring Boot application, to specify which authorization server to use, simply do: @@ -93,7 +93,7 @@ This endpoint is referred to as a https://openid.net/specs/openid-connect-discov And that's it! -==== Startup Expectations +=== Startup Expectations When this property and these dependencies are used, Resource Server will automatically configure itself to validate JWT-encoded Bearer Tokens. @@ -109,7 +109,7 @@ A consequence of this process is that the authorization server must be up and re [NOTE] If the authorization server is down when Resource Server queries it (given appropriate timeouts), then startup will fail. -==== Runtime Expectations +=== Runtime Expectations Once the application is started up, Resource Server will attempt to process any request containing an `Authorization: Bearer` header: @@ -139,7 +139,7 @@ From here, consider jumping to: * <> [[oauth2resourceserver-jwt-architecture]] -=== How JWT Authentication Works +== How JWT Authentication Works Next, let's see the architectural components that Spring Security uses to support https://tools.ietf.org/html/rfc7519[JWT] Authentication in servlet-based applications, like the one we just saw. @@ -165,7 +165,7 @@ image:{icondir}/number_5.png[] When authentication is successful, the <> by the authentication `Filter`. [[oauth2resourceserver-jwt-jwkseturi]] -=== Specifying the Authorization Server JWK Set Uri Directly +== Specifying the Authorization Server JWK Set Uri Directly If the authorization server doesn't support any configuration endpoints, or if Resource Server must be able to start up independently from the authorization server, then the `jwk-set-uri` can be supplied as well: @@ -190,7 +190,7 @@ We still specify the `issuer-uri` so that Resource Server still validates the `i This property can also be supplied directly on the <>. [[oauth2resourceserver-jwt-sansboot]] -=== Overriding or Replacing Boot Auto Configuration +== Overriding or Replacing Boot Auto Configuration There are two ``@Bean``s that Spring Boot generates on Resource Server's behalf. @@ -343,7 +343,7 @@ And the `JwtDecoder` like so: ==== [[oauth2resourceserver-jwt-jwkseturi-dsl]] -==== Using `jwkSetUri()` +=== Using `jwkSetUri()` An authorization server's JWK Set Uri can be configured <> or it can be supplied in the DSL: @@ -403,7 +403,7 @@ class DirectlyConfiguredJwkSetUri : WebSecurityConfigurerAdapter() { Using `jwkSetUri()` takes precedence over any configuration property. [[oauth2resourceserver-jwt-decoder-dsl]] -==== Using `decoder()` +=== Using `decoder()` More powerful than `jwkSetUri()` is `decoder()`, which will completely replace any Boot auto configuration of <>: @@ -463,7 +463,7 @@ class DirectlyConfiguredJwtDecoder : WebSecurityConfigurerAdapter() { This is handy when deeper configuration, like <>, <>, or <>, is necessary. [[oauth2resourceserver-jwt-decoder-bean]] -==== Exposing a `JwtDecoder` `@Bean` +=== Exposing a `JwtDecoder` `@Bean` Or, exposing a <> `@Bean` has the same effect as `decoder()`: @@ -488,14 +488,14 @@ fun jwtDecoder(): JwtDecoder { ==== [[oauth2resourceserver-jwt-decoder-algorithm]] -=== Configuring Trusted Algorithms +== Configuring Trusted Algorithms By default, `NimbusJwtDecoder`, and hence Resource Server, will only trust and verify tokens using `RS256`. You can customize this via <>, <>, or from the <>. [[oauth2resourceserver-jwt-boot-algorithm]] -==== Via Spring Boot +=== Via Spring Boot The simplest way to set the algorithm is as a property: @@ -511,7 +511,7 @@ spring: ---- [[oauth2resourceserver-jwt-decoder-builder]] -==== Using a Builder +=== Using a Builder For greater power, though, we can use a builder that ships with `NimbusJwtDecoder`: @@ -592,7 +592,7 @@ fun jwtDecoder(): JwtDecoder { ==== [[oauth2resourceserver-jwt-decoder-jwk-response]] -==== From JWK Set response +=== From JWK Set response Since Spring Security's JWT support is based off of Nimbus, you can use all it's great features as well. @@ -632,13 +632,13 @@ fun jwtDecoder(): JwtDecoder { ==== [[oauth2resourceserver-jwt-decoder-public-key]] -=== Trusting a Single Asymmetric Key +== Trusting a Single Asymmetric Key Simpler than backing a Resource Server with a JWK Set endpoint is to hard-code an RSA public key. The public key can be provided via <> or by <>. [[oauth2resourceserver-jwt-decoder-public-key-boot]] -==== Via Spring Boot +=== Via Spring Boot Specifying a key via Spring Boot is quite simple. The key's location can be specified like so: @@ -705,7 +705,7 @@ val key: RSAPublicKey? = null ==== [[oauth2resourceserver-jwt-decoder-public-key-builder]] -==== Using a Builder +=== Using a Builder To wire an `RSAPublicKey` directly, you can simply use the appropriate `NimbusJwtDecoder` builder, like so: @@ -730,7 +730,7 @@ fun jwtDecoder(): JwtDecoder { ==== [[oauth2resourceserver-jwt-decoder-secret-key]] -=== Trusting a Single Symmetric Key +== Trusting a Single Symmetric Key Using a single symmetric key is also simple. You can simply load in your `SecretKey` and use the appropriate `NimbusJwtDecoder` builder, like so: @@ -756,7 +756,7 @@ fun jwtDecoder(): JwtDecoder { ==== [[oauth2resourceserver-jwt-authorization]] -=== Configuring 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: @@ -837,7 +837,7 @@ fun getMessages(): List { } ==== [[oauth2resourceserver-jwt-authorization-extraction]] -==== Extracting Authorities Manually +=== 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. @@ -1024,14 +1024,14 @@ class CustomAuthenticationConverterConfig : WebSecurityConfigurerAdapter() { ==== [[oauth2resourceserver-jwt-validation]] -=== Configuring 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-jwt-validation-clockskew]] -==== Customizing Timestamp Validation +=== 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. @@ -1081,7 +1081,7 @@ fun jwtDecoder(): JwtDecoder { By default, Resource Server configures a clock skew of 60 seconds. [[oauth2resourceserver-jwt-validation-custom]] -==== Configuring a Custom Validator +=== Configuring a Custom Validator Adding a check for the `aud` claim is simple with the `OAuth2TokenValidator` API: @@ -1192,7 +1192,7 @@ fun jwtDecoder(): JwtDecoder { ==== [[oauth2resourceserver-jwt-claimsetmapping]] -=== Configuring Claim Set Mapping +== 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. @@ -1206,7 +1206,7 @@ Or, quite simply, a resource server may want to add or remove claims from a JWT For these purposes, Resource Server supports mapping the JWT claim set with `MappedJwtClaimSetConverter`. [[oauth2resourceserver-jwt-claimsetmapping-singleclaim]] -==== Customizing the Conversion of a Single Claim +=== Customizing the Conversion of a Single Claim By default, `MappedJwtClaimSetConverter` will attempt to coerce claims into the following types: @@ -1257,7 +1257,7 @@ fun jwtDecoder(): JwtDecoder { This will keep all the defaults, except it will override the default claim converter for `sub`. [[oauth2resourceserver-jwt-claimsetmapping-add]] -==== Adding a Claim +=== Adding a Claim `MappedJwtClaimSetConverter` can also be used to add a custom claim, for example, to adapt to an existing system: @@ -1276,7 +1276,7 @@ MappedJwtClaimSetConverter.withDefaults(mapOf("custom" to Converter ==== [[oauth2resourceserver-jwt-claimsetmapping-remove]] -==== Removing a Claim +=== Removing a Claim And removing a claim is also simple, using the same API: @@ -1295,7 +1295,7 @@ MappedJwtClaimSetConverter.withDefaults(mapOf("legacyclaim" to Converter, Map>`: @@ -1360,7 +1360,7 @@ fun jwtDecoder(): JwtDecoder { ==== [[oauth2resourceserver-jwt-timeouts]] -=== Configuring Timeouts +== Configuring Timeouts By default, Resource Server uses connection and socket timeouts of 30 seconds each for coordinating with the authorization server. @@ -1436,14 +1436,14 @@ NOTE: Whether it's socket or cache timeouts, you may instead want to work with N To do so, remember that `NimbusJwtDecoder` ships with a constructor that takes Nimbus's `JWTProcessor`. [[oauth2resourceserver-opaque-minimaldependencies]] -=== Minimal Dependencies for Introspection +== Minimal Dependencies for Introspection As described in <> most of Resource Server support is collected in `spring-security-oauth2-resource-server`. However unless a custom <> is provided, the Resource Server will fallback to NimbusOpaqueTokenIntrospector. Meaning that both `spring-security-oauth2-resource-server` and `oauth2-oidc-sdk` are necessary in order to have a working minimal Resource Server that supports opaque Bearer Tokens. Please refer to `spring-security-oauth2-resource-server` in order to determin the correct version for `oauth2-oidc-sdk`. [[oauth2resourceserver-opaque-minimalconfiguration]] -=== Minimal Configuration for Introspection +== Minimal Configuration for Introspection Typically, an opaque token can be verified via an https://tools.ietf.org/html/rfc7662[OAuth 2.0 Introspection Endpoint], hosted by the authorization server. This can be handy when revocation is a requirement. @@ -1452,7 +1452,7 @@ When using https://spring.io/projects/spring-boot[Spring Boot], configuring an a First, include the needed dependencies and second, indicate the introspection endpoint details. [[oauth2resourceserver-opaque-introspectionuri]] -==== Specifying the Authorization Server +=== Specifying the Authorization Server To specify where the introspection endpoint is, simply do: @@ -1477,13 +1477,13 @@ If the authorization server responses that the token is valid, then it is. And that's it! -==== Startup Expectations +=== Startup Expectations When this property and these dependencies are used, Resource Server will automatically configure itself to validate Opaque Bearer Tokens. This startup process is quite a bit simpler than for JWTs since no endpoints need to be discovered and no additional validation rules get added. -==== Runtime Expectations +=== Runtime Expectations Once the application is started up, Resource Server will attempt to process any request containing an `Authorization: Bearer` header: @@ -1510,7 +1510,7 @@ From here, you may want to jump to: * <> [[oauth2resourceserver-opaque-architecture]] -=== How Opaque Token Authentication Works +== How Opaque Token Authentication Works Next, let's see the architectural components that Spring Security uses to support https://tools.ietf.org/html/rfc7662[opaque token] Authentication in servlet-based applications, like the one we just saw. @@ -1532,7 +1532,7 @@ When authentication is successful, the <> by the authentication `Filter`. [[oauth2resourceserver-opaque-attributes]] -=== Looking Up Attributes Post-Authentication +== Looking Up Attributes Post-Authentication Once a token is authenticated, an instance of `BearerTokenAuthentication` is set in the `SecurityContext`. @@ -1580,7 +1580,7 @@ fun foo(@AuthenticationPrincipal principal: OAuth2AuthenticatedPrincipal): Strin ---- ==== -==== Looking Up Attributes Via SpEL +=== Looking Up Attributes Via SpEL Of course, this also means that attributes can be accessed via SpEL. @@ -1607,7 +1607,7 @@ fun forFoosEyesOnly(): String { ==== [[oauth2resourceserver-opaque-sansboot]] -=== Overriding or Replacing Boot Auto Configuration +== Overriding or Replacing Boot Auto Configuration There are two ``@Bean``s that Spring Boot generates on Resource Server's behalf. @@ -1758,7 +1758,7 @@ And the <> or it can be supplied in the DSL: @@ -1820,7 +1820,7 @@ class DirectlyConfiguredIntrospectionUri : WebSecurityConfigurerAdapter() { Using `introspectionUri()` takes precedence over any configuration property. [[oauth2resourceserver-opaque-introspector-dsl]] -==== Using `introspector()` +=== Using `introspector()` More powerful than `introspectionUri()` is `introspector()`, which will completely replace any Boot auto configuration of <>: @@ -1880,7 +1880,7 @@ class DirectlyConfiguredIntrospector : WebSecurityConfigurerAdapter() { This is handy when deeper configuration, like <>, <>, or <>, is necessary. [[oauth2resourceserver-opaque-introspector-bean]] -==== Exposing a `OpaqueTokenIntrospector` `@Bean` +=== Exposing a `OpaqueTokenIntrospector` `@Bean` Or, exposing a <> `@Bean` has the same effect as `introspector()`: @@ -1893,7 +1893,7 @@ public OpaqueTokenIntrospector introspector() { ---- [[oauth2resourceserver-opaque-authorization]] -=== Configuring Authorization +== Configuring Authorization An OAuth 2.0 Introspection endpoint will typically return a `scope` attribute, indicating the scopes (or authorities) it's been granted, for example: @@ -1974,7 +1974,7 @@ fun getMessages(): List {} ==== [[oauth2resourceserver-opaque-authorization-extraction]] -==== Extracting Authorities Manually +=== Extracting Authorities Manually By default, Opaque Token support will extract the scope claim from an introspection response and parse it into individual `GrantedAuthority` instances. @@ -2058,7 +2058,7 @@ fun introspector(): OpaqueTokenIntrospector { ==== [[oauth2resourceserver-opaque-timeouts]] -=== Configuring Timeouts +== Configuring Timeouts By default, Resource Server uses connection and socket timeouts of 30 seconds each for coordinating with the authorization server. @@ -2099,7 +2099,7 @@ fun introspector(builder: RestTemplateBuilder, properties: OAuth2ResourceServerP ==== [[oauth2resourceserver-opaque-jwt-introspector]] -=== Using Introspection with JWTs +== Using Introspection with JWTs A common question is whether or not introspection is compatible with JWTs. Spring Security's Opaque Token support has been designed to not care about the format of the token -- it will gladly pass any token to the introspection endpoint provided. @@ -2204,7 +2204,7 @@ fun introspector(): OpaqueTokenIntrospector { ==== [[oauth2resourceserver-opaque-userinfo]] -=== Calling a `/userinfo` Endpoint +== Calling a `/userinfo` Endpoint Generally speaking, a Resource Server doesn't care about the underlying user, but instead about the authorities that have been granted. @@ -2324,7 +2324,7 @@ fun introspector(): OpaqueTokenIntrospector { ==== [[oauth2reourceserver-opaqueandjwt]] -=== Supporting both JWT and Opaque Token +== Supporting both JWT and Opaque Token In some cases, you may have a need to access both kinds of tokens. For example, you may support more than one tenant where one tenant issues JWTs and the other issues opaque tokens. @@ -2407,7 +2407,7 @@ http { ==== [[oauth2resourceserver-multitenancy]] -=== Multi-tenancy +== Multi-tenancy A resource server is considered multi-tenant when there are multiple strategies for verifying a bearer token, keyed by some tenant identifier. @@ -2419,7 +2419,7 @@ In each case, there are two things that need to be done and trade-offs associate 1. Resolve the tenant 2. Propagate the tenant -==== Resolving the Tenant By Claim +=== Resolving the Tenant By Claim One way to differentiate tenants is by the issuer claim. Since the issuer claim accompanies signed JWTs, this can be done with the `JwtIssuerAuthenticationManagerResolver`, like so: @@ -2478,7 +2478,7 @@ This is nice because the issuer endpoints are loaded lazily. In fact, the corresponding `JwtAuthenticationProvider` is instantiated only when the first request with the corresponding issuer is sent. This allows for an application startup that is independent from those authorization servers being up and available. -===== Dynamic Tenants +==== Dynamic Tenants Of course, you may not want to restart the application each time a new tenant is added. In this case, you can configure the `JwtIssuerAuthenticationManagerResolver` with a repository of `AuthenticationManager` instances, which you can edit at runtime, like so: @@ -2538,7 +2538,7 @@ This approach allows us to add and remove elements from the repository (shown as NOTE: It would be unsafe to simply take any issuer and construct an `AuthenticationManager` from it. The issuer should be one that the code can verify from a trusted source like a list of allowed issuers. -===== Parsing the Claim Only Once +==== Parsing the Claim Only Once You may have observed that this strategy, while simple, comes with the trade-off that the JWT is parsed once by the `AuthenticationManagerResolver` and then again by the <> later on in the request. @@ -2768,12 +2768,12 @@ For example, if you are resolving it by subdomain, you may need to address the d However, if you resolve it by a claim in the bearer token, read on to learn about <>. [[oauth2resourceserver-bearertoken-resolver]] -=== Bearer Token Resolution +== Bearer Token Resolution By default, Resource Server looks for a bearer token in the `Authorization` header. This, however, can be customized in a handful of ways. -==== Reading the Bearer Token from a Custom Header +=== Reading the Bearer Token from a Custom Header For example, you may have a need to read the bearer token from a custom header. To achieve this, you can expose a `DefaultBearerTokenResolver` as a bean, or wire an instance into the DSL, as you can see in the following example: @@ -2818,7 +2818,7 @@ fun bearerTokenResolver(): BearerTokenResolver { Or, in circumstances where a provider is using both a custom header and value, you can use `HeaderBearerTokenResolver` instead. -==== Reading the Bearer Token from a Form Parameter +=== Reading the Bearer Token from a Form Parameter Or, you may wish to read the token from a form parameter, which you can do by configuring the `DefaultBearerTokenResolver`, as you can see below: @@ -2861,7 +2861,7 @@ http { ---- ==== -=== Bearer Token Propagation +== Bearer Token Propagation Now that you're resource server has validated the token, it might be handy to pass it to downstream services. This is quite simple with `{security-api-url}org/springframework/security/oauth2/server/resource/web/reactive/function/client/ServletBearerExchangeFilterFunction.html[ServletBearerExchangeFilterFunction]`, which you can see in the following example: @@ -2951,7 +2951,7 @@ In this case, the filter will fall back and simply forward the request onto the Unlike the {security-api-url}org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.html[OAuth 2.0 Client filter function], this filter function makes no attempt to renew the token, should it be expired. To obtain this level of support, please use the OAuth 2.0 Client filter. -==== `RestTemplate` support +=== `RestTemplate` support There is no `RestTemplate` equivalent for `ServletBearerExchangeFilterFunction` at the moment, but you can propagate the request's bearer token quite simply with your own interceptor: @@ -3011,7 +3011,7 @@ Unlike the {security-api-url}org/springframework/security/oauth2/client/OAuth2Au To obtain this level of support, please create an interceptor using the <>. [[oauth2resourceserver-bearertoken-failure]] -=== Bearer Token Failure +== Bearer Token Failure A bearer token may be invalid for a number of reasons. For example, the token may no longer be active. diff --git a/docs/modules/ROOT/pages/servlet/test/index.adoc b/docs/modules/ROOT/pages/servlet/test/index.adoc index da7d74db4a..c6597e2aa8 100644 --- a/docs/modules/ROOT/pages/servlet/test/index.adoc +++ b/docs/modules/ROOT/pages/servlet/test/index.adoc @@ -3,11 +3,9 @@ This section describes the testing support provided by Spring Security. -[TIP] -==== To use the Spring Security test support, you must include `spring-security-test-{spring-security-version}.jar` as a dependency of your project. -==== -include::method.adoc[] +At a high level Spring Security's test support provides integration for: -include::mockmvc.adoc[] +* xref:servlet/test/method.adoc[Method Security] - provides test support for Spring Security's method security. +* xref:servlet/test/mockmvc.adoc[MockMvc] - Provides test support to integrate with Spring's `MockMvc`. From b8a362a60f456b55389fe85ceddd3d0c2f528dab Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 30 Jul 2021 13:52:15 -0500 Subject: [PATCH 04/34] Remove include servlet/saml2/index.adoc --- .../ROOT/pages/overview/whats-new.adoc | 2 +- .../ROOT/pages/servlet/appendix/faq.adoc | 82 +- .../pages/servlet/appendix/namespace.adoc | 458 ++--- .../pages/servlet/appendix/proxy-server.adoc | 2 +- .../servlet/authentication/anonymous.adoc | 10 +- .../pages/servlet/authentication/cas.adoc | 22 +- .../pages/servlet/authentication/events.adoc | 6 +- .../pages/servlet/authentication/jaas.adoc | 20 +- .../pages/servlet/authentication/logout.adoc | 12 +- .../pages/servlet/authentication/openid.adoc | 4 +- .../pages/servlet/authentication/preauth.adoc | 20 +- .../servlet/authentication/rememberme.adoc | 14 +- .../pages/servlet/authentication/runas.adoc | 6 +- .../authentication/session-management.adoc | 16 +- .../pages/servlet/authentication/x509.adoc | 8 +- .../pages/servlet/authorization/acls.adoc | 8 +- .../authorization/expression-based.adoc | 26 +- .../authorization/method-security.adoc | 16 +- .../servlet/authorization/secure-objects.adoc | 8 +- .../servlet/integrations/concurrency.adoc | 8 +- .../ROOT/pages/servlet/integrations/cors.adoc | 2 +- .../ROOT/pages/servlet/integrations/data.adoc | 6 +- .../pages/servlet/integrations/jackson.adoc | 2 +- .../servlet/integrations/jsp-taglibs.adoc | 16 +- .../servlet/integrations/localization.adoc | 2 +- .../ROOT/pages/servlet/integrations/mvc.adoc | 16 +- .../servlet/integrations/servlet-api.adoc | 26 +- .../pages/servlet/integrations/websocket.adoc | 32 +- .../ROOT/pages/servlet/saml2/index.adoc | 1616 ++++++++++++++++- .../ROOT/pages/servlet/test/method.adoc | 14 +- .../ROOT/pages/servlet/test/mockmvc.adoc | 66 +- 31 files changed, 2080 insertions(+), 466 deletions(-) diff --git a/docs/modules/ROOT/pages/overview/whats-new.adoc b/docs/modules/ROOT/pages/overview/whats-new.adoc index 3117f3dad1..1bfb085a41 100644 --- a/docs/modules/ROOT/pages/overview/whats-new.adoc +++ b/docs/modules/ROOT/pages/overview/whats-new.adoc @@ -1,5 +1,5 @@ [[new]] -== What's New in Spring Security 5.6 += What's New in Spring Security 5.6 Spring Security 5.6 provides a number of new features. Below are the highlights of the release. diff --git a/docs/modules/ROOT/pages/servlet/appendix/faq.adoc b/docs/modules/ROOT/pages/servlet/appendix/faq.adoc index 911f173982..87dcff4564 100644 --- a/docs/modules/ROOT/pages/servlet/appendix/faq.adoc +++ b/docs/modules/ROOT/pages/servlet/appendix/faq.adoc @@ -1,5 +1,5 @@ [[appendix-faq]] -== Spring Security FAQ += Spring Security FAQ * <> * <> @@ -7,7 +7,7 @@ * <> [[appendix-faq-general-questions]] -=== General Questions +== General Questions . <> . <> @@ -16,7 +16,7 @@ [[appendix-faq-other-concerns]] -==== Will Spring Security take care of all my application security requirements? +=== Will Spring Security take care of all my application security requirements? Spring Security provides you with a very flexible framework for your authentication and authorization requirements, but there are many other considerations for building a secure application that are outside its scope. Web applications are vulnerable to all kinds of attacks which you should be familiar with, preferably before you start development so you can design and code with them in mind from the beginning. @@ -24,7 +24,7 @@ Check out the https://www.owasp.org/[OWASP web site] for information on the majo [[appendix-faq-web-xml]] -==== Why not just use web.xml security? +=== Why not just use web.xml security? Let's assume you're developing an enterprise application based on Spring. There are four security concerns you typically need to address: authentication, web request security, service layer security (i.e. your methods that implement business logic), and domain object instance security (i.e. different domain objects have different permissions). With these typical requirements in mind: @@ -64,7 +64,7 @@ Although when considered within the context of web container portability, config [[appendix-faq-requirements]] -==== What Java and Spring Framework versions are required? +=== What Java and Spring Framework versions are required? Spring Security 3.0 and 3.1 require at least JDK 1.5 and also require Spring 3.0.3 as a minimum. Ideally you should be using the latest release versions to avoid problems. @@ -74,7 +74,7 @@ It should also be compatible with applications using Spring 2.5.x. [[appendix-faq-start-simple]] -==== I'm new to Spring Security and I need to build an application that supports CAS single sign-on over HTTPS, while allowing Basic authentication locally for certain URLs, authenticating against multiple back end user information sources (LDAP and JDBC). I've copied some configuration files I found but it doesn't work. +=== I'm new to Spring Security and I need to build an application that supports CAS single sign-on over HTTPS, while allowing Basic authentication locally for certain URLs, authenticating against multiple back end user information sources (LDAP and JDBC). I've copied some configuration files I found but it doesn't work. What could be wrong? Or substitute an alternative complex scenario... @@ -92,7 +92,7 @@ This will take you through a series of steps to get up and running and get some If you are using other technologies which you aren't familiar with then you should do some research and try to make sure you can use them in isolation before combining them in a complex system. [[appendix-faq-common-problems]] -=== Common Problems +== Common Problems . Authentication .. <> @@ -115,7 +115,7 @@ If you are using other technologies which you aren't familiar with then you shou .. <> [[appendix-faq-bad-credentials]] -==== When I try to log in, I get an error message that says "Bad Credentials". What's wrong? +=== When I try to log in, I get an error message that says "Bad Credentials". What's wrong? This means that authentication has failed. It doesn't say why, as it is good practice to avoid giving details which might help an attacker guess account names or passwords. @@ -129,7 +129,7 @@ If you are using hashed passwords, make sure the value stored in your database i [[appendix-faq-login-loop]] -==== My application goes into an "endless loop" when I try to login, what's going on? +=== My application goes into an "endless loop" when I try to login, what's going on? A common user problem with infinite loop and redirecting to the login page is caused by accidentally configuring the login page as a "secured" resource. Make sure your configuration allows anonymous access to the login page, either by excluding it from the security filter chain or marking it as requiring ROLE_ANONYMOUS. @@ -140,7 +140,7 @@ From Spring Security 2.0.1 onwards, when you are using namespace-based configura [[appendix-faq-anon-access-denied]] -==== I get an exception with the message "Access is denied (user is anonymous);". What's wrong? +=== I get an exception with the message "Access is denied (user is anonymous);". What's wrong? This is a debug level message which occurs the first time an anonymous user attempts to access a protected resource. @@ -158,7 +158,7 @@ It is normal and shouldn't be anything to worry about. [[appendix-faq-cached-secure-page]] -==== Why can I still see a secured page even after I've logged out of my application? +=== Why can I still see a secured page even after I've logged out of my application? The most common reason for this is that your browser has cached the page and you are seeing a copy which is being retrieved from the browsers cache. Verify this by checking whether the browser is actually sending the request (check your server access logs, the debug log or use a suitable browser debugging plugin such as "Tamper Data" for Firefox). This has nothing to do with Spring Security and you should configure your application or server to set the appropriate `Cache-Control` response headers. @@ -166,7 +166,7 @@ Note that SSL requests are never cached. [[auth-exception-credentials-not-found]] -==== I get an exception with the message "An Authentication object was not found in the SecurityContext". What's wrong? +=== I get an exception with the message "An Authentication object was not found in the SecurityContext". What's wrong? This is a another debug level message which occurs the first time an anonymous user attempts to access a protected resource, but when you do not have an `AnonymousAuthenticationFilter` in your filter chain configuration. @@ -184,7 +184,7 @@ It is normal and shouldn't be anything to worry about. [[appendix-faq-ldap-authentication]] -==== I can't get LDAP authentication to work. +=== I can't get LDAP authentication to work. What's wrong with my configuration? Note that the permissions for an LDAP directory often do not allow you to read the password for a user. @@ -232,7 +232,7 @@ fun ldapAuthenticationIsSuccessful() { ---- ==== -==== Session Management +=== Session Management Session management issues are a common source of forum questions. If you are developing Java web applications, you should understand how the session is maintained between the servlet container and the user's browser. @@ -242,7 +242,7 @@ This is entirely handled by the servlet container. [[appendix-faq-concurrent-session-same-browser]] -==== I'm using Spring Security's concurrent session control to prevent users from logging in more than once at a time. +=== I'm using Spring Security's concurrent session control to prevent users from logging in more than once at a time. When I open another browser window after logging in, it doesn't stop me from logging in again. Why can I log in more than once? @@ -256,7 +256,7 @@ If they are already authenticated with the same session, then re-authenticating [[appendix-faq-new-session-on-authentication]] -==== Why does the session Id change when I authenticate through Spring Security? +=== Why does the session Id change when I authenticate through Spring Security? With the default configuration, Spring Security changes the session ID when the user authenticates. If you're using a Servlet 3.1 or newer container, the session ID is simply changed. @@ -266,7 +266,7 @@ You can find more about this online and in the reference manual. [[appendix-faq-tomcat-https-session]] -==== I'm using Tomcat (or some other servlet container) and have enabled HTTPS for my login page, switching back to HTTP afterwards. +=== I'm using Tomcat (or some other servlet container) and have enabled HTTPS for my login page, switching back to HTTP afterwards. It doesn't work - I just end up back at the login page after authenticating. This happens because sessions created under HTTPS, for which the session cookie is marked as "secure", cannot subsequently be used under HTTP. The browser will not send the cookie back to the server and any session state will be lost (including the security context information). Starting a session in HTTP first should work as the session cookie won't be marked as secure. @@ -278,13 +278,13 @@ Even clicking on an HTTPS link from a page accessed over HTTP is potentially ris If you need more convincing, check out a tool like https://github.com/moxie0/sslstrip/[sslstrip]. -==== I'm not switching between HTTP and HTTPS but my session is still getting lost +=== I'm not switching between HTTP and HTTPS but my session is still getting lost Sessions are maintained either by exchanging a session cookie or by adding a `jsessionid` parameter to URLs (this happens automatically if you are using JSTL to output URLs, or if you call `HttpServletResponse.encodeUrl` on URLs (before a redirect, for example). If clients have cookies disabled, and you are not rewriting URLs to include the `jsessionid`, then the session will be lost. Note that the use of cookies is preferred for security reasons, as it does not expose the session information in the URL. [[appendix-faq-session-listener-missing]] -==== I'm trying to use the concurrent session-control support but it won't let me log back in, even if I'm sure I've logged out and haven't exceeded the allowed sessions. +=== I'm trying to use the concurrent session-control support but it won't let me log back in, even if I'm sure I've logged out and haven't exceeded the allowed sessions. Make sure you have added the listener to your web.xml file. It is essential to make sure that the Spring Security session registry is notified when a session is destroyed. @@ -299,7 +299,7 @@ Without it, the session information will not be removed from the registry. ---- [[appendix-faq-unwanted-session-creation]] -==== Spring Security is creating a session somewhere, even though I've configured it not to, by setting the create-session attribute to never. +=== Spring Security is creating a session somewhere, even though I've configured it not to, by setting the create-session attribute to never. This usually means that the user's application is creating a session somewhere, but that they aren't aware of it. The most common culprit is a JSP. Many people aren't aware that JSPs create sessions by default. @@ -308,19 +308,19 @@ To prevent a JSP from creating a session, add the directive `<%@ page session="f If you are having trouble working out where a session is being created, you can add some debugging code to track down the location(s). One way to do this would be to add a `javax.servlet.http.HttpSessionListener` to your application, which calls `Thread.dumpStack()` in the `sessionCreated` method. [[appendix-faq-forbidden-csrf]] -==== I get a 403 Forbidden when performing a POST +=== I get a 403 Forbidden when performing a POST If an HTTP 403 Forbidden is returned for HTTP POST, but works for HTTP GET then the issue is most likely related to https://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/#csrf[CSRF]. Either provide the CSRF Token or disable CSRF protection (not recommended). [[appendix-faq-no-security-on-forward]] -==== I'm forwarding a request to another URL using the RequestDispatcher, but my security constraints aren't being applied. +=== I'm forwarding a request to another URL using the RequestDispatcher, but my security constraints aren't being applied. Filters are not applied by default to forwards or includes. If you really want the security filters to be applied to forwards and/or includes, then you have to configure these explicitly in your web.xml using the element, a child element of . [[appendix-faq-method-security-in-web-context]] -==== I have added Spring Security's element to my application context but if I add security annotations to my Spring MVC controller beans (Struts actions etc.) then they don't seem to have an effect. +=== I have added Spring Security's element to my application context but if I add security annotations to my Spring MVC controller beans (Struts actions etc.) then they don't seem to have an effect. In a Spring web application, the application context which holds the Spring MVC beans for the dispatcher servlet is often separate from the main application context. It is often defined in a file called `myapp-servlet.xml`, where "myapp" is the name assigned to the Spring `DispatcherServlet` in `web.xml`. An application can have multiple ``DispatcherServlet``s, each with its own isolated application context. @@ -333,7 +333,7 @@ Generally we would recommend applying method security at the service layer rathe [[appendix-faq-no-filters-no-context]] -==== I have a user who has definitely been authenticated, but when I try to access the SecurityContextHolder during some requests, the Authentication is null. +=== I have a user who has definitely been authenticated, but when I try to access the SecurityContextHolder during some requests, the Authentication is null. Why can't I see the user information? If you have excluded the request from the security filter chain using the attribute `filters='none'` in the `` element that matches the URL pattern, then the `SecurityContextHolder` will not be populated for that request. @@ -341,12 +341,12 @@ Check the debug log to see whether the request is passing through the filter cha (You are reading the debug log, right?). [[appendix-faq-method-security-with-taglib]] -==== The authorize JSP Tag doesn't respect my method security annotations when using the URL attribute. +=== The authorize JSP Tag doesn't respect my method security annotations when using the URL attribute. Method security will not hide links when using the `url` attribute in `` because we cannot readily reverse engineer what URL is mapped to what controller endpoint as controllers can rely on headers, current user, etc to determine what method to invoke. [[appendix-faq-architecture]] -=== Spring Security Architecture Questions +== Spring Security Architecture Questions . <> . <> @@ -357,14 +357,14 @@ Method security will not hide links when using the `url` attribute in `> . <> @@ -453,7 +453,7 @@ See this https://spring.io/blog/2010/08/02/spring-security-in-google-app-engine/ [[appendix-faq-extra-login-fields]] -==== I need to login in with more information than just the username. +=== I need to login in with more information than just the username. How do I add support for extra login fields (e.g. a company name)? @@ -465,14 +465,14 @@ You will also need to customize the actual authentication process. If you are using a custom authentication token class, for example, you will have to write an `AuthenticationProvider` to handle it (or extend the standard `DaoAuthenticationProvider`). If you have concatenated the fields, you can implement your own `UserDetailsService` which splits them up and loads the appropriate user data for authentication. [[appendix-faq-matching-url-fragments]] -==== How do I apply different intercept-url constraints where only the fragment value of the requested URLs differs (e.g./foo#bar and /foo#blah? +=== How do I apply different intercept-url constraints where only the fragment value of the requested URLs differs (e.g./foo#bar and /foo#blah? You can't do this, since the fragment is not transmitted from the browser to the server. The URLs above are identical from the server's perspective. This is a common question from GWT users. [[appendix-faq-request-details-in-user-service]] -==== How do I access the user's IP Address (or other web-request data) in a UserDetailsService? +=== How do I access the user's IP Address (or other web-request data) in a UserDetailsService? Obviously you can't (without resorting to something like thread-local variables) since the only information supplied to the interface is the username. Instead of implementing `UserDetailsService`, you should implement `AuthenticationProvider` directly and extract the information from the supplied `Authentication` token. @@ -482,7 +482,7 @@ If you are using the namespace, for example with the `` element, the [[appendix-faq-access-session-from-user-service]] -==== How do I access the HttpSession from a UserDetailsService? +=== How do I access the HttpSession from a UserDetailsService? You can't, since the `UserDetailsService` has no awareness of the servlet API. If you want to store custom user data, then you should customize the `UserDetails` object which is returned. This can then be accessed at any point, via the thread-local `SecurityContextHolder`. A call to `SecurityContextHolder.getContext().getAuthentication().getPrincipal()` will return this custom object. @@ -491,14 +491,14 @@ If you really need to access the session, then it must be done by customizing th [[appendix-faq-password-in-user-service]] -==== How do I access the user's password in a UserDetailsService? +=== How do I access the user's password in a UserDetailsService? You can't (and shouldn't). You are probably misunderstanding its purpose. See "<>" above. [[appendix-faq-dynamic-url-metadata]] -==== How do I define the secured URLs within an application dynamically? +=== How do I define the secured URLs within an application dynamically? People often ask about how to store the mapping between secured URLs and security metadata attributes in a database, rather than in the application context. @@ -575,7 +575,7 @@ For more information, look at the code for `DefaultFilterInvocationSecurityMetad [[appendix-faq-ldap-authorities]] -==== How do I authenticate against LDAP but load user roles from a database? +=== How do I authenticate against LDAP but load user roles from a database? The `LdapAuthenticationProvider` bean (which handles normal LDAP authentication in Spring Security) is configured with two separate strategy interfaces, one which performs the authentication and one which loads the user authorities, called `LdapAuthenticator` and `LdapAuthoritiesPopulator` respectively. The `DefaultLdapAuthoritiesPopulator` loads the user authorities from the LDAP directory and has various configuration parameters to allow you to specify how these should be retrieved. @@ -637,7 +637,7 @@ You should also consult the Javadoc for the relevant classes and interfaces. [[appendix-faq-namespace-post-processor]] -==== I want to modify the property of a bean that is created by the namespace, but there is nothing in the schema to support it. +=== I want to modify the property of a bean that is created by the namespace, but there is nothing in the schema to support it. What can I do short of abandoning namespace use? The namespace functionality is intentionally limited, so it doesn't cover everything that you can do with plain beans. diff --git a/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc b/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc index f33d01b906..a2e029da6b 100644 --- a/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc +++ b/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc @@ -1,5 +1,5 @@ [[appendix-namespace]] -== The Security Namespace += The Security Namespace This appendix provides a reference to the elements available in the security namespace and information on the underlying beans they create (a knowledge of the individual classes and how they work together is assumed - you can find more information in the project Javadoc and elsewhere in this document). If you haven't used the namespace before, please read the <> on namespace configuration, as this is intended as a supplement to the information there. Using a good quality XML editor while editing a configuration based on the schema is recommended as this will provide contextual information on which elements and attributes are available as well as comments explaining their purpose. @@ -7,16 +7,16 @@ The namespace is written in https://relaxng.org/[RELAX NG] Compact format and la If you are familiar with this format, you may wish to examine the https://raw.githubusercontent.com/spring-projects/spring-security/main/config/src/main/resources/org/springframework/security/config/spring-security-4.1.rnc[schema file] directly. [[nsa-web]] -=== Web Application Security +== Web Application Security [[nsa-debug]] -==== +=== Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment. [[nsa-http]] -==== +=== If you use an `` element within your application, a `FilterChainProxy` bean named "springSecurityFilterChain" is created and the configuration within the element is used to build a filter chain within `FilterChainProxy`. As of Spring Security 3.1, additional `http` elements can be used to add extra filter chains footnote:[See the pass:specialcharacters,macros[<>] for how to set up the mapping from your `web.xml` ]. @@ -34,7 +34,7 @@ These are fixed and cannot be replaced with alternatives. [[nsa-http-attributes]] -===== Attributes +==== Attributes The attributes on the `` element control some of the properties on the core filters. @@ -151,7 +151,7 @@ The default value is true. [[nsa-http-children]] -===== Child Elements of +==== Child Elements of * <> * <> * <> @@ -177,18 +177,18 @@ The default value is true. [[nsa-access-denied-handler]] -==== +=== This element allows you to set the `errorPage` property for the default `AccessDeniedHandler` used by the `ExceptionTranslationFilter`, using the <> attribute, or to supply your own implementation using the<> attribute. This is discussed in more detail in the section on the <>. [[nsa-access-denied-handler-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-access-denied-handler-attributes]] -===== Attributes +==== Attributes [[nsa-access-denied-handler-error-page]] @@ -202,12 +202,12 @@ Defines a reference to a Spring bean of type `AccessDeniedHandler`. [[nsa-cors]] -==== +=== This element allows for configuring a `CorsFilter`. If no `CorsFilter` or `CorsConfigurationSource` is specified and Spring MVC is on the classpath, a `HandlerMappingIntrospector` is used as the `CorsConfigurationSource`. [[nsa-cors-attributes]] -===== Attributes +==== Attributes The attributes on the `` element control the headers element. [[nsa-cors-ref]] @@ -219,12 +219,12 @@ Optional attribute that specifies the bean name of a `CorsFilter`. Optional attribute that specifies the bean name of a `CorsConfigurationSource` to be injected into a `CorsFilter` created by the XML namespace. [[nsa-cors-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-headers]] -==== +=== This element allows for configuring additional (security) headers to be send with the response. It enables easy configuration for several headers and also allows for setting custom headers through the <> element. Additional information, can be found in the <> section of the reference. @@ -248,7 +248,7 @@ https://www.w3.org/TR/CSP2/[Content Security Policy (CSP)] is a mechanism that w ** `Feature-Policy` - Can be set using the <> element, 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. [[nsa-headers-attributes]] -===== Attributes +==== Attributes The attributes on the `` element control the headers element. @@ -264,14 +264,14 @@ The default is false (the headers are enabled). [[nsa-headers-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-headers-children]] -===== Child Elements of +==== Child Elements of * <> @@ -289,12 +289,12 @@ The default is false (the headers are enabled). [[nsa-cache-control]] -==== +=== Adds `Cache-Control`, `Pragma`, and `Expires` headers to ensure that the browser does not cache your secured pages. [[nsa-cache-control-attributes]] -===== Attributes +==== Attributes [[nsa-cache-control-disabled]] * **disabled** @@ -303,7 +303,7 @@ Default false. [[nsa-cache-control-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -311,13 +311,13 @@ Default false. [[nsa-hsts]] -==== +=== When enabled adds the https://tools.ietf.org/html/rfc6797[Strict-Transport-Security] header to the response for any secure request. This allows the server to instruct browsers to automatically use HTTPS for future requests. [[nsa-hsts-attributes]] -===== Attributes +==== Attributes [[nsa-hsts-disabled]] * **disabled** @@ -347,20 +347,20 @@ Specifies if preload should be included. Default false. [[nsa-hsts-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-hpkp]] -==== +=== When enabled adds the https://tools.ietf.org/html/rfc7469[Public Key Pinning Extension for HTTP] header to the response for any secure request. This allows HTTPS websites to resist impersonation by attackers using mis-issued or otherwise fraudulent certificates. [[nsa-hpkp-attributes]] -===== Attributes +==== Attributes [[nsa-hpkp-disabled]] * **disabled** @@ -391,28 +391,28 @@ Specifies the URI to which the browser should report pin validation failures. [[nsa-hpkp-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-pins]] -==== +=== The list of pins [[nsa-pins-children]] -===== Child Elements of +==== Child Elements of * <> [[nsa-pin]] -==== +=== A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute [[nsa-pin-attributes]] -===== Attributes +==== Attributes [[nsa-pin-algorithm]] * **algorithm** @@ -421,19 +421,19 @@ Default is SHA256. [[nsa-pin-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-content-security-policy]] -==== +=== When enabled adds the https://www.w3.org/TR/CSP2/[Content Security Policy (CSP)] header to the response. CSP is a mechanism that web applications can leverage to mitigate content injection vulnerabilities, such as cross-site scripting (XSS). [[nsa-content-security-policy-attributes]] -===== Attributes +==== Attributes [[nsa-content-security-policy-policy-directives]] * **policy-directives** @@ -445,18 +445,18 @@ Set to true, to enable the Content-Security-Policy-Report-Only header for report Defaults to false. [[nsa-content-security-policy-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-referrer-policy]] -==== +=== When enabled adds the https://www.w3.org/TR/referrer-policy/[Referrer Policy] header to the response. [[nsa-referrer-policy-attributes]] -===== Attributes +==== Attributes [[nsa-referrer-policy-policy]] * **policy** @@ -464,37 +464,37 @@ The policy for the Referrer-Policy header. Default "no-referrer". [[nsa-referrer-policy-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-feature-policy]] -==== +=== When enabled adds the https://wicg.github.io/feature-policy/[Feature Policy] header to the response. [[nsa-feature-policy-attributes]] -===== Attributes +==== Attributes [[nsa-feature-policy-policy-directives]] * **policy-directives** The security policy directive(s) for the Feature-Policy header. [[nsa-feature-policy-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-frame-options]] -==== +=== When enabled adds the https://tools.ietf.org/html/draft-ietf-websec-x-frame-options[X-Frame-Options header] to the response, this allows newer browsers to do some security checks and prevent https://en.wikipedia.org/wiki/Clickjacking[clickjacking] attacks. [[nsa-frame-options-attributes]] -===== Attributes +==== Attributes [[nsa-frame-options-disabled]] * **disabled** @@ -515,34 +515,34 @@ On the other hand, if you specify SAMEORIGIN, you can still use the page in a fr [[nsa-frame-options-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-permissions-policy]] -==== +=== Adds the https://w3c.github.io/webappsec-permissions-policy/[Permissions-Policy header] to the response. [[nsa-permissions-policy-attributes]] -===== Attributes +==== Attributes [[nsa-permissions-policy-policy]] * **policy** The policy value to write for the `Permissions-Policy` header [[nsa-permissions-policy-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-xss-protection]] -==== +=== Adds the https://blogs.msdn.com/b/ie/archive/2008/07/02/ie8-security-part-iv-the-xss-filter.aspx[X-XSS-Protection header] to the response to assist in protecting against https://en.wikipedia.org/wiki/Cross-site_scripting#Non-Persistent[reflected / Type-1 Cross-Site Scripting (XSS)] attacks. This is in no-way a full protection to XSS attacks! [[nsa-xss-protection-attributes]] -===== Attributes +==== Attributes [[nsa-xss-protection-disabled]] @@ -564,20 +564,20 @@ Note that there are sometimes ways of bypassing this mode which can often times [[nsa-xss-protection-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-content-type-options]] -==== +=== Add the X-Content-Type-Options header with the value of nosniff to the response. This https://blogs.msdn.com/b/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx[disables MIME-sniffing] for IE8+ and Chrome extensions. [[nsa-content-type-options-attributes]] -===== Attributes +==== Attributes [[nsa-content-type-options-disabled]] * **disabled** @@ -585,7 +585,7 @@ Specifies if Content Type Options should be disabled. Default false. [[nsa-content-type-options-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -593,12 +593,12 @@ Default false. [[nsa-header]] -====
+===
Add additional headers to the response, both the name and value need to be specified. [[nsa-header-attributes]] -===== Attributes +==== Attributes [[nsa-header-name]] @@ -617,7 +617,7 @@ Reference to a custom implementation of the `HeaderWriter` interface. [[nsa-header-parents]] -===== Parent Elements of
+==== Parent Elements of
* <> @@ -625,13 +625,13 @@ Reference to a custom implementation of the `HeaderWriter` interface. [[nsa-anonymous]] -==== +=== Adds an `AnonymousAuthenticationFilter` to the stack and an `AnonymousAuthenticationProvider`. Required if you are using the `IS_AUTHENTICATED_ANONYMOUSLY` attribute. [[nsa-anonymous-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -639,7 +639,7 @@ Required if you are using the `IS_AUTHENTICATED_ANONYMOUSLY` attribute. [[nsa-anonymous-attributes]] -===== Attributes +==== Attributes [[nsa-anonymous-enabled]] @@ -671,14 +671,14 @@ if unset, defaults to `anonymousUser`. [[nsa-csrf]] -==== +=== This element will add https://en.wikipedia.org/wiki/Cross-site_request_forgery[Cross Site Request Forger (CSRF)] protection to the application. It also updates the default RequestCache to only replay "GET" requests upon successful authentication. Additional information can be found in the <> section of the reference. [[nsa-csrf-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -686,7 +686,7 @@ Additional information can be found in the < Attributes +==== Attributes [[nsa-csrf-disabled]] * **disabled** @@ -707,14 +707,14 @@ Default is any HTTP method except "GET", "TRACE", "HEAD", "OPTIONS". [[nsa-custom-filter]] -==== +=== This element is used to add a filter to the filter chain. It doesn't create any additional beans but is used to select a bean of type `javax.servlet.Filter` which is already defined in the application context and add that at a particular position in the filter chain maintained by Spring Security. Full details can be found in the <>. [[nsa-custom-filter-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -722,7 +722,7 @@ Full details can be found in the <>. [[nsa-custom-filter-attributes]] -===== Attributes +==== Attributes [[nsa-custom-filter-after]] @@ -749,13 +749,13 @@ Defines a reference to a Spring bean that implements `Filter`. [[nsa-expression-handler]] -==== +=== Defines the `SecurityExpressionHandler` instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied. [[nsa-expression-handler-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -766,7 +766,7 @@ A default implementation (with no ACL support) will be used if not supplied. [[nsa-expression-handler-attributes]] -===== Attributes +==== Attributes [[nsa-expression-handler-ref]] @@ -775,7 +775,7 @@ Defines a reference to a Spring bean that implements `SecurityExpressionHandler` [[nsa-form-login]] -==== +=== Used to add an `UsernamePasswordAuthenticationFilter` to the filter stack and an `LoginUrlAuthenticationEntryPoint` to the application context to provide authentication on demand. This will always take precedence over other namespace-created entry points. If no attributes are supplied, a login page will be generated automatically at the URL "/login" footnote:[ @@ -785,7 +785,7 @@ The class `DefaultLoginPageGeneratingFilter` is responsible for rendering the lo [[nsa-form-login-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -793,7 +793,7 @@ The class `DefaultLoginPageGeneratingFilter` is responsible for rendering the lo [[nsa-form-login-attributes]] -===== Attributes +==== Attributes [[nsa-form-login-always-use-default-target]] @@ -870,17 +870,17 @@ Maps a `ForwardAuthenticationFailureHandler` to `authenticationFailureHandler` p [[nsa-oauth2-login]] -==== +=== The <> feature configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider. [[nsa-oauth2-login-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-oauth2-login-attributes]] -===== Attributes +==== Attributes [[nsa-oauth2-login-client-registration-repository-ref]] @@ -954,17 +954,17 @@ Reference to the `JwtDecoderFactory` used by `OidcAuthorizationCodeAuthenticatio [[nsa-oauth2-client]] -==== +=== Configures <> support. [[nsa-oauth2-client-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-oauth2-client-attributes]] -===== Attributes +==== Attributes [[nsa-oauth2-client-client-registration-repository-ref]] @@ -983,24 +983,24 @@ Reference to the `OAuth2AuthorizedClientService`. [[nsa-oauth2-client-children]] -===== Child Elements of +==== Child Elements of * <> [[nsa-authorization-code-grant]] -==== +=== Configures <>. [[nsa-authorization-code-grant-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-authorization-code-grant-attributes]] -===== Attributes +==== Attributes [[nsa-authorization-code-grant-authorization-request-repository-ref]] @@ -1019,30 +1019,30 @@ Reference to the `OAuth2AccessTokenResponseClient`. [[nsa-client-registrations]] -==== +=== A container element for client(s) registered (<>) with an OAuth 2.0 or OpenID Connect 1.0 Provider. [[nsa-client-registrations-children]] -===== Child Elements of +==== Child Elements of * <> * <> [[nsa-client-registration]] -==== +=== Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider. [[nsa-client-registration-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-client-registration-attributes]] -===== Attributes +==== Attributes [[nsa-client-registration-registration-id]] @@ -1093,18 +1093,18 @@ A reference to the associated provider. May reference a `` element or [[nsa-provider]] -==== +=== The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider. [[nsa-provider-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-provider-attributes]] -===== Attributes +==== Attributes [[nsa-provider-provider-id]] @@ -1148,23 +1148,23 @@ The URI used to retrieve the https://tools.ietf.org/html/rfc7517[JSON Web Key (J The URI used to initially configure a `ClientRegistration` using discovery of an OpenID Connect Provider's https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[Configuration endpoint] or an Authorization Server's https://tools.ietf.org/html/rfc8414#section-3[Metadata endpoint]. [[nsa-oauth2-resource-server]] -==== +=== Adds a `BearerTokenAuthenticationFilter`, `BearerTokenAuthenticationEntryPoint`, and `BearerTokenAccessDeniedHandler` to the configuration. In addition, either `` or `` must be specified. [[nsa-oauth2-resource-server-parents]] -===== Parents Elements of +==== Parents Elements of * <> [[nsa-oauth2-resource-server-children]] -===== Child Elements of +==== Child Elements of * <> * <> [[nsa-oauth2-resource-server-attributes]] -===== Attributes +==== Attributes [[nsa-oauth2-resource-server-authentication-manager-resolver-ref]] * **authentication-manager-resolver-ref** @@ -1179,18 +1179,18 @@ Reference to a `BearerTokenResolver` which will retrieve the bearer token from t Reference to a `AuthenticationEntryPoint` which will handle unauthorized requests [[nsa-jwt]] -==== +=== Represents an OAuth 2.0 Resource Server that will authorize JWTs [[nsa-jwt-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-jwt-attributes]] -===== Attributes +==== Attributes [[nsa-jwt-jwt-authentication-converter-ref]] * **jwt-authentication-converter-ref** @@ -1205,16 +1205,16 @@ Reference to a `JwtDecoder`. This is a larger component that overrides `jwk-set- The JWK Set Uri used to load signing verification keys from an OAuth 2.0 Authorization Server [[nsa-opaque-token]] -==== +=== Represents an OAuth 2.0 Resource Server that will authorize opaque tokens [[nsa-opaque-token-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-opaque-token-attributes]] -===== Attributes +==== Attributes [[nsa-opaque-token-introspector-ref]] * **introspector-ref** @@ -1233,13 +1233,13 @@ The Client Id to use for client authentication against the provided `introspecti The Client Secret to use for client authentication against the provided `introspection-uri`. [[nsa-http-basic]] -==== +=== Adds a `BasicAuthenticationFilter` and `BasicAuthenticationEntryPoint` to the configuration. The latter will only be used as the configuration entry point if form-based login is not enabled. [[nsa-http-basic-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -1247,7 +1247,7 @@ The latter will only be used as the configuration entry point if form-based logi [[nsa-http-basic-attributes]] -===== Attributes +==== Attributes [[nsa-http-basic-authentication-details-source-ref]] @@ -1261,13 +1261,13 @@ Sets the `AuthenticationEntryPoint` which is used by the `BasicAuthenticationFil [[nsa-http-firewall]] -==== Element +=== Element This is a top-level element which can be used to inject a custom implementation of `HttpFirewall` into the `FilterChainProxy` created by the namespace. The default implementation should be suitable for most applications. [[nsa-http-firewall-attributes]] -===== Attributes +==== Attributes [[nsa-http-firewall-ref]] @@ -1276,7 +1276,7 @@ Defines a reference to a Spring bean that implements `HttpFirewall`. [[nsa-intercept-url]] -==== +=== This element is used to define the set of URL patterns that the application is interested in and to configure how they should be handled. It is used to construct the `FilterInvocationSecurityMetadataSource` used by the `FilterSecurityInterceptor`. It is also responsible for configuring a `ChannelProcessingFilter` if particular URLs need to be accessed by HTTPS, for example. @@ -1285,7 +1285,7 @@ So the most specific patterns should come first and the most general should come [[nsa-intercept-url-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -1294,7 +1294,7 @@ So the most specific patterns should come first and the most general should come [[nsa-intercept-url-attributes]] -===== Attributes +==== Attributes [[nsa-intercept-url-access]] @@ -1341,12 +1341,12 @@ NOTE: This property is invalid for < +=== Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication. [[nsa-jee-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -1354,7 +1354,7 @@ Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integ [[nsa-jee-attributes]] -===== Attributes +==== Attributes [[nsa-jee-mappable-roles]] @@ -1368,13 +1368,13 @@ A reference to a user-service (or UserDetailsService bean) Id [[nsa-logout]] -==== +=== Adds a `LogoutFilter` to the filter stack. This is configured with a `SecurityContextLogoutHandler`. [[nsa-logout-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -1382,7 +1382,7 @@ This is configured with a `SecurityContextLogoutHandler`. [[nsa-logout-attributes]] -===== Attributes +==== Attributes [[nsa-logout-delete-cookies]] @@ -1419,7 +1419,7 @@ May be used to supply an instance of `LogoutSuccessHandler` which will be invoke [[nsa-openid-login]] -==== +=== Similar to `` and has the same attributes. The default value for `login-processing-url` is "/login/openid". An `OpenIDAuthenticationFilter` and `OpenIDAuthenticationProvider` will be registered. @@ -1428,7 +1428,7 @@ Again, this can be specified by `id`, using the `user-service-ref` attribute, or [[nsa-openid-login-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -1436,7 +1436,7 @@ Again, this can be specified by `id`, using the `user-service-ref` attribute, or [[nsa-openid-login-attributes]] -===== Attributes +==== Attributes [[nsa-openid-login-always-use-default-target]] @@ -1514,13 +1514,13 @@ Defaults to "username". [[nsa-openid-login-children]] -===== Child Elements of +==== Child Elements of * <> [[nsa-attribute-exchange]] -==== +=== The `attribute-exchange` element defines the list of attributes which should be requested from the identity provider. An example can be found in the <> section of the namespace configuration chapter. More than one can be used, in which case each must have an `identifier-match` attribute, containing a regular expression which is matched against the supplied OpenID identifier. @@ -1528,7 +1528,7 @@ This allows different attribute lists to be fetched from different providers (Go [[nsa-attribute-exchange-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -1536,7 +1536,7 @@ This allows different attribute lists to be fetched from different providers (Go [[nsa-attribute-exchange-attributes]] -===== Attributes +==== Attributes [[nsa-attribute-exchange-identifier-match]] @@ -1545,7 +1545,7 @@ A regular expression which will be compared against the claimed identity, when d [[nsa-attribute-exchange-children]] -===== Child Elements of +==== Child Elements of * <> @@ -1553,12 +1553,12 @@ A regular expression which will be compared against the claimed identity, when d [[nsa-openid-attribute]] -==== +=== Attributes used when making an OpenID AX https://openid.net/specs/openid-attribute-exchange-1_0.html#fetch_request[ Fetch Request] [[nsa-openid-attribute-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -1566,7 +1566,7 @@ Attributes used when making an OpenID AX https://openid.net/specs/openid-attribu [[nsa-openid-attribute-attributes]] -===== Attributes +==== Attributes [[nsa-openid-attribute-count]] @@ -1595,23 +1595,23 @@ For example, https://axschema.org/contact/email. See your OP's documentation for valid attribute types. [[nsa-password-management]] -==== +=== This element configures password management. [[nsa-password-management-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-password-management-attributes]] -===== Attributes +==== Attributes [[nsa-password-management-change-password-page]] * **change-password-page** The change password page. Defaults to "/change-password". [[nsa-port-mappings]] -==== +=== By default, an instance of `PortMapperImpl` will be added to the configuration for use in redirecting to secure and insecure URLs. This element can optionally be used to override the default mappings which that class defines. Each child `` element defines a pair of HTTP:HTTPS ports. @@ -1620,7 +1620,7 @@ An example of overriding these can be found in <>. [[nsa-port-mappings-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -1628,7 +1628,7 @@ An example of overriding these can be found in <>. [[nsa-port-mappings-children]] -===== Child Elements of +==== Child Elements of * <> @@ -1636,12 +1636,12 @@ An example of overriding these can be found in <>. [[nsa-port-mapping]] -==== +=== Provides a method to map http ports to https ports when forcing a redirect. [[nsa-port-mapping-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -1649,7 +1649,7 @@ Provides a method to map http ports to https ports when forcing a redirect. [[nsa-port-mapping-attributes]] -===== Attributes +==== Attributes [[nsa-port-mapping-http]] @@ -1663,13 +1663,13 @@ The https port to use. [[nsa-remember-me]] -==== +=== Adds the `RememberMeAuthenticationFilter` to the stack. This in turn will be configured with either a `TokenBasedRememberMeServices`, a `PersistentTokenBasedRememberMeServices` or a user-specified bean implementing `RememberMeServices` depending on the attribute settings. [[nsa-remember-me-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -1677,7 +1677,7 @@ This in turn will be configured with either a `TokenBasedRememberMeServices`, a [[nsa-remember-me-attributes]] -===== Attributes +==== Attributes [[nsa-remember-me-authentication-success-handler-ref]] @@ -1757,17 +1757,17 @@ If there are multiple instances, you can specify a bean `id` explicitly using th [[nsa-request-cache]] -==== Element +=== Element Sets the `RequestCache` instance which will be used by the `ExceptionTranslationFilter` to store request information before invoking an `AuthenticationEntryPoint`. [[nsa-request-cache-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-request-cache-attributes]] -===== Attributes +==== Attributes [[nsa-request-cache-ref]] @@ -1776,12 +1776,12 @@ Defines a reference to a Spring bean that is a `RequestCache`. [[nsa-session-management]] -==== +=== Session-management related functionality is implemented by the addition of a `SessionManagementFilter` to the filter stack. [[nsa-session-management-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -1789,7 +1789,7 @@ Session-management related functionality is implemented by the addition of a `Se [[nsa-session-management-attributes]] -===== Attributes +==== Attributes [[nsa-session-management-invalid-session-url]] @@ -1831,7 +1831,7 @@ See the Javadoc for this class for more details. [[nsa-session-management-children]] -===== Child Elements of +==== Child Elements of * <> @@ -1839,7 +1839,7 @@ See the Javadoc for this class for more details. [[nsa-concurrency-control]] -==== +=== Adds support for concurrent session control, allowing limits to be placed on the number of active sessions a user can have. A `ConcurrentSessionFilter` will be created, and a `ConcurrentSessionControlAuthenticationStrategy` will be used with the `SessionManagementFilter`. If a `form-login` element has been declared, the strategy object will also be injected into the created authentication filter. @@ -1847,7 +1847,7 @@ An instance of `SessionRegistry` (a `SessionRegistryImpl` instance unless the us [[nsa-concurrency-control-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -1855,7 +1855,7 @@ An instance of `SessionRegistry` (a `SessionRegistryImpl` instance unless the us [[nsa-concurrency-control-attributes]] -===== Attributes +==== Attributes [[nsa-concurrency-control-error-if-maximum-exceeded]] @@ -1893,7 +1893,7 @@ The other concurrent session control beans will be wired up to use it. [[nsa-x509]] -==== +=== Adds support for X.509 authentication. An `X509AuthenticationFilter` will be added to the stack and an `Http403ForbiddenEntryPoint` bean will be created. The latter will only be used if no other authentication mechanisms are in use (its only functionality is to return an HTTP 403 error code). @@ -1901,7 +1901,7 @@ A `PreAuthenticatedAuthenticationProvider` will also be created which delegates [[nsa-x509-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -1909,7 +1909,7 @@ A `PreAuthenticatedAuthenticationProvider` will also be created which delegates [[nsa-x509-attributes]] -===== Attributes +==== Attributes [[nsa-x509-authentication-details-source-ref]] @@ -1929,12 +1929,12 @@ If not set, an attempt will be made to locate a suitable instance automatically [[nsa-filter-chain-map]] -==== +=== Used to explicitly configure a FilterChainProxy instance with a FilterChainMap [[nsa-filter-chain-map-attributes]] -===== Attributes +==== Attributes [[nsa-filter-chain-map-request-matcher]] @@ -1944,7 +1944,7 @@ Currently the options are 'ant' (for ant path patterns), 'regex' for regular exp [[nsa-filter-chain-map-children]] -===== Child Elements of +==== Child Elements of * <> @@ -1952,13 +1952,13 @@ Currently the options are 'ant' (for ant path patterns), 'regex' for regular exp [[nsa-filter-chain]] -==== +=== Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with most general ones at the bottom. [[nsa-filter-chain-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -1966,7 +1966,7 @@ When multiple filter-chain elements are assembled in a list in order to configur [[nsa-filter-chain-attributes]] -===== Attributes +==== Attributes [[nsa-filter-chain-filters]] @@ -1986,7 +1986,7 @@ A reference to a `RequestMatcher` that will be used to determine if any `Filter` [[nsa-filter-security-metadata-source]] -==== +=== Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the element. The intercept-url elements used should only contain pattern, method and access attributes. @@ -1994,7 +1994,7 @@ Any others will result in a configuration error. [[nsa-filter-security-metadata-source-attributes]] -===== Attributes +==== Attributes [[nsa-filter-security-metadata-source-id]] @@ -2017,19 +2017,19 @@ If the expression evaluates to 'true', access will be granted. [[nsa-filter-security-metadata-source-children]] -===== Child Elements of +==== Child Elements of * <> [[nsa-websocket-security]] -=== WebSocket Security +== WebSocket Security Spring Security 4.0+ provides support for authorizing messages. One concrete example of where this is useful is to provide authorization in WebSocket based applications. [[nsa-websocket-message-broker]] -==== +=== The websocket-message-broker element has two different modes. If the <> is not specified, then it will do the following things: @@ -2051,7 +2051,7 @@ This is more cumbersome, but provides greater control over the configuration. [[nsa-websocket-message-broker-attributes]] -===== Attributes +==== Attributes [[nsa-websocket-message-broker-id]] * **id** A bean identifier, used for referring to the ChannelSecurityInterceptor bean elsewhere in the context. @@ -2063,27 +2063,27 @@ If not specified, Spring Security will automatically integrate with the messagin Changing the default is useful if it is necessary to allow other origins to make SockJS connections. [[nsa-websocket-message-broker-children]] -===== Child Elements of +==== Child Elements of * <> * <> [[nsa-intercept-message]] -==== +=== Defines an authorization rule for a message. [[nsa-intercept-message-parents]] -===== Parent Elements of +==== Parent Elements of * <> [[nsa-intercept-message-attributes]] -===== Attributes +==== Attributes [[nsa-intercept-message-pattern]] * **pattern** An ant based pattern that matches on the Message destination. @@ -2098,7 +2098,7 @@ Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEA For example, "denyAll" will deny access to all of the matching Messages; "permitAll" will grant access to all of the matching Messages; "hasRole('ADMIN') requires the current user to have the role 'ROLE_ADMIN' for the matching Messages. [[nsa-authentication]] -=== Authentication Services +== Authentication Services Before Spring Security 3.0, an `AuthenticationManager` was automatically registered internally. Now you must register one explicitly using the `` element. This creates an instance of Spring Security's `ProviderManager` class, which needs to be configured with a list of one or more `AuthenticationProvider` instances. @@ -2106,14 +2106,14 @@ These can either be created using syntax elements provided by the namespace, or [[nsa-authentication-manager]] -==== +=== Every Spring Security application which uses the namespace must have include this element somewhere. It is responsible for registering the `AuthenticationManager` which provides authentication services to the application. All elements which create `AuthenticationProvider` instances should be children of this element. [[nsa-authentication-manager-attributes]] -===== Attributes +==== Attributes [[nsa-authentication-manager-alias]] @@ -2134,7 +2134,7 @@ It is the same as the alias element, but provides a more consistent experience w [[nsa-authentication-manager-children]] -===== Child Elements of +==== Child Elements of * <> @@ -2143,7 +2143,7 @@ It is the same as the alias element, but provides a more consistent experience w [[nsa-authentication-provider]] -==== +=== Unless used with a `ref` attribute, this element is shorthand for configuring a `DaoAuthenticationProvider`. `DaoAuthenticationProvider` loads user information from a `UserDetailsService` and compares the username/password combination with the values supplied at login. The `UserDetailsService` instance can be defined either by using an available namespace element (`jdbc-user-service` or by using the `user-service-ref` attribute to point to a bean defined elsewhere in the application context). @@ -2151,7 +2151,7 @@ The `UserDetailsService` instance can be defined either by using an available na [[nsa-authentication-provider-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -2159,7 +2159,7 @@ The `UserDetailsService` instance can be defined either by using an available na [[nsa-authentication-provider-attributes]] -===== Attributes +==== Attributes [[nsa-authentication-provider-ref]] @@ -2187,7 +2187,7 @@ A reference to a bean that implements UserDetailsService that may be created usi [[nsa-authentication-provider-children]] -===== Child Elements of +==== Child Elements of * <> @@ -2198,12 +2198,12 @@ A reference to a bean that implements UserDetailsService that may be created usi [[nsa-jdbc-user-service]] -==== +=== Causes creation of a JDBC-based UserDetailsService. [[nsa-jdbc-user-service-attributes]] -===== Attributes +==== Attributes [[nsa-jdbc-user-service-authorities-by-username-query]] @@ -2277,13 +2277,13 @@ select username, password, enabled from users where username = ? [[nsa-password-encoder]] -==== +=== Authentication providers can optionally be configured to use a password encoder as described in the <>. This will result in the bean being injected with the appropriate `PasswordEncoder` instance. [[nsa-password-encoder-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -2292,7 +2292,7 @@ This will result in the bean being injected with the appropriate `PasswordEncode [[nsa-password-encoder-attributes]] -===== Attributes +==== Attributes [[nsa-password-encoder-hash]] @@ -2307,13 +2307,13 @@ Defines a reference to a Spring bean that implements `PasswordEncoder`. [[nsa-user-service]] -==== +=== Creates an in-memory UserDetailsService from a properties file or a list of "user" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required. [[nsa-user-service-attributes]] -===== Attributes +==== Attributes [[nsa-user-service-id]] @@ -2336,7 +2336,7 @@ username=password,grantedAuthority[,grantedAuthority][,enabled|disabled] [[nsa-user-service-children]] -===== Child Elements of +==== Child Elements of * <> @@ -2344,12 +2344,12 @@ username=password,grantedAuthority[,grantedAuthority][,enabled|disabled] [[nsa-user]] -==== +=== Represents a user in the application. [[nsa-user-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -2357,7 +2357,7 @@ Represents a user in the application. [[nsa-user-attributes]] -===== Attributes +==== Attributes [[nsa-user-authorities]] @@ -2392,15 +2392,15 @@ Cannot be empty. -=== Method Security +== Method Security [[nsa-method-security]] -==== +=== This element is the primary means of adding support for securing methods on Spring Security beans. Methods can be secured by the use of annotations (defined at the interface or class level) or by defining a set of pointcuts. [[nsa-method-security-attributes]] -===== attributes +==== attributes [[nsa-method-security-pre-post-enabled]] * **pre-post-enabled** @@ -2423,18 +2423,18 @@ If true, class based proxying will be used instead of interface based proxying. Defaults to "false". [[nsa-method-security-children]] -===== Child Elements of +==== Child Elements of * <> [[nsa-global-method-security]] -==== +=== This element is the primary means of adding support for securing methods on Spring Security beans. Methods can be secured by the use of annotations (defined at the interface or class level) or by defining a set of pointcuts as child elements, using AspectJ syntax. [[nsa-global-method-security-attributes]] -===== Attributes +==== Attributes [[nsa-global-method-security-access-decision-manager-ref]] @@ -2498,7 +2498,7 @@ Defaults to "disabled". [[nsa-global-method-security-children]] -===== Child Elements of +==== Child Elements of * <> @@ -2509,13 +2509,13 @@ Defaults to "disabled". [[nsa-after-invocation-provider]] -==== +=== This element can be used to decorate an `AfterInvocationProvider` for use by the security interceptor maintained by the `` namespace. You can define zero or more of these within the `global-method-security` element, each with a `ref` attribute pointing to an `AfterInvocationProvider` bean instance within your application context. [[nsa-after-invocation-provider-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -2523,7 +2523,7 @@ You can define zero or more of these within the `global-method-security` element [[nsa-after-invocation-provider-attributes]] -===== Attributes +==== Attributes [[nsa-after-invocation-provider-ref]] @@ -2532,13 +2532,13 @@ Defines a reference to a Spring bean that implements `AfterInvocationProvider`. [[nsa-pre-post-annotation-handling]] -==== +=== Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replaced entirely. Only applies if these annotations are enabled. [[nsa-pre-post-annotation-handling-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -2546,7 +2546,7 @@ Only applies if these annotations are enabled. [[nsa-pre-post-annotation-handling-children]] -===== Child Elements of +==== Child Elements of * <> @@ -2556,12 +2556,12 @@ Only applies if these annotations are enabled. [[nsa-invocation-attribute-factory]] -==== +=== Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods. [[nsa-invocation-attribute-factory-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -2569,7 +2569,7 @@ Defines the PrePostInvocationAttributeFactory instance which is used to generate [[nsa-invocation-attribute-factory-attributes]] -===== Attributes +==== Attributes [[nsa-invocation-attribute-factory-ref]] @@ -2578,12 +2578,12 @@ Defines a reference to a Spring bean Id. [[nsa-post-invocation-advice]] -==== +=== Customizes the `PostInvocationAdviceProvider` with the ref as the `PostInvocationAuthorizationAdvice` for the element. [[nsa-post-invocation-advice-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -2591,7 +2591,7 @@ Customizes the `PostInvocationAdviceProvider` with the ref as the `PostInvocatio [[nsa-post-invocation-advice-attributes]] -===== Attributes +==== Attributes [[nsa-post-invocation-advice-ref]] @@ -2600,12 +2600,12 @@ Defines a reference to a Spring bean Id. [[nsa-pre-invocation-advice]] -==== +=== Customizes the `PreInvocationAuthorizationAdviceVoter` with the ref as the `PreInvocationAuthorizationAdviceVoter` for the element. [[nsa-pre-invocation-advice-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -2613,7 +2613,7 @@ Customizes the `PreInvocationAuthorizationAdviceVoter` with the ref as the `PreI [[nsa-pre-invocation-advice-attributes]] -===== Attributes +==== Attributes [[nsa-pre-invocation-advice-ref]] @@ -2622,14 +2622,14 @@ Defines a reference to a Spring bean Id. [[nsa-protect-pointcut]] -==== Securing Methods using +=== Securing Methods using `` Rather than defining security attributes on an individual method or class basis using the `@Secured` annotation, you can define cross-cutting security constraints across whole sets of methods and interfaces in your service layer using the `` element. You can find an example in the <>. [[nsa-protect-pointcut-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -2637,7 +2637,7 @@ You can find an example in the <>. [[nsa-protect-pointcut-attributes]] -===== Attributes +==== Attributes [[nsa-protect-pointcut-access]] @@ -2653,12 +2653,12 @@ For example, `execution(int com.foo.TargetObject.countLength(String))`. [[nsa-intercept-methods]] -==== +=== Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods [[nsa-intercept-methods-attributes]] -===== Attributes +==== Attributes [[nsa-intercept-methods-access-decision-manager-ref]] @@ -2667,7 +2667,7 @@ Optional AccessDecisionManager bean ID to be used by the created method security [[nsa-intercept-methods-children]] -===== Child Elements of +==== Child Elements of * <> @@ -2675,12 +2675,12 @@ Optional AccessDecisionManager bean ID to be used by the created method security [[nsa-method-security-metadata-source]] -==== +=== Creates a MethodSecurityMetadataSource instance [[nsa-method-security-metadata-source-attributes]] -===== Attributes +==== Attributes [[nsa-method-security-metadata-source-id]] @@ -2697,7 +2697,7 @@ If the expression evaluates to 'true', access will be granted. [[nsa-method-security-metadata-source-children]] -===== Child Elements of +==== Child Elements of * <> @@ -2705,13 +2705,13 @@ If the expression evaluates to 'true', access will be granted. [[nsa-protect]] -==== +=== Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix "protect" declarations with any services provided "global-method-security". [[nsa-protect-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -2720,7 +2720,7 @@ We strongly advise you NOT to mix "protect" declarations with any services provi [[nsa-protect-attributes]] -===== Attributes +==== Attributes [[nsa-protect-access]] @@ -2735,14 +2735,14 @@ A method name [[nsa-ldap]] -=== LDAP Namespace Options +== LDAP Namespace Options LDAP is covered in some details in <>. We will expand on that here with some explanation of how the namespace options map to Spring beans. The LDAP implementation uses Spring LDAP extensively, so some familiarity with that project's API may be useful. [[nsa-ldap-server]] -==== Defining the LDAP Server using the +=== Defining the LDAP Server using the `` Element This element sets up a Spring LDAP `ContextSource` for use by the other LDAP beans, defining the location of the LDAP server and other information (such as a username and password, if it doesn't allow anonymous access) for connecting to it. It can also be used to create an embedded server for testing. @@ -2756,7 +2756,7 @@ This is actually the bean `id` of the `ContextSource` instance, if you want to u [[nsa-ldap-server-attributes]] -===== Attributes +==== Attributes [[nsa-ldap-server-mode]] * **mode** @@ -2805,14 +2805,14 @@ Specifies the ldap server URL when not using the embedded LDAP server. [[nsa-ldap-authentication-provider]] -==== +=== This element is shorthand for the creation of an `LdapAuthenticationProvider` instance. By default this will be configured with a `BindAuthenticator` instance and a `DefaultAuthoritiesPopulator`. As with all namespace authentication providers, it must be included as a child of the `authentication-provider` element. [[nsa-ldap-authentication-provider-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -2820,7 +2820,7 @@ As with all namespace authentication providers, it must be included as a child o [[nsa-ldap-authentication-provider-attributes]] -===== Attributes +==== Attributes [[nsa-ldap-authentication-provider-group-role-attribute]] @@ -2905,7 +2905,7 @@ If these attributes aren't set and no `user-dn-pattern` has been supplied as an [[nsa-ldap-authentication-provider-children]] -===== Child Elements of +==== Child Elements of * <> @@ -2913,12 +2913,12 @@ If these attributes aren't set and no `user-dn-pattern` has been supplied as an [[nsa-password-compare]] -==== +=== This is used as child element to `` and switches the authentication strategy from `BindAuthenticator` to `PasswordComparisonAuthenticator`. [[nsa-password-compare-parents]] -===== Parent Elements of +==== Parent Elements of * <> @@ -2926,7 +2926,7 @@ This is used as child element to `` and switches the authenticati [[nsa-password-compare-attributes]] -===== Attributes +==== Attributes [[nsa-password-compare-hash]] @@ -2942,7 +2942,7 @@ Defaults to "userPassword". [[nsa-password-compare-children]] -===== Child Elements of +==== Child Elements of * <> @@ -2950,14 +2950,14 @@ Defaults to "userPassword". [[nsa-ldap-user-service]] -==== +=== This element configures an LDAP `UserDetailsService`. The class used is `LdapUserDetailsService` which is a combination of a `FilterBasedLdapUserSearch` and a `DefaultLdapAuthoritiesPopulator`. The attributes it supports have the same usage as in ``. [[nsa-ldap-user-service-attributes]] -===== Attributes +==== Attributes [[nsa-ldap-user-service-cache-ref]] diff --git a/docs/modules/ROOT/pages/servlet/appendix/proxy-server.adoc b/docs/modules/ROOT/pages/servlet/appendix/proxy-server.adoc index 18d650bf45..ac3f182a16 100644 --- a/docs/modules/ROOT/pages/servlet/appendix/proxy-server.adoc +++ b/docs/modules/ROOT/pages/servlet/appendix/proxy-server.adoc @@ -1,5 +1,5 @@ [[appendix-proxy-server]] -== Proxy Server Configuration += Proxy Server Configuration When using a proxy server it is important to ensure that you have configured your application properly. For example, many applications will have a load balancer that responds to request for https://example.com/ by forwarding the request to an application server at https://192.168.1:8080 diff --git a/docs/modules/ROOT/pages/servlet/authentication/anonymous.adoc b/docs/modules/ROOT/pages/servlet/authentication/anonymous.adoc index eee0f8e6f4..b98e87e075 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/anonymous.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/anonymous.adoc @@ -1,9 +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). @@ -21,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. @@ -88,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. @@ -102,7 +102,7 @@ The `AuthenticatedVoter` approach is more powerful, since it allows you to diffe If you don't need this functionality though, then you can stick with `ROLE_ANONYMOUS`, which will be processed by Spring Security's standard `RoleVoter`. [[anonymous-auth-mvc-controller]] -=== Getting Anonymous Authentications with Spring MVC +== Getting Anonymous Authentications with Spring MVC https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-arguments[Spring MVC resolves parameters of type `Principal`] using its own argument resolver. diff --git a/docs/modules/ROOT/pages/servlet/authentication/cas.adoc b/docs/modules/ROOT/pages/servlet/authentication/cas.adoc index 29c245eb95..5287dd6d92 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/cas.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/cas.adoc @@ -1,8 +1,8 @@ [[servlet-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. @@ -11,7 +11,7 @@ You can learn more about CAS at https://www.apereo.org. 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. @@ -34,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. @@ -87,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. @@ -96,7 +96,7 @@ A full CAS sample application can be found in the Spring Security <> contains a working example in the `ProxyTicketSampleServlet`. Example code can be found below: @@ -379,7 +379,7 @@ protected fun doGet(request: HttpServletRequest, response: HttpServletResponse?) ==== [[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/modules/ROOT/pages/servlet/authentication/events.adoc b/docs/modules/ROOT/pages/servlet/authentication/events.adoc index 6bb9896718..c016d37574 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/events.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/events.adoc @@ -1,5 +1,5 @@ [[servlet-events]] -== Authentication Events += Authentication Events For each authentication that succeeds or fails, a `AuthenticationSuccessEvent` or `AbstractAuthenticationFailureEvent` is fired, respectively. @@ -68,7 +68,7 @@ class AuthenticationEvents { While similar to `AuthenticationSuccessHandler` and `AuthenticationFailureHandler`, these are nice in that they can be used independently from the servlet API. -=== Adding Exception Mappings +== Adding Exception Mappings `DefaultAuthenticationEventPublisher` by default will publish an `AbstractAuthenticationFailureEvent` for the following events: @@ -121,7 +121,7 @@ fun authenticationEventPublisher ---- ==== -=== Default Event +== Default Event And, you can supply a catch-all event to fire in the case of any `AuthenticationException`: diff --git a/docs/modules/ROOT/pages/servlet/authentication/jaas.adoc b/docs/modules/ROOT/pages/servlet/authentication/jaas.adoc index 216435bf43..76671ba579 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/jaas.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/jaas.adoc @@ -1,21 +1,21 @@ [[servlet-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. @@ -33,7 +33,7 @@ If the `LoginModule` requests a callback against the ``InternalCallbackHandler`` [[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. @@ -50,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`. @@ -65,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 standard JAAS configuration files, using it in conjunction 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. @@ -116,7 +116,7 @@ class="org.springframework.security.authentication.jaas.DefaultJaasAuthenticatio [[jaas-jaasauthenticationprovider]] -=== JaasAuthenticationProvider +== JaasAuthenticationProvider The `JaasAuthenticationProvider` assumes the default `Configuration` is an instance of https://docs.oracle.com/javase/8/docs/jre/api/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`. @@ -157,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/modules/ROOT/pages/servlet/authentication/logout.adoc b/docs/modules/ROOT/pages/servlet/authentication/logout.adoc index 7ebb287ac2..9ad2110072 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/logout.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/logout.adoc @@ -1,8 +1,8 @@ [[jc-logout]] -== Handling Logouts += Handling Logouts [[logout-java-configuration]] -=== Logout Java/Kotlin Configuration +== Logout Java/Kotlin 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: @@ -86,13 +86,13 @@ For many common scenarios, these handlers are applied under the covers when using the fluent API. [[ns-logout]] -=== Logout XML Configuration +== 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 +== 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. @@ -115,7 +115,7 @@ E.g. `deleteCookies()` allows specifying the names of one or more cookies to be This is a shortcut compared to adding a `CookieClearingLogoutHandler`. [[jc-logout-success-handler]] -=== LogoutSuccessHandler +== LogoutSuccessHandler The `LogoutSuccessHandler` is called after a successful logout by the `LogoutFilter`, to handle e.g. redirection or forwarding to the appropriate destination. @@ -137,7 +137,7 @@ Instead of redirecting to a URL upon the successful logout, this `LogoutSuccessH If not configured a status code 200 will be returned by default. [[jc-logout-references]] -=== Further Logout-Related References +== Further Logout-Related References - <> - <> diff --git a/docs/modules/ROOT/pages/servlet/authentication/openid.adoc b/docs/modules/ROOT/pages/servlet/authentication/openid.adoc index 01362127ff..9ecc0b8246 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/openid.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/openid.adoc @@ -1,5 +1,5 @@ [[servlet-openid]] -== OpenID Support += OpenID Support [NOTE] The OpenID 1.0 and 2.0 protocols have been deprecated and users are encouraged to migrate to OpenID Connect, which is supported by spring-security-oauth2. @@ -27,7 +27,7 @@ Note that we have omitted the password attribute from the above user configurati 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 +== Attribute Exchange Support for OpenID https://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: diff --git a/docs/modules/ROOT/pages/servlet/authentication/preauth.adoc b/docs/modules/ROOT/pages/servlet/authentication/preauth.adoc index f23b57a203..67da7fc4b9 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/preauth.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/preauth.adoc @@ -1,5 +1,5 @@ [[servlet-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: @@ -59,7 +59,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. @@ -69,7 +69,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: @@ -85,19 +85,19 @@ 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 <> 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. It will be called if the user is rejected by the `AbstractPreAuthenticatedProcessingFilter` resulting in a null authentication. 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. @@ -110,7 +110,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] @@ -143,7 +143,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/modules/ROOT/pages/servlet/authentication/rememberme.adoc b/docs/modules/ROOT/pages/servlet/authentication/rememberme.adoc index b2375d62cb..fd65a3ce62 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/rememberme.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/rememberme.adoc @@ -1,9 +1,9 @@ [[servlet-rememberme]] -== 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. @@ -14,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: @@ -50,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 https://web.archive.org/web/20180819014446/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: @@ -74,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. @@ -97,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`. @@ -130,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/modules/ROOT/pages/servlet/authentication/runas.adoc b/docs/modules/ROOT/pages/servlet/authentication/runas.adoc index 0b439ad369..258e5a033c 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/runas.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/runas.adoc @@ -1,8 +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`. @@ -12,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/modules/ROOT/pages/servlet/authentication/session-management.adoc b/docs/modules/ROOT/pages/servlet/authentication/session-management.adoc index bee0394d4b..6024d34a1b 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/session-management.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/session-management.adoc @@ -1,9 +1,9 @@ [[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. -=== Detecting Timeouts +== 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: @@ -74,7 +74,7 @@ Header always set Set-Cookie "JSESSIONID=;Path=/tutorial;Expires=Thu, 01 Jan 197 [[ns-concurrent-sessions]] -=== Concurrent Session Control +== 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 configuration to keep Spring Security updated about session lifecycle events: @@ -165,7 +165,7 @@ If you are using a customized authentication filter for form-based login, then y More details can be found in the <>. [[ns-session-fixation]] -=== Session Fixation Attack Protection +== Session Fixation Attack Protection https://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 @@ -189,7 +189,7 @@ When session fixation protection occurs, it results in a `SessionFixationProtect 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 +== 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. @@ -203,7 +203,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: @@ -230,7 +230,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. @@ -327,7 +327,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/modules/ROOT/pages/servlet/authentication/x509.adoc b/docs/modules/ROOT/pages/servlet/authentication/x509.adoc index 138721e127..5dc29b08d1 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/x509.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/x509.adoc @@ -1,9 +1,9 @@ [[servlet-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. @@ -19,7 +19,7 @@ For example, if you're using Tomcat then read the instructions here https://tomc 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. @@ -51,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 {gh-samples-url}/servlet/java-configuration/authentication/x509/server[Spring Security Samples repository]. 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/modules/ROOT/pages/servlet/authorization/acls.adoc b/docs/modules/ROOT/pages/servlet/authorization/acls.adoc index 32ef05e387..1735e9000b 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/acls.adoc +++ b/docs/modules/ROOT/pages/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/modules/ROOT/pages/servlet/authorization/expression-based.adoc b/docs/modules/ROOT/pages/servlet/authorization/expression-based.adoc index 1070783c92..f5b916d636 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/expression-based.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/expression-based.adoc @@ -1,18 +1,18 @@ [[el-access]] -== Expression-Based Access Control += Expression-Based Access Control Spring Security 3.0 introduced the ability to use Spring EL expressions as an authorization mechanism in addition to the simple use of configuration attributes and access-decision voters which have been seen before. Expression-based access control is built on the same architecture but allows complicated Boolean logic to be encapsulated in a single expression. -=== Overview +== Overview Spring Security uses Spring EL for expression support and you should look at how that works if you are interested in understanding the topic in more depth. Expressions are evaluated with a "root object" as part of the evaluation context. Spring Security uses specific classes for web and method security as the root object, in order to provide built-in expressions and access to values such as the current principal. [[el-common-built-in]] -==== Common Built-In Expressions +=== Common Built-In Expressions The base class for expression root objects is `SecurityExpressionRoot`. This provides some common expressions which are available in both web and method security. @@ -83,7 +83,7 @@ For example, `hasPermission(1, 'com.example.domain.Message', 'read')` [[el-access-web]] -=== Web Security Expressions +== Web Security Expressions To use expressions to secure individual URLs, you would first need to set the `use-expressions` attribute in the `` element to `true`. Spring Security will then expect the `access` attributes of the `` elements to contain Spring EL expressions. The expressions should evaluate to a Boolean, defining whether access should be allowed or not. @@ -109,7 +109,7 @@ If expressions are being used, a `WebExpressionVoter` will be added to the `Acce So if you aren't using the namespace and want to use expressions, you will have to add one of these to your configuration. [[el-access-web-beans]] -==== Referring to Beans in Web Security Expressions +=== Referring to Beans in Web Security Expressions If you wish to extend the expressions that are available, you can easily refer to any Spring Bean you expose. For example, assuming you have a Bean with the name of `webSecurity` that contains the following method signature: @@ -172,7 +172,7 @@ http { ==== [[el-access-web-path-variables]] -==== Path Variables in Web Security Expressions +=== Path Variables in Web Security Expressions At times it is nice to be able to refer to path variables within a URL. For example, consider a RESTful application that looks up a user by id from the URL path in the format `+/user/{userId}+`. @@ -240,13 +240,13 @@ http { In this configuration URLs that match would pass in the path variable (and convert it) into checkUserId method. For example, if the URL were `/user/123/resource`, then the id passed in would be `123`. -=== Method Security Expressions +== Method Security Expressions Method security is a bit more complicated than a simple allow or deny rule. Spring Security 3.0 introduced some new annotations in order to allow comprehensive support for the use of expressions. [[el-pre-post-annotations]] -==== @Pre and @Post Annotations +=== @Pre and @Post Annotations There are four annotations which support expression attributes to allow pre and post-invocation authorization checks and also to support filtering of submitted collection arguments or return values. They are `@PreAuthorize`, `@PreFilter`, `@PostAuthorize` and `@PostFilter`. Their use is enabled through the `global-method-security` namespace element: @@ -256,7 +256,7 @@ Their use is enabled through the `global-method-security` namespace element: ---- -===== Access Control using @PreAuthorize and @PostAuthorize +==== Access Control using @PreAuthorize and @PostAuthorize The most obviously useful annotation is `@PreAuthorize` which decides whether a method can actually be invoked or not. For example (from the {gh-samples-url}/servlet/xml/java/contacts[Contacts] sample application) @@ -412,7 +412,7 @@ This can be achieved using the `@PostAuthorize` annotation. To access the return value from a method, use the built-in name `returnObject` in the expression. -- -===== Filtering using @PreFilter and @PostFilter +==== Filtering using @PreFilter and @PostFilter Spring Security supports filtering of collections, arrays, maps and streams using expressions. This is most commonly performed on the return value of a method. For example: @@ -447,13 +447,13 @@ If you are filtering large collections and removing many of the entries then thi [[el-method-built-in]] -==== Built-In Expressions +=== Built-In Expressions There are some built-in expressions which are specific to method security, which we have already seen in use above. The `filterTarget` and `returnValue` values are simple enough, but the use of the `hasPermission()` expression warrants a closer look. [[el-permission-evaluator]] -===== The PermissionEvaluator interface +==== The PermissionEvaluator interface `hasPermission()` expressions are delegated to an instance of `PermissionEvaluator`. It is intended to bridge between the expression system and Spring Security's ACL system, allowing you to specify authorization constraints on domain objects, based on abstract permissions. It has no explicit dependencies on the ACL module, so you could swap that out for an alternative implementation if required. @@ -494,7 +494,7 @@ Where `myPermissionEvaluator` is the bean which implements `PermissionEvaluator` Usually this will be the implementation from the ACL module which is called `AclPermissionEvaluator`. See the {gh-samples-url}/servlet/xml/java/contacts[Contacts] sample application configuration for more details. -===== Method Security Meta Annotations +==== Method Security Meta Annotations You can make use of meta annotations for method security to make your code more readable. This is especially convenient if you find that you are repeating the same complex expression throughout your code base. diff --git a/docs/modules/ROOT/pages/servlet/authorization/method-security.adoc b/docs/modules/ROOT/pages/servlet/authorization/method-security.adoc index b960be9e22..ce49cdd45e 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/method-security.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/method-security.adoc @@ -1,12 +1,12 @@ [[jc-method]] -== 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. -=== EnableMethodSecurity +== EnableMethodSecurity In Spring Security 5.6, we can enable annotation-based security using the `@EnableMethodSecurity` annotation on any `@Configuration` instance. @@ -149,7 +149,7 @@ class MethodSecurityConfig { ---- ==== -==== Customizing Authorization +=== Customizing Authorization Spring Security's `@PreAuthorize`, `@PostAuthorize`, `@PreFilter`, and `@PostFilter` ship with rich expression-based support. @@ -245,7 +245,7 @@ We expose `GrantedAuthorityDefaults` using a `static` method to ensure that Spri ==== [[jc-method-security-custom-authorization-manager]] -==== Custom Authorization Managers +=== Custom Authorization Managers Method authorization is a combination of before- and after-method authorization. @@ -598,7 +598,7 @@ class MethodSecurityConfig { and it will be invoked after the `@PostAuthorize` interceptor. [[jc-enable-global-method-security]] -=== EnableGlobalMethodSecurity +== 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. @@ -740,7 +740,7 @@ interface BankService { ---- ==== -=== GlobalMethodSecurityConfiguration +== 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. @@ -776,7 +776,7 @@ open class MethodSecurityConfig : GlobalMethodSecurityConfiguration() { For additional information about methods that can be overridden, refer to the `GlobalMethodSecurityConfiguration` Javadoc. [[ns-global-method]] -=== The Element +== 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`: @@ -889,7 +889,7 @@ If two annotations are found which apply to a particular method, then only one o ==== [[ns-protect-pointcut]] -=== Adding Security Pointcuts using 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: diff --git a/docs/modules/ROOT/pages/servlet/authorization/secure-objects.adoc b/docs/modules/ROOT/pages/servlet/authorization/secure-objects.adoc index 3182ac322f..5c24678b16 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/secure-objects.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/secure-objects.adoc @@ -1,9 +1,9 @@ [[secure-object-impls]] -== Secure Object Implementations += Secure Object Implementations [[aop-alliance]] -=== AOP Alliance (MethodInvocation) Security Interceptor +== AOP Alliance (MethodInvocation) Security Interceptor Prior to Spring Security 2.0, securing ``MethodInvocation``s needed quite a lot of boiler plate configuration. Now the recommended approach for method security is to use <>. This way the method security infrastructure beans are configured automatically for you so you don't really need to know about the implementation classes. @@ -15,7 +15,7 @@ The interceptor uses a `MethodSecurityMetadataSource` instance to obtain the con `MapBasedMethodSecurityMetadataSource` is used to store configuration attributes keyed by method names (which can be wildcarded) and will be used internally when the attributes are defined in the application context using the `` or `` elements. Other implementations will be used to handle annotation-based configuration. -==== Explicit MethodSecurityInterceptor Configuration +=== Explicit MethodSecurityInterceptor Configuration You can of course configure a `MethodSecurityInterceptor` directly in your application context for use with one of Spring AOP's proxying mechanisms: [source,xml] @@ -36,7 +36,7 @@ You can of course configure a `MethodSecurityInterceptor` directly in your appli ---- [[aspectj]] -=== AspectJ (JoinPoint) Security Interceptor +== AspectJ (JoinPoint) Security Interceptor The AspectJ security interceptor is very similar to the AOP Alliance security interceptor discussed in the previous section. Indeed we will only discuss the differences in this section. diff --git a/docs/modules/ROOT/pages/servlet/integrations/concurrency.adoc b/docs/modules/ROOT/pages/servlet/integrations/concurrency.adoc index a2ac3fe2a2..202b51ab09 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/concurrency.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/concurrency.adoc @@ -1,5 +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. @@ -7,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. @@ -122,7 +122,7 @@ 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. @@ -244,7 +244,7 @@ val executor = DelegatingSecurityContextExecutor(delegateExecutor) 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 running 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/modules/ROOT/pages/servlet/integrations/cors.adoc b/docs/modules/ROOT/pages/servlet/integrations/cors.adoc index cf9eae9629..aa5cb3ff17 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/cors.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/cors.adoc @@ -1,5 +1,5 @@ [[cors]] -== 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/modules/ROOT/pages/servlet/integrations/data.adoc b/docs/modules/ROOT/pages/servlet/integrations/data.adoc index 2e618b11f5..9212daeda3 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/data.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/data.adoc @@ -1,11 +1,11 @@ [[data]] -== Spring Data Integration += Spring Data Integration Spring Security provides Spring Data integration that allows referring to the current user within your queries. It is not only useful but necessary to include the user in the queries to support paged results since filtering the results afterwards would not scale. [[data-configuration]] -=== Spring Data & Spring Security Configuration +== Spring Data & Spring Security Configuration To use this support, add `org.springframework.security:spring-security-data` dependency and provide a bean of type `SecurityEvaluationContextExtension`. In Java Configuration, this would look like: @@ -38,7 +38,7 @@ In XML Configuration, this would look like: ---- [[data-query]] -=== Security Expressions within @Query +== Security Expressions within @Query Now Spring Security can be used within your queries. For example: diff --git a/docs/modules/ROOT/pages/servlet/integrations/jackson.adoc b/docs/modules/ROOT/pages/servlet/integrations/jackson.adoc index b78a880998..1f26756b08 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/jackson.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/jackson.adoc @@ -1,5 +1,5 @@ [[jackson]] -== Jackson Support += Jackson Support Spring Security provides 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/modules/ROOT/pages/servlet/integrations/jsp-taglibs.adoc b/docs/modules/ROOT/pages/servlet/integrations/jsp-taglibs.adoc index 3f31683258..28a02e34ac 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/jsp-taglibs.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/jsp-taglibs.adoc @@ -1,9 +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] @@ -12,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. @@ -65,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. @@ -77,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. @@ -86,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. @@ -113,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. [[taglibs-csrfinput]] -=== 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. @@ -134,7 +134,7 @@ Spring Security handles Spring forms automatically. ---- [[taglibs-csrfmeta]] -=== 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/modules/ROOT/pages/servlet/integrations/localization.adoc b/docs/modules/ROOT/pages/servlet/integrations/localization.adoc index 0f8f9b402e..e1fc22b9a2 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/localization.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/localization.adoc @@ -1,5 +1,5 @@ [[localization]] -== 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. diff --git a/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc b/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc index 796be873fd..ef2d849355 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc @@ -1,11 +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. @@ -16,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. @@ -212,7 +212,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. @@ -448,7 +448,7 @@ open fun findMessagesForUser(@CurrentUser customUser: CustomUser?): ModelAndView [[mvc-async]] -=== Spring MVC Async Integration +== Spring MVC Async Integration Spring Web MVC 3.2+ has excellent support for https://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 invokes a `Callable` returned by your controllers. @@ -495,9 +495,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 https://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: @@ -541,7 +541,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/modules/ROOT/pages/servlet/integrations/servlet-api.adoc b/docs/modules/ROOT/pages/servlet/integrations/servlet-api.adoc index ad76aa54a0..6bc74d8af5 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/servlet-api.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/servlet-api.adoc @@ -1,14 +1,14 @@ [[servletapi]] -== Servlet API integration += Servlet API integration This section describes how Spring Security is integrated with the Servlet API. [[servletapi-25]] -=== Servlet 2.5+ Integration +== Servlet 2.5+ Integration [[servletapi-remote-user]] -==== HttpServletRequest.getRemoteUser() +=== HttpServletRequest.getRemoteUser() The https://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. @@ -16,7 +16,7 @@ Knowing if the user is authenticated or not can be useful for determining if cer [[servletapi-user-principal]] -==== HttpServletRequest.getUserPrincipal() +=== HttpServletRequest.getUserPrincipal() The https://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. @@ -55,7 +55,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 https://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: @@ -78,18 +78,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 https://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 https://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": @@ -121,7 +121,7 @@ It is not necessary to catch the ServletException if you want Spring Security to ==== [[servletapi-logout]] -==== HttpServletRequest.logout() +=== HttpServletRequest.logout() The https://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. @@ -130,7 +130,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 https://docs.oracle.com/javaee/6/api/javax/servlet/AsyncContext.html#start%28java.lang.Runnable%29[AsyncContext.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: @@ -174,7 +174,7 @@ async.start { ==== [[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: @@ -265,9 +265,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 committing 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 https://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/modules/ROOT/pages/servlet/integrations/websocket.adoc b/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc index 43b83c752e..2ef0312334 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc @@ -1,5 +1,5 @@ [[websocket]] -== WebSocket Security += WebSocket Security Spring Security 4 added support for securing https://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. @@ -12,7 +12,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`. @@ -69,7 +69,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. @@ -78,7 +78,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`. @@ -168,12 +168,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. @@ -188,7 +188,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 destination that starts with the https://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. @@ -206,7 +206,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 https://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`. @@ -217,13 +217,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 https://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. @@ -238,13 +238,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 https://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 https://en.wikipedia.org/wiki/Defense_in_depth_(computing)[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. @@ -310,7 +310,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: @@ -348,13 +348,13 @@ open class WebSocketSecurityConfig : AbstractSecurityWebSocketMessageBrokerConfi [[websocket-sockjs]] -=== Working with SockJS +== Working with SockJS https://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. @@ -418,7 +418,7 @@ open class WebSecurityConfig : WebSecurityConfigurerAdapter() { ==== [[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/modules/ROOT/pages/servlet/saml2/index.adoc b/docs/modules/ROOT/pages/servlet/saml2/index.adoc index e872f5d9d8..e0adeaf0c7 100644 --- a/docs/modules/ROOT/pages/servlet/saml2/index.adoc +++ b/docs/modules/ROOT/pages/servlet/saml2/index.adoc @@ -1,4 +1,1618 @@ [[servlet-saml2]] = SAML2 -include::saml2-login.adoc[] +Spring Security provides comprehensive SAML 2 support. +This section discusses how to integrate SAML 2 into your servlet based application. + +[[servlet-saml2login]] +== SAML 2.0 Login +:figures: images/servlet/saml2 +:icondir: images/icons + +The SAML 2.0 Login feature provides an application with the capability to act as a SAML 2.0 Relying Party, having users https://wiki.shibboleth.net/confluence/display/CONCEPT/FlowsAndConfig[log in] to the application by using their existing account at a SAML 2.0 Asserting Party (Okta, ADFS, etc). + +NOTE: SAML 2.0 Login is implemented by using the *Web Browser SSO Profile*, as specified in +https://www.oasis-open.org/committees/download.php/35389/sstc-saml-profiles-errata-2.0-wd-06-diff.pdf#page=15[SAML 2 Profiles]. + +[[servlet-saml2login-spring-security-history]] +Since 2009, support for relying parties has existed as an https://github.com/spring-projects/spring-security-saml/tree/1e013b07a7772defd6a26fcfae187c9bf661ee8f#spring-saml[extension project]. +In 2019, the process began to port that into https://github.com/spring-projects/spring-security[Spring Security] proper. +This process is similar to the one started in 2017 for <>. + +[NOTE] +==== +A working sample for {gh-samples-url}/servlet/spring-boot/java/saml2-login[SAML 2.0 Login] is available in the {gh-samples-url}[Spring Security Samples repository]. +==== + +Let's take a look at how SAML 2.0 Relying Party Authentication works within Spring Security. +First, we see that, like <>, Spring Security takes the user to a third-party for performing authentication. +It does this through a series of redirects. + +.Redirecting to Asserting Party Authentication +image::{figures}/saml2webssoauthenticationrequestfilter.png[] + +The figure above builds off our <> and <> diagrams: + +image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource `/private` for which it is not authorized. + +image:{icondir}/number_2.png[] Spring Security's <> indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. + +image:{icondir}/number_3.png[] Since the user lacks authorization, the <> initiates __Start Authentication__. +The configured <> is an instance of {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[`LoginUrlAuthenticationEntryPoint`] which redirects to <` generating endpoint>>, `Saml2WebSsoAuthenticationRequestFilter`. +Or, if you've <>, it will first redirect to a picker page. + +image:{icondir}/number_4.png[] Next, the `Saml2WebSsoAuthenticationRequestFilter` creates, signs, serializes, and encodes a `` using its configured <>. + +image:{icondir}/number_5.png[] Then, the browser takes this `` and presents it to the asserting party. +The asserting party attempts to authentication the user. +If successful, it will return a `` back to the browser. + +image:{icondir}/number_6.png[] The browser then POSTs the `` to the assertion consumer service endpoint. + +[[servlet-saml2login-authentication-saml2webssoauthenticationfilter]] +.Authenticating a `` +image::{figures}/saml2webssoauthenticationfilter.png[] + +The figure builds off our <> diagram. + +image:{icondir}/number_1.png[] When the browser submits a `` to the application, it <>. +This filter calls its configured `AuthenticationConverter` to create a `Saml2AuthenticationToken` by extracting the response from the `HttpServletRequest`. +This converter additionally resolves the <> and supplies it to `Saml2AuthenticationToken`. + +image:{icondir}/number_2.png[] Next, the filter passes the token to its configured <>. +By default, it will use the <>. + +image:{icondir}/number_3.png[] If authentication fails, then __Failure__ + +* The <> is cleared out. +* The <> is invoked to restart the authentication process. + +image:{icondir}/number_4.png[] If authentication is successful, then __Success__. + +* The <> is set on the <>. +* The `Saml2WebSsoAuthenticationFilter` invokes `FilterChain#doFilter(request,response)` to continue with the rest of the application logic. + +[[servlet-saml2login-minimaldependencies]] +=== Minimal Dependencies + +SAML 2.0 service provider support resides in `spring-security-saml2-service-provider`. +It builds off of the OpenSAML library. + +[[servlet-saml2login-minimalconfiguration]] +=== Minimal Configuration + +When using https://spring.io/projects/spring-boot[Spring Boot], configuring an application as a service provider consists of two basic steps. +First, include the needed dependencies and second, indicate the necessary asserting party metadata. + +[NOTE] +Also, this presupposes that you've already <>. + +==== Specifying Identity Provider Metadata + +In a Spring Boot application, to specify an identity provider's metadata, simply do: + +[source,yml] +---- +spring: + security: + saml2: + relyingparty: + registration: + adfs: + identityprovider: + entity-id: https://idp.example.com/issuer + verification.credentials: + - certificate-location: "classpath:idp.crt" + singlesignon.url: https://idp.example.com/issuer/sso + singlesignon.sign-request: false +---- + +where + +* `https://idp.example.com/issuer` is the value contained in the `Issuer` attribute of the SAML responses that the identity provider will issue +* `classpath:idp.crt` is the location on the classpath for the identity provider's certificate for verifying SAML responses, and +* `https://idp.example.com/issuer/sso` is the endpoint where the identity provider is expecting `AuthnRequest` s. + +And that's it! + +[NOTE] +Identity Provider and Asserting Party are synonymous, as are Service Provider and Relying Party. +These are frequently abbreviated as AP and RP, respectively. + +==== Runtime Expectations + +As configured above, the application processes any `+POST /login/saml2/sso/{registrationId}+` request containing a `SAMLResponse` parameter: + +[source,html] +---- +POST /login/saml2/sso/adfs HTTP/1.1 + +SAMLResponse=PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZ... +---- + +There are two ways to see induce your asserting party to generate a `SAMLResponse`: + +* First, you can navigate to your asserting party. +It likely has some kind of link or button for each registered relying party that you can click to send the `SAMLResponse`. +* Second, you can navigate to a protected page in your app, for example, `http://localhost:8080`. +Your app then redirects to the configured asserting party which then sends the `SAMLResponse`. + +From here, consider jumping to: + +* <> +* <> +* <> + +[[servlet-saml2login-architecture]] +=== How SAML 2.0 Login Integrates with OpenSAML + +Spring Security's SAML 2.0 support has a couple of design goals: + +* First, rely on a library for SAML 2.0 operations and domain objects. +To achieve this, Spring Security uses OpenSAML. +* Second, ensure this library is not required when using Spring Security's SAML support. +To achieve this, any interfaces or classes where Spring Security uses OpenSAML in the contract remain encapsulated. +This makes it possible for you to switch out OpenSAML for some other library or even an unsupported version of OpenSAML. + +As a natural outcome of the above two goals, Spring Security's SAML API is quite small relative to other modules. +Instead, classes like `OpenSaml4AuthenticationRequestFactory` and `OpenSaml4AuthenticationProvider` expose `Converter` s that customize various steps in the authentication process. + +For example, once your application receives a `SAMLResponse` and delegates to `Saml2WebSsoAuthenticationFilter`, the filter will delegate to `OpenSaml4AuthenticationProvider`. + +[NOTE] +For backward compatibility, Spring Security will use the latest OpenSAML 3 by default. +Note, though that OpenSAML 3 has reached it's end-of-life and updating to OpenSAML 4.x is recommended. +For that reason, Spring Security supports both OpenSAML 3.x and 4.x. +If you manage your OpenSAML dependency to 4.x, then Spring Security will select its OpenSAML 4.x implementations. + +.Authenticating an OpenSAML `Response` +image:{figures}/opensamlauthenticationprovider.png[] + +This figure builds off of the <>. + +image:{icondir}/number_1.png[] The `Saml2WebSsoAuthenticationFilter` formulates the `Saml2AuthenticationToken` and invokes the <>. + +image:{icondir}/number_2.png[] The <> invokes the OpenSAML authentication provider. + +image:{icondir}/number_3.png[] The authentication provider deserializes the response into an OpenSAML `Response` and checks its signature. +If the signature is invalid, authentication fails. + +image:{icondir}/number_4.png[] Then, the provider <>. +If any decryptions fail, authentication fails. + +image:{icondir}/number_5.png[] Next, the provider validates the response's `Issuer` and `Destination` values. +If they don't match what's in the `RelyingPartyRegistration`, authentication fails. + +image:{icondir}/number_6.png[] After that, the provider verifies the signature of each `Assertion`. +If any signature is invalid, authentication fails. +Also, if neither the response nor the assertions have signatures, authentication fails. +Either the response or all the assertions must have signatures. + +image:{icondir}/number_7.png[] Then, the provider <>. +If any decryptions fail, authentication fails. + +image:{icondir}/number_8.png[] Next, the provider validates each assertion's `ExpiresAt` and `NotBefore` timestamps, the `` and any `` conditions. +If any validations fail, authentication fails. + +image:{icondir}/number_9.png[] Following that, the provider takes the first assertion's `AttributeStatement` and maps it to a `Map>`. +It also grants the `ROLE_USER` granted authority. + +image:{icondir}/number_10.png[] And finally, it takes the `NameID` from the first assertion, the `Map` of attributes, and the `GrantedAuthority` and constructs a `Saml2AuthenticatedPrincipal`. +Then, it places that principal and the authorities into a `Saml2Authentication`. + +The resulting `Authentication#getPrincipal` is a Spring Security `Saml2AuthenticatedPrincipal` object, and `Authentication#getName` maps to the first assertion's `NameID` element. + +[[servlet-saml2login-opensaml-customization]] +==== Customizing OpenSAML Configuration + +Any class that uses both Spring Security and OpenSAML should statically initialize `OpenSamlInitializationService` at the beginning of the class, like so: + +==== +.Java +[source,java,role="primary"] +---- +static { + OpenSamlInitializationService.initialize(); +} +---- + + +.Kotlin +[source,kotlin,role="secondary"] +---- +companion object { + init { + OpenSamlInitializationService.initialize() + } +} +---- +==== + +This replaces OpenSAML's `InitializationService#initialize`. + +Occasionally, it can be valuable to customize how OpenSAML builds, marshalls, and unmarshalls SAML objects. +In these circumstances, you may instead want to call `OpenSamlInitializationService#requireInitialize(Consumer)` that gives you access to OpenSAML's `XMLObjectProviderFactory`. + +For example, when sending an unsigned AuthNRequest, you may want to force reauthentication. +In that case, you can register your own `AuthnRequestMarshaller`, like so: + +==== +.Java +[source,java,role="primary"] +---- +static { + OpenSamlInitializationService.requireInitialize(factory -> { + AuthnRequestMarshaller marshaller = new AuthnRequestMarshaller() { + @Override + public Element marshall(XMLObject object, Element element) throws MarshallingException { + configureAuthnRequest((AuthnRequest) object); + return super.marshall(object, element); + } + + public Element marshall(XMLObject object, Document document) throws MarshallingException { + configureAuthnRequest((AuthnRequest) object); + return super.marshall(object, document); + } + + private void configureAuthnRequest(AuthnRequest authnRequest) { + authnRequest.setForceAuthn(true); + } + } + + factory.getMarshallerFactory().registerMarshaller(AuthnRequest.DEFAULT_ELEMENT_NAME, marshaller); + }); +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +companion object { + init { + OpenSamlInitializationService.requireInitialize { + val marshaller = object : AuthnRequestMarshaller() { + override fun marshall(xmlObject: XMLObject, element: Element): Element { + configureAuthnRequest(xmlObject as AuthnRequest) + return super.marshall(xmlObject, element) + } + + override fun marshall(xmlObject: XMLObject, document: Document): Element { + configureAuthnRequest(xmlObject as AuthnRequest) + return super.marshall(xmlObject, document) + } + + private fun configureAuthnRequest(authnRequest: AuthnRequest) { + authnRequest.isForceAuthn = true + } + } + it.marshallerFactory.registerMarshaller(AuthnRequest.DEFAULT_ELEMENT_NAME, marshaller) + } + } +} +---- +==== + +The `requireInitialize` method may only be called once per application instance. + +[[servlet-saml2login-sansboot]] +=== Overriding or Replacing Boot Auto Configuration + +There are two `@Bean` s that Spring Boot generates for a relying party. + +The first is a `WebSecurityConfigurerAdapter` that configures the app as a relying party. +When including `spring-security-saml2-service-provider`, the `WebSecurityConfigurerAdapter` looks like: + +.Default JWT Configuration +==== +.Java +[source,java,role="primary"] +---- +protected void configure(HttpSecurity http) { + http + .authorizeRequests(authorize -> authorize + .anyRequest().authenticated() + ) + .saml2Login(withDefaults()); +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +fun configure(http: HttpSecurity) { + http { + authorizeRequests { + authorize(anyRequest, authenticated) + } + saml2Login { } + } +} +---- +==== + +If the application doesn't expose a `WebSecurityConfigurerAdapter` bean, then Spring Boot will expose the above default one. + +You can replace this by exposing the bean within the application: + +.Custom SAML 2.0 Login Configuration +==== +.Java +[source,java,role="primary"] +---- +@EnableWebSecurity +public class MyCustomSecurityConfiguration extends WebSecurityConfigurerAdapter { + protected void configure(HttpSecurity http) { + http + .authorizeRequests(authorize -> authorize + .mvcMatchers("/messages/**").hasAuthority("ROLE_USER") + .anyRequest().authenticated() + ) + .saml2Login(withDefaults()); + } +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +@EnableWebSecurity +class MyCustomSecurityConfiguration : WebSecurityConfigurerAdapter() { + override fun configure(http: HttpSecurity) { + http { + authorizeRequests { + authorize("/messages/**", hasAuthority("ROLE_USER")) + authorize(anyRequest, authenticated) + } + saml2Login { + } + } + } +} +---- +==== + +The above requires the role of `USER` for any URL that starts with `/messages/`. + +[[servlet-saml2login-relyingpartyregistrationrepository]] +The second `@Bean` Spring Boot creates is a {security-api-url}org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationRepository.html[`RelyingPartyRegistrationRepository`], which represents the asserting party and relying party metadata. +This includes things like the location of the SSO endpoint the relying party should use when requesting authentication from the asserting party. + +You can override the default by publishing your own `RelyingPartyRegistrationRepository` bean. +For example, you can look up the asserting party's configuration by hitting its metadata endpoint like so: + +.Relying Party Registration Repository +==== +.Java +[source,java,role="primary"] +---- +@Value("${metadata.location}") +String assertingPartyMetadataLocation; + +@Bean +public RelyingPartyRegistrationRepository relyingPartyRegistrations() { + RelyingPartyRegistration registration = RelyingPartyRegistrations + .fromMetadataLocation(assertingPartyMetadataLocation) + .registrationId("example") + .build(); + return new InMemoryRelyingPartyRegistrationRepository(registration); +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +@Value("\${metadata.location}") +var assertingPartyMetadataLocation: String? = null + +@Bean +open fun relyingPartyRegistrations(): RelyingPartyRegistrationRepository? { + val registration = RelyingPartyRegistrations + .fromMetadataLocation(assertingPartyMetadataLocation) + .registrationId("example") + .build() + return InMemoryRelyingPartyRegistrationRepository(registration) +} +---- +==== + +Or you can provide each detail manually, as you can see below: + +.Relying Party Registration Repository Manual Configuration +==== +.Java +[source,java,role="primary"] +---- +@Value("${verification.key}") +File verificationKey; + +@Bean +public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception { + X509Certificate certificate = X509Support.decodeCertificate(this.verificationKey); + Saml2X509Credential credential = Saml2X509Credential.verification(certificate); + RelyingPartyRegistration registration = RelyingPartyRegistration + .withRegistrationId("example") + .assertingPartyDetails(party -> party + .entityId("https://idp.example.com/issuer") + .singleSignOnServiceLocation("https://idp.example.com/SSO.saml2") + .wantAuthnRequestsSigned(false) + .verificationX509Credentials(c -> c.add(credential)) + ) + .build(); + return new InMemoryRelyingPartyRegistrationRepository(registration); +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +@Value("\${verification.key}") +var verificationKey: File? = null + +@Bean +open fun relyingPartyRegistrations(): RelyingPartyRegistrationRepository { + val certificate: X509Certificate? = X509Support.decodeCertificate(verificationKey!!) + val credential: Saml2X509Credential = Saml2X509Credential.verification(certificate) + val registration = RelyingPartyRegistration + .withRegistrationId("example") + .assertingPartyDetails { party: AssertingPartyDetails.Builder -> + party + .entityId("https://idp.example.com/issuer") + .singleSignOnServiceLocation("https://idp.example.com/SSO.saml2") + .wantAuthnRequestsSigned(false) + .verificationX509Credentials { c: MutableCollection -> + c.add( + credential + ) + } + } + .build() + return InMemoryRelyingPartyRegistrationRepository(registration) +} +---- +==== + +[NOTE] +Note that `X509Support` is an OpenSAML class, used here in the snippet for brevity + +[[servlet-saml2login-relyingpartyregistrationrepository-dsl]] + +Alternatively, you can directly wire up the repository using the DSL, which will also override the auto-configured `WebSecurityConfigurerAdapter`: + +.Custom Relying Party Registration DSL +==== +.Java +[source,java,role="primary"] +---- +@EnableWebSecurity +public class MyCustomSecurityConfiguration extends WebSecurityConfigurerAdapter { + protected void configure(HttpSecurity http) { + http + .authorizeRequests(authorize -> authorize + .mvcMatchers("/messages/**").hasAuthority("ROLE_USER") + .anyRequest().authenticated() + ) + .saml2Login(saml2 -> saml2 + .relyingPartyRegistrationRepository(relyingPartyRegistrations()) + ); + } +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +@EnableWebSecurity +class MyCustomSecurityConfiguration : WebSecurityConfigurerAdapter() { + override fun configure(http: HttpSecurity) { + http { + authorizeRequests { + authorize("/messages/**", hasAuthority("ROLE_USER")) + authorize(anyRequest, authenticated) + } + saml2Login { + relyingPartyRegistrationRepository = relyingPartyRegistrations() + } + } + } +} +---- +==== + +[NOTE] +A relying party can be multi-tenant by registering more than one relying party in the `RelyingPartyRegistrationRepository`. + +[[servlet-saml2login-relyingpartyregistration]] +=== RelyingPartyRegistration +A {security-api-url}org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.html[`RelyingPartyRegistration`] +instance represents a link between an relying party and assering party's metadata. + +In a `RelyingPartyRegistration`, you can provide relying party metadata like its `Issuer` value, where it expects SAML Responses to be sent to, and any credentials that it owns for the purposes of signing or decrypting payloads. + +Also, you can provide asserting party metadata like its `Issuer` value, where it expects AuthnRequests to be sent to, and any public credentials that it owns for the purposes of the relying party verifying or encrypting payloads. + +The following `RelyingPartyRegistration` is the minimum required for most setups: + +==== +.Java +[source,java,role="primary"] +---- +RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations + .fromMetadataLocation("https://ap.example.org/metadata") + .registrationId("my-id") + .build(); +---- +.Kotlin +[source,kotlin,role="secondary"] +---- +val relyingPartyRegistration = RelyingPartyRegistrations + .fromMetadataLocation("https://ap.example.org/metadata") + .registrationId("my-id") + .build() +---- +==== + +Note that you can also create a `RelyingPartyRegistration` from an arbitrary `InputStream` source. +One such example is when the metadata is stored in a database: + +[source,java] +---- +String xml = fromDatabase(); +try (InputStream source = new ByteArrayInputStream(xml.getBytes())) { + RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations + .fromMetadata(source) + .registrationId("my-id") + .build(); +} +---- + +Though a more sophisticated setup is also possible, like so: + +==== +.Java +[source,java,role="primary"] +---- +RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("my-id") + .entityId("{baseUrl}/{registrationId}") + .decryptionX509Credentials(c -> c.add(relyingPartyDecryptingCredential())) + .assertionConsumerServiceLocation("/my-login-endpoint/{registrationId}") + .assertingPartyDetails(party -> party + .entityId("https://ap.example.org") + .verificationX509Credentials(c -> c.add(assertingPartyVerifyingCredential())) + .singleSignOnServiceLocation("https://ap.example.org/SSO.saml2") + ) + .build(); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +val relyingPartyRegistration = + RelyingPartyRegistration.withRegistrationId("my-id") + .entityId("{baseUrl}/{registrationId}") + .decryptionX509Credentials { c: MutableCollection -> + c.add(relyingPartyDecryptingCredential()) + } + .assertionConsumerServiceLocation("/my-login-endpoint/{registrationId}") + .assertingPartyDetails { party -> party + .entityId("https://ap.example.org") + .verificationX509Credentials { c -> c.add(assertingPartyVerifyingCredential()) } + .singleSignOnServiceLocation("https://ap.example.org/SSO.saml2") + } + .build() +---- +==== + +[TIP] +The top-level metadata methods are details about the relying party. +The methods inside `assertingPartyDetails` are details about the asserting party. + +[NOTE] +The location where a relying party is expecting SAML Responses is the Assertion Consumer Service Location. + +The default for the relying party's `entityId` is `+{baseUrl}/saml2/service-provider-metadata/{registrationId}+`. +This is this value needed when configuring the asserting party to know about your relying party. + +The default for the `assertionConsumerServiceLocation` is `+/login/saml2/sso/{registrationId}+`. +It's mapped by default to <> in the filter chain. + +[[servlet-saml2login-rpr-uripatterns]] +==== URI Patterns + +You probably noticed in the above examples the `+{baseUrl}+` and `+{registrationId}+` placeholders. + +These are useful for generating URIs. As such, the relying party's `entityId` and `assertionConsumerServiceLocation` support the following placeholders: + +* `baseUrl` - the scheme, host, and port of a deployed application +* `registrationId` - the registration id for this relying party +* `baseScheme` - the scheme of a deployed application +* `baseHost` - the host of a deployed application +* `basePort` - the port of a deployed application + +For example, the `assertionConsumerServiceLocation` defined above was: + +`+/my-login-endpoint/{registrationId}+` + +which in a deployed application would translate to + +`+/my-login-endpoint/adfs+` + +The `entityId` above was defined as: + +`+{baseUrl}/{registrationId}+` + +which in a deployed application would translate to + +`+https://rp.example.com/adfs+` + +[[servlet-saml2login-rpr-credentials]] +==== Credentials + +You also likely noticed the credential that was used. + +Oftentimes, a relying party will use the same key to sign payloads as well as decrypt them. +Or it will use the same key to verify payloads as well as encrypt them. + +Because of this, Spring Security ships with `Saml2X509Credential`, a SAML-specific credential that simplifies configuring the same key for different use cases. + +At a minimum, it's necessary to have a certificate from the asserting party so that the asserting party's signed responses can be verified. + +To construct a `Saml2X509Credential` that you'll use to verify assertions from the asserting party, you can load the file and use +the `CertificateFactory` like so: + +==== +.Java +[source,java,role="primary"] +---- +Resource resource = new ClassPathResource("ap.crt"); +try (InputStream is = resource.getInputStream()) { + X509Certificate certificate = (X509Certificate) + CertificateFactory.getInstance("X.509").generateCertificate(is); + return Saml2X509Credential.verification(certificate); +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +val resource = ClassPathResource("ap.crt") +resource.inputStream.use { + return Saml2X509Credential.verification( + CertificateFactory.getInstance("X.509").generateCertificate(it) as X509Certificate? + ) +} +---- +==== + +Let's say that the asserting party is going to also encrypt the assertion. +In that case, the relying party will need a private key to be able to decrypt the encrypted value. + +In that case, you'll need an `RSAPrivateKey` as well as its corresponding `X509Certificate`. +You can load the first using Spring Security's `RsaKeyConverters` utility class and the second as you did before: + +==== +.Java +[source,java,role="primary"] +---- +X509Certificate certificate = relyingPartyDecryptionCertificate(); +Resource resource = new ClassPathResource("rp.crt"); +try (InputStream is = resource.getInputStream()) { + RSAPrivateKey rsa = RsaKeyConverters.pkcs8().convert(is); + return Saml2X509Credential.decryption(rsa, certificate); +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +val certificate: X509Certificate = relyingPartyDecryptionCertificate() +val resource = ClassPathResource("rp.crt") +resource.inputStream.use { + val rsa: RSAPrivateKey = RsaKeyConverters.pkcs8().convert(it) + return Saml2X509Credential.decryption(rsa, certificate) +} +---- +==== + +[TIP] +When you specify the locations of these files as the appropriate Spring Boot properties, then Spring Boot will perform these conversions for you. + +[[servlet-saml2login-rpr-relyingpartyregistrationresolver]] +==== Resolving the Relying Party from the Request + +As seen so far, Spring Security resolves the `RelyingPartyRegistration` by looking for the registration id in the URI path. + +There are a number of reasons you may want to customize. Among them: + +* You may know that you will never be a multi-tenant application and so want to have a simpler URL scheme +* You may identify tenants in a way other than by the URI path + +To customize the way that a `RelyingPartyRegistration` is resolved, you can configure a custom `Converter`. +The default looks up the registration id from the URI's last path element and looks it up in your `RelyingPartyRegistrationRepository`. + +You can provide a simpler resolver that, for example, always returns the same relying party: + +==== +.Java +[source,java,role="primary"] +---- +public class SingleRelyingPartyRegistrationResolver + implements Converter { + + @Override + public RelyingPartyRegistration convert(HttpServletRequest request) { + return this.relyingParty; + } +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +class SingleRelyingPartyRegistrationResolver : Converter { + override fun convert(request: HttpServletRequest?): RelyingPartyRegistration? { + return this.relyingParty + } +} +---- +==== + +Then, you can provide this resolver to the appropriate filters that <` s>>, <` s>>, and <` metadata>>. + +[NOTE] +Remember that if you have any placeholders in your `RelyingPartyRegistration`, your resolver implementation should resolve them. + +[[servlet-saml2login-rpr-duplicated]] +==== Duplicated Relying Party Configurations + +When an application uses multiple asserting parties, some configuration is duplicated between `RelyingPartyRegistration` instances: + +* The relying party's `entityId` +* Its `assertionConsumerServiceLocation`, and +* Its credentials, for example its signing or decryption credentials + +What's nice about this setup is credentials may be more easily rotated for some identity providers vs others. + +The duplication can be alleviated in a few different ways. + +First, in YAML this can be alleviated with references, like so: + +[source,yaml] +---- +spring: + security: + saml2: + relyingparty: + okta: + signing.credentials: &relying-party-credentials + - private-key-location: classpath:rp.key + - certificate-location: classpath:rp.crt + identityprovider: + entity-id: ... + azure: + signing.credentials: *relying-party-credentials + identityprovider: + entity-id: ... +---- + +Second, in a database, it's not necessary to replicate `RelyingPartyRegistration` 's model. + +Third, in Java, you can create a custom configuration method, like so: + +==== +.Java +[source,java,role="primary"] +---- +private RelyingPartyRegistration.Builder + addRelyingPartyDetails(RelyingPartyRegistration.Builder builder) { + + Saml2X509Credential signingCredential = ... + builder.signingX509Credentials(c -> c.addAll(signingCredential)); + // ... other relying party configurations +} + +@Bean +public RelyingPartyRegistrationRepository relyingPartyRegistrations() { + RelyingPartyRegistration okta = addRelyingPartyDetails( + RelyingPartyRegistrations + .fromMetadataLocation(oktaMetadataUrl) + .registrationId("okta")).build(); + + RelyingPartyRegistration azure = addRelyingPartyDetails( + RelyingPartyRegistrations + .fromMetadataLocation(oktaMetadataUrl) + .registrationId("azure")).build(); + + return new InMemoryRelyingPartyRegistrationRepository(okta, azure); +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +private fun addRelyingPartyDetails(builder: RelyingPartyRegistration.Builder): RelyingPartyRegistration.Builder { + val signingCredential: Saml2X509Credential = ... + builder.signingX509Credentials { c: MutableCollection -> + c.add( + signingCredential + ) + } + // ... other relying party configurations +} + +@Bean +open fun relyingPartyRegistrations(): RelyingPartyRegistrationRepository? { + val okta = addRelyingPartyDetails( + RelyingPartyRegistrations + .fromMetadataLocation(oktaMetadataUrl) + .registrationId("okta") + ).build() + val azure = addRelyingPartyDetails( + RelyingPartyRegistrations + .fromMetadataLocation(oktaMetadataUrl) + .registrationId("azure") + ).build() + return InMemoryRelyingPartyRegistrationRepository(okta, azure) +} +---- +==== + +[[servlet-saml2login-sp-initiated-factory]] +=== Producing `` s + +As stated earlier, Spring Security's SAML 2.0 support produces a `` to commence authentication with the asserting party. + +Spring Security achieves this in part by registering the `Saml2WebSsoAuthenticationRequestFilter` in the filter chain. +This filter by default responds to endpoint `+/saml2/authenticate/{registrationId}+`. + +For example, if you were deployed to `https://rp.example.com` and you gave your registration an ID of `okta`, you could navigate to: + +`https://rp.example.org/saml2/authenticate/ping` + +and the result would be a redirect that included a `SAMLRequest` parameter containing the signed, deflated, and encoded ``. + +[[servlet-saml2login-sp-initiated-factory-signing]] +==== Changing How the `` Gets Sent + +By default, Spring Security signs each `` and send it as a GET to the asserting party. + +Many asserting parties don't require a signed ``. +This can be configured automatically via `RelyingPartyRegistrations`, or you can supply it manually, like so: + + +.Not Requiring Signed AuthnRequests +==== +.Boot +[source,yaml,role="primary"] +---- +spring: + security: + saml2: + relyingparty: + okta: + identityprovider: + entity-id: ... + singlesignon.sign-request: false +---- + +.Java +[source,java,role="secondary"] +---- +RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta") + // ... + .assertingPartyDetails(party -> party + // ... + .wantAuthnRequestsSigned(false) + ) + .build(); +---- + +.Kotlin +[source,java,role="secondary"] +---- +var relyingPartyRegistration: RelyingPartyRegistration = + RelyingPartyRegistration.withRegistrationId("okta") + // ... + .assertingPartyDetails { party: AssertingPartyDetails.Builder -> party + // ... + .wantAuthnRequestsSigned(false) + } + .build(); +---- +==== + +Otherwise, you will need to specify a private key to `RelyingPartyRegistration#signingX509Credentials` so that Spring Security can sign the `` before sending. + +[[servlet-saml2login-sp-initiated-factory-algorithm]] +By default, Spring Security will sign the `` using `rsa-sha256`, though some asserting parties will require a different algorithm, as indicated in their metadata. + +You can configure the algorithm based on the asserting party's <>. + +Or, you can provide it manually: + +==== +.Java +[source,java,role="primary"] +---- +String metadataLocation = "classpath:asserting-party-metadata.xml"; +RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations.fromMetadataLocation(metadataLocation) + // ... + .assertingPartyDetails((party) -> party + // ... + .signingAlgorithms((sign) -> sign.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512)) + ) + .build(); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +var metadataLocation = "classpath:asserting-party-metadata.xml" +var relyingPartyRegistration: RelyingPartyRegistration = + RelyingPartyRegistrations.fromMetadataLocation(metadataLocation) + // ... + .assertingPartyDetails { party: AssertingPartyDetails.Builder -> party + // ... + .signingAlgorithms { sign: MutableList -> + sign.add( + SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512 + ) + } + } + .build(); +---- +==== + +NOTE: The snippet above uses the OpenSAML `SignatureConstants` class to supply the algorithm name. +But, that's just for convenience. +Since the datatype is `String`, you can supply the name of the algorithm directly. + +[[servlet-saml2login-sp-initiated-factory-binding]] +Some asserting parties require that the `` be POSTed. +This can be configured automatically via `RelyingPartyRegistrations`, or you can supply it manually, like so: + +==== +.Java +[source,java,role="primary"] +---- +RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta") + // ... + .assertingPartyDetails(party -> party + // ... + .singleSignOnServiceBinding(Saml2MessageBinding.POST) + ) + .build(); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +var relyingPartyRegistration: RelyingPartyRegistration? = + RelyingPartyRegistration.withRegistrationId("okta") + // ... + .assertingPartyDetails { party: AssertingPartyDetails.Builder -> party + // ... + .singleSignOnServiceBinding(Saml2MessageBinding.POST) + } + .build() +---- +==== + +[[servlet-saml2login-sp-initiated-factory-custom-authnrequest]] +==== Customizing OpenSAML's `AuthnRequest` Instance + +There are a number of reasons that you may want to adjust an `AuthnRequest`. +For example, you may want `ForceAuthN` to be set to `true`, which Spring Security sets to `false` by default. + +If you don't need information from the `HttpServletRequest` to make your decision, then the easiest way is to <>. +This will give you access to post-process the `AuthnRequest` instance before it's serialized. + +But, if you do need something from the request, then you can use create a custom `Saml2AuthenticationRequestContext` implementation and then a `Converter` to build an `AuthnRequest` yourself, like so: + +==== +.Java +[source,java,role="primary"] +---- +@Component +public class AuthnRequestConverter implements + Converter { + + private final AuthnRequestBuilder authnRequestBuilder; + private final IssuerBuilder issuerBuilder; + + // ... constructor + + public AuthnRequest convert(Saml2AuthenticationRequestContext context) { + MySaml2AuthenticationRequestContext myContext = (MySaml2AuthenticationRequestContext) context; + Issuer issuer = issuerBuilder.buildObject(); + issuer.setValue(myContext.getIssuer()); + + AuthnRequest authnRequest = authnRequestBuilder.buildObject(); + authnRequest.setIssuer(issuer); + authnRequest.setDestination(myContext.getDestination()); + authnRequest.setAssertionConsumerServiceURL(myContext.getAssertionConsumerServiceUrl()); + + // ... additional settings + + authRequest.setForceAuthn(myContext.getForceAuthn()); + return authnRequest; + } +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +@Component +class AuthnRequestConverter : Converter { + private val authnRequestBuilder: AuthnRequestBuilder? = null + private val issuerBuilder: IssuerBuilder? = null + + // ... constructor + override fun convert(context: MySaml2AuthenticationRequestContext): AuthnRequest { + val myContext: MySaml2AuthenticationRequestContext = context + val issuer: Issuer = issuerBuilder.buildObject() + issuer.value = myContext.getIssuer() + val authnRequest: AuthnRequest = authnRequestBuilder.buildObject() + authnRequest.issuer = issuer + authnRequest.destination = myContext.getDestination() + authnRequest.assertionConsumerServiceURL = myContext.getAssertionConsumerServiceUrl() + + // ... additional settings + authRequest.setForceAuthn(myContext.getForceAuthn()) + return authnRequest + } +} +---- +==== + +Then, you can construct your own `Saml2AuthenticationRequestContextResolver` and `Saml2AuthenticationRequestFactory` and publish them as `@Bean` s: + +==== +.Java +[source,java,role="primary"] +---- +@Bean +Saml2AuthenticationRequestContextResolver authenticationRequestContextResolver() { + Saml2AuthenticationRequestContextResolver resolver = + new DefaultSaml2AuthenticationRequestContextResolver(); + return request -> { + Saml2AuthenticationRequestContext context = resolver.resolve(request); + return new MySaml2AuthenticationRequestContext(context, request.getParameter("force") != null); + }; +} + +@Bean +Saml2AuthenticationRequestFactory authenticationRequestFactory( + AuthnRequestConverter authnRequestConverter) { + + OpenSaml4AuthenticationRequestFactory authenticationRequestFactory = + new OpenSaml4AuthenticationRequestFactory(); + authenticationRequestFactory.setAuthenticationRequestContextConverter(authnRequestConverter); + return authenticationRequestFactory; +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +@Bean +open fun authenticationRequestContextResolver(): Saml2AuthenticationRequestContextResolver { + val resolver: Saml2AuthenticationRequestContextResolver = DefaultSaml2AuthenticationRequestContextResolver() + return Saml2AuthenticationRequestContextResolver { request: HttpServletRequest -> + val context = resolver.resolve(request) + MySaml2AuthenticationRequestContext( + context, + request.getParameter("force") != null + ) + } +} + +@Bean +open fun authenticationRequestFactory( + authnRequestConverter: AuthnRequestConverter? +): Saml2AuthenticationRequestFactory? { + val authenticationRequestFactory = OpenSaml4AuthenticationRequestFactory() + authenticationRequestFactory.setAuthenticationRequestContextConverter(authnRequestConverter) + return authenticationRequestFactory +} +---- +==== + +[[servlet-saml2login-authenticate-responses]] +=== Authenticating `` s + +To verify SAML 2.0 Responses, Spring Security uses <> by default. + +You can configure this in a number of ways including: + +1. Setting a clock skew to timestamp validation +2. Mapping the response to a list of `GrantedAuthority` instances +3. Customizing the strategy for validating assertions +4. Customizing the strategy for decrypting response and assertion elements + +To configure these, you'll use the `saml2Login#authenticationManager` method in the DSL. + +[[servlet-saml2login-opensamlauthenticationprovider-clockskew]] +==== Setting a Clock Skew + +It's not uncommon for the asserting and relying parties to have system clocks that aren't perfectly synchronized. +For that reason, you can configure `OpenSaml4AuthenticationProvider` 's default assertion validator with some tolerance: + +==== +.Java +[source,java,role="primary"] +---- +@EnableWebSecurity +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + OpenSaml4AuthenticationProvider authenticationProvider = new OpenSaml4AuthenticationProvider(); + authenticationProvider.setAssertionValidator(OpenSaml4AuthenticationProvider + .createDefaultAssertionValidator(assertionToken -> { + Map params = new HashMap<>(); + params.put(CLOCK_SKEW, Duration.ofMinutes(10).toMillis()); + // ... other validation parameters + return new ValidationContext(params); + }) + ); + + http + .authorizeRequests(authz -> authz + .anyRequest().authenticated() + ) + .saml2Login(saml2 -> saml2 + .authenticationManager(new ProviderManager(authenticationProvider)) + ); + } +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +@EnableWebSecurity +open class SecurityConfig : WebSecurityConfigurerAdapter() { + override fun configure(http: HttpSecurity) { + val authenticationProvider = OpenSaml4AuthenticationProvider() + authenticationProvider.setAssertionValidator( + OpenSaml4AuthenticationProvider + .createDefaultAssertionValidator(Converter { + val params: MutableMap = HashMap() + params[CLOCK_SKEW] = + Duration.ofMinutes(10).toMillis() + ValidationContext(params) + }) + ) + http { + authorizeRequests { + authorize(anyRequest, authenticated) + } + saml2Login { + authenticationManager = ProviderManager(authenticationProvider) + } + } + } +} +---- +==== + +[[servlet-saml2login-opensamlauthenticationprovider-userdetailsservice]] +==== Coordinating with a `UserDetailsService` + +Or, perhaps you would like to include user details from a legacy `UserDetailsService`. +In that case, the response authentication converter can come in handy, as can be seen below: + +==== +.Java +[source,java,role="primary"] +---- +@EnableWebSecurity +public class SecurityConfig extends WebSecurityConfigurerAdapter { + @Autowired + UserDetailsService userDetailsService; + + @Override + protected void configure(HttpSecurity http) throws Exception { + OpenSaml4AuthenticationProvider authenticationProvider = new OpenSaml4AuthenticationProvider(); + authenticationProvider.setResponseAuthenticationConverter(responseToken -> { + Saml2Authentication authentication = OpenSaml4AuthenticationProvider + .createDefaultResponseAuthenticationConverter() <1> + .convert(responseToken); + Assertion assertion = responseToken.getResponse().getAssertions().get(0); + String username = assertion.getSubject().getNameID().getValue(); + UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); <2> + return MySaml2Authentication(userDetails, authentication); <3> + }); + + http + .authorizeRequests(authz -> authz + .anyRequest().authenticated() + ) + .saml2Login(saml2 -> saml2 + .authenticationManager(new ProviderManager(authenticationProvider)) + ); + } +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +@EnableWebSecurity +open class SecurityConfig : WebSecurityConfigurerAdapter() { + @Autowired + var userDetailsService: UserDetailsService? = null + + override fun configure(http: HttpSecurity) { + val authenticationProvider = OpenSaml4AuthenticationProvider() + authenticationProvider.setResponseAuthenticationConverter { responseToken: OpenSaml4AuthenticationProvider.ResponseToken -> + val authentication = OpenSaml4AuthenticationProvider + .createDefaultResponseAuthenticationConverter() <1> + .convert(responseToken) + val assertion: Assertion = responseToken.response.assertions[0] + val username: String = assertion.subject.nameID.value + val userDetails = userDetailsService!!.loadUserByUsername(username) <2> + MySaml2Authentication(userDetails, authentication) <3> + } + http { + authorizeRequests { + authorize(anyRequest, authenticated) + } + saml2Login { + authenticationManager = ProviderManager(authenticationProvider) + } + } + } +} +---- +==== +<1> First, call the default converter, which extracts attributes and authorities from the response +<2> Second, call the <> using the relevant information +<3> Third, return a custom authentication that includes the user details + +[NOTE] +It's not required to call `OpenSaml4AuthenticationProvider` 's default authentication converter. +It returns a `Saml2AuthenticatedPrincipal` containing the attributes it extracted from `AttributeStatement` s as well as the single `ROLE_USER` authority. + +[[servlet-saml2login-opensamlauthenticationprovider-additionalvalidation]] +==== Performing Additional Response Validation + +`OpenSaml4AuthenticationProvider` validates the `Issuer` and `Destination` values right after decrypting the `Response`. +You can customize the validation by extending the default validator concatenating with your own response validator, or you can replace it entirely with yours. + +For example, you can throw a custom exception with any additional information available in the `Response` object, like so: +[source,java] +---- +OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider(); +provider.setResponseValidator((responseToken) -> { + Saml2ResponseValidatorResult result = OpenSamlAuthenticationProvider + .createDefaultResponseValidator() + .convert(responseToken) + .concat(myCustomValidator.convert(responseToken)); + if (!result.getErrors().isEmpty()) { + String inResponseTo = responseToken.getInResponseTo(); + throw new CustomSaml2AuthenticationException(result, inResponseTo); + } + return result; +}); +---- + +==== Performing Additional Assertion Validation +`OpenSaml4AuthenticationProvider` performs minimal validation on SAML 2.0 Assertions. +After verifying the signature, it will: + +1. Validate `` and `` conditions +2. Validate `` s, expect for any IP address information + +To perform additional validation, you can configure your own assertion validator that delegates to `OpenSaml4AuthenticationProvider` 's default and then performs its own. + +[[servlet-saml2login-opensamlauthenticationprovider-onetimeuse]] +For example, you can use OpenSAML's `OneTimeUseConditionValidator` to also validate a `` condition, like so: + +==== +.Java +[source,java,role="primary"] +---- +OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider(); +OneTimeUseConditionValidator validator = ...; +provider.setAssertionValidator(assertionToken -> { + Saml2ResponseValidatorResult result = OpenSaml4AuthenticationProvider + .createDefaultAssertionValidator() + .convert(assertionToken); + Assertion assertion = assertionToken.getAssertion(); + OneTimeUse oneTimeUse = assertion.getConditions().getOneTimeUse(); + ValidationContext context = new ValidationContext(); + try { + if (validator.validate(oneTimeUse, assertion, context) == ValidationResult.VALID) { + return result; + } + } catch (Exception e) { + return result.concat(new Saml2Error(INVALID_ASSERTION, e.getMessage())); + } + return result.concat(new Saml2Error(INVALID_ASSERTION, context.getValidationFailureMessage())); +}); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +var provider = OpenSaml4AuthenticationProvider() +var validator: OneTimeUseConditionValidator = ... +provider.setAssertionValidator { assertionToken -> + val result = OpenSaml4AuthenticationProvider + .createDefaultAssertionValidator() + .convert(assertionToken) + val assertion: Assertion = assertionToken.assertion + val oneTimeUse: OneTimeUse = assertion.conditions.oneTimeUse + val context = ValidationContext() + try { + if (validator.validate(oneTimeUse, assertion, context) == ValidationResult.VALID) { + return@setAssertionValidator result + } + } catch (e: Exception) { + return@setAssertionValidator result.concat(Saml2Error(INVALID_ASSERTION, e.message)) + } + result.concat(Saml2Error(INVALID_ASSERTION, context.validationFailureMessage)) +} +---- +==== + +[NOTE] +While recommended, it's not necessary to call `OpenSaml4AuthenticationProvider` 's default assertion validator. +A circumstance where you would skip it would be if you don't need it to check the `` or the `` since you are doing those yourself. + +[[servlet-saml2login-opensamlauthenticationprovider-decryption]] +==== Customizing Decryption + +Spring Security decrypts ``, ``, and `` elements automatically by using the decryption <> registered in the <>. + +`OpenSaml4AuthenticationProvider` exposes <>. +The response decrypter is for decrypting encrypted elements of the ``, like ``. +The assertion decrypter is for decrypting encrypted elements of the ``, like `` and ``. + +You can replace `OpenSaml4AuthenticationProvider`'s default decryption strategy with your own. +For example, if you have a separate service that decrypts the assertions in a ``, you can use it instead like so: + +==== +.Java +[source,java,role="primary"] +---- +MyDecryptionService decryptionService = ...; +OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider(); +provider.setResponseElementsDecrypter((responseToken) -> decryptionService.decrypt(responseToken.getResponse())); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +val decryptionService: MyDecryptionService = ... +val provider = OpenSaml4AuthenticationProvider() +provider.setResponseElementsDecrypter { responseToken -> decryptionService.decrypt(responseToken.response) } +---- +==== + +If you are also decrypting individual elements in a ``, you can customize the assertion decrypter, too: + +==== +.Java +[source,java,role="primary"] +---- +provider.setAssertionElementsDecrypter((assertionToken) -> decryptionService.decrypt(assertionToken.getAssertion())); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +provider.setAssertionElementsDecrypter { assertionToken -> decryptionService.decrypt(assertionToken.assertion) } +---- +==== + +NOTE: There are two separate decrypters since assertions can be signed separately from responses. +Trying to decrypt a signed assertion's elements before signature verification may invalidate the signature. +If your asserting party signs the response only, then it's safe to decrypt all elements using only the response decrypter. + +[[servlet-saml2login-authenticationmanager-custom]] +==== Using a Custom Authentication Manager + +[[servlet-saml2login-opensamlauthenticationprovider-authenticationmanager]] +Of course, the `authenticationManager` DSL method can be also used to perform a completely custom SAML 2.0 authentication. +This authentication manager should expect a `Saml2AuthenticationToken` object containing the SAML 2.0 Response XML data. + +==== +.Java +[source,java,role="primary"] +---- +@EnableWebSecurity +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + AuthenticationManager authenticationManager = new MySaml2AuthenticationManager(...); + http + .authorizeRequests(authorize -> authorize + .anyRequest().authenticated() + ) + .saml2Login(saml2 -> saml2 + .authenticationManager(authenticationManager) + ) + ; + } +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +@EnableWebSecurity +open class SecurityConfig : WebSecurityConfigurerAdapter() { + override fun configure(http: HttpSecurity) { + val customAuthenticationManager: AuthenticationManager = MySaml2AuthenticationManager(...) + http { + authorizeRequests { + authorize(anyRequest, authenticated) + } + saml2Login { + authenticationManager = customAuthenticationManager + } + } + } +} +---- +==== + +[[servlet-saml2login-authenticatedprincipal]] +=== Using `Saml2AuthenticatedPrincipal` + +With the relying party correctly configured for a given asserting party, it's ready to accept assertions. +Once the relying party validates an assertion, the result is a `Saml2Authentication` with a `Saml2AuthenticatedPrincipal`. + +This means that you can access the principal in your controller like so: + +==== +.Java +[source,java,role="primary"] +---- +@Controller +public class MainController { + @GetMapping("/") + public String index(@AuthenticationPrincipal Saml2AuthenticatedPrincipal principal, Model model) { + String email = principal.getFirstAttribute("email"); + model.setAttribute("email", email); + return "index"; + } +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +@Controller +class MainController { + @GetMapping("/") + fun index(@AuthenticationPrincipal principal: Saml2AuthenticatedPrincipal, model: Model): String { + val email = principal.getFirstAttribute("email") + model.setAttribute("email", email) + return "index" + } +} +---- +==== + +[TIP] +Because the SAML 2.0 specification allows for each attribute to have multiple values, you can either call `getAttribute` to get the list of attributes or `getFirstAttribute` to get the first in the list. +`getFirstAttribute` is quite handy when you know that there is only one value. + +[[servlet-saml2login-metadata]] +=== Producing `` Metadata + +You can publish a metadata endpoint by adding the `Saml2MetadataFilter` to the filter chain, as you'll see below: + +==== +.Java +[source,java,role="primary"] +---- +Converter relyingPartyRegistrationResolver = + new DefaultRelyingPartyRegistrationResolver(this.relyingPartyRegistrationRepository); +Saml2MetadataFilter filter = new Saml2MetadataFilter( + relyingPartyRegistrationResolver, + new OpenSamlMetadataResolver()); + +http + // ... + .saml2Login(withDefaults()) + .addFilterBefore(filter, Saml2WebSsoAuthenticationFilter.class); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +val relyingPartyRegistrationResolver: Converter = + DefaultRelyingPartyRegistrationResolver(this.relyingPartyRegistrationRepository) +val filter = Saml2MetadataFilter( + relyingPartyRegistrationResolver, + OpenSamlMetadataResolver() +) + +http { + //... + saml2Login { } + addFilterBefore(filter) +} +---- +==== + +You can use this metadata endpoint to register your relying party with your asserting party. +This is often as simple as finding the correct form field to supply the metadata endpoint. + +By default, the metadata endpoint is `+/saml2/service-provider-metadata/{registrationId}+`. +You can change this by calling the `setRequestMatcher` method on the filter: + +==== +.Java +[source,java,role="primary"] +---- +filter.setRequestMatcher(new AntPathRequestMatcher("/saml2/metadata/{registrationId}", "GET")); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +filter.setRequestMatcher(AntPathRequestMatcher("/saml2/metadata/{registrationId}", "GET")) +---- +==== + +ensuring that the `registrationId` hint is at the end of the path. + +Or, if you have registered a custom relying party registration resolver in the constructor, then you can specify a path without a `registrationId` hint, like so: + +==== +.Java +[source,java,role="primary"] +---- +filter.setRequestMatcher(new AntPathRequestMatcher("/saml2/metadata", "GET")); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +filter.setRequestMatcher(AntPathRequestMatcher("/saml2/metadata", "GET")) +---- +==== + +[[servlet-saml2login-logout]] +=== Performing Single Logout + +Spring Security does not yet support single logout. + +Generally speaking, though, you can achieve this by creating and registering a custom `LogoutSuccessHandler` and `RequestMatcher`: + +==== +.Java +[source,java,role="primary"] +---- +http + // ... + .logout(logout -> logout + .logoutSuccessHandler(myCustomSuccessHandler()) + .logoutRequestMatcher(myRequestMatcher()) + ) +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +http { + logout { + // ... + logoutSuccessHandler = myCustomSuccessHandler() + logoutRequestMatcher = myRequestMatcher() + } +} +---- +==== + +The success handler will send logout requests to the asserting party. + +The request matcher will detect logout requests from the asserting party. + diff --git a/docs/modules/ROOT/pages/servlet/test/method.adoc b/docs/modules/ROOT/pages/servlet/test/method.adoc index 82a2f9ceb9..e5e639464e 100644 --- a/docs/modules/ROOT/pages/servlet/test/method.adoc +++ b/docs/modules/ROOT/pages/servlet/test/method.adoc @@ -1,5 +1,5 @@ [[test-method]] -== Testing Method Security += Testing Method Security This section demonstrates how to use Spring Security's Test support to test method based security. We first introduce a `MessageService` that requires the user to be authenticated in order to access it. @@ -41,7 +41,7 @@ Hello org.springframework.security.authentication.UsernamePasswordAuthentication ---- [[test-method-setup]] -=== Security Test Setup +== Security Test Setup Before we can use Spring Security Test support, we must perform some setup. An example can be seen below: @@ -98,7 +98,7 @@ fun getMessageUnauthenticated() { ==== [[test-method-withmockuser]] -=== @WithMockUser +== @WithMockUser The question is "How could we most easily run the test as a specific user?" The answer is to use `@WithMockUser`. @@ -294,7 +294,7 @@ You can change this to happen during the `TestExecutionListener.beforeTestExecut [[test-method-withanonymoususer]] -=== @WithAnonymousUser +== @WithAnonymousUser Using `@WithAnonymousUser` allows running as an anonymous user. This is especially convenient when you wish to run most of your tests with a specific user, but want to run a few tests as an anonymous user. @@ -358,7 +358,7 @@ You can change this to happen during the `TestExecutionListener.beforeTestExecut [[test-method-withuserdetails]] -=== @WithUserDetails +== @WithUserDetails While `@WithMockUser` is a very convenient way to get started, it may not work in all instances. For example, it is common for applications to expect that the `Authentication` principal be of a specific type. @@ -462,7 +462,7 @@ You can change this to happen during the `TestExecutionListener.beforeTestExecut [[test-method-withsecuritycontext]] -=== @WithSecurityContext +== @WithSecurityContext We have seen that `@WithMockUser` is an excellent choice if we are not using a custom `Authentication` principal. Next we discovered that `@WithUserDetails` would allow us to use a custom `UserDetailsService` to create our `Authentication` principal but required the user to exist. @@ -596,7 +596,7 @@ You can change this to happen during the `TestExecutionListener.beforeTestExecut [[test-method-meta-annotations]] -=== Test Meta Annotations +== Test Meta Annotations If you reuse the same user within your tests often, it is not ideal to have to repeatedly specify the attributes. For example, if there are many tests related to an administrative user with the username "admin" and the roles `ROLE_USER` and `ROLE_ADMIN` you would have to write: diff --git a/docs/modules/ROOT/pages/servlet/test/mockmvc.adoc b/docs/modules/ROOT/pages/servlet/test/mockmvc.adoc index f28c866ae0..5a58d3118d 100644 --- a/docs/modules/ROOT/pages/servlet/test/mockmvc.adoc +++ b/docs/modules/ROOT/pages/servlet/test/mockmvc.adoc @@ -1,10 +1,10 @@ [[test-mockmvc]] -== Spring MVC Test Integration += Spring MVC Test Integration Spring Security provides comprehensive integration with https://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#spring-mvc-test-framework[Spring MVC Test] [[test-mockmvc-setup]] -=== Setting Up MockMvc and Spring Security +== Setting Up MockMvc and Spring Security In order to use Spring Security with Spring MVC Test it is necessary to add the Spring Security `FilterChainProxy` as a `Filter`. It is also necessary to add Spring Security's `TestSecurityContextHolderPostProcessor` to support <>. @@ -68,7 +68,7 @@ class CsrfShowcaseTests { <1> `SecurityMockMvcConfigurers.springSecurity()` will perform all of the initial setup we need to integrate Spring Security with Spring MVC Test [[test-mockmvc-smmrpp]] -=== SecurityMockMvcRequestPostProcessors +== SecurityMockMvcRequestPostProcessors Spring MVC Test provides a convenient interface called a `RequestPostProcessor` that can be used to modify a request. Spring Security provides a number of `RequestPostProcessor` implementations that make testing easier. @@ -89,7 +89,7 @@ import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequ ==== [[test-mockmvc-csrf]] -==== Testing with CSRF Protection +=== Testing with CSRF Protection When testing any non-safe HTTP methods and using Spring Security's CSRF protection, you must be sure to include a valid CSRF Token in the request. To specify a valid CSRF token as a request parameter using the following: @@ -150,7 +150,7 @@ mvc.post("/") { ==== [[test-mockmvc-securitycontextholder]] -==== Running a Test as a User in Spring MVC Test +=== Running a Test as a User in Spring MVC Test It is often desirable to run tests as a specific user. There are two simple ways of populating the user: @@ -159,7 +159,7 @@ There are two simple ways of populating the user: * <> [[test-mockmvc-securitycontextholder-rpp]] -==== Running as a User in Spring MVC Test with RequestPostProcessor +=== Running as a User in Spring MVC Test with RequestPostProcessor There are a number of options available to associate a user to the current `HttpServletRequest`. For example, the following will run as a user (which does not need to exist) with the username "user", the password "password", and the role "ROLE_USER": @@ -364,7 +364,7 @@ mvc.get("/") { ---- ==== -===== Running as a User in Spring MVC Test with Annotations +==== Running as a User in Spring MVC Test with Annotations As an alternative to using a `RequestPostProcessor` to create your user, you can use annotations described in <>. For example, the following will run the test with the user with username "user", password "password", and role "ROLE_USER": @@ -423,7 +423,7 @@ fun requestProtectedUrlWithUser() { ---- ==== -==== Testing HTTP Basic Authentication +=== Testing HTTP Basic Authentication While it has always been possible to authenticate with HTTP Basic, it was a bit tedious to remember the header name, format, and encode the values. Now this can be done using Spring Security's `httpBasic` `RequestPostProcessor`. @@ -454,7 +454,7 @@ Authorization: Basic dXNlcjpwYXNzd29yZA== ---- [[testing-oauth2]] -==== Testing OAuth 2.0 +=== Testing OAuth 2.0 When it comes to OAuth 2.0, the same principles covered earlier still apply: Ultimately, it depends on what your method under test is expecting to be in the `SecurityContextHolder`. @@ -507,7 +507,7 @@ fun foo(@AuthenticationPrincipal user: OidcUser): String { then Spring Security's test support can come in handy. [[testing-oidc-login]] -==== Testing OIDC Login +=== Testing OIDC Login Testing the method above with Spring MVC Test would require simulating some kind of grant flow with an authorization server. Certainly this would be a daunting task, which is why Spring Security ships with support for removing this boilerplate. @@ -589,7 +589,7 @@ Further, it also links that `OidcUser` to a simple instance of `OAuth2Authorized This can be handy if your tests <>.. [[testing-oidc-login-authorities]] -===== Configuring Authorities +==== Configuring Authorities In many circumstances, your method is protected by filter or method security and needs your `Authentication` to have certain granted authorities to allow the request. @@ -619,7 +619,7 @@ mvc.get("/endpoint") { ==== [[testing-oidc-login-claims]] -===== Configuring Claims +==== Configuring Claims And while granted authorities are quite common across all of Spring Security, we also have claims in the case of OAuth 2.0. @@ -678,7 +678,7 @@ mvc.get("/endpoint") { since `OidcUser` collects its claims from `OidcIdToken`. [[testing-oidc-login-user]] -===== Additional Configurations +==== Additional Configurations There are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects: @@ -724,7 +724,7 @@ mvc.get("/endpoint") { ==== [[testing-oauth2-login]] -==== Testing OAuth 2.0 Login +=== Testing OAuth 2.0 Login As with <>, testing OAuth 2.0 Login presents a similar challenge of mocking a grant flow. And because of that, Spring Security also has test support for non-OIDC use cases. @@ -812,7 +812,7 @@ Further, it also links that `OAuth2User` to a simple instance of `OAuth2Authoriz This can be handy if your tests <>. [[testing-oauth2-login-authorities]] -===== Configuring Authorities +==== Configuring Authorities In many circumstances, your method is protected by filter or method security and needs your `Authentication` to have certain granted authorities to allow the request. @@ -842,7 +842,7 @@ mvc.get("/endpoint") { ==== [[testing-oauth2-login-claims]] -===== Configuring Claims +==== Configuring Claims And while granted authorities are quite common across all of Spring Security, we also have claims in the case of OAuth 2.0. @@ -897,7 +897,7 @@ mvc.get("/endpoint") { ==== [[testing-oauth2-login-user]] -===== Additional Configurations +==== Additional Configurations There are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects: @@ -942,7 +942,7 @@ mvc.get("/endpoint") { ==== [[testing-oauth2-client]] -==== Testing OAuth 2.0 Clients +=== Testing OAuth 2.0 Clients Independent of how your user authenticates, you may have other tokens and client registrations that are in play for the request you are testing. For example, your controller may be relying on the client credentials grant to get a token that isn't associated with the user at all: @@ -1054,7 +1054,7 @@ assertThat(authorizedClient.accessToken.scopes).containsExactly("read") The client can then be retrieved as normal using `@RegisteredOAuth2AuthorizedClient` in a controller method. [[testing-oauth2-client-scopes]] -===== Configuring Scopes +==== Configuring Scopes In many circumstances, the OAuth 2.0 access token comes with a set of scopes. If your controller inspects these, say like so: @@ -1121,7 +1121,7 @@ mvc.get("/endpoint") { ==== [[testing-oauth2-client-registration]] -===== Additional Configurations +==== Additional Configurations There are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects: @@ -1167,7 +1167,7 @@ mvc.get("/endpoint") { ==== [[testing-jwt]] -==== Testing JWT Authentication +=== Testing JWT Authentication In order to make an authorized request on a resource server, you need a bearer token. @@ -1177,7 +1177,7 @@ All of this can be quite daunting, especially when this isn't the focus of your Fortunately, there are a number of simple ways that you can overcome this difficulty and allow your tests to focus on authorization and not on representing bearer tokens. We'll look at two of them now: -===== `jwt() RequestPostProcessor` +==== `jwt() RequestPostProcessor` The first way is via a `RequestPostProcessor`. The simplest of these would look something like this: @@ -1357,7 +1357,7 @@ mvc.get("/endpoint") { ---- ==== -===== `authentication()` `RequestPostProcessor` +==== `authentication()` `RequestPostProcessor` The second way is by using the `authentication()` `RequestPostProcessor`. Essentially, you can instantiate your own `JwtAuthenticationToken` and provide it in your test, like so: @@ -1399,7 +1399,7 @@ mvc.get("/endpoint") { Note that as an alternative to these, you can also mock the `JwtDecoder` bean itself with a `@MockBean` annotation. [[testing-opaque-token]] -==== Testing Opaque Token Authentication +=== Testing Opaque Token Authentication Similar to <>, opaque tokens require an authorization server in order to verify their validity, which can make testing more difficult. To help with that, Spring Security has test support for opaque tokens. @@ -1484,7 +1484,7 @@ assertThat(token.authorities).containsExactly(SimpleGrantedAuthority("SCOPE_read Spring Security does the necessary work to make sure that the `BearerTokenAuthentication` instance is available for your controller methods. [[testing-opaque-token-authorities]] -===== Configuring Authorities +==== Configuring Authorities In many circumstances, your method is protected by filter or method security and needs your `Authentication` to have certain granted authorities to allow the request. @@ -1514,7 +1514,7 @@ mvc.get("/endpoint") { ==== [[testing-opaque-token-attributes]] -===== Configuring Claims +==== Configuring Claims And while granted authorities are quite common across all of Spring Security, we also have attributes in the case of OAuth 2.0. @@ -1569,7 +1569,7 @@ mvc.get("/endpoint") { ==== [[testing-opaque-token-principal]] -===== Additional Configurations +==== Additional Configurations There are additional methods, too, for further configuring the authentication; it simply depends on what data your controller expects. @@ -1616,7 +1616,7 @@ mvc.get("/endpoint") { Note that as an alternative to using `opaqueToken()` test support, you can also mock the `OpaqueTokenIntrospector` bean itself with a `@MockBean` annotation. -=== SecurityMockMvcRequestBuilders +== SecurityMockMvcRequestBuilders Spring MVC Test also provides a `RequestBuilder` interface that can be used to create the `MockHttpServletRequest` used in your test. Spring Security provides a few `RequestBuilder` implementations that can be used to make testing easier. @@ -1636,7 +1636,7 @@ import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequ ---- ==== -==== Testing Form Based Authentication +=== Testing Form Based Authentication You can easily create a request to test a form based authentication using Spring Security's testing support. For example, the following will submit a POST to "/login" with the username "user", the password "password", and a valid CSRF token: @@ -1696,7 +1696,7 @@ mvc ==== [[test-logout]] -==== Testing Logout +=== Testing Logout While fairly trivial using standard Spring MVC Test, you can use Spring Security's testing support to make testing log out easier. For example, the following will submit a POST to "/logout" with a valid CSRF token: @@ -1736,7 +1736,7 @@ mvc ---- ==== -=== SecurityMockMvcResultMatchers +== SecurityMockMvcResultMatchers At times it is desirable to make various security related assertions about a request. To accommodate this need, Spring Security Test support implements Spring MVC Test's `ResultMatcher` interface. @@ -1757,7 +1757,7 @@ import org.springframework.security.test.web.servlet.response.SecurityMockMvcRes ---- ==== -==== Unauthenticated Assertion +=== Unauthenticated Assertion At times it may be valuable to assert that there is no authenticated user associated with the result of a `MockMvc` invocation. For example, you might want to test submitting an invalid username and password and verify that no user is authenticated. @@ -1781,7 +1781,7 @@ mvc ---- ==== -==== Authenticated Assertion +=== Authenticated Assertion It is often times that we must assert that an authenticated user exists. For example, we may want to verify that we authenticated successfully. From 88ac7a5d2e147583cbe875b9475e5af7192b9f6d Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 30 Jul 2021 11:54:39 -0500 Subject: [PATCH 05/34] Fixup servlet/authentication/architecture/index.adoc --- .../servlet/authentication/architecture/index.adoc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc index a7f768cc1f..9b0305bc5f 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc @@ -3,7 +3,19 @@ :figures: images/servlet/authentication/architecture :icondir: images/icons -This discussion expands on <> to discuss the Servlet Authentication architecture. +This discussion expands on <> to describe the main architectural components of Spring Security's used in Servlet authentication. +If you need concrete flows that explain how these pieces fit together, look at the <> specific sections. + +* <> - The `SecurityContextHolder` is where Spring Security stores the details of who is <>. +* <> - is obtained from the `SecurityContextHolder` and contains the `Authentication` of the currently authenticated user. +* <> - Can be the input to `AuthenticationManager` to provide the credentials a user has provided to authenticate or the current user from the `SecurityContext`. +* <> - An authority that is granted to the principal on the `Authentication` (i.e. roles, scopes, etc.) +* <> - the API that defines how Spring Security's Filters perform <>. +* <> - the most common implementation of `AuthenticationManager`. +* <> - used by `ProviderManager` to perform a specific type of authentication. +* <> - used for requesting credentials from a client (i.e. redirecting to a log in page, sending a `WWW-Authenticate` response, etc.) +* <> - a base `Filter` used for authentication. +This also gives a good idea of the high level flow of authentication and how pieces work together. [[servlet-authentication-securitycontextholder]] == SecurityContextHolder From f5274926cfd24b4e924bf42d08674478af2acd6d Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 30 Jul 2021 12:15:27 -0500 Subject: [PATCH 06/34] Fix up reactive/oauth2/index.adoc links --- docs/modules/ROOT/pages/reactive/oauth2/index.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/modules/ROOT/pages/reactive/oauth2/index.adoc b/docs/modules/ROOT/pages/reactive/oauth2/index.adoc index 560181008c..eb3f150f7f 100644 --- a/docs/modules/ROOT/pages/reactive/oauth2/index.adoc +++ b/docs/modules/ROOT/pages/reactive/oauth2/index.adoc @@ -3,6 +3,6 @@ Spring Security provides OAuth2 and WebFlux integration for reactive applications. -* xref:reactive/oauth2/login.adoc[OAuth 2.0 Login] -* xref:reactive/oauth2/access-token.adoc[OAuth2 Client] -* xref:reactive/oauth2/resource-server.adoc[OAuth 2.0 Resource Server] +* xref:reactive/oauth2/login.adoc[OAuth 2.0 Login] - Authenticating with OAuth 2.0 +* xref:reactive/oauth2/access-token.adoc[OAuth2 Client] - Making requests to an OAuth2 Resource Server as an OAuth2 Client +* xref:reactive/oauth2/resource-server.adoc[OAuth 2.0 Resource Server] - protecting a REST endpoint using OAuth 2.0 From d2affef356f50c903699099b3a60438384adf0d2 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 30 Jul 2021 15:07:36 -0500 Subject: [PATCH 07/34] Fix images - Move images into assets/ - Remove figures form antora.yml - Add :figures: to each page that uses it --- docs/antora.yml | 1 - .../ROOT/{ => assets}/images/Authentication.gif | Bin .../ROOT/{ => assets}/images/icons/number_1.png | Bin .../ROOT/{ => assets}/images/icons/number_10.png | Bin .../ROOT/{ => assets}/images/icons/number_2.png | Bin .../ROOT/{ => assets}/images/icons/number_3.png | Bin .../ROOT/{ => assets}/images/icons/number_4.png | Bin .../ROOT/{ => assets}/images/icons/number_5.png | Bin .../ROOT/{ => assets}/images/icons/number_6.png | Bin .../ROOT/{ => assets}/images/icons/number_7.png | Bin .../ROOT/{ => assets}/images/icons/number_8.png | Bin .../ROOT/{ => assets}/images/icons/number_9.png | Bin .../ROOT/{ => assets}/images/icons/numbers.odg | Bin docs/modules/ROOT/{ => assets}/images/note.png | Bin docs/modules/ROOT/{ => assets}/images/palette.otg | Bin .../ROOT/{ => assets}/images/s2-banner-rhs.png | Bin .../ROOT/{ => assets}/images/s2_box_logo.png | Bin .../images/security-interception.graffle | Bin .../{ => assets}/images/security-interception.png | Bin .../servlet/architecture/delegatingfilterproxy.odg | Bin .../servlet/architecture/delegatingfilterproxy.png | Bin .../architecture/exceptiontranslationfilter.odg | Bin .../architecture/exceptiontranslationfilter.png | Bin .../images/servlet/architecture/filterchain.gif | Bin .../images/servlet/architecture/filterchain.odg | Bin .../images/servlet/architecture/filterchain.png | Bin .../servlet/architecture/filterchainproxy.odg | Bin .../servlet/architecture/filterchainproxy.png | Bin .../architecture/multi-securityfilterchain.odg | Bin .../architecture/multi-securityfilterchain.png | Bin .../servlet/architecture/securityfilterchain.odg | Bin .../servlet/architecture/securityfilterchain.png | Bin .../abstractauthenticationprocessingfilter.odg | Bin .../abstractauthenticationprocessingfilter.png | Bin .../architecture/providermanager-parent.odg | Bin .../architecture/providermanager-parent.png | Bin .../authentication/architecture/providermanager.odg | Bin .../authentication/architecture/providermanager.png | Bin .../architecture/providermanagers-parent.odg | Bin .../architecture/providermanagers-parent.png | Bin .../architecture/securitycontextholder.odg | Bin .../architecture/securitycontextholder.png | Bin .../unpwd/basicauthenticationentrypoint.odg | Bin .../unpwd/basicauthenticationentrypoint.png | Bin .../unpwd/basicauthenticationfilter.odg | Bin .../unpwd/basicauthenticationfilter.png | Bin .../unpwd/daoauthenticationprovider.odg | Bin .../unpwd/daoauthenticationprovider.png | Bin .../unpwd/loginurlauthenticationentrypoint.odg | Bin .../unpwd/loginurlauthenticationentrypoint.png | Bin .../unpwd/usernamepasswordauthenticationfilter.odg | Bin .../unpwd/usernamepasswordauthenticationfilter.png | Bin .../authorization}/access-decision-voting.graffle | Bin .../authorization}/access-decision-voting.png | Bin .../servlet/authorization}/after-invocation.graffle | Bin .../servlet/authorization}/after-invocation.png | Bin .../authorization/filtersecurityinterceptor.odg | Bin .../authorization/filtersecurityinterceptor.png | Bin .../servlet/oauth2/beareraccessdeniedhandler.odg | Bin .../oauth2/bearerauthenticationentrypoint.odg | Bin .../oauth2/bearerauthenticationentrypoint.png | Bin .../oauth2/bearertokenauthenticationfilter.odg | Bin .../oauth2/bearertokenauthenticationfilter.png | Bin .../servlet/oauth2/jwtauthenticationprovider.odg | Bin .../servlet/oauth2/jwtauthenticationprovider.png | Bin .../oauth2/opaquetokenauthenticationprovider.odg | Bin .../oauth2/opaquetokenauthenticationprovider.png | Bin .../saml2/opensamlauthenticationprovider.odg | Bin .../saml2/opensamlauthenticationprovider.png | Bin .../saml2/saml2webssoauthenticationfilter.odg | Bin .../saml2/saml2webssoauthenticationfilter.png | Bin .../saml2webssoauthenticationrequestfilter.odg | Bin .../saml2webssoauthenticationrequestfilter.png | Bin docs/modules/ROOT/{ => assets}/images/tip.png | Bin .../ROOT/pages/servlet/architecture/index.adoc | 3 +-- .../servlet/authentication/architecture/index.adoc | 4 ++-- .../pages/servlet/authentication/unpwd/basic.adoc | 1 + .../unpwd/dao-authentication-provider.adoc | 1 + .../pages/servlet/authentication/unpwd/form.adoc | 1 + .../pages/servlet/authorization/architecture.adoc | 6 +++--- .../servlet/authorization/authorize-requests.adoc | 3 +-- .../pages/servlet/oauth2/oauth2-resourceserver.adoc | 3 +-- docs/modules/ROOT/pages/servlet/saml2/index.adoc | 3 +-- 83 files changed, 12 insertions(+), 14 deletions(-) rename docs/modules/ROOT/{ => assets}/images/Authentication.gif (100%) rename docs/modules/ROOT/{ => assets}/images/icons/number_1.png (100%) rename docs/modules/ROOT/{ => assets}/images/icons/number_10.png (100%) rename docs/modules/ROOT/{ => assets}/images/icons/number_2.png (100%) rename docs/modules/ROOT/{ => assets}/images/icons/number_3.png (100%) rename docs/modules/ROOT/{ => assets}/images/icons/number_4.png (100%) rename docs/modules/ROOT/{ => assets}/images/icons/number_5.png (100%) rename docs/modules/ROOT/{ => assets}/images/icons/number_6.png (100%) rename docs/modules/ROOT/{ => assets}/images/icons/number_7.png (100%) rename docs/modules/ROOT/{ => assets}/images/icons/number_8.png (100%) rename docs/modules/ROOT/{ => assets}/images/icons/number_9.png (100%) rename docs/modules/ROOT/{ => assets}/images/icons/numbers.odg (100%) rename docs/modules/ROOT/{ => assets}/images/note.png (100%) rename docs/modules/ROOT/{ => assets}/images/palette.otg (100%) rename docs/modules/ROOT/{ => assets}/images/s2-banner-rhs.png (100%) rename docs/modules/ROOT/{ => assets}/images/s2_box_logo.png (100%) rename docs/modules/ROOT/{ => assets}/images/security-interception.graffle (100%) rename docs/modules/ROOT/{ => assets}/images/security-interception.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/architecture/delegatingfilterproxy.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/architecture/delegatingfilterproxy.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/architecture/exceptiontranslationfilter.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/architecture/exceptiontranslationfilter.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/architecture/filterchain.gif (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/architecture/filterchain.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/architecture/filterchain.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/architecture/filterchainproxy.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/architecture/filterchainproxy.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/architecture/multi-securityfilterchain.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/architecture/multi-securityfilterchain.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/architecture/securityfilterchain.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/architecture/securityfilterchain.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/architecture/providermanager-parent.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/architecture/providermanager-parent.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/architecture/providermanager.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/architecture/providermanager.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/architecture/providermanagers-parent.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/architecture/providermanagers-parent.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/architecture/securitycontextholder.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/architecture/securitycontextholder.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/unpwd/basicauthenticationentrypoint.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/unpwd/basicauthenticationentrypoint.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/unpwd/basicauthenticationfilter.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/unpwd/basicauthenticationfilter.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/unpwd/daoauthenticationprovider.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/unpwd/daoauthenticationprovider.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.png (100%) rename docs/modules/ROOT/{images => assets/images/servlet/authorization}/access-decision-voting.graffle (100%) rename docs/modules/ROOT/{images => assets/images/servlet/authorization}/access-decision-voting.png (100%) rename docs/modules/ROOT/{images => assets/images/servlet/authorization}/after-invocation.graffle (100%) rename docs/modules/ROOT/{images => assets/images/servlet/authorization}/after-invocation.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authorization/filtersecurityinterceptor.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/authorization/filtersecurityinterceptor.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/oauth2/beareraccessdeniedhandler.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/oauth2/bearerauthenticationentrypoint.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/oauth2/bearerauthenticationentrypoint.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/oauth2/bearertokenauthenticationfilter.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/oauth2/bearertokenauthenticationfilter.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/oauth2/jwtauthenticationprovider.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/oauth2/jwtauthenticationprovider.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/oauth2/opaquetokenauthenticationprovider.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/oauth2/opaquetokenauthenticationprovider.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/saml2/opensamlauthenticationprovider.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/saml2/opensamlauthenticationprovider.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/saml2/saml2webssoauthenticationfilter.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/saml2/saml2webssoauthenticationfilter.png (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/saml2/saml2webssoauthenticationrequestfilter.odg (100%) rename docs/modules/ROOT/{ => assets}/images/servlet/saml2/saml2webssoauthenticationrequestfilter.png (100%) rename docs/modules/ROOT/{ => assets}/images/tip.png (100%) diff --git a/docs/antora.yml b/docs/antora.yml index 4bb53235cd..f1d4fefedb 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -8,7 +8,6 @@ asciidoc: idprefix: '' idseparator: '-' apacheds-core-version: 1.5.5 - figures: servlet/architecture gh-old-samples-url: https://github.com/spring-projects/spring-security/tree/5.4.x/samples gh-samples-url: https://github.com/spring-projects/spring-security-samples/tree/main gh-url: https://github.com/spring-projects/spring-security/tree/main diff --git a/docs/modules/ROOT/images/Authentication.gif b/docs/modules/ROOT/assets/images/Authentication.gif similarity index 100% rename from docs/modules/ROOT/images/Authentication.gif rename to docs/modules/ROOT/assets/images/Authentication.gif diff --git a/docs/modules/ROOT/images/icons/number_1.png b/docs/modules/ROOT/assets/images/icons/number_1.png similarity index 100% rename from docs/modules/ROOT/images/icons/number_1.png rename to docs/modules/ROOT/assets/images/icons/number_1.png diff --git a/docs/modules/ROOT/images/icons/number_10.png b/docs/modules/ROOT/assets/images/icons/number_10.png similarity index 100% rename from docs/modules/ROOT/images/icons/number_10.png rename to docs/modules/ROOT/assets/images/icons/number_10.png diff --git a/docs/modules/ROOT/images/icons/number_2.png b/docs/modules/ROOT/assets/images/icons/number_2.png similarity index 100% rename from docs/modules/ROOT/images/icons/number_2.png rename to docs/modules/ROOT/assets/images/icons/number_2.png diff --git a/docs/modules/ROOT/images/icons/number_3.png b/docs/modules/ROOT/assets/images/icons/number_3.png similarity index 100% rename from docs/modules/ROOT/images/icons/number_3.png rename to docs/modules/ROOT/assets/images/icons/number_3.png diff --git a/docs/modules/ROOT/images/icons/number_4.png b/docs/modules/ROOT/assets/images/icons/number_4.png similarity index 100% rename from docs/modules/ROOT/images/icons/number_4.png rename to docs/modules/ROOT/assets/images/icons/number_4.png diff --git a/docs/modules/ROOT/images/icons/number_5.png b/docs/modules/ROOT/assets/images/icons/number_5.png similarity index 100% rename from docs/modules/ROOT/images/icons/number_5.png rename to docs/modules/ROOT/assets/images/icons/number_5.png diff --git a/docs/modules/ROOT/images/icons/number_6.png b/docs/modules/ROOT/assets/images/icons/number_6.png similarity index 100% rename from docs/modules/ROOT/images/icons/number_6.png rename to docs/modules/ROOT/assets/images/icons/number_6.png diff --git a/docs/modules/ROOT/images/icons/number_7.png b/docs/modules/ROOT/assets/images/icons/number_7.png similarity index 100% rename from docs/modules/ROOT/images/icons/number_7.png rename to docs/modules/ROOT/assets/images/icons/number_7.png diff --git a/docs/modules/ROOT/images/icons/number_8.png b/docs/modules/ROOT/assets/images/icons/number_8.png similarity index 100% rename from docs/modules/ROOT/images/icons/number_8.png rename to docs/modules/ROOT/assets/images/icons/number_8.png diff --git a/docs/modules/ROOT/images/icons/number_9.png b/docs/modules/ROOT/assets/images/icons/number_9.png similarity index 100% rename from docs/modules/ROOT/images/icons/number_9.png rename to docs/modules/ROOT/assets/images/icons/number_9.png diff --git a/docs/modules/ROOT/images/icons/numbers.odg b/docs/modules/ROOT/assets/images/icons/numbers.odg similarity index 100% rename from docs/modules/ROOT/images/icons/numbers.odg rename to docs/modules/ROOT/assets/images/icons/numbers.odg diff --git a/docs/modules/ROOT/images/note.png b/docs/modules/ROOT/assets/images/note.png similarity index 100% rename from docs/modules/ROOT/images/note.png rename to docs/modules/ROOT/assets/images/note.png diff --git a/docs/modules/ROOT/images/palette.otg b/docs/modules/ROOT/assets/images/palette.otg similarity index 100% rename from docs/modules/ROOT/images/palette.otg rename to docs/modules/ROOT/assets/images/palette.otg diff --git a/docs/modules/ROOT/images/s2-banner-rhs.png b/docs/modules/ROOT/assets/images/s2-banner-rhs.png similarity index 100% rename from docs/modules/ROOT/images/s2-banner-rhs.png rename to docs/modules/ROOT/assets/images/s2-banner-rhs.png diff --git a/docs/modules/ROOT/images/s2_box_logo.png b/docs/modules/ROOT/assets/images/s2_box_logo.png similarity index 100% rename from docs/modules/ROOT/images/s2_box_logo.png rename to docs/modules/ROOT/assets/images/s2_box_logo.png diff --git a/docs/modules/ROOT/images/security-interception.graffle b/docs/modules/ROOT/assets/images/security-interception.graffle similarity index 100% rename from docs/modules/ROOT/images/security-interception.graffle rename to docs/modules/ROOT/assets/images/security-interception.graffle diff --git a/docs/modules/ROOT/images/security-interception.png b/docs/modules/ROOT/assets/images/security-interception.png similarity index 100% rename from docs/modules/ROOT/images/security-interception.png rename to docs/modules/ROOT/assets/images/security-interception.png diff --git a/docs/modules/ROOT/images/servlet/architecture/delegatingfilterproxy.odg b/docs/modules/ROOT/assets/images/servlet/architecture/delegatingfilterproxy.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/architecture/delegatingfilterproxy.odg rename to docs/modules/ROOT/assets/images/servlet/architecture/delegatingfilterproxy.odg diff --git a/docs/modules/ROOT/images/servlet/architecture/delegatingfilterproxy.png b/docs/modules/ROOT/assets/images/servlet/architecture/delegatingfilterproxy.png similarity index 100% rename from docs/modules/ROOT/images/servlet/architecture/delegatingfilterproxy.png rename to docs/modules/ROOT/assets/images/servlet/architecture/delegatingfilterproxy.png diff --git a/docs/modules/ROOT/images/servlet/architecture/exceptiontranslationfilter.odg b/docs/modules/ROOT/assets/images/servlet/architecture/exceptiontranslationfilter.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/architecture/exceptiontranslationfilter.odg rename to docs/modules/ROOT/assets/images/servlet/architecture/exceptiontranslationfilter.odg diff --git a/docs/modules/ROOT/images/servlet/architecture/exceptiontranslationfilter.png b/docs/modules/ROOT/assets/images/servlet/architecture/exceptiontranslationfilter.png similarity index 100% rename from docs/modules/ROOT/images/servlet/architecture/exceptiontranslationfilter.png rename to docs/modules/ROOT/assets/images/servlet/architecture/exceptiontranslationfilter.png diff --git a/docs/modules/ROOT/images/servlet/architecture/filterchain.gif b/docs/modules/ROOT/assets/images/servlet/architecture/filterchain.gif similarity index 100% rename from docs/modules/ROOT/images/servlet/architecture/filterchain.gif rename to docs/modules/ROOT/assets/images/servlet/architecture/filterchain.gif diff --git a/docs/modules/ROOT/images/servlet/architecture/filterchain.odg b/docs/modules/ROOT/assets/images/servlet/architecture/filterchain.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/architecture/filterchain.odg rename to docs/modules/ROOT/assets/images/servlet/architecture/filterchain.odg diff --git a/docs/modules/ROOT/images/servlet/architecture/filterchain.png b/docs/modules/ROOT/assets/images/servlet/architecture/filterchain.png similarity index 100% rename from docs/modules/ROOT/images/servlet/architecture/filterchain.png rename to docs/modules/ROOT/assets/images/servlet/architecture/filterchain.png diff --git a/docs/modules/ROOT/images/servlet/architecture/filterchainproxy.odg b/docs/modules/ROOT/assets/images/servlet/architecture/filterchainproxy.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/architecture/filterchainproxy.odg rename to docs/modules/ROOT/assets/images/servlet/architecture/filterchainproxy.odg diff --git a/docs/modules/ROOT/images/servlet/architecture/filterchainproxy.png b/docs/modules/ROOT/assets/images/servlet/architecture/filterchainproxy.png similarity index 100% rename from docs/modules/ROOT/images/servlet/architecture/filterchainproxy.png rename to docs/modules/ROOT/assets/images/servlet/architecture/filterchainproxy.png diff --git a/docs/modules/ROOT/images/servlet/architecture/multi-securityfilterchain.odg b/docs/modules/ROOT/assets/images/servlet/architecture/multi-securityfilterchain.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/architecture/multi-securityfilterchain.odg rename to docs/modules/ROOT/assets/images/servlet/architecture/multi-securityfilterchain.odg diff --git a/docs/modules/ROOT/images/servlet/architecture/multi-securityfilterchain.png b/docs/modules/ROOT/assets/images/servlet/architecture/multi-securityfilterchain.png similarity index 100% rename from docs/modules/ROOT/images/servlet/architecture/multi-securityfilterchain.png rename to docs/modules/ROOT/assets/images/servlet/architecture/multi-securityfilterchain.png diff --git a/docs/modules/ROOT/images/servlet/architecture/securityfilterchain.odg b/docs/modules/ROOT/assets/images/servlet/architecture/securityfilterchain.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/architecture/securityfilterchain.odg rename to docs/modules/ROOT/assets/images/servlet/architecture/securityfilterchain.odg diff --git a/docs/modules/ROOT/images/servlet/architecture/securityfilterchain.png b/docs/modules/ROOT/assets/images/servlet/architecture/securityfilterchain.png similarity index 100% rename from docs/modules/ROOT/images/servlet/architecture/securityfilterchain.png rename to docs/modules/ROOT/assets/images/servlet/architecture/securityfilterchain.png diff --git a/docs/modules/ROOT/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.odg b/docs/modules/ROOT/assets/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.odg rename to docs/modules/ROOT/assets/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.odg diff --git a/docs/modules/ROOT/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.png b/docs/modules/ROOT/assets/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.png similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.png rename to docs/modules/ROOT/assets/images/servlet/authentication/architecture/abstractauthenticationprocessingfilter.png diff --git a/docs/modules/ROOT/images/servlet/authentication/architecture/providermanager-parent.odg b/docs/modules/ROOT/assets/images/servlet/authentication/architecture/providermanager-parent.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/architecture/providermanager-parent.odg rename to docs/modules/ROOT/assets/images/servlet/authentication/architecture/providermanager-parent.odg diff --git a/docs/modules/ROOT/images/servlet/authentication/architecture/providermanager-parent.png b/docs/modules/ROOT/assets/images/servlet/authentication/architecture/providermanager-parent.png similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/architecture/providermanager-parent.png rename to docs/modules/ROOT/assets/images/servlet/authentication/architecture/providermanager-parent.png diff --git a/docs/modules/ROOT/images/servlet/authentication/architecture/providermanager.odg b/docs/modules/ROOT/assets/images/servlet/authentication/architecture/providermanager.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/architecture/providermanager.odg rename to docs/modules/ROOT/assets/images/servlet/authentication/architecture/providermanager.odg diff --git a/docs/modules/ROOT/images/servlet/authentication/architecture/providermanager.png b/docs/modules/ROOT/assets/images/servlet/authentication/architecture/providermanager.png similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/architecture/providermanager.png rename to docs/modules/ROOT/assets/images/servlet/authentication/architecture/providermanager.png diff --git a/docs/modules/ROOT/images/servlet/authentication/architecture/providermanagers-parent.odg b/docs/modules/ROOT/assets/images/servlet/authentication/architecture/providermanagers-parent.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/architecture/providermanagers-parent.odg rename to docs/modules/ROOT/assets/images/servlet/authentication/architecture/providermanagers-parent.odg diff --git a/docs/modules/ROOT/images/servlet/authentication/architecture/providermanagers-parent.png b/docs/modules/ROOT/assets/images/servlet/authentication/architecture/providermanagers-parent.png similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/architecture/providermanagers-parent.png rename to docs/modules/ROOT/assets/images/servlet/authentication/architecture/providermanagers-parent.png diff --git a/docs/modules/ROOT/images/servlet/authentication/architecture/securitycontextholder.odg b/docs/modules/ROOT/assets/images/servlet/authentication/architecture/securitycontextholder.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/architecture/securitycontextholder.odg rename to docs/modules/ROOT/assets/images/servlet/authentication/architecture/securitycontextholder.odg diff --git a/docs/modules/ROOT/images/servlet/authentication/architecture/securitycontextholder.png b/docs/modules/ROOT/assets/images/servlet/authentication/architecture/securitycontextholder.png similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/architecture/securitycontextholder.png rename to docs/modules/ROOT/assets/images/servlet/authentication/architecture/securitycontextholder.png diff --git a/docs/modules/ROOT/images/servlet/authentication/unpwd/basicauthenticationentrypoint.odg b/docs/modules/ROOT/assets/images/servlet/authentication/unpwd/basicauthenticationentrypoint.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/unpwd/basicauthenticationentrypoint.odg rename to docs/modules/ROOT/assets/images/servlet/authentication/unpwd/basicauthenticationentrypoint.odg diff --git a/docs/modules/ROOT/images/servlet/authentication/unpwd/basicauthenticationentrypoint.png b/docs/modules/ROOT/assets/images/servlet/authentication/unpwd/basicauthenticationentrypoint.png similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/unpwd/basicauthenticationentrypoint.png rename to docs/modules/ROOT/assets/images/servlet/authentication/unpwd/basicauthenticationentrypoint.png diff --git a/docs/modules/ROOT/images/servlet/authentication/unpwd/basicauthenticationfilter.odg b/docs/modules/ROOT/assets/images/servlet/authentication/unpwd/basicauthenticationfilter.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/unpwd/basicauthenticationfilter.odg rename to docs/modules/ROOT/assets/images/servlet/authentication/unpwd/basicauthenticationfilter.odg diff --git a/docs/modules/ROOT/images/servlet/authentication/unpwd/basicauthenticationfilter.png b/docs/modules/ROOT/assets/images/servlet/authentication/unpwd/basicauthenticationfilter.png similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/unpwd/basicauthenticationfilter.png rename to docs/modules/ROOT/assets/images/servlet/authentication/unpwd/basicauthenticationfilter.png diff --git a/docs/modules/ROOT/images/servlet/authentication/unpwd/daoauthenticationprovider.odg b/docs/modules/ROOT/assets/images/servlet/authentication/unpwd/daoauthenticationprovider.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/unpwd/daoauthenticationprovider.odg rename to docs/modules/ROOT/assets/images/servlet/authentication/unpwd/daoauthenticationprovider.odg diff --git a/docs/modules/ROOT/images/servlet/authentication/unpwd/daoauthenticationprovider.png b/docs/modules/ROOT/assets/images/servlet/authentication/unpwd/daoauthenticationprovider.png similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/unpwd/daoauthenticationprovider.png rename to docs/modules/ROOT/assets/images/servlet/authentication/unpwd/daoauthenticationprovider.png diff --git a/docs/modules/ROOT/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.odg b/docs/modules/ROOT/assets/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.odg rename to docs/modules/ROOT/assets/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.odg diff --git a/docs/modules/ROOT/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.png b/docs/modules/ROOT/assets/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.png similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.png rename to docs/modules/ROOT/assets/images/servlet/authentication/unpwd/loginurlauthenticationentrypoint.png diff --git a/docs/modules/ROOT/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.odg b/docs/modules/ROOT/assets/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.odg rename to docs/modules/ROOT/assets/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.odg diff --git a/docs/modules/ROOT/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.png b/docs/modules/ROOT/assets/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.png similarity index 100% rename from docs/modules/ROOT/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.png rename to docs/modules/ROOT/assets/images/servlet/authentication/unpwd/usernamepasswordauthenticationfilter.png diff --git a/docs/modules/ROOT/images/access-decision-voting.graffle b/docs/modules/ROOT/assets/images/servlet/authorization/access-decision-voting.graffle similarity index 100% rename from docs/modules/ROOT/images/access-decision-voting.graffle rename to docs/modules/ROOT/assets/images/servlet/authorization/access-decision-voting.graffle diff --git a/docs/modules/ROOT/images/access-decision-voting.png b/docs/modules/ROOT/assets/images/servlet/authorization/access-decision-voting.png similarity index 100% rename from docs/modules/ROOT/images/access-decision-voting.png rename to docs/modules/ROOT/assets/images/servlet/authorization/access-decision-voting.png diff --git a/docs/modules/ROOT/images/after-invocation.graffle b/docs/modules/ROOT/assets/images/servlet/authorization/after-invocation.graffle similarity index 100% rename from docs/modules/ROOT/images/after-invocation.graffle rename to docs/modules/ROOT/assets/images/servlet/authorization/after-invocation.graffle diff --git a/docs/modules/ROOT/images/after-invocation.png b/docs/modules/ROOT/assets/images/servlet/authorization/after-invocation.png similarity index 100% rename from docs/modules/ROOT/images/after-invocation.png rename to docs/modules/ROOT/assets/images/servlet/authorization/after-invocation.png diff --git a/docs/modules/ROOT/images/servlet/authorization/filtersecurityinterceptor.odg b/docs/modules/ROOT/assets/images/servlet/authorization/filtersecurityinterceptor.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/authorization/filtersecurityinterceptor.odg rename to docs/modules/ROOT/assets/images/servlet/authorization/filtersecurityinterceptor.odg diff --git a/docs/modules/ROOT/images/servlet/authorization/filtersecurityinterceptor.png b/docs/modules/ROOT/assets/images/servlet/authorization/filtersecurityinterceptor.png similarity index 100% rename from docs/modules/ROOT/images/servlet/authorization/filtersecurityinterceptor.png rename to docs/modules/ROOT/assets/images/servlet/authorization/filtersecurityinterceptor.png diff --git a/docs/modules/ROOT/images/servlet/oauth2/beareraccessdeniedhandler.odg b/docs/modules/ROOT/assets/images/servlet/oauth2/beareraccessdeniedhandler.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/oauth2/beareraccessdeniedhandler.odg rename to docs/modules/ROOT/assets/images/servlet/oauth2/beareraccessdeniedhandler.odg diff --git a/docs/modules/ROOT/images/servlet/oauth2/bearerauthenticationentrypoint.odg b/docs/modules/ROOT/assets/images/servlet/oauth2/bearerauthenticationentrypoint.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/oauth2/bearerauthenticationentrypoint.odg rename to docs/modules/ROOT/assets/images/servlet/oauth2/bearerauthenticationentrypoint.odg diff --git a/docs/modules/ROOT/images/servlet/oauth2/bearerauthenticationentrypoint.png b/docs/modules/ROOT/assets/images/servlet/oauth2/bearerauthenticationentrypoint.png similarity index 100% rename from docs/modules/ROOT/images/servlet/oauth2/bearerauthenticationentrypoint.png rename to docs/modules/ROOT/assets/images/servlet/oauth2/bearerauthenticationentrypoint.png diff --git a/docs/modules/ROOT/images/servlet/oauth2/bearertokenauthenticationfilter.odg b/docs/modules/ROOT/assets/images/servlet/oauth2/bearertokenauthenticationfilter.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/oauth2/bearertokenauthenticationfilter.odg rename to docs/modules/ROOT/assets/images/servlet/oauth2/bearertokenauthenticationfilter.odg diff --git a/docs/modules/ROOT/images/servlet/oauth2/bearertokenauthenticationfilter.png b/docs/modules/ROOT/assets/images/servlet/oauth2/bearertokenauthenticationfilter.png similarity index 100% rename from docs/modules/ROOT/images/servlet/oauth2/bearertokenauthenticationfilter.png rename to docs/modules/ROOT/assets/images/servlet/oauth2/bearertokenauthenticationfilter.png diff --git a/docs/modules/ROOT/images/servlet/oauth2/jwtauthenticationprovider.odg b/docs/modules/ROOT/assets/images/servlet/oauth2/jwtauthenticationprovider.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/oauth2/jwtauthenticationprovider.odg rename to docs/modules/ROOT/assets/images/servlet/oauth2/jwtauthenticationprovider.odg diff --git a/docs/modules/ROOT/images/servlet/oauth2/jwtauthenticationprovider.png b/docs/modules/ROOT/assets/images/servlet/oauth2/jwtauthenticationprovider.png similarity index 100% rename from docs/modules/ROOT/images/servlet/oauth2/jwtauthenticationprovider.png rename to docs/modules/ROOT/assets/images/servlet/oauth2/jwtauthenticationprovider.png diff --git a/docs/modules/ROOT/images/servlet/oauth2/opaquetokenauthenticationprovider.odg b/docs/modules/ROOT/assets/images/servlet/oauth2/opaquetokenauthenticationprovider.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/oauth2/opaquetokenauthenticationprovider.odg rename to docs/modules/ROOT/assets/images/servlet/oauth2/opaquetokenauthenticationprovider.odg diff --git a/docs/modules/ROOT/images/servlet/oauth2/opaquetokenauthenticationprovider.png b/docs/modules/ROOT/assets/images/servlet/oauth2/opaquetokenauthenticationprovider.png similarity index 100% rename from docs/modules/ROOT/images/servlet/oauth2/opaquetokenauthenticationprovider.png rename to docs/modules/ROOT/assets/images/servlet/oauth2/opaquetokenauthenticationprovider.png diff --git a/docs/modules/ROOT/images/servlet/saml2/opensamlauthenticationprovider.odg b/docs/modules/ROOT/assets/images/servlet/saml2/opensamlauthenticationprovider.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/saml2/opensamlauthenticationprovider.odg rename to docs/modules/ROOT/assets/images/servlet/saml2/opensamlauthenticationprovider.odg diff --git a/docs/modules/ROOT/images/servlet/saml2/opensamlauthenticationprovider.png b/docs/modules/ROOT/assets/images/servlet/saml2/opensamlauthenticationprovider.png similarity index 100% rename from docs/modules/ROOT/images/servlet/saml2/opensamlauthenticationprovider.png rename to docs/modules/ROOT/assets/images/servlet/saml2/opensamlauthenticationprovider.png diff --git a/docs/modules/ROOT/images/servlet/saml2/saml2webssoauthenticationfilter.odg b/docs/modules/ROOT/assets/images/servlet/saml2/saml2webssoauthenticationfilter.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/saml2/saml2webssoauthenticationfilter.odg rename to docs/modules/ROOT/assets/images/servlet/saml2/saml2webssoauthenticationfilter.odg diff --git a/docs/modules/ROOT/images/servlet/saml2/saml2webssoauthenticationfilter.png b/docs/modules/ROOT/assets/images/servlet/saml2/saml2webssoauthenticationfilter.png similarity index 100% rename from docs/modules/ROOT/images/servlet/saml2/saml2webssoauthenticationfilter.png rename to docs/modules/ROOT/assets/images/servlet/saml2/saml2webssoauthenticationfilter.png diff --git a/docs/modules/ROOT/images/servlet/saml2/saml2webssoauthenticationrequestfilter.odg b/docs/modules/ROOT/assets/images/servlet/saml2/saml2webssoauthenticationrequestfilter.odg similarity index 100% rename from docs/modules/ROOT/images/servlet/saml2/saml2webssoauthenticationrequestfilter.odg rename to docs/modules/ROOT/assets/images/servlet/saml2/saml2webssoauthenticationrequestfilter.odg diff --git a/docs/modules/ROOT/images/servlet/saml2/saml2webssoauthenticationrequestfilter.png b/docs/modules/ROOT/assets/images/servlet/saml2/saml2webssoauthenticationrequestfilter.png similarity index 100% rename from docs/modules/ROOT/images/servlet/saml2/saml2webssoauthenticationrequestfilter.png rename to docs/modules/ROOT/assets/images/servlet/saml2/saml2webssoauthenticationrequestfilter.png diff --git a/docs/modules/ROOT/images/tip.png b/docs/modules/ROOT/assets/images/tip.png similarity index 100% rename from docs/modules/ROOT/images/tip.png rename to docs/modules/ROOT/assets/images/tip.png diff --git a/docs/modules/ROOT/pages/servlet/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/architecture/index.adoc index ec5b4bc4d8..c107f82fbd 100644 --- a/docs/modules/ROOT/pages/servlet/architecture/index.adoc +++ b/docs/modules/ROOT/pages/servlet/architecture/index.adoc @@ -1,7 +1,6 @@ [[servlet-architecture]] = Servlet Security: The Big Picture -:figures: images/servlet/architecture -:icondir: images/icons +:figures: servlet/architecture This section discusses Spring Security's high level architecture within Servlet based applications. We build on this high level understanding within <>, <>, <> sections of the reference. diff --git a/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc index 9b0305bc5f..a688d41272 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc @@ -1,7 +1,6 @@ [[servlet-authentication-architecture]] = Servlet Authentication Architecture -:figures: images/servlet/authentication/architecture -:icondir: images/icons +:figures: servlet/authentication/architecture This discussion expands on <> to describe the main architectural components of Spring Security's used in Servlet authentication. If you need concrete flows that explain how these pieces fit together, look at the <> specific sections. @@ -20,6 +19,7 @@ This also gives a good idea of the high level flow of authentication and how pie [[servlet-authentication-securitycontextholder]] == SecurityContextHolder +Hi {figures} there At the heart of Spring Security's authentication model is the `SecurityContextHolder`. It contains the <>. diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc index 411fefcb13..b03716b085 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc @@ -1,5 +1,6 @@ [[servlet-authentication-basic]] = Basic Authentication +:figures: servlet/authentication/unpwd This section provides details on how Spring Security provides support for https://tools.ietf.org/html/rfc7617[Basic HTTP Authentication] for servlet based applications. // FIXME: describe authenticationentrypoint, authenticationfailurehandler, authenticationsuccesshandler diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/dao-authentication-provider.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/dao-authentication-provider.adoc index 3818a019ac..f7283e58ee 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/dao-authentication-provider.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/dao-authentication-provider.adoc @@ -1,5 +1,6 @@ [[servlet-authentication-daoauthenticationprovider]] = DaoAuthenticationProvider +:figures: servlet/authentication/unpwd {security-api-url}org/springframework/security/authentication/dao/DaoAuthenticationProvider.html[`DaoAuthenticationProvider`] is an <> implementation that leverages a <> and <> to authenticate a username and password. diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc index f8745f89ab..f22ebdd992 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc @@ -1,5 +1,6 @@ [[servlet-authentication-form]] = Form Login +:figures: servlet/authentication/unpwd Spring Security provides support for username and password being provided through an html form. This section provides details on how form based authentication works within Spring Security. diff --git a/docs/modules/ROOT/pages/servlet/authorization/architecture.adoc b/docs/modules/ROOT/pages/servlet/authorization/architecture.adoc index 25bf39f375..2be495a653 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/architecture.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/architecture.adoc @@ -2,7 +2,7 @@ [[authz-arch]] = Authorization Architecture - +:figures: servlet/authorization [[authz-authorities]] == Authorities @@ -70,7 +70,7 @@ Whilst users can implement their own `AccessDecisionManager` to control all aspe [[authz-access-voting]] .Voting Decision Manager -image::images/access-decision-voting.png[] +image::{figures}/access-decision-voting.png[] @@ -140,7 +140,7 @@ Whilst you could easily implement your own AOP concern to achieve this, Spring S [[authz-after-invocation]] .After Invocation Implementation -image::images/after-invocation.png[] +image::{figures}/after-invocation.png[] Like many other parts of Spring Security, `AfterInvocationManager` has a single concrete implementation, `AfterInvocationProviderManager`, which polls a list of ``AfterInvocationProvider``s. Each `AfterInvocationProvider` is allowed to modify the return object or throw an `AccessDeniedException`. diff --git a/docs/modules/ROOT/pages/servlet/authorization/authorize-requests.adoc b/docs/modules/ROOT/pages/servlet/authorization/authorize-requests.adoc index 31ce88ecee..055ab80c20 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/authorize-requests.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/authorize-requests.adoc @@ -1,7 +1,6 @@ [[servlet-authorization-filtersecurityinterceptor]] = Authorize HttpServletRequest with FilterSecurityInterceptor -:figures: images/servlet/authorization -:icondir: images/icons +:figures: servlet/authorization This section builds on <> by digging deeper into how <> works within Servlet based applications. diff --git a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc index 2919b251ba..b4565b3300 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc @@ -1,7 +1,6 @@ [[oauth2resourceserver]] = OAuth 2.0 Resource Server -:figures: images/servlet/oauth2 -:icondir: images/icons +:figures: servlet/oauth2 Spring Security supports protecting endpoints using two forms of OAuth 2.0 https://tools.ietf.org/html/rfc6750.html[Bearer Tokens]: diff --git a/docs/modules/ROOT/pages/servlet/saml2/index.adoc b/docs/modules/ROOT/pages/servlet/saml2/index.adoc index e0adeaf0c7..ec19543759 100644 --- a/docs/modules/ROOT/pages/servlet/saml2/index.adoc +++ b/docs/modules/ROOT/pages/servlet/saml2/index.adoc @@ -1,13 +1,12 @@ [[servlet-saml2]] = SAML2 +:figures: servlet/saml2 Spring Security provides comprehensive SAML 2 support. This section discusses how to integrate SAML 2 into your servlet based application. [[servlet-saml2login]] == SAML 2.0 Login -:figures: images/servlet/saml2 -:icondir: images/icons The SAML 2.0 Login feature provides an application with the capability to act as a SAML 2.0 Relying Party, having users https://wiki.shibboleth.net/confluence/display/CONCEPT/FlowsAndConfig[log in] to the application by using their existing account at a SAML 2.0 Asserting Party (Okta, ADFS, etc). From 1f90df6a1494795fb6132b6ac3b1031318408fc8 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 30 Jul 2021 16:56:54 -0500 Subject: [PATCH 08/34] mkdir -p build/ids find -name "*.adoc" | xargs -I{file} awk -v file={file} '/\[\[/ { gsub("\[|\]", ""); id=$0; gsub("./docs/modules/ROOT/pages/", "", file); gsub("\[|\]", ""); id=$0;getline;text=$0; sub("^=+ ","", text); print file > "build/ids/"id".id"; print text > "build/ids/"id".text" }' {file} find docs/modules -name "*.adoc"|while read adoc_file_to_replace; do echo "Replacing $adoc_file_to_replace" for id_file in build/ids/*.id; do id=$(basename $id_file | sed 's/\.id$//') xref_page=$(cat $id_file) if [[ "$adoc_file_to_replace" -ef "./docs/modules/ROOT/pages/$xref_page" ]] then echo " - Skipping same page refid $id " else sed -i -E "s%<<$id(|,([^,>]+))>>%xref:${xref_page}#${id}[\2]%g" $adoc_file_to_replace fi done done --- .../ROOT/pages/overview/community.adoc | 2 +- .../features/authentication/index.adoc | 2 +- .../overview/features/exploits/csrf.adoc | 2 +- .../overview/features/exploits/headers.adoc | 28 +++++------ .../overview/features/exploits/http.adoc | 4 +- .../ROOT/pages/overview/features/index.adoc | 2 +- .../overview/getting-spring-security.adoc | 10 ++-- .../ROOT/pages/reactive/exploits/csrf.adoc | 24 +++++----- .../ROOT/pages/reactive/exploits/headers.adoc | 24 +++++----- .../ROOT/pages/reactive/exploits/http.adoc | 6 +-- docs/modules/ROOT/pages/reactive/index.adoc | 2 +- docs/modules/ROOT/pages/reactive/method.adoc | 2 +- .../pages/reactive/oauth2/access-token.adoc | 2 +- .../ROOT/pages/reactive/oauth2/login.adoc | 8 ++-- .../reactive/oauth2/resource-server.adoc | 2 +- .../registered-oauth2-authorized-client.adoc | 2 +- docs/modules/ROOT/pages/reactive/test.adoc | 6 +-- .../ROOT/pages/reactive/webclient.adoc | 2 +- docs/modules/ROOT/pages/reactive/x509.adoc | 2 +- .../servlet/appendix/database-schema.adoc | 6 +-- .../pages/servlet/appendix/namespace.adoc | 40 ++++++++-------- .../pages/servlet/architecture/index.adoc | 16 +++---- .../servlet/authentication/anonymous.adoc | 2 +- .../authentication/architecture/index.adoc | 30 ++++++------ .../pages/servlet/authentication/cas.adoc | 6 +-- .../pages/servlet/authentication/index.adoc | 22 ++++----- .../pages/servlet/authentication/jaas.adoc | 2 +- .../pages/servlet/authentication/logout.adoc | 16 +++---- .../pages/servlet/authentication/openid.adoc | 2 +- .../pages/servlet/authentication/preauth.adoc | 8 ++-- .../servlet/authentication/rememberme.adoc | 2 +- .../servlet/authentication/unpwd/basic.adoc | 18 ++++---- .../unpwd/dao-authentication-provider.adoc | 14 +++--- .../servlet/authentication/unpwd/digest.adoc | 6 +-- .../servlet/authentication/unpwd/form.adoc | 22 ++++----- .../authentication/unpwd/in-memory.adoc | 10 ++-- .../servlet/authentication/unpwd/index.adoc | 14 +++--- .../servlet/authentication/unpwd/jdbc.adoc | 8 ++-- .../servlet/authentication/unpwd/ldap.adoc | 4 +- .../unpwd/password-encoder.adoc | 4 +- .../unpwd/user-details-service.adoc | 4 +- .../authentication/unpwd/user-details.adoc | 4 +- .../servlet/authorization/architecture.adoc | 2 +- .../authorization/authorize-requests.adoc | 12 ++--- .../authorization/method-security.adoc | 2 +- .../servlet/authorization/secure-objects.adoc | 2 +- .../ROOT/pages/servlet/exploits/csrf.adoc | 28 +++++------ .../ROOT/pages/servlet/exploits/headers.adoc | 26 +++++------ .../ROOT/pages/servlet/exploits/http.adoc | 6 +-- .../ROOT/pages/servlet/exploits/index.adoc | 2 +- .../ROOT/pages/servlet/hello/index.adoc | 2 +- .../servlet/integrations/concurrency.adoc | 4 +- .../ROOT/pages/servlet/integrations/data.adoc | 2 +- .../pages/servlet/integrations/jackson.adoc | 2 +- .../servlet/integrations/jsp-taglibs.adoc | 2 +- .../ROOT/pages/servlet/integrations/mvc.adoc | 6 +-- .../servlet/integrations/servlet-api.adoc | 2 +- .../pages/servlet/integrations/websocket.adoc | 14 +++--- .../servlet/java-configuration/index.adoc | 2 +- .../ROOT/pages/servlet/namespace/index.adoc | 16 +++---- .../pages/servlet/oauth2/oauth2-client.adoc | 6 +-- .../pages/servlet/oauth2/oauth2-login.adoc | 12 ++--- .../servlet/oauth2/oauth2-resourceserver.adoc | 46 +++++++++---------- .../ROOT/pages/servlet/saml2/index.adoc | 26 +++++------ .../ROOT/pages/servlet/test/mockmvc.adoc | 6 +-- 65 files changed, 310 insertions(+), 310 deletions(-) diff --git a/docs/modules/ROOT/pages/overview/community.adoc b/docs/modules/ROOT/pages/overview/community.adoc index 893d17727b..34cd5e504c 100644 --- a/docs/modules/ROOT/pages/overview/community.adoc +++ b/docs/modules/ROOT/pages/overview/community.adoc @@ -11,7 +11,7 @@ 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 <>. +* Try one of our many xref:overview/samples.adoc#samples[sample applications]. * 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 diff --git a/docs/modules/ROOT/pages/overview/features/authentication/index.adoc b/docs/modules/ROOT/pages/overview/features/authentication/index.adoc index 2dbe3ae377..991b671e92 100644 --- a/docs/modules/ROOT/pages/overview/features/authentication/index.adoc +++ b/docs/modules/ROOT/pages/overview/features/authentication/index.adoc @@ -10,4 +10,4 @@ Once authentication is performed we know the identity and can perform authorizat Spring Security provides built in support for authenticating users. This section is dedicated to generic authentication support that applies in both Servlet and WebFlux environments. -Refer to the sections on authentication for <> and WebFlux for details on what is supported for each stack. +Refer to the sections on authentication for xref:servlet/authentication/index.adoc#servlet-authentication[Servlet] and WebFlux for details on what is supported for each stack. diff --git a/docs/modules/ROOT/pages/overview/features/exploits/csrf.adoc b/docs/modules/ROOT/pages/overview/features/exploits/csrf.adoc index 026faecde6..8c9517363e 100644 --- a/docs/modules/ROOT/pages/overview/features/exploits/csrf.adoc +++ b/docs/modules/ROOT/pages/overview/features/exploits/csrf.adoc @@ -14,7 +14,7 @@ In the following sections we will explore: [NOTE] ==== This portion of the documentation discusses the general topic of CSRF protection. -Refer to the relevant sections for specific information on CSRF protection for <> and <> based applications. +Refer to the relevant sections for specific information on CSRF protection for xref:servlet/exploits/csrf.adoc#servlet-csrf[servlet] and xref:reactive/exploits/csrf.adoc#webflux-csrf[WebFlux] based applications. ==== [[csrf-explained]] diff --git a/docs/modules/ROOT/pages/overview/features/exploits/headers.adoc b/docs/modules/ROOT/pages/overview/features/exploits/headers.adoc index 565272a29c..be19d3028c 100644 --- a/docs/modules/ROOT/pages/overview/features/exploits/headers.adoc +++ b/docs/modules/ROOT/pages/overview/features/exploits/headers.adoc @@ -4,7 +4,7 @@ [NOTE] ==== This portion of the documentation discusses the general topic of Security HTTP Response Headers. -Refer to the relevant sections for specific information on Security HTTP Response Headers <> and <> based applications. +Refer to the relevant sections for specific information on Security HTTP Response Headers xref:servlet/exploits/headers.adoc#servlet-headers[servlet] and xref:reactive/exploits/headers.adoc#webflux-headers[WebFlux] based applications. ==== There are many https://owasp.org/www-project-secure-headers/#div-headers[HTTP response headers] that can be used to increase the security of web applications. @@ -16,7 +16,7 @@ If necessary, Spring Security can also be configured to provide <> and <> based applications. +Refer to the relevant sections to see how to customize the defaults for both xref:servlet/exploits/headers.adoc#servlet-headers-default[servlet] and xref:reactive/exploits/headers.adoc#webflux-headers-default[webflux] based applications. ==== Spring Security provides a default set of security related HTTP response headers to provide secure defaults. @@ -53,7 +53,7 @@ For additional details on each of these headers, refer to the corresponding sect [NOTE] ==== -Refer to the relevant sections to see how to customize the defaults for both <> and <> based applications. +Refer to the relevant sections to see how to customize the defaults for both xref:servlet/exploits/headers.adoc#servlet-headers-cache-control[servlet] and xref:reactive/exploits/headers.adoc#webflux-headers-cache-control[webflux] based applications. ==== Spring Security's default is to disable caching to protect user's content. @@ -81,7 +81,7 @@ This allows for applications to ensure that static resources like CSS and JavaSc [NOTE] ==== -Refer to the relevant sections to see how to customize the defaults for both <> and <> based applications. +Refer to the relevant sections to see how to customize the defaults for both xref:servlet/exploits/headers.adoc#servlet-headers-content-type-options[servlet] and xref:reactive/exploits/headers.adoc#webflux-headers-content-type-options[webflux] based applications. ==== Historically browsers, including Internet Explorer, would try to guess the content type of a request using https://en.wikipedia.org/wiki/Content_sniffing[content sniffing]. @@ -114,7 +114,7 @@ X-Content-Type-Options: nosniff [NOTE] ==== -Refer to the relevant sections to see how to customize the defaults for both <> and <> based applications. +Refer to the relevant sections to see how to customize the defaults for both xref:servlet/exploits/headers.adoc#servlet-headers-hsts[servlet] and xref:reactive/exploits/headers.adoc#webflux-headers-hsts[webflux] based applications. ==== When you type in your bank's website, do you enter mybank.example.com or do you enter https://mybank.example.com[]? @@ -154,7 +154,7 @@ For more details on HSTS preload please see https://hstspreload.org. [NOTE] ==== -In order to remain passive Spring Security still provides <>, but for the reasons listed above HPKP is no longer recommended by the security team. +In order to remain passive Spring Security still provides xref:servlet/exploits/headers.adoc#servlet-headers-hpkp[support for HPKP in servlet environments], but for the reasons listed above HPKP is no longer recommended by the security team. ==== https://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning[HTTP Public Key Pinning (HPKP)] specifies to a web client which public key to use with certain web server to prevent Man in the Middle (MITM) attacks with forged certificates. @@ -170,7 +170,7 @@ Is HTTP Public Key Pinning Dead?] and https://scotthelme.co.uk/im-giving-up-on-h [NOTE] ==== -Refer to the relevant sections to see how to customize the defaults for both <> and <> based applications. +Refer to the relevant sections to see how to customize the defaults for both xref:servlet/exploits/headers.adoc#servlet-headers-frame-options[servlet] and xref:reactive/exploits/headers.adoc#webflux-headers-frame-options[webflux] based applications. ==== Allowing your website to be added to a frame can be a security issue. @@ -200,7 +200,7 @@ X-Frame-Options: DENY [NOTE] ==== -Refer to the relevant sections to see how to customize the defaults for both <> and <> based applications. +Refer to the relevant sections to see how to customize the defaults for both xref:servlet/exploits/headers.adoc#servlet-headers-xss-protection[servlet] and xref:reactive/exploits/headers.adoc#webflux-headers-xss-protection[webflux] based applications. ==== 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]. @@ -223,7 +223,7 @@ X-XSS-Protection: 1; mode=block [NOTE] ==== -Refer to the relevant sections to see how to configure both <> and <> based applications. +Refer to the relevant sections to see how to configure both xref:servlet/exploits/headers.adoc#servlet-headers-csp[servlet] and xref:reactive/exploits/headers.adoc#webflux-headers-csp[webflux] based applications. ==== 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). @@ -299,7 +299,7 @@ https://www.w3.org/TR/CSP2/[W3C Candidate Recommendation] [NOTE] ==== -Refer to the relevant sections to see how to configure both <> and <> based applications. +Refer to the relevant sections to see how to configure both xref:servlet/exploits/headers.adoc#servlet-headers-referrer[servlet] and xref:reactive/exploits/headers.adoc#webflux-headers-referrer[webflux] based applications. ==== 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 @@ -322,7 +322,7 @@ The Referrer-Policy response header instructs the browser to let the destination [NOTE] ==== -Refer to the relevant sections to see how to configure both <> and <> based applications. +Refer to the relevant sections to see how to configure both xref:servlet/exploits/headers.adoc#servlet-headers-feature[servlet] and xref:reactive/exploits/headers.adoc#webflux-headers-feature[webflux] based applications. ==== 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. @@ -344,7 +344,7 @@ These policies restrict what APIs the site can access or modify the browser's de [NOTE] ==== -Refer to the relevant sections to see how to configure both <> and <> based applications. +Refer to the relevant sections to see how to configure both xref:servlet/exploits/headers.adoc#servlet-headers-permissions[servlet] and xref:reactive/exploits/headers.adoc#webflux-headers-permissions[webflux] based applications. ==== https://w3c.github.io/webappsec-permissions-policy/[Permissions 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. @@ -366,7 +366,7 @@ These policies restrict what APIs the site can access or modify the browser's de [NOTE] ==== -Refer to the relevant sections to see how to configure both <> and <> based applications. +Refer to the relevant sections to see how to configure both xref:servlet/exploits/headers.adoc#servlet-headers-clear-site-data[servlet] and xref:reactive/exploits/headers.adoc#webflux-headers-clear-site-data[webflux] based applications. ==== https://www.w3.org/TR/clear-site-data/[Clear Site Data] is a mechanism by which any browser-side data - cookies, local storage, and the like - can be removed when an HTTP response contains this header: @@ -384,7 +384,7 @@ This is a nice clean-up action to perform on logout. [NOTE] ==== -Refer to the relevant section to see how to configure <> based applications. +Refer to the relevant section to see how to configure xref:servlet/exploits/headers.adoc#servlet-headers-custom[servlet] based applications. ==== Spring Security has mechanisms to make it convenient to add the more common security headers to your application. diff --git a/docs/modules/ROOT/pages/overview/features/exploits/http.adoc b/docs/modules/ROOT/pages/overview/features/exploits/http.adoc index 44a4e59692..579745c1aa 100644 --- a/docs/modules/ROOT/pages/overview/features/exploits/http.adoc +++ b/docs/modules/ROOT/pages/overview/features/exploits/http.adoc @@ -9,12 +9,12 @@ However, it does provide a number of features that help with HTTPS usage. [[http-redirect]] == Redirect to HTTPS -When a client uses HTTP, Spring Security can be configured to redirect to HTTPS both <> and <> environments. +When a client uses HTTP, Spring Security can be configured to redirect to HTTPS both xref:servlet/exploits/http.adoc#servlet-http-redirect[Servlet] and xref:reactive/exploits/http.adoc#webflux-http-redirect[WebFlux] environments. [[http-hsts]] == Strict Transport Security -Spring Security provides support for <> and enables it by default. +Spring Security provides support for xref:overview/features/exploits/headers.adoc#headers-hsts[Strict Transport Security] and enables it by default. [[http-proxy-server]] == Proxy Server Configuration diff --git a/docs/modules/ROOT/pages/overview/features/index.adoc b/docs/modules/ROOT/pages/overview/features/index.adoc index 8ae9eb3feb..0edf1f1650 100644 --- a/docs/modules/ROOT/pages/overview/features/index.adoc +++ b/docs/modules/ROOT/pages/overview/features/index.adoc @@ -1,6 +1,6 @@ [[features]] = Features -Spring Security provides comprehensive support for <>, authorization, and protection against <>. +Spring Security provides comprehensive support for xref:overview/features/authentication/index.adoc#authentication[authentication], authorization, and protection against xref:overview/features/exploits/index.adoc#exploits[common exploits]. It also provides integration with other libraries to simplify its usage. diff --git a/docs/modules/ROOT/pages/overview/getting-spring-security.adoc b/docs/modules/ROOT/pages/overview/getting-spring-security.adoc index 51f77fe29f..a9b2eef369 100644 --- a/docs/modules/ROOT/pages/overview/getting-spring-security.adoc +++ b/docs/modules/ROOT/pages/overview/getting-spring-security.adoc @@ -2,7 +2,7 @@ = Getting Spring Security This section discusses all you need to know about getting the Spring Security binaries. -See <> for how to obtain the source code. +See xref:overview/community.adoc#community-source[] for how to obtain the source code. == Release Numbering @@ -72,7 +72,7 @@ You can do so by adding a Maven property, as the following example shows: ---- ==== -If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate <>. +If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:overview/modules.adoc#modules[]. [[getting-maven-no-boot]] === Maven Without Spring Boot @@ -118,7 +118,7 @@ A minimal Spring Security Maven set of dependencies typically looks like the fol ---- ==== -If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate <>. +If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:overview/modules.adoc#modules[]. Spring Security builds against Spring Framework {spring-core-version} but should generally work with any newer version of Spring Framework 5.x. Many users are likely to run afoul of the fact that Spring Security's transitive dependencies resolve Spring Framework {spring-core-version}, which can cause strange classpath problems. @@ -236,7 +236,7 @@ ext['spring.version']='{spring-core-version}' ---- ==== -If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate <>. +If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:overview/modules.adoc#modules[]. === Gradle Without Spring Boot @@ -274,7 +274,7 @@ dependencies { ---- ==== -If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate <>. +If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:overview/modules.adoc#modules[]. Spring Security builds against Spring Framework {spring-core-version} but should generally work with any newer version of Spring Framework 5.x. Many users are likely to run afoul of the fact that Spring Security's transitive dependencies resolve Spring Framework {spring-core-version}, which can cause strange classpath problems. diff --git a/docs/modules/ROOT/pages/reactive/exploits/csrf.adoc b/docs/modules/ROOT/pages/reactive/exploits/csrf.adoc index c3a92fea4f..d596977b40 100644 --- a/docs/modules/ROOT/pages/reactive/exploits/csrf.adoc +++ b/docs/modules/ROOT/pages/reactive/exploits/csrf.adoc @@ -1,7 +1,7 @@ [[webflux-csrf]] = Cross Site Request Forgery (CSRF) for WebFlux Environments -This section discusses Spring Security's <> support for WebFlux environments. +This section discusses Spring Security's xref:overview/features/exploits/csrf.adoc#csrf[Cross Site Request Forgery (CSRF)] support for WebFlux environments. [[webflux-csrf-using]] == Using Spring Security CSRF Protection @@ -14,7 +14,7 @@ The steps to using Spring Security's CSRF protection are outlined below: [[webflux-csrf-idempotent]] === Use proper HTTP verbs The first step to protecting against CSRF attacks is to ensure your website uses proper HTTP verbs. -This is covered in detail in <>. +This is covered in detail in xref:overview/features/exploits/csrf.adoc#csrf-protection-idempotent[Safe Methods Must be Idempotent]. [[webflux-csrf-configure]] === Configure CSRF Protection @@ -73,7 +73,7 @@ If you do not need the ability to read the cookie with JavaScript directly, it i [[webflux-csrf-configure-disable]] ==== Disable CSRF Protection CSRF protection is enabled by default. -However, it is simple to disable CSRF protection if it <>. +However, it is simple to disable CSRF protection if it xref:overview/features/exploits/csrf.adoc#csrf-when[makes sense for your application]. The Java configuration below will disable CSRF protection. @@ -109,7 +109,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain [[webflux-csrf-include]] === Include the CSRF Token -In order for the <> to protect against CSRF attacks, we must include the actual CSRF token in the HTTP request. +In order for the xref:overview/features/exploits/csrf.adoc#csrf-protection-stp[synchronizer token pattern] to protect against CSRF attacks, we must include the actual CSRF token in the HTTP request. This must be included in a part of the request (i.e. form parameter, HTTP header, etc) that is not automatically included in the HTTP request by the browser. Spring Security's https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/server/csrf/CsrfWebFilter.html[CsrfWebFilter] exposes a https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/csrf/CsrfToken.html[Mono] as a `ServerWebExchange` attribute named `org.springframework.security.web.server.csrf.CsrfToken`. @@ -272,19 +272,19 @@ An example of doing this with Thymeleaf is shown below: == CSRF Considerations There are a few special considerations to consider when implementing protection against CSRF attacks. This section discusses those considerations as it pertains to WebFlux environments. -Refer to <> for a more general discussion. +Refer to xref:overview/features/exploits/csrf.adoc#csrf-considerations[] for a more general discussion. [[webflux-considerations-csrf-login]] === Logging In -It is important to <> requests to protect against forging log in attempts. +It is important to xref:overview/features/exploits/csrf.adoc#csrf-considerations-login[require CSRF for log in] requests to protect against forging log in attempts. Spring Security's WebFlux support does this out of the box. [[webflux-considerations-csrf-logout]] === Logging Out -It is important to <> requests to protect against forging log out attempts. +It is important to xref:overview/features/exploits/csrf.adoc#csrf-considerations-logout[require CSRF for log out] requests to protect against forging log out attempts. By default Spring Security's `LogoutWebFilter` only processes HTTP post requests. This ensures that log out requires a CSRF token and that a malicious user cannot forcibly log out your users. @@ -333,7 +333,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain By default Spring Security stores the CSRF token in the `WebSession`. This can lead to a situation where the session expires which means there is not an expected CSRF token to validate against. -We've already discussed <> to session timeouts. +We've already discussed xref:overview/features/exploits/csrf.adoc#csrf-considerations-login[general solutions] to session timeouts. This section discusses the specifics of CSRF timeouts as it pertains to the WebFlux support. It is simple to change storage of the expected CSRF token to be in a cookie. @@ -344,7 +344,7 @@ For details, refer to the <> section. // FIXME: We need a WebFlux multipart body vs action story. WebFlux always has multipart enabled. [[webflux-csrf-considerations-multipart]] === Multipart (file upload) -We have <> how protecting multipart requests (file uploads) from CSRF attacks causes a https://en.wikipedia.org/wiki/Chicken_or_the_egg[chicken and the egg] problem. +We have xref:overview/features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] how protecting multipart requests (file uploads) from CSRF attacks causes a https://en.wikipedia.org/wiki/Chicken_or_the_egg[chicken and the egg] problem. This section discusses how to implement placing the CSRF token in the <> and <> within a WebFlux application. [NOTE] @@ -355,7 +355,7 @@ More information about using multipart forms with Spring can be found within the [[webflux-csrf-considerations-multipart-body]] ==== Place CSRF Token in the Body -We have <> the trade-offs of placing the CSRF token in the body. +We have xref:overview/features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] the trade-offs of placing the CSRF token in the body. In a WebFlux application, this can be configured with the following configuration: @@ -391,7 +391,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain [[webflux-csrf-considerations-multipart-url]] ==== Include CSRF Token in URL -We have <> the trade-offs of placing the CSRF token in the URL. +We have xref:overview/features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] the trade-offs of placing the CSRF token in the URL. Since the `CsrfToken` is exposed as an `ServerHttpRequest` <>, we can use that to create an `action` with the CSRF token in it. An example with Thymeleaf is shown below: @@ -407,6 +407,6 @@ An example with Thymeleaf is shown below: [[webflux-csrf-considerations-override-method]] === HiddenHttpMethodFilter -We have <> overriding the HTTP method. +We have xref:overview/features/exploits/csrf.adoc#csrf-considerations-override-method[already discussed] overriding the HTTP method. In a Spring WebFlux application, overriding the HTTP method is done using https://docs.spring.io/spring-framework/docs/5.2.x/javadoc-api/org/springframework/web/filter/reactive/HiddenHttpMethodFilter.html[HiddenHttpMethodFilter]. diff --git a/docs/modules/ROOT/pages/reactive/exploits/headers.adoc b/docs/modules/ROOT/pages/reactive/exploits/headers.adoc index a076a2fedb..ed916c6baf 100644 --- a/docs/modules/ROOT/pages/reactive/exploits/headers.adoc +++ b/docs/modules/ROOT/pages/reactive/exploits/headers.adoc @@ -1,17 +1,17 @@ [[webflux-headers]] = Security HTTP Response Headers -<> can be used to increase the security of web applications. +xref:overview/features/exploits/headers.adoc#headers[Security HTTP Response Headers] can be used to increase the security of web applications. This section is dedicated to WebFlux based support for Security HTTP Response Headers. [[webflux-headers-default]] == Default Security Headers -Spring Security provides a <> to provide secure defaults. +Spring Security provides a xref:overview/features/exploits/headers.adoc#headers-default[default set of Security HTTP Response Headers] to provide secure defaults. 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 you want the defaults except you wish to specify `SAMEORIGIN` for <>. +For example, assume that you want the defaults except you wish to specify `SAMEORIGIN` for xref:servlet/exploits/headers.adoc#servlet-headers-frame-options[X-Frame-Options]. You can easily do this with the following Configuration: @@ -85,7 +85,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { [[webflux-headers-cache-control]] == Cache Control -Spring Security includes <> headers by default. +Spring Security includes xref:overview/features/exploits/headers.adoc#headers-cache-control[Cache Control] headers by default. However, if you actually want to cache specific responses, your application can selectively add them to the https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/server/reactive/ServerHttpResponse.html[ServerHttpResponse] to override the header set by Spring Security. This is useful to ensure things like CSS, JavaScript, and images are properly cached. @@ -131,7 +131,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { [[webflux-headers-content-type-options]] == Content Type Options -Spring Security includes <> headers by default. +Spring Security includes xref:overview/features/exploits/headers.adoc#headers-content-type-options[Content-Type] headers by default. However, you can disable it with: .Content Type Options Disabled @@ -169,7 +169,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { [[webflux-headers-hsts]] == HTTP Strict Transport Security (HSTS) -Spring Security provides the <> header by default. +Spring Security provides the xref:overview/features/exploits/headers.adoc#headers-hsts[Strict Transport Security] header by default. However, you can customize the results explicitly. For example, the following is an example of explicitly providing HSTS: @@ -214,7 +214,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { [[webflux-headers-frame-options]] == X-Frame-Options -By default, Spring Security disables rendering within an iframe using <>. +By default, Spring Security disables rendering within an iframe using xref:overview/features/exploits/headers.adoc#headers-frame-options[X-Frame-Options]. You can customize frame options to use the same origin using the following: @@ -293,7 +293,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { [[webflux-headers-csp]] == Content Security Policy (CSP) -Spring Security does not add <> by default, because a reasonable default is impossible to know without context of the application. +Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-csp[Content Security Policy] by default, because a reasonable default is impossible to know without context of the application. 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: @@ -385,7 +385,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { [[webflux-headers-referrer]] == Referrer Policy -Spring Security does not add <> headers by default. +Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-referrer[Referrer Policy] headers by default. You can enable the Referrer Policy header using configuration as shown below: .Referrer Policy Configuration @@ -427,7 +427,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { [[webflux-headers-feature]] == Feature Policy -Spring Security does not add <> headers by default. +Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-feature[Feature Policy] headers by default. The following `Feature-Policy` header: .Feature-Policy Example @@ -475,7 +475,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { [[webflux-headers-permissions]] == Permissions Policy -Spring Security does not add <> headers by default. +Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-permissions[Permissions Policy] headers by default. The following `Permissions-Policy` header: .Permissions-Policy Example @@ -527,7 +527,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { [[webflux-headers-clear-site-data]] == Clear Site Data -Spring Security does not add <> headers by default. +Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-clear-site-data[Clear-Site-Data] headers by default. The following Clear-Site-Data header: .Clear-Site-Data Example diff --git a/docs/modules/ROOT/pages/reactive/exploits/http.adoc b/docs/modules/ROOT/pages/reactive/exploits/http.adoc index 387a05a719..258d10f74f 100644 --- a/docs/modules/ROOT/pages/reactive/exploits/http.adoc +++ b/docs/modules/ROOT/pages/reactive/exploits/http.adoc @@ -1,7 +1,7 @@ [[webflux-http]] = HTTP -All HTTP based communication should be protected <>. +All HTTP based communication should be protected xref:overview/features/exploits/http.adoc#http[using TLS]. Below you can find details around WebFlux specific features that assist with HTTPS usage. @@ -79,9 +79,9 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain [[webflux-hsts]] == Strict Transport Security -Spring Security provides support for <> and enables it by default. +Spring Security provides support for xref:servlet/exploits/headers.adoc#servlet-headers-hsts[Strict Transport Security] and enables it by default. [[webflux-http-proxy-server]] == Proxy Server Configuration -Spring Security <>. +Spring Security xref:overview/features/exploits/http.adoc#http-proxy-server[integrates with proxy servers]. diff --git a/docs/modules/ROOT/pages/reactive/index.adoc b/docs/modules/ROOT/pages/reactive/index.adoc index 2e9d23500d..8791a47c32 100644 --- a/docs/modules/ROOT/pages/reactive/index.adoc +++ b/docs/modules/ROOT/pages/reactive/index.adoc @@ -1,4 +1,4 @@ = Reactive Applications -Reactive applications work very differently than <>. +Reactive applications work very differently than xref:servlet/index.adoc#servlet-applications[]. This section discusses how Spring Security works with reactive applications which are typically written using Spring's WebFlux. diff --git a/docs/modules/ROOT/pages/reactive/method.adoc b/docs/modules/ROOT/pages/reactive/method.adoc index 21a183fd34..fbb07d4d7e 100644 --- a/docs/modules/ROOT/pages/reactive/method.adoc +++ b/docs/modules/ROOT/pages/reactive/method.adoc @@ -162,7 +162,7 @@ It is important to note that any of the expressions in standard method security However, at this time we only support return type of `Boolean` or `boolean` of the expression. This means that the expression must not block. -When integrating with <>, the Reactor Context is automatically established by Spring Security according to the authenticated user. +When integrating with xref:reactive/webflux.adoc#jc-webflux[], the Reactor Context is automatically established by Spring Security according to the authenticated user. ==== .Java diff --git a/docs/modules/ROOT/pages/reactive/oauth2/access-token.adoc b/docs/modules/ROOT/pages/reactive/oauth2/access-token.adoc index ac732704a2..27d272c089 100644 --- a/docs/modules/ROOT/pages/reactive/oauth2/access-token.adoc +++ b/docs/modules/ROOT/pages/reactive/oauth2/access-token.adoc @@ -49,4 +49,4 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { ---- ==== -You can now leverage Spring Security's <> or <> support to obtain and use the access token. +You can now leverage Spring Security's <> or xref:reactive/registered-oauth2-authorized-client.adoc#webflux-roac[@RegisteredOAuth2AuthorizedClient] support to obtain and use the access token. diff --git a/docs/modules/ROOT/pages/reactive/oauth2/login.adoc b/docs/modules/ROOT/pages/reactive/oauth2/login.adoc index ecbe18dc0f..2f80da08b1 100644 --- a/docs/modules/ROOT/pages/reactive/oauth2/login.adoc +++ b/docs/modules/ROOT/pages/reactive/oauth2/login.adoc @@ -39,11 +39,11 @@ The redirect URI is the path in the application that the end-user's user-agent i 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 <>. +The *_registrationId_* is a unique identifier for the xref:servlet/oauth2/oauth2-client.adoc#oauth2Client-client-registration[ClientRegistration]. For our example, the `registrationId` is `google`. -IMPORTANT: If the OAuth Client is running behind a proxy server, it is recommended to check <> to ensure the application is correctly configured. -Also, see the supported <> for `redirect-uri`. +IMPORTANT: If the OAuth Client is running behind a proxy server, it is recommended to check xref:overview/features/exploits/http.adoc#http-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured. +Also, see the supported xref:servlet/oauth2/oauth2-client.adoc#oauth2Client-auth-code-redirect-uri[ `URI` template variables] for `redirect-uri`. [[webflux-oauth2-login-sample-config]] === Configure `application.yml` @@ -68,7 +68,7 @@ spring: .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. +<2> Following the base property prefix is the ID for the xref:servlet/oauth2/oauth2-client.adoc#oauth2Client-client-registration[ClientRegistration], such as google. ==== . Replace the values in the `client-id` and `client-secret` property with the OAuth 2.0 credentials you created earlier. diff --git a/docs/modules/ROOT/pages/reactive/oauth2/resource-server.adoc b/docs/modules/ROOT/pages/reactive/oauth2/resource-server.adoc index 8f45337314..443b54aff7 100644 --- a/docs/modules/ROOT/pages/reactive/oauth2/resource-server.adoc +++ b/docs/modules/ROOT/pages/reactive/oauth2/resource-server.adoc @@ -896,7 +896,7 @@ fun jwtDecoder(): ReactiveJwtDecoder { [[webflux-oauth2resourceserver-opaque-minimaldependencies]] === Minimal Dependencies for Introspection -As described in <> most of Resource Server support is collected in `spring-security-oauth2-resource-server`. +As described in xref:servlet/oauth2/oauth2-resourceserver.adoc#oauth2resourceserver-jwt-minimaldependencies[Minimal Dependencies for JWT] most of Resource Server support is collected in `spring-security-oauth2-resource-server`. However unless a custom <> is provided, the Resource Server will fallback to ReactiveOpaqueTokenIntrospector. Meaning that both `spring-security-oauth2-resource-server` and `oauth2-oidc-sdk` are necessary in order to have a working minimal Resource Server that supports opaque Bearer Tokens. Please refer to `spring-security-oauth2-resource-server` in order to determin the correct version for `oauth2-oidc-sdk`. diff --git a/docs/modules/ROOT/pages/reactive/registered-oauth2-authorized-client.adoc b/docs/modules/ROOT/pages/reactive/registered-oauth2-authorized-client.adoc index 30beaf7807..b8cc5c6367 100644 --- a/docs/modules/ROOT/pages/reactive/registered-oauth2-authorized-client.adoc +++ b/docs/modules/ROOT/pages/reactive/registered-oauth2-authorized-client.adoc @@ -8,7 +8,7 @@ Spring Security allows resolving an access token using `@RegisteredOAuth2Authori A working example can be found in {gh-samples-url}/reactive/webflux/java/oauth2/webclient[*OAuth 2.0 WebClient WebFlux sample*]. ==== -After configuring Spring Security for <> or as an <>, an `OAuth2AuthorizedClient` can be resolved using the following: +After configuring Spring Security for xref:reactive/oauth2/login.adoc#webflux-oauth2-login[OAuth2 Login] or as an xref:reactive/oauth2/access-token.adoc#webflux-oauth2-client[OAuth2 Client], an `OAuth2AuthorizedClient` can be resolved using the following: ==== .Java diff --git a/docs/modules/ROOT/pages/reactive/test.adoc b/docs/modules/ROOT/pages/reactive/test.adoc index d341dea14a..632e58e18f 100644 --- a/docs/modules/ROOT/pages/reactive/test.adoc +++ b/docs/modules/ROOT/pages/reactive/test.adoc @@ -4,7 +4,7 @@ [[test-erms]] == Testing Reactive Method Security -For example, we can test our example from <> using the same setup and annotations we did in <>. +For example, we can test our example from xref:reactive/method.adoc#jc-erms[] using the same setup and annotations we did in xref:servlet/test/method.adoc#test-method[]. Here is a minimal sample of what we can do: ==== @@ -381,7 +381,7 @@ assertThat(user.authorities).containsExactly(SimpleGrantedAuthority("SCOPE_read" ---- ==== -Spring Security does the necessary work to make sure that the `OidcUser` instance is available for <>. +Spring Security does the necessary work to make sure that the `OidcUser` instance is available for xref:servlet/integrations/mvc.adoc#mvc-authentication-principal[the `@AuthenticationPrincipal` annotation]. Further, it also links that `OidcUser` to a simple instance of `OAuth2AuthorizedClient` that it deposits into a mock `ServerOAuth2AuthorizedClientRepository`. This can be handy if your tests <>.. @@ -600,7 +600,7 @@ assertThat(user.authorities).containsExactly(SimpleGrantedAuthority("SCOPE_read" ---- ==== -Spring Security does the necessary work to make sure that the `OAuth2User` instance is available for <>. +Spring Security does the necessary work to make sure that the `OAuth2User` instance is available for xref:servlet/integrations/mvc.adoc#mvc-authentication-principal[the `@AuthenticationPrincipal` annotation]. Further, it also links that `OAuth2User` to a simple instance of `OAuth2AuthorizedClient` that it deposits in a mock `ServerOAuth2AuthorizedClientRepository`. This can be handy if your tests <>. diff --git a/docs/modules/ROOT/pages/reactive/webclient.adoc b/docs/modules/ROOT/pages/reactive/webclient.adoc index 181ac58b2c..270420a4fa 100644 --- a/docs/modules/ROOT/pages/reactive/webclient.adoc +++ b/docs/modules/ROOT/pages/reactive/webclient.adoc @@ -3,7 +3,7 @@ [NOTE] ==== The following documentation is for use within Reactive environments. -For Servlet environments, refer to <> environments. +For Servlet environments, refer to xref:servlet/oauth2/oauth2-client.adoc#oauth2Client-webclient-servlet[ WebClient for Servlet] environments. ==== diff --git a/docs/modules/ROOT/pages/reactive/x509.adoc b/docs/modules/ROOT/pages/reactive/x509.adoc index cb1c4e0a47..880632b060 100644 --- a/docs/modules/ROOT/pages/reactive/x509.adoc +++ b/docs/modules/ROOT/pages/reactive/x509.adoc @@ -1,7 +1,7 @@ [[reactive-x509]] = Reactive X.509 Authentication -Similar to <>, reactive x509 authentication filter allows extracting an authentication token from a certificate provided by a client. +Similar to xref:servlet/authentication/x509.adoc#servlet-x509[Servlet X.509 authentication], reactive x509 authentication filter allows extracting an authentication token from a certificate provided by a client. Below is an example of a reactive x509 security configuration: ==== diff --git a/docs/modules/ROOT/pages/servlet/appendix/database-schema.adoc b/docs/modules/ROOT/pages/servlet/appendix/database-schema.adoc index ab840e131f..f6cf413b68 100644 --- a/docs/modules/ROOT/pages/servlet/appendix/database-schema.adoc +++ b/docs/modules/ROOT/pages/servlet/appendix/database-schema.adoc @@ -78,7 +78,7 @@ If you write your own or choose to implement `AuthenticationProvider` without a == Persistent Login (Remember-Me) Schema -This table is used to store data used by the more secure <> remember-me implementation. +This table is used to store data used by the more secure xref:servlet/authentication/rememberme.adoc#remember-me-persistent-token[persistent token] remember-me implementation. If you are using `JdbcTokenRepositoryImpl` either directly or through the namespace, then you will need this table. Remember to adjust this schema to match the database dialect you are using. @@ -96,7 +96,7 @@ create table persistent_logins ( [[dbschema-acl]] == ACL Schema -There are four tables used by the Spring Security <> implementation. +There are four tables used by the Spring Security xref:servlet/authorization/acls.adoc#domain-acls[ACL] implementation. . `acl_sid` stores the security identities recognised by the ACL system. These can be unique principals or authorities which may apply to multiple principals. @@ -367,7 +367,7 @@ END; [[dbschema-oauth2-client]] == OAuth 2.0 Client Schema -The JDBC implementation of <> (`JdbcOAuth2AuthorizedClientService`) requires a table for persisting `OAuth2AuthorizedClient`(s). +The JDBC implementation of xref:servlet/oauth2/oauth2-client.adoc#oauth2Client-authorized-repo-service[ OAuth2AuthorizedClientService] (`JdbcOAuth2AuthorizedClientService`) requires a table for persisting `OAuth2AuthorizedClient`(s). You will need to adjust this schema to match the database dialect you are using. [source,ddl] diff --git a/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc b/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc index a2e029da6b..ec3d417bde 100644 --- a/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc +++ b/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc @@ -1,7 +1,7 @@ [[appendix-namespace]] = The Security Namespace This appendix provides a reference to the elements available in the security namespace and information on the underlying beans they create (a knowledge of the individual classes and how they work together is assumed - you can find more information in the project Javadoc and elsewhere in this document). -If you haven't used the namespace before, please read the <> on namespace configuration, as this is intended as a supplement to the information there. +If you haven't used the namespace before, please read the xref:servlet/namespace/index.adoc#ns-config[introductory chapter] on namespace configuration, as this is intended as a supplement to the information there. Using a good quality XML editor while editing a configuration based on the schema is recommended as this will provide contextual information on which elements and attributes are available as well as comments explaining their purpose. The namespace is written in https://relaxng.org/[RELAX NG] Compact format and later converted into an XSD schema. If you are familiar with this format, you may wish to examine the https://raw.githubusercontent.com/spring-projects/spring-security/main/config/src/main/resources/org/springframework/security/config/spring-security-4.1.rnc[schema file] directly. @@ -19,15 +19,15 @@ This may include sensitive information, such as request parameters or headers, a === If you use an `` element within your application, a `FilterChainProxy` bean named "springSecurityFilterChain" is created and the configuration within the element is used to build a filter chain within `FilterChainProxy`. -As of Spring Security 3.1, additional `http` elements can be used to add extra filter chains footnote:[See the pass:specialcharacters,macros[<>] for how to set up the mapping from your `web.xml` ]. +As of Spring Security 3.1, additional `http` elements can be used to add extra filter chains footnote:[See the pass:specialcharacters,macros[xref:servlet/namespace/index.adoc#ns-web-xml[introductory chapter]] for how to set up the mapping from your `web.xml` ]. Some core filters are always created in a filter chain and others will be added to the stack depending on the attributes and child elements which are present. The positions of the standard filters are fixed (see -<> in the namespace introduction), removing a common source of errors with previous versions of the framework when users had to configure the filter chain explicitly in the +xref:servlet/namespace/index.adoc#filter-stack[the filter order table] in the namespace introduction), removing a common source of errors with previous versions of the framework when users had to configure the filter chain explicitly in the `FilterChainProxy` bean. You can, of course, still do this if you need full control of the configuration. -All filters which require a reference to the <> will be automatically injected with the internal instance created by the namespace configuration. +All filters which require a reference to the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] will be automatically injected with the internal instance created by the namespace configuration. Each `` namespace block always creates an `SecurityContextPersistenceFilter`, an `ExceptionTranslationFilter` and a `FilterSecurityInterceptor`. These are fixed and cannot be replaced with alternatives. @@ -146,7 +146,7 @@ Defaults to `true`. [[nsa-http-use-expressions]] * **use-expressions** -Enables EL-expressions in the `access` attribute, as described in the chapter on <>. +Enables EL-expressions in the `access` attribute, as described in the chapter on xref:servlet/authorization/expression-based.adoc#el-access-web[expression-based access-control]. The default value is true. @@ -179,7 +179,7 @@ The default value is true. [[nsa-access-denied-handler]] === This element allows you to set the `errorPage` property for the default `AccessDeniedHandler` used by the `ExceptionTranslationFilter`, using the <> attribute, or to supply your own implementation using the<> attribute. -This is discussed in more detail in the section on the <>. +This is discussed in more detail in the section on the xref:servlet/architecture/index.adoc#servlet-exceptiontranslationfilter[ExceptionTranslationFilter]. [[nsa-access-denied-handler-parents]] @@ -227,7 +227,7 @@ Optional attribute that specifies the bean name of a `CorsConfigurationSource` t === This element allows for configuring additional (security) headers to be send with the response. It enables easy configuration for several headers and also allows for setting custom headers through the <> element. -Additional information, can be found in the <> section of the reference. +Additional information, can be found in the xref:overview/features/exploits/headers.adoc#headers[Security Headers] section of the reference. ** `Cache-Control`, `Pragma`, and `Expires` - Can be set using the <> element. This ensures that the browser does not cache your secured pages. @@ -674,7 +674,7 @@ if unset, defaults to `anonymousUser`. === This element will add https://en.wikipedia.org/wiki/Cross-site_request_forgery[Cross Site Request Forger (CSRF)] protection to the application. It also updates the default RequestCache to only replay "GET" requests upon successful authentication. -Additional information can be found in the <> section of the reference. +Additional information can be found in the xref:overview/features/exploits/csrf.adoc#csrf[Cross Site Request Forgery (CSRF)] section of the reference. [[nsa-csrf-parents]] @@ -710,7 +710,7 @@ Default is any HTTP method except "GET", "TRACE", "HEAD", "OPTIONS". === This element is used to add a filter to the filter chain. It doesn't create any additional beans but is used to select a bean of type `javax.servlet.Filter` which is already defined in the application context and add that at a particular position in the filter chain maintained by Spring Security. -Full details can be found in the <>. +Full details can be found in the xref:servlet/namespace/index.adoc#ns-custom-filters[ namespace chapter]. [[nsa-custom-filter-parents]] @@ -871,7 +871,7 @@ Maps a `ForwardAuthenticationFailureHandler` to `authenticationFailureHandler` p [[nsa-oauth2-login]] === -The <> feature configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider. +The xref:servlet/oauth2/oauth2-login.adoc#oauth2login[OAuth 2.0 Login] feature configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider. [[nsa-oauth2-login-parents]] @@ -955,7 +955,7 @@ Reference to the `JwtDecoderFactory` used by `OidcAuthorizationCodeAuthenticatio [[nsa-oauth2-client]] === -Configures <> support. +Configures xref:servlet/oauth2/oauth2-client.adoc#oauth2client[OAuth 2.0 Client] support. [[nsa-oauth2-client-parents]] @@ -990,7 +990,7 @@ Reference to the `OAuth2AuthorizedClientService`. [[nsa-authorization-code-grant]] === -Configures <>. +Configures xref:servlet/oauth2/oauth2-client.adoc#oauth2Client-auth-grant-support[OAuth 2.0 Authorization Code Grant]. [[nsa-authorization-code-grant-parents]] @@ -1020,7 +1020,7 @@ Reference to the `OAuth2AccessTokenResponseClient`. [[nsa-client-registrations]] === -A container element for client(s) registered (<>) with an OAuth 2.0 or OpenID Connect 1.0 Provider. +A container element for client(s) registered (xref:servlet/oauth2/oauth2-client.adoc#oauth2Client-client-registration[ClientRegistration]) with an OAuth 2.0 or OpenID Connect 1.0 Provider. [[nsa-client-registrations-children]] @@ -1522,7 +1522,7 @@ Defaults to "username". [[nsa-attribute-exchange]] === The `attribute-exchange` element defines the list of attributes which should be requested from the identity provider. -An example can be found in the <> section of the namespace configuration chapter. +An example can be found in the xref:servlet/authentication/openid.adoc#servlet-openid[OpenID Support] section of the namespace configuration chapter. More than one can be used, in which case each must have an `identifier-match` attribute, containing a regular expression which is matched against the supplied OpenID identifier. This allows different attribute lists to be fetched from different providers (Google, Yahoo etc). @@ -1616,7 +1616,7 @@ By default, an instance of `PortMapperImpl` will be added to the configuration f This element can optionally be used to override the default mappings which that class defines. Each child `` element defines a pair of HTTP:HTTPS ports. The default mappings are 80:443 and 8080:8443. -An example of overriding these can be found in <>. +An example of overriding these can be found in xref:servlet/exploits/http.adoc#servlet-http-redirect[]. [[nsa-port-mappings-parents]] @@ -2124,7 +2124,7 @@ This attribute allows you to define an alias name for the internal instance for [[nsa-authentication-manager-erase-credentials]] * **erase-credentials** If set to true, the AuthenticationManager will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated. -Literally it maps to the `eraseCredentialsAfterAuthentication` property of the <>. +Literally it maps to the `eraseCredentialsAfterAuthentication` property of the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`ProviderManager`]. [[nsa-authentication-manager-id]] @@ -2278,7 +2278,7 @@ select username, password, enabled from users where username = ? [[nsa-password-encoder]] === -Authentication providers can optionally be configured to use a password encoder as described in the <>. +Authentication providers can optionally be configured to use a password encoder as described in the xref:overview/features/authentication/password-storage.adoc#authentication-password-storage[]. This will result in the bean being injected with the appropriate `PasswordEncoder` instance. @@ -2625,7 +2625,7 @@ Defines a reference to a Spring bean Id. === Securing Methods using `` Rather than defining security attributes on an individual method or class basis using the `@Secured` annotation, you can define cross-cutting security constraints across whole sets of methods and interfaces in your service layer using the `` element. -You can find an example in the <>. +You can find an example in the xref:servlet/authorization/method-security.adoc#ns-protect-pointcut[namespace introduction]. [[nsa-protect-pointcut-parents]] @@ -2736,7 +2736,7 @@ A method name [[nsa-ldap]] == LDAP Namespace Options -LDAP is covered in some details in <>. +LDAP is covered in some details in xref:servlet/authentication/unpwd/ldap.adoc#servlet-authentication-ldap[its own chapter]. We will expand on that here with some explanation of how the namespace options map to Spring beans. The LDAP implementation uses Spring LDAP extensively, so some familiarity with that project's API may be useful. @@ -2746,7 +2746,7 @@ The LDAP implementation uses Spring LDAP extensively, so some familiarity with t `` Element This element sets up a Spring LDAP `ContextSource` for use by the other LDAP beans, defining the location of the LDAP server and other information (such as a username and password, if it doesn't allow anonymous access) for connecting to it. It can also be used to create an embedded server for testing. -Details of the syntax for both options are covered in the <>. +Details of the syntax for both options are covered in the xref:servlet/authentication/unpwd/ldap.adoc#servlet-authentication-ldap[LDAP chapter]. The actual `ContextSource` implementation is `DefaultSpringSecurityContextSource` which extends Spring LDAP's `LdapContextSource` class. The `manager-dn` and `manager-password` attributes map to the latter's `userDn` and `password` properties respectively. diff --git a/docs/modules/ROOT/pages/servlet/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/architecture/index.adoc index c107f82fbd..61fcb9f401 100644 --- a/docs/modules/ROOT/pages/servlet/architecture/index.adoc +++ b/docs/modules/ROOT/pages/servlet/architecture/index.adoc @@ -3,7 +3,7 @@ :figures: servlet/architecture This section discusses Spring Security's high level architecture within Servlet based applications. -We build on this high level understanding within <>, <>, <> sections of the reference. +We build on this high level understanding within xref:servlet/authentication/index.adoc#servlet-authentication[], xref:servlet/authorization/index.adoc#servlet-authorization[], xref:servlet/exploits/index.adoc#servlet-exploits[] sections of the reference. // FIXME: Add links to other sections of architecture [[servlet-filters-review]] @@ -128,7 +128,7 @@ For that reason, if you are attempting to troubleshoot Spring Security's Servlet Second, since `FilterChainProxy` is central to Spring Security usage it can perform tasks that are not viewed as optional. // FIXME: Add a link to SecurityContext For example, it clears out the `SecurityContext` to avoid memory leaks. -It also applies Spring Security's <> to protect applications against certain types of attacks. +It also applies Spring Security's xref:servlet/exploits/firewall.adoc#servlet-httpfirewall[`HttpFirewall`] to protect applications against certain types of attacks. In addition, it provides more flexibility in determining when a `SecurityFilterChain` should be invoked. In a Servlet container, ``Filter``s are invoked based upon the URL alone. @@ -179,14 +179,14 @@ Below is a comprehensive list of Spring Security Filter ordering: * CasAuthenticationFilter * OAuth2LoginAuthenticationFilter * Saml2WebSsoAuthenticationFilter -* <> +* xref:servlet/authentication/unpwd/form.adoc#servlet-authentication-usernamepasswordauthenticationfilter[`UsernamePasswordAuthenticationFilter`] * OpenIDAuthenticationFilter * DefaultLoginPageGeneratingFilter * DefaultLogoutPageGeneratingFilter * ConcurrentSessionFilter -* <> +* xref:servlet/authentication/unpwd/digest.adoc#servlet-authentication-digest[`DigestAuthenticationFilter`] * BearerTokenAuthenticationFilter -* <> +* xref:servlet/authentication/unpwd/basic.adoc#servlet-authentication-basic[`BasicAuthenticationFilter`] * RequestCacheAwareFilter * SecurityContextHolderAwareRequestFilter * JaasApiIntegrationFilter @@ -195,7 +195,7 @@ Below is a comprehensive list of Spring Security Filter ordering: * OAuth2AuthorizationCodeGrantFilter * SessionManagementFilter * <> -* <> +* xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] * SwitchUserFilter [[servlet-exceptiontranslationfilter]] @@ -211,7 +211,7 @@ image::{figures}/exceptiontranslationfilter.png[] * image:{icondir}/number_1.png[] First, the `ExceptionTranslationFilter` invokes `FilterChain.doFilter(request, response)` to invoke the rest of the application. * image:{icondir}/number_2.png[] If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__. -** The <> is cleared out. +** The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[] is cleared out. ** The `HttpServletRequest` is saved in the {security-api-url}org/springframework/security/web/savedrequest/RequestCache.html[`RequestCache`]. When the user successfully authenticates, the `RequestCache` is used to replay the original request. // FIXME: add link to authentication success @@ -243,6 +243,6 @@ try { } ---- <1> You will recall from <> that invoking `FilterChain.doFilter(request, response)` is the equivalent of invoking the rest of the application. -This means that if another part of the application, (i.e. <> or method security) throws an `AuthenticationException` or `AccessDeniedException` it will be caught and handled here. +This means that if another part of the application, (i.e. xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] or method security) throws an `AuthenticationException` or `AccessDeniedException` it will be caught and handled here. <2> If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__. <3> Otherwise, __Access Denied__ diff --git a/docs/modules/ROOT/pages/servlet/authentication/anonymous.adoc b/docs/modules/ROOT/pages/servlet/authentication/anonymous.adoc index b98e87e075..d30fd584bf 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/anonymous.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/anonymous.adoc @@ -96,7 +96,7 @@ If an `AccessDeniedException` is thrown, and the authentication is of an anonymo This is a necessary distinction, otherwise principals would always be deemed "authenticated" and never be given an opportunity to login via form, basic, digest or some other normal authentication mechanism. You will often see the `ROLE_ANONYMOUS` attribute in the above interceptor configuration replaced with `IS_AUTHENTICATED_ANONYMOUSLY`, which is effectively the same thing when defining access controls. -This is an example of the use of the `AuthenticatedVoter` which we will see in the <>. +This is an example of the use of the `AuthenticatedVoter` which we will see in the xref:servlet/authorization/architecture.adoc#authz-authenticated-voter[authorization chapter]. It uses an `AuthenticationTrustResolver` to process this particular configuration attribute and grant access to anonymous users. The `AuthenticatedVoter` approach is more powerful, since it allows you to differentiate between anonymous, remember-me and fully-authenticated users. If you don't need this functionality though, then you can stick with `ROLE_ANONYMOUS`, which will be processed by Spring Security's standard `RoleVoter`. diff --git a/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc index a688d41272..07b8afac58 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc @@ -2,14 +2,14 @@ = Servlet Authentication Architecture :figures: servlet/authentication/architecture -This discussion expands on <> to describe the main architectural components of Spring Security's used in Servlet authentication. -If you need concrete flows that explain how these pieces fit together, look at the <> specific sections. +This discussion expands on xref:servlet/architecture/index.adoc#servlet-architecture[] to describe the main architectural components of Spring Security's used in Servlet authentication. +If you need concrete flows that explain how these pieces fit together, look at the xref:servlet/authentication/index.adoc#servlet-authentication-mechanisms[Authentication Mechanism] specific sections. -* <> - The `SecurityContextHolder` is where Spring Security stores the details of who is <>. +* <> - The `SecurityContextHolder` is where Spring Security stores the details of who is xref:overview/features/authentication/index.adoc#authentication[authenticated]. * <> - is obtained from the `SecurityContextHolder` and contains the `Authentication` of the currently authenticated user. * <> - Can be the input to `AuthenticationManager` to provide the credentials a user has provided to authenticate or the current user from the `SecurityContext`. * <> - An authority that is granted to the principal on the `Authentication` (i.e. roles, scopes, etc.) -* <> - the API that defines how Spring Security's Filters perform <>. +* <> - the API that defines how Spring Security's Filters perform xref:overview/features/authentication/index.adoc#authentication[authentication]. * <> - the most common implementation of `AuthenticationManager`. * <> - used by `ProviderManager` to perform a specific type of authentication. * <> - used for requesting credentials from a client (i.e. redirecting to a log in page, sending a `WWW-Authenticate` response, etc.) @@ -26,7 +26,7 @@ It contains the <>. image::{figures}/securitycontextholder.png[] -The `SecurityContextHolder` is where Spring Security stores the details of who is <>. +The `SecurityContextHolder` is where Spring Security stores the details of who is xref:overview/features/authentication/index.adoc#authentication[authenticated]. Spring Security does not care how the `SecurityContextHolder` is populated. If it contains a value, then it is used as the currently authenticated user. @@ -63,7 +63,7 @@ Spring Security does not care what type of `Authentication` implementation is se Here we use `TestingAuthenticationToken` because it is very simple. A more common production scenario is `UsernamePasswordAuthenticationToken(userDetails, password, authorities)`. <3> Finally, we set the `SecurityContext` on the `SecurityContextHolder`. -Spring Security will use this information for <>. +Spring Security will use this information for xref:servlet/authorization/index.adoc#servlet-authorization[authorization]. If you wish to obtain information about the authenticated principal, you can do so by accessing the `SecurityContextHolder`. @@ -94,7 +94,7 @@ val authorities = authentication.authorities By default the `SecurityContextHolder` uses a `ThreadLocal` to store these details, which means that the `SecurityContext` is always available to methods in the same thread, even if the `SecurityContext` is not explicitly passed around as an argument to those methods. Using a `ThreadLocal` in this way is quite safe if care is taken to clear the thread after the present principal's request is processed. -Spring Security's <> ensures that the `SecurityContext` is always cleared. +Spring Security's xref:servlet/architecture/index.adoc#servlet-filterchainproxy[FilterChainProxy] ensures that the `SecurityContext` is always cleared. Some applications aren't entirely suitable for using a `ThreadLocal`, because of the specific way they work with threads. For example, a Swing client might want all threads in a Java Virtual Machine to use the same security context. @@ -125,7 +125,7 @@ The current `Authentication` can be obtained from the <>. +When authenticating with a username/password this is often an instance of xref:servlet/authentication/unpwd/user-details.adoc#servlet-authentication-userdetails[`UserDetails`]. * `credentials` - often a password. In many cases this will be cleared after the user is authenticated to ensure it is not leaked. * `authorities` - the <> are high level permissions the user is granted. @@ -141,7 +141,7 @@ A `GrantedAuthority` is, not surprisingly, an authority that is granted to the p Such authorities are usually "roles", such as `ROLE_ADMINISTRATOR` or `ROLE_HR_SUPERVISOR`. These roles are later on configured for web authorization, method authorization and domain object authorization. Other parts of Spring Security are capable of interpreting these authorities, and expect them to be present. -When using username/password based authentication ``GrantedAuthority``s are usually loaded by the <>. +When using username/password based authentication ``GrantedAuthority``s are usually loaded by the xref:servlet/authentication/unpwd/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`]. Usually the `GrantedAuthority` objects are application-wide permissions. They are not specific to a given domain object. @@ -151,8 +151,8 @@ Of course, Spring Security is expressly designed to handle this common requireme [[servlet-authentication-authenticationmanager]] == AuthenticationManager -{security-api-url}org/springframework/security/authentication/AuthenticationManager.html[`AuthenticationManager`] is the API that defines how Spring Security's Filters perform <>. -The <> that is returned is then set on the <> by the controller (i.e. <>) that invoked the `AuthenticationManager`. +{security-api-url}org/springframework/security/authentication/AuthenticationManager.html[`AuthenticationManager`] is the API that defines how Spring Security's Filters perform xref:overview/features/authentication/index.adoc#authentication[authentication]. +The <> that is returned is then set on the <> by the controller (i.e. xref:servlet/architecture/index.adoc#servlet-security-filters[Spring Security's ``Filters``s]) that invoked the `AuthenticationManager`. If you are not integrating with __Spring Security's ``Filters``s__ you can set the `SecurityContextHolder` directly and are not required to use an `AuthenticationManager`. While the implementation of `AuthenticationManager` could be anything, the most common implementation is <>. @@ -179,7 +179,7 @@ The parent can be any type of `AuthenticationManager`, but it is often an instan image::{figures}/providermanager-parent.png[] In fact, multiple `ProviderManager` instances might share the same parent `AuthenticationManager`. -This is somewhat common in scenarios where there are multiple <> instances that have some authentication in common (the shared parent `AuthenticationManager`), but also different authentication mechanisms (the different `ProviderManager` instances). +This is somewhat common in scenarios where there are multiple xref:servlet/architecture/index.adoc#servlet-securityfilterchain[`SecurityFilterChain`] instances that have some authentication in common (the shared parent `AuthenticationManager`), but also different authentication mechanisms (the different `ProviderManager` instances). image::{figures}/providermanagers-parent.png[] @@ -199,7 +199,7 @@ See the {security-api-url}org/springframework/security/authentication/ProviderMa Multiple {security-api-url}org/springframework/security/authentication/AuthenticationProvider.html[``AuthenticationProvider``s] can be injected into <>. Each `AuthenticationProvider` performs a specific type of authentication. -For example, <> supports username/password based authentication while `JwtAuthenticationProvider` supports authenticating a JWT token. +For example, xref:servlet/authentication/unpwd/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] supports username/password based authentication while `JwtAuthenticationProvider` supports authenticating a JWT token. [[servlet-authentication-authenticationentrypoint]] == Request Credentials with `AuthenticationEntryPoint` @@ -211,7 +211,7 @@ In these cases, Spring Security does not need to provide an HTTP response that r In other cases, a client will make an unauthenticated request to a resource that they are not authorized to access. In this case, an implementation of `AuthenticationEntryPoint` is used to request credentials from the client. -The `AuthenticationEntryPoint` implementation might perform a <>, respond with an <> header, etc. +The `AuthenticationEntryPoint` implementation might perform a xref:servlet/authentication/unpwd/form.adoc#servlet-authentication-form[redirect to a log in page], respond with an xref:servlet/authentication/unpwd/basic.adoc#servlet-authentication-basic[WWW-Authenticate] header, etc. @@ -230,7 +230,7 @@ image::{figures}/abstractauthenticationprocessingfilter.png[] image:{icondir}/number_1.png[] When the user submits their credentials, the `AbstractAuthenticationProcessingFilter` creates an <> from the `HttpServletRequest` to be authenticated. The type of `Authentication` created depends on the subclass of `AbstractAuthenticationProcessingFilter`. -For example, <> creates a `UsernamePasswordAuthenticationToken` from a __username__ and __password__ that are submitted in the `HttpServletRequest`. +For example, xref:servlet/authentication/unpwd/form.adoc#servlet-authentication-usernamepasswordauthenticationfilter[`UsernamePasswordAuthenticationFilter`] creates a `UsernamePasswordAuthenticationToken` from a __username__ and __password__ that are submitted in the `HttpServletRequest`. image:{icondir}/number_2.png[] Next, the <> is passed into the <> to be authenticated. diff --git a/docs/modules/ROOT/pages/servlet/authentication/cas.adoc b/docs/modules/ROOT/pages/servlet/authentication/cas.adoc index 5287dd6d92..35f3ad8c23 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/cas.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/cas.adoc @@ -92,7 +92,7 @@ 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. Each section builds upon the previous section. -A full CAS sample application can be found in the Spring Security <>. +A full CAS sample application can be found in the Spring Security xref:overview/samples.adoc#samples[Samples]. [[cas-st]] @@ -139,7 +139,7 @@ The following beans should be configured to commence the CAS authentication proc ---- For CAS to operate, the `ExceptionTranslationFilter` must have its `authenticationEntryPoint` property set to the `CasAuthenticationEntryPoint` bean. -This can easily be done using <> as is done in the example above. +This can easily be done using xref:servlet/appendix/namespace.adoc#nsa-http-entry-point-ref[entry-point-ref] as is done in the example above. The `CasAuthenticationEntryPoint` must refer to the `ServiceProperties` bean (discussed above), which provides the URL to the enterprise's CAS login server. This is where the user's browser will be redirected. @@ -337,7 +337,7 @@ An example configuration is shown below. [[cas-pt-client-sample]] ==== 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 CAS <> contains a working example in the `ProxyTicketSampleServlet`. +The CAS xref:overview/samples.adoc#samples[sample application] contains a working example in the `ProxyTicketSampleServlet`. Example code can be found below: ==== diff --git a/docs/modules/ROOT/pages/servlet/authentication/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/index.adoc index 9e976aff2b..3d8053ac1d 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/index.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/index.adoc @@ -1,8 +1,8 @@ [[servlet-authentication]] = Authentication -Spring Security provides comprehensive support for <>. -We start by discussing the overall <>. +Spring Security provides comprehensive support for xref:overview/features/authentication/index.adoc#authentication[]. +We start by discussing the overall xref:servlet/architecture/index.adoc#servlet-architecture[Servlet Authentication Architecture]. As you might expect, this section is more abstract describing the architecture without much discussion on how it applies to concrete flows. If you prefer, you can refer to <> for concrete ways in which users can authenticate. @@ -13,12 +13,12 @@ These sections focus on specific ways you may want to authenticate and point bac // FIXME: brief description -* <> - how to authenticate with a username/password -* <> - OAuth 2.0 Log In with OpenID Connect and non-standard OAuth 2.0 Login (i.e. GitHub) -* <> - SAML 2.0 Log In -* <> - Central Authentication Server (CAS) Support -* <> - how to remember a user past session expiration -* <> - authenticate with JAAS -* <> - OpenID Authentication (not to be confused with OpenID Connect) -* <> - authenticate with an external mechanism such as https://www.siteminder.com/[SiteMinder] or Java EE security but still use Spring Security for authorization and protection against common exploits. -* <> - X509 Authentication +* xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd[Username and Password] - how to authenticate with a username/password +* xref:servlet/oauth2/oauth2-login.adoc#oauth2login[OAuth 2.0 Login] - OAuth 2.0 Log In with OpenID Connect and non-standard OAuth 2.0 Login (i.e. GitHub) +* xref:servlet/saml2/index.adoc#servlet-saml2[SAML 2.0 Login] - SAML 2.0 Log In +* xref:servlet/authentication/cas.adoc#servlet-cas[Central Authentication Server (CAS)] - Central Authentication Server (CAS) Support +* xref:servlet/authentication/rememberme.adoc#servlet-rememberme[Remember Me] - how to remember a user past session expiration +* xref:servlet/authentication/jaas.adoc#servlet-jaas[JAAS Authentication] - authenticate with JAAS +* xref:servlet/authentication/openid.adoc#servlet-openid[OpenID] - OpenID Authentication (not to be confused with OpenID Connect) +* xref:servlet/authentication/preauth.adoc#servlet-preauth[] - authenticate with an external mechanism such as https://www.siteminder.com/[SiteMinder] or Java EE security but still use Spring Security for authorization and protection against common exploits. +* xref:servlet/authentication/x509.adoc#servlet-x509[X509 Authentication] - X509 Authentication diff --git a/docs/modules/ROOT/pages/servlet/authentication/jaas.adoc b/docs/modules/ROOT/pages/servlet/authentication/jaas.adoc index 76671ba579..9e241932be 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/jaas.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/jaas.adoc @@ -166,5 +166,5 @@ This means that the `Subject` can be accessed using: Subject subject = Subject.getSubject(AccessController.getContext()); ---- -This integration can easily be configured using the <> attribute. +This integration can easily be configured using the xref:servlet/appendix/namespace.adoc#nsa-http-jaas-api-provision[jaas-api-provision] attribute. This feature is useful when integrating with legacy or external API's that rely on the JAAS Subject being populated. diff --git a/docs/modules/ROOT/pages/servlet/authentication/logout.adoc b/docs/modules/ROOT/pages/servlet/authentication/logout.adoc index 9ad2110072..c529c9afed 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/logout.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/logout.adoc @@ -74,7 +74,7 @@ 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. +Please see the documentation for the xref:servlet/appendix/namespace.adoc#nsa-logout[ logout element] in the Spring Security XML Namespace section for further details. ==== Generally, in order to customize logout functionality, you can add @@ -108,7 +108,7 @@ Various implementations are provided: - {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. +Please see xref:servlet/authentication/rememberme.adoc#remember-me-impls[] 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. @@ -140,9 +140,9 @@ If not configured a status code 200 will be returned by default. == Further Logout-Related References - <> -- <> -- <> -- <> -- <> in section CSRF Caveats -- Section <> (CAS protocol) -- Documentation for the <> in the Spring Security XML Namespace section +- xref:servlet/test/mockmvc.adoc#test-logout[ Testing Logout] +- xref:servlet/integrations/servlet-api.adoc#servletapi-logout[ HttpServletRequest.logout()] +- xref:servlet/authentication/rememberme.adoc#remember-me-impls[] +- xref:servlet/exploits/csrf.adoc#servlet-considerations-csrf-logout[ Logging Out] in section CSRF Caveats +- Section xref:servlet/authentication/cas.adoc#cas-singlelogout[ Single Logout] (CAS protocol) +- Documentation for the xref:servlet/appendix/namespace.adoc#nsa-logout[ logout element] in the Spring Security XML Namespace section diff --git a/docs/modules/ROOT/pages/servlet/authentication/openid.adoc b/docs/modules/ROOT/pages/servlet/authentication/openid.adoc index 9ecc0b8246..9ada981dd5 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/openid.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/openid.adoc @@ -53,7 +53,7 @@ OpenIDAuthenticationToken token = List attributes = token.getAttributes(); ---- -We can obtain the `OpenIDAuthenticationToken` from the <>. +We can obtain the `OpenIDAuthenticationToken` from the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[]. The `OpenIDAttribute` contains the attribute type and the retrieved value (or values in the case of multi-valued attributes). 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. diff --git a/docs/modules/ROOT/pages/servlet/authentication/preauth.adoc b/docs/modules/ROOT/pages/servlet/authentication/preauth.adoc index 67da7fc4b9..b6ad3ad06e 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/preauth.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/preauth.adoc @@ -19,7 +19,7 @@ In some cases, the external mechanism may supply role/authority information for == 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. +You don't need to know about these classes if you want to use something like xref:servlet/authentication/x509.adoc#servlet-x509[X.509 authentication], as it already has a namespace configuration option which is simpler to use and get started with. If you need to use explicit bean configuration or are planning on writing your own implementation then an understanding of how the provided implementations work will be useful. You will find classes under the `org.springframework.security.web.authentication.preauth`. We just provide an outline here so you should consult the Javadoc and source where appropriate. @@ -86,14 +86,14 @@ The `PreAuthenticatedGrantedAuthoritiesUserDetailsService` class does this. Alternatively, it may delegate to a standard `UserDetailsService` via the `UserDetailsByNameServiceWrapper` implementation. === Http403ForbiddenEntryPoint -The <> 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. +The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] 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. It will be called if the user is rejected by the `AbstractPreAuthenticatedProcessingFilter` resulting in a null authentication. It always returns a `403`-forbidden response code if called. == Concrete Implementations -X.509 authentication is covered in its <>. +X.509 authentication is covered in its xref:servlet/authentication/x509.adoc#servlet-x509[own chapter]. Here we'll look at some classes which provide support for other pre-authenticated scenarios. @@ -139,7 +139,7 @@ A typical configuration using this filter would look like this: ---- -We've assumed here that the <> is being used for configuration. +We've assumed here that the xref:servlet/namespace/index.adoc#ns-config[security namespace] is being used for configuration. It's also assumed that you have added a `UserDetailsService` (called "userDetailsService") to your configuration to load the user's roles. diff --git a/docs/modules/ROOT/pages/servlet/authentication/rememberme.adoc b/docs/modules/ROOT/pages/servlet/authentication/rememberme.adoc index fd65a3ce62..e954e72ac7 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/rememberme.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/rememberme.adoc @@ -36,7 +36,7 @@ If a principal is aware a token has been captured, they can easily change their If more significant security is needed you should use the approach described in the next section. Alternatively, remember-me services should simply not be used at all. -If you are familiar with the topics discussed in the chapter on <>, you can enable remember-me authentication just by adding the `` element: +If you are familiar with the topics discussed in the chapter on xref:servlet/namespace/index.adoc#ns-config[namespace configuration], you can enable remember-me authentication just by adding the `` element: [source,xml] ---- diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc index b03716b085..098d04672a 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc @@ -11,14 +11,14 @@ First, we see the https://tools.ietf.org/html/rfc7235#section-4.1[WWW-Authentica .Sending WWW-Authenticate Header image::{figures}/basicauthenticationentrypoint.png[] -The figure builds off our <> diagram. +The figure builds off our xref:servlet/architecture/index.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource `/private` for which it is not authorized. -image:{icondir}/number_2.png[] Spring Security's <> indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. +image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. -image:{icondir}/number_3.png[] Since the user is not authenticated, <> initiates __Start Authentication__. -The configured <> is an instance of {security-api-url}org/springframework/security/web/authentication/www/BasicAuthenticationEntryPoint.html[`BasicAuthenticationEntryPoint`] which sends a WWW-Authenticate header. +image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture/index.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__. +The configured xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/web/authentication/www/BasicAuthenticationEntryPoint.html[`BasicAuthenticationEntryPoint`] which sends a WWW-Authenticate header. The `RequestCache` is typically a `NullRequestCache` that does not save the request since the client is capable of replaying the requests it originally requested. When a client receives the WWW-Authenticate header it knows it should retry with a username and password. @@ -28,17 +28,17 @@ Below is the flow for the username and password being processed. .Authenticating Username and Password image::{figures}/basicauthenticationfilter.png[] -The figure builds off our <> diagram. +The figure builds off our xref:servlet/architecture/index.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. -image:{icondir}/number_1.png[] When the user submits their username and password, the `BasicAuthenticationFilter` creates a `UsernamePasswordAuthenticationToken` which is a type of <> by extracting the username and password from the `HttpServletRequest`. +image:{icondir}/number_1.png[] When the user submits their username and password, the `BasicAuthenticationFilter` creates a `UsernamePasswordAuthenticationToken` which is a type of xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] by extracting the username and password from the `HttpServletRequest`. image:{icondir}/number_2.png[] Next, the `UsernamePasswordAuthenticationToken` is passed into the `AuthenticationManager` to be authenticated. -The details of what `AuthenticationManager` looks like depend on how the <>. +The details of what `AuthenticationManager` looks like depend on how the xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd-storage[user information is stored]. image:{icondir}/number_3.png[] If authentication fails, then __Failure__ -* The <> is cleared out. +* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[] is cleared out. * `RememberMeServices.loginFail` is invoked. If remember me is not configured, this is a no-op. // FIXME: link to rememberme @@ -46,7 +46,7 @@ If remember me is not configured, this is a no-op. image:{icondir}/number_4.png[] If authentication is successful, then __Success__. -* The <> is set on the <>. +* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[] is set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[]. * `RememberMeServices.loginSuccess` is invoked. If remember me is not configured, this is a no-op. // FIXME: link to rememberme diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/dao-authentication-provider.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/dao-authentication-provider.adoc index f7283e58ee..3d716d14c2 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/dao-authentication-provider.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/dao-authentication-provider.adoc @@ -2,21 +2,21 @@ = DaoAuthenticationProvider :figures: servlet/authentication/unpwd -{security-api-url}org/springframework/security/authentication/dao/DaoAuthenticationProvider.html[`DaoAuthenticationProvider`] is an <> implementation that leverages a <> and <> to authenticate a username and password. +{security-api-url}org/springframework/security/authentication/dao/DaoAuthenticationProvider.html[`DaoAuthenticationProvider`] is an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that leverages a xref:servlet/authentication/unpwd/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`] and xref:servlet/authentication/unpwd/password-encoder.adoc#servlet-authentication-password-storage[`PasswordEncoder`] to authenticate a username and password. Let's take a look at how `DaoAuthenticationProvider` works within Spring Security. -The figure explains details of how the <> in figures from <> works. +The figure explains details of how the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] in figures from xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd-input[Reading the Username & Password] works. .`DaoAuthenticationProvider` Usage image::{figures}/daoauthenticationprovider.png[] -image:{icondir}/number_1.png[] The authentication `Filter` from <> passes a `UsernamePasswordAuthenticationToken` to the `AuthenticationManager` which is implemented by <>. +image:{icondir}/number_1.png[] The authentication `Filter` from xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd-input[Reading the Username & Password] passes a `UsernamePasswordAuthenticationToken` to the `AuthenticationManager` which is implemented by xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`ProviderManager`]. -image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an <> of type `DaoAuthenticationProvider`. +image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[] of type `DaoAuthenticationProvider`. image:{icondir}/number_3.png[] `DaoAuthenticationProvider` looks up the `UserDetails` from the `UserDetailsService`. -image:{icondir}/number_4.png[] `DaoAuthenticationProvider` then uses the <> to validate the password on the `UserDetails` returned in the previous step. +image:{icondir}/number_4.png[] `DaoAuthenticationProvider` then uses the xref:servlet/authentication/unpwd/password-encoder.adoc#servlet-authentication-password-storage[`PasswordEncoder`] to validate the password on the `UserDetails` returned in the previous step. -image:{icondir}/number_5.png[] When authentication is successful, the <> that is returned is of type `UsernamePasswordAuthenticationToken` and has a principal that is the `UserDetails` returned by the configured `UserDetailsService`. -Ultimately, the returned `UsernamePasswordAuthenticationToken` will be set on the <> by the authentication `Filter`. +image:{icondir}/number_5.png[] When authentication is successful, the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `UsernamePasswordAuthenticationToken` and has a principal that is the `UserDetails` returned by the configured `UserDetailsService`. +Ultimately, the returned `UsernamePasswordAuthenticationToken` will be set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`. diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/digest.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/digest.adoc index 3503f3f21c..51763da932 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/digest.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/digest.adoc @@ -11,14 +11,14 @@ All of these storage formats are considered insecure. Instead, you should store credentials using a one way adaptive password hash (i.e. bCrypt, PBKDF2, SCrypt, etc) which is not supported by Digest Authentication. ==== -Digest Authentication attempts to solve many of the weaknesses of <>, specifically by ensuring credentials are never sent in clear text across the wire. +Digest Authentication attempts to solve many of the weaknesses of xref:servlet/authentication/unpwd/basic.adoc#servlet-authentication-basic[Basic authentication], specifically by ensuring credentials are never sent in clear text across the wire. Many https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Digest#Browser_compatibility[browsers support Digest Authentication]. The standard governing HTTP Digest Authentication is defined by https://tools.ietf.org/html/rfc2617[RFC 2617], which updates an earlier version of the Digest Authentication standard prescribed by https://tools.ietf.org/html/rfc2069[RFC 2069]. Most user agents implement RFC 2617. Spring Security's Digest Authentication support is compatible with the "`auth`" quality of protection (`qop`) prescribed by RFC 2617, which also provides backward compatibility with RFC 2069. Digest Authentication was seen as a more attractive option if you need to use unencrypted HTTP (i.e. no TLS/HTTPS) and wish to maximise security of the authentication process. -However, everyone should use <>. +However, everyone should use xref:overview/features/exploits/http.adoc#http[HTTPS]. Central to Digest Authentication is a "nonce". This is a value the server generates. @@ -34,7 +34,7 @@ key: A private key to prevent modification of the nonce token ---- ==== -You will need to ensure you <> insecure plain text <> using `NoOpPasswordEncoder`. +You will need to ensure you xref:overview/features/authentication/password-storage.adoc#authentication-password-storage-configuration[configure] insecure plain text xref:overview/features/authentication/password-storage.adoc#authentication-password-storage[Password Storage] using `NoOpPasswordEncoder`. The following provides an example of configuring Digest Authentication with Java Configuration: .Digest Authentication diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc index f22ebdd992..8ecd652912 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc @@ -12,13 +12,13 @@ First, we see how the user is redirected to the log in form. .Redirecting to the Log In Page image::{figures}/loginurlauthenticationentrypoint.png[] -The figure builds off our <> diagram. +The figure builds off our xref:servlet/architecture/index.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource `/private` for which it is not authorized. -image:{icondir}/number_2.png[] Spring Security's <> indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. +image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. -image:{icondir}/number_3.png[] Since the user is not authenticated, <> initiates __Start Authentication__ and sends a redirect to the log in page with the configured <>. +image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture/index.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__ and sends a redirect to the log in page with the configured xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`]. In most cases the `AuthenticationEntryPoint` is an instance of {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[`LoginUrlAuthenticationEntryPoint`]. image:{icondir}/number_4.png[] The browser will then request the log in page that it was redirected to. @@ -27,22 +27,22 @@ image:{icondir}/number_5.png[] Something within the application, must <>, so this diagram should look pretty similar. +The `UsernamePasswordAuthenticationFilter` extends xref:servlet/authentication/architecture/index.adoc#servlet-authentication-abstractprocessingfilter[], so this diagram should look pretty similar. .Authenticating Username and Password image::{figures}/usernamepasswordauthenticationfilter.png[] -The figure builds off our <> diagram. +The figure builds off our xref:servlet/architecture/index.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. -image:{icondir}/number_1.png[] When the user submits their username and password, the `UsernamePasswordAuthenticationFilter` creates a `UsernamePasswordAuthenticationToken` which is a type of <> by extracting the username and password from the `HttpServletRequest`. +image:{icondir}/number_1.png[] When the user submits their username and password, the `UsernamePasswordAuthenticationFilter` creates a `UsernamePasswordAuthenticationToken` which is a type of xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] by extracting the username and password from the `HttpServletRequest`. image:{icondir}/number_2.png[] Next, the `UsernamePasswordAuthenticationToken` is passed into the `AuthenticationManager` to be authenticated. -The details of what `AuthenticationManager` looks like depend on how the <>. +The details of what `AuthenticationManager` looks like depend on how the xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd-storage[user information is stored]. image:{icondir}/number_3.png[] If authentication fails, then __Failure__ -* The <> is cleared out. +* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[] is cleared out. * `RememberMeServices.loginFail` is invoked. If remember me is not configured, this is a no-op. // FIXME: link to rememberme @@ -53,13 +53,13 @@ image:{icondir}/number_4.png[] If authentication is successful, then __Success__ * `SessionAuthenticationStrategy` is notified of a new log in. // FIXME: Add link to SessionAuthenticationStrategy -* The <> is set on the <>. +* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[] is set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[]. // FIXME: link securitycontextpersistencefilter * `RememberMeServices.loginSuccess` is invoked. If remember me is not configured, this is a no-op. // FIXME: link to rememberme * `ApplicationEventPublisher` publishes an `InteractiveAuthenticationSuccessEvent`. -* The `AuthenticationSuccessHandler` is invoked. Typically this is a `SimpleUrlAuthenticationSuccessHandler` which will redirect to a request saved by <> when we redirect to the log in page. +* The `AuthenticationSuccessHandler` is invoked. Typically this is a `SimpleUrlAuthenticationSuccessHandler` which will redirect to a request saved by xref:servlet/architecture/index.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] when we redirect to the log in page. [[servlet-authentication-form-min]] Spring Security form log in is enabled by default. @@ -183,7 +183,7 @@ Below is a https://www.thymeleaf.org/[Thymeleaf] template that produces an HTML There are a few key points about the default HTML form: * The form should perform a `post` to `/login` -* The form will need to include a <> which is <> by Thymeleaf. +* The form will need to include a xref:servlet/exploits/csrf.adoc#servlet-csrf[CSRF Token] which is xref:servlet/exploits/csrf.adoc#servlet-csrf-include-form-auto[automatically included] by Thymeleaf. * The form should specify the username in a parameter named `username` * The form should specify the password in a parameter named `password` * If the HTTP parameter error is found, it indicates the user failed to provide a valid username / password diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/in-memory.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/in-memory.adoc index d5b178516b..b080006d50 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/in-memory.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/in-memory.adoc @@ -1,11 +1,11 @@ [[servlet-authentication-inmemory]] = In-Memory Authentication -Spring Security's `InMemoryUserDetailsManager` implements <> to provide support for username/password based authentication that is stored in memory. +Spring Security's `InMemoryUserDetailsManager` implements xref:servlet/authentication/unpwd/user-details-service.adoc#servlet-authentication-userdetailsservice[UserDetailsService] to provide support for username/password based authentication that is stored in memory. `InMemoryUserDetailsManager` provides management of `UserDetails` by implementing the `UserDetailsManager` interface. -`UserDetails` based authentication is used by Spring Security when it is configured to <> for authentication. +`UserDetails` based authentication is used by Spring Security when it is configured to xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd-input[accept a username/password] for authentication. -In this sample we use <> to encode the password of `password` and get the encoded password of `+{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW+`. +In this sample we use xref:overview/features/authentication/password-storage.adoc#authentication-password-storage-boot-cli[Spring Boot CLI] to encode the password of `password` and get the encoded password of `+{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW+`. .InMemoryUserDetailsManager Java Configuration ==== @@ -64,7 +64,7 @@ fun users(): UserDetailsService { The samples above store the passwords in a secure format, but leave a lot to be desired in terms of getting started experience. -In the sample below we leverage <> to ensure that the password stored in memory is protected. +In the sample below we leverage xref:overview/features/authentication/password-storage.adoc#authentication-password-storage-dep-getting-started[User.withDefaultPasswordEncoder] to ensure that the password stored in memory is protected. However, it does not protect against obtaining the password by decompiling the source code. For this reason, `User.withDefaultPasswordEncoder` should only be used for "getting started" and is not intended for production. @@ -114,7 +114,7 @@ fun users(): UserDetailsService { ==== There is no simple way to use `User.withDefaultPasswordEncoder` with XML based configuration. -For demos or just getting started, you can choose to prefix the password with `+{noop}+` to indicate <>. +For demos or just getting started, you can choose to prefix the password with `+{noop}+` to indicate xref:overview/features/authentication/password-storage.adoc#authentication-password-storage-dpe-format[no encoding should be used]. . `+{noop}+` XML Configuration ==== diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/index.adoc index e59dedf3da..c87a68217a 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/index.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/index.adoc @@ -11,17 +11,17 @@ As such, Spring Security provides comprehensive support for authenticating with Spring Security provides the following built in mechanisms for reading a username and password from the `HttpServletRequest`: -* <> -* <> -* <> +* xref:servlet/authentication/unpwd/form.adoc#servlet-authentication-form[Form Login] +* xref:servlet/authentication/unpwd/basic.adoc#servlet-authentication-basic[Basic Authentication] +* xref:servlet/authentication/unpwd/digest.adoc#servlet-authentication-digest[Digest Authentication] [[servlet-authentication-unpwd-storage]] == Storage Mechanisms Each of the supported mechanisms for reading a username and password can leverage any of the supported storage mechanisms: -* Simple Storage with <> -* Relational Databases with <> -* Custom data stores with <> -* LDAP storage with <> +* Simple Storage with xref:servlet/authentication/unpwd/in-memory.adoc#servlet-authentication-inmemory[] +* Relational Databases with xref:servlet/authentication/unpwd/jdbc.adoc#servlet-authentication-jdbc[] +* Custom data stores with xref:servlet/authentication/unpwd/user-details-service.adoc#servlet-authentication-userdetailsservice[] +* LDAP storage with xref:servlet/authentication/unpwd/ldap.adoc#servlet-authentication-ldap[] diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/jdbc.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/jdbc.adoc index 009e551512..75055a6a8f 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/jdbc.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/jdbc.adoc @@ -1,9 +1,9 @@ [[servlet-authentication-jdbc]] = JDBC Authentication -Spring Security's `JdbcDaoImpl` implements <> to provide support for username/password based authentication that is retrieved using JDBC. +Spring Security's `JdbcDaoImpl` implements xref:servlet/authentication/unpwd/user-details-service.adoc#servlet-authentication-userdetailsservice[UserDetailsService] to provide support for username/password based authentication that is retrieved using JDBC. `JdbcUserDetailsManager` extends `JdbcDaoImpl` to provide management of `UserDetails` through the `UserDetailsManager` interface. -`UserDetails` based authentication is used by Spring Security when it is configured to <> for authentication. +`UserDetails` based authentication is used by Spring Security when it is configured to xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd-input[accept a username/password] for authentication. In the following sections we will discuss: @@ -147,8 +147,8 @@ In a production environment, you will want to ensure you setup a connection to a [[servlet-authentication-jdbc-bean]] == JdbcUserDetailsManager Bean -In this sample we use <> to encode the password of `password` and get the encoded password of `+{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW+`. -See the <> section for more details about how to store passwords. +In this sample we use xref:overview/features/authentication/password-storage.adoc#authentication-password-storage-boot-cli[Spring Boot CLI] to encode the password of `password` and get the encoded password of `+{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW+`. +See the xref:overview/features/authentication/password-storage.adoc#authentication-password-storage[PasswordEncoder] section for more details about how to store passwords. .JdbcUserDetailsManager ==== diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/ldap.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/ldap.adoc index 57f456deb8..aa3d327c05 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/ldap.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/ldap.adoc @@ -4,7 +4,7 @@ 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. -Spring Security's LDAP based authentication is used by Spring Security when it is configured to <> for authentication. +Spring Security's LDAP based authentication is used by Spring Security when it is configured to xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd-input[accept a username/password] for authentication. However, despite leveraging a username/password for authentication it does not integrate using `UserDetailsService` because in <> the LDAP server does not return the password so the application cannot perform validation of the password. There are many different scenarios for how an LDAP server may be configured so Spring Security's LDAP provider is fully configurable. @@ -258,7 +258,7 @@ fun contextSource(container: UnboundIdContainer): ContextSource { [[servlet-authentication-ldap-authentication]] == Authentication -Spring Security's LDAP support does not use the <> because LDAP bind authentication does not allow clients to read the password or even a hashed version of the password. +Spring Security's LDAP support does not use the xref:servlet/authentication/unpwd/user-details-service.adoc#servlet-authentication-userdetailsservice[] because LDAP bind authentication does not allow clients to read the password or even a hashed version of the password. This means there is no way a password to be read and then authenticated by Spring Security. For this reason, LDAP support is implemented using the `LdapAuthenticator` interface. diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/password-encoder.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/password-encoder.adoc index 73f9e1f2e1..aa443acdf6 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/password-encoder.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/password-encoder.adoc @@ -1,5 +1,5 @@ [[servlet-authentication-password-storage]] = PasswordEncoder -Spring Security's servlet support storing passwords securely by integrating with <>. -Customizing the `PasswordEncoder` implementation used by Spring Security can be done by <>. +Spring Security's servlet support storing passwords securely by integrating with xref:overview/features/authentication/password-storage.adoc#authentication-password-storage[`PasswordEncoder`]. +Customizing the `PasswordEncoder` implementation used by Spring Security can be done by xref:overview/features/authentication/password-storage.adoc#authentication-password-storage-configuration[exposing a `PasswordEncoder` Bean]. diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details-service.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details-service.adoc index 9147cedbd2..92a1d3519f 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details-service.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details-service.adoc @@ -1,8 +1,8 @@ [[servlet-authentication-userdetailsservice]] = UserDetailsService -{security-api-url}org/springframework/security/core/userdetails/UserDetailsService.html[`UserDetailsService`] is used by <> for retrieving a username, password, and other attributes for authenticating with a username and password. -Spring Security provides <> and <> implementations of `UserDetailsService`. +{security-api-url}org/springframework/security/core/userdetails/UserDetailsService.html[`UserDetailsService`] is used by xref:servlet/authentication/unpwd/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] for retrieving a username, password, and other attributes for authenticating with a username and password. +Spring Security provides xref:servlet/authentication/unpwd/in-memory.adoc#servlet-authentication-inmemory[in-memory] and xref:servlet/authentication/unpwd/jdbc.adoc#servlet-authentication-jdbc[JDBC] implementations of `UserDetailsService`. You can define custom authentication by exposing a custom `UserDetailsService` as a bean. For example, the following will customize authentication assuming that `CustomUserDetailsService` implements `UserDetailsService`: diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details.adoc index d56626b37e..1b55e00a0b 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details.adoc @@ -1,5 +1,5 @@ [[servlet-authentication-userdetails]] = UserDetails -{security-api-url}org/springframework/security/core/userdetails/UserDetails.html[`UserDetails`] is returned by the <>. -The <> validates the `UserDetails` and then returns an <> that has a principal that is the `UserDetails` returned by the configured `UserDetailsService`. +{security-api-url}org/springframework/security/core/userdetails/UserDetails.html[`UserDetails`] is returned by the xref:servlet/authentication/unpwd/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`]. +The xref:servlet/authentication/unpwd/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] validates the `UserDetails` and then returns an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] that has a principal that is the `UserDetails` returned by the configured `UserDetailsService`. diff --git a/docs/modules/ROOT/pages/servlet/authorization/architecture.adoc b/docs/modules/ROOT/pages/servlet/authorization/architecture.adoc index 2be495a653..32363ae6c6 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/architecture.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/architecture.adoc @@ -6,7 +6,7 @@ [[authz-authorities]] == Authorities -<>, discusses how all `Authentication` implementations store a list of `GrantedAuthority` objects. +xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`], discusses how all `Authentication` implementations store a list of `GrantedAuthority` objects. These represent the authorities that have been granted to the principal. The `GrantedAuthority` objects are inserted into the `Authentication` object by the `AuthenticationManager` and are later read by ``AccessDecisionManager``s when making authorization decisions. diff --git a/docs/modules/ROOT/pages/servlet/authorization/authorize-requests.adoc b/docs/modules/ROOT/pages/servlet/authorization/authorize-requests.adoc index 055ab80c20..60f4d69251 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/authorize-requests.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/authorize-requests.adoc @@ -2,22 +2,22 @@ = Authorize HttpServletRequest with FilterSecurityInterceptor :figures: servlet/authorization -This section builds on <> by digging deeper into how <> works within Servlet based applications. +This section builds on xref:servlet/architecture/index.adoc#servlet-architecture[Servlet Architecture and Implementation] by digging deeper into how xref:servlet/authorization/index.adoc#servlet-authorization[authorization] works within Servlet based applications. -The {security-api-url}org/springframework/security/web/access/intercept/FilterSecurityInterceptor.html[`FilterSecurityInterceptor`] provides <> for ``HttpServletRequest``s. -It is inserted into the <> as one of the <>. +The {security-api-url}org/springframework/security/web/access/intercept/FilterSecurityInterceptor.html[`FilterSecurityInterceptor`] provides xref:servlet/authorization/index.adoc#servlet-authorization[authorization] for ``HttpServletRequest``s. +It is inserted into the xref:servlet/architecture/index.adoc#servlet-filterchainproxy[] as one of the xref:servlet/architecture/index.adoc#servlet-security-filters[]. .Authorize HttpServletRequest image::{figures}/filtersecurityinterceptor.png[] -* image:{icondir}/number_1.png[] First, the `FilterSecurityInterceptor` obtains an <> from the <>. +* image:{icondir}/number_1.png[] First, the `FilterSecurityInterceptor` obtains an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[] from the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[]. * image:{icondir}/number_2.png[] Second, `FilterSecurityInterceptor` creates a {security-api-url}org/springframework/security/web/FilterInvocation.html[`FilterInvocation`] from the `HttpServletRequest`, `HttpServletResponse`, and `FilterChain` that are passed into the `FilterSecurityInterceptor`. // FIXME: link to FilterInvocation * image:{icondir}/number_3.png[] Next, it passes the `FilterInvocation` to `SecurityMetadataSource` to get the ``ConfigAttribute``s. * image:{icondir}/number_4.png[] Finally, it passes the `Authentication`, `FilterInvocation`, and ``ConfigAttribute``s to the `AccessDecisionManager`. ** image:{icondir}/number_5.png[] If authorization is denied, an `AccessDeniedException` is thrown. -In this case the <> handles the `AccessDeniedException`. -** image:{icondir}/number_6.png[] If access is granted, `FilterSecurityInterceptor` continues with the <> which allows the application to process normally. +In this case the xref:servlet/architecture/index.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] handles the `AccessDeniedException`. +** image:{icondir}/number_6.png[] If access is granted, `FilterSecurityInterceptor` continues with the xref:servlet/architecture/index.adoc#servlet-filters-review[FilterChain] which allows the application to process normally. // configuration (xml/java) diff --git a/docs/modules/ROOT/pages/servlet/authorization/method-security.adoc b/docs/modules/ROOT/pages/servlet/authorization/method-security.adoc index ce49cdd45e..8b194fd275 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/method-security.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/method-security.adoc @@ -3,7 +3,7 @@ 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 <>. +From 3.0 you can also make use of new xref:servlet/authorization/expression-based.adoc#el-access[expression-based annotations]. 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. == EnableMethodSecurity diff --git a/docs/modules/ROOT/pages/servlet/authorization/secure-objects.adoc b/docs/modules/ROOT/pages/servlet/authorization/secure-objects.adoc index 5c24678b16..59d2f96e4b 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/secure-objects.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/secure-objects.adoc @@ -5,7 +5,7 @@ [[aop-alliance]] == AOP Alliance (MethodInvocation) Security Interceptor Prior to Spring Security 2.0, securing ``MethodInvocation``s needed quite a lot of boiler plate configuration. -Now the recommended approach for method security is to use <>. +Now the recommended approach for method security is to use xref:servlet/namespace/index.adoc#ns-method-security[namespace configuration]. This way the method security infrastructure beans are configured automatically for you so you don't really need to know about the implementation classes. We'll just provide a quick overview of the classes that are involved here. diff --git a/docs/modules/ROOT/pages/servlet/exploits/csrf.adoc b/docs/modules/ROOT/pages/servlet/exploits/csrf.adoc index 117de637a2..aaa47d88b7 100644 --- a/docs/modules/ROOT/pages/servlet/exploits/csrf.adoc +++ b/docs/modules/ROOT/pages/servlet/exploits/csrf.adoc @@ -1,7 +1,7 @@ [[servlet-csrf]] = Cross Site Request Forgery (CSRF) for Servlet Environments -This section discusses Spring Security's <> support for servlet environments. +This section discusses Spring Security's xref:overview/features/exploits/csrf.adoc#csrf[Cross Site Request Forgery (CSRF)] support for servlet environments. [[servlet-csrf-using]] == Using Spring Security CSRF Protection @@ -14,7 +14,7 @@ The steps to using Spring Security's CSRF protection are outlined below: [[servlet-csrf-idempotent]] === Use proper HTTP verbs The first step to protecting against CSRF attacks is to ensure your website uses proper HTTP verbs. -This is covered in detail in <>. +This is covered in detail in xref:overview/features/exploits/csrf.adoc#csrf-protection-idempotent[Safe Methods Must be Idempotent]. [[servlet-csrf-configure]] === Configure CSRF Protection @@ -105,7 +105,7 @@ If you do not need the ability to read the cookie with JavaScript directly, it i [[servlet-csrf-configure-disable]] ==== Disable CSRF Protection CSRF protection is enabled by default. -However, it is simple to disable CSRF protection if it <>. +However, it is simple to disable CSRF protection if it xref:overview/features/exploits/csrf.adoc#csrf-when[makes sense for your application]. The XML configuration below will disable CSRF protection. @@ -162,7 +162,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-csrf-include]] === Include the CSRF Token -In order for the <> to protect against CSRF attacks, we must include the actual CSRF token in the HTTP request. +In order for the xref:overview/features/exploits/csrf.adoc#csrf-protection-stp[synchronizer token pattern] to protect against CSRF attacks, we must include the actual CSRF token in the HTTP request. This must be included in a part of the request (i.e. form parameter, HTTP header, etc) that is not automatically included in the HTTP request by the browser. Spring Security's https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/csrf/CsrfFilter.html[CsrfFilter] exposes a https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/csrf/CsrfToken.html[CsrfToken] as an `HttpServletRequest` attribute named `_csrf`. @@ -196,7 +196,7 @@ This means that if you leverage https://docs.spring.io/spring/docs/current/sprin ===== csrfInput Tag If you are using JSPs, then you can use https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-view-jsp-formtaglib[Spring’s form tag library]. -However, if that is not an option, you can also easily include the token with the <> tag. +However, if that is not an option, you can also easily include the token with the xref:servlet/integrations/jsp-taglibs.adoc#taglibs-csrfinput[csrfInput] tag. [[servlet-csrf-include-form-attr]] ===== CsrfToken Request Attribute @@ -274,7 +274,7 @@ $(function () { [[servlet-csrf-include-ajax-meta-tag]] ====== csrfMeta tag -If you are using JSPs a simple way to write the CSRF token to the `meta` tags is by leveraging the <> tag. +If you are using JSPs a simple way to write the CSRF token to the `meta` tags is by leveraging the xref:servlet/integrations/jsp-taglibs.adoc#taglibs-csrfmeta[csrfMeta] tag. [[servlet-csrf-include-ajax-meta-attr]] ====== CsrfToken Request Attribute @@ -301,19 +301,19 @@ An example of doing this with a JSP is shown below: == CSRF Considerations There are a few special considerations to consider when implementing protection against CSRF attacks. This section discusses those considerations as it pertains to servlet environments. -Refer to <> for a more general discussion. +Refer to xref:overview/features/exploits/csrf.adoc#csrf-considerations[] for a more general discussion. [[servlet-considerations-csrf-login]] === Logging In -It is important to <> requests to protect against forging log in attempts. +It is important to xref:overview/features/exploits/csrf.adoc#csrf-considerations-login[require CSRF for log in] requests to protect against forging log in attempts. Spring Security's servlet support does this out of the box. [[servlet-considerations-csrf-logout]] === Logging Out -It is important to <> requests to protect against forging log out attempts. +It is important to xref:overview/features/exploits/csrf.adoc#csrf-considerations-logout[require CSRF for log out] requests to protect against forging log out attempts. If CSRF protection is enabled (default), Spring Security's `LogoutFilter` to only process HTTP POST. This ensures that log out requires a CSRF token and that a malicious user cannot forcibly log out your users. @@ -367,7 +367,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { By default Spring Security stores the CSRF token in the `HttpSession`. This can lead to a situation where the session expires which means there is not an expected CSRF token to validate against. -We've already discussed <> to session timeouts. +We've already discussed xref:overview/features/exploits/csrf.adoc#csrf-considerations-login[general solutions] to session timeouts. This section discusses the specifics of CSRF timeouts as it pertains to the servlet support. It is simple to change storage of the expected CSRF token to be in a cookie. @@ -375,13 +375,13 @@ For details, refer to the <> section. If a token does expire, you might want to customize how it is handled by specifying a custom `AccessDeniedHandler`. The custom `AccessDeniedHandler` can process the `InvalidCsrfTokenException` any way you like. -For an example of how to customize the `AccessDeniedHandler` refer to the provided links for both <> and {gh-url}/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpServerAccessDeniedHandlerTests.java#L64[Java configuration]. +For an example of how to customize the `AccessDeniedHandler` refer to the provided links for both xref:servlet/appendix/namespace.adoc#nsa-access-denied-handler[xml] and {gh-url}/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpServerAccessDeniedHandlerTests.java#L64[Java configuration]. // FIXME: We should add a custom AccessDeniedHandler section in the reference and update the links above [[servlet-csrf-considerations-multipart]] === Multipart (file upload) -We have <> how protecting multipart requests (file uploads) from CSRF attacks causes a https://en.wikipedia.org/wiki/Chicken_or_the_egg[chicken and the egg] problem. +We have xref:overview/features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] how protecting multipart requests (file uploads) from CSRF attacks causes a https://en.wikipedia.org/wiki/Chicken_or_the_egg[chicken and the egg] problem. This section discusses how to implement placing the CSRF token in the <> and <> within a servlet application. [NOTE] @@ -392,7 +392,7 @@ More information about using multipart forms with Spring can be found within the [[servlet-csrf-considerations-multipart-body]] ==== Place CSRF Token in the Body -We have <> the tradeoffs of placing the CSRF token in the body. +We have xref:overview/features/exploits/csrf.adoc#csrf-considerations-multipart-body[already discussed] the tradeoffs of placing the CSRF token in the body. In this section we will discuss how to configure Spring Security to read the CSRF from the body. In order to read the CSRF token from the body, the `MultipartFilter` is specified before the Spring Security filter. @@ -473,7 +473,7 @@ An example with a jsp is shown below [[servlet-csrf-considerations-override-method]] === HiddenHttpMethodFilter -We have <> the trade-offs of placing the CSRF token in the body. +We have xref:overview/features/exploits/csrf.adoc#csrf-considerations-multipart-body[already discussed] the trade-offs of placing the CSRF token in the body. In Spring's Servlet support, overriding the HTTP method is done using https://docs.spring.io/spring-framework/docs/5.2.x/javadoc-api/org/springframework/web/filter/reactive/HiddenHttpMethodFilter.html[HiddenHttpMethodFilter]. More information can be found in https://docs.spring.io/spring/docs/5.2.x/spring-framework-reference/web.html#mvc-rest-method-conversion[HTTP Method Conversion] section of the reference documentation. diff --git a/docs/modules/ROOT/pages/servlet/exploits/headers.adoc b/docs/modules/ROOT/pages/servlet/exploits/headers.adoc index e45c5c96c9..734271c6b0 100644 --- a/docs/modules/ROOT/pages/servlet/exploits/headers.adoc +++ b/docs/modules/ROOT/pages/servlet/exploits/headers.adoc @@ -1,13 +1,13 @@ [[servlet-headers]] = Security HTTP Response Headers -<> can be used to increase the security of web applications. +xref:overview/features/exploits/headers.adoc#headers[Security HTTP Response Headers] can be used to increase the security of web applications. This section is dedicated to servlet based support for Security HTTP Response Headers. [[servlet-headers-default]] == Default Security Headers -Spring Security provides a <> to provide secure defaults. +Spring Security provides a xref:overview/features/exploits/headers.adoc#headers-default[default set of Security HTTP Response Headers] to provide secure defaults. 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. @@ -71,7 +71,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { 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 is provided below: -If you are using Spring Security's Configuration the following will only add <>. +If you are using Spring Security's Configuration the following will only add xref:overview/features/exploits/headers.adoc#headers-cache-control[Cache Control]. .Customize Cache Control Headers ==== @@ -177,7 +177,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-cache-control]] == Cache Control -Spring Security includes <> headers by default. +Spring Security includes xref:overview/features/exploits/headers.adoc#headers-cache-control[Cache Control] headers by default. However, if you actually want to cache specific responses, your application can selectively invoke https://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. @@ -242,7 +242,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-content-type-options]] == Content Type Options -Spring Security includes <> headers by default. +Spring Security includes xref:overview/features/exploits/headers.adoc#headers-content-type-options[Content-Type] headers by default. However, you can disable it with: .Content Type Options Disabled @@ -300,7 +300,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-hsts]] == HTTP Strict Transport Security (HSTS) -Spring Security provides the <> header by default. +Spring Security provides the xref:overview/features/exploits/headers.adoc#headers-hsts[Strict Transport Security] header by default. However, you can customize the results explicitly. For example, the following is an example of explicitly providing HSTS: @@ -366,7 +366,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-hpkp]] == HTTP Public Key Pinning (HPKP) -For passivity reasons, Spring Security provides servlet support for <> but it is <>. +For passivity reasons, Spring Security provides servlet support for xref:overview/features/exploits/headers.adoc#headers-hpkp[HTTP Public Key Pinning] but it is xref:overview/features/exploits/headers.adoc#headers-hpkp-deprecated[no longer recommended]. You can enable HPKP headers with the following Configuration: @@ -437,7 +437,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-frame-options]] == X-Frame-Options -By default, Spring Security disables rendering within an iframe using <>. +By default, Spring Security disables rendering within an iframe using xref:overview/features/exploits/headers.adoc#headers-frame-options[X-Frame-Options]. You can customize frame options to use the same origin within a Configuration using the following: @@ -560,7 +560,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-csp]] == Content Security Policy (CSP) -Spring Security does not add <> by default, because a reasonable default is impossible to know without context of the application. +Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-csp[Content Security Policy] by default, because a reasonable default is impossible to know without context of the application. 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: @@ -693,7 +693,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-referrer]] == Referrer Policy -Spring Security does not add <> headers by default. +Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-referrer[Referrer Policy] headers by default. You can enable the Referrer Policy header using the configuration as shown below: .Referrer Policy @@ -753,7 +753,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-feature]] == Feature Policy -Spring Security does not add <> headers by default. +Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-feature[Feature Policy] headers by default. The following `Feature-Policy` header: .Feature-Policy Example @@ -819,7 +819,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-permissions]] == Permissions Policy -Spring Security does not add <> headers by default. +Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-permissions[Permissions Policy] headers by default. The following `Permissions-Policy` header: .Permissions-Policy Example @@ -889,7 +889,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-clear-site-data]] == Clear Site Data -Spring Security does not add <> headers by default. +Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-clear-site-data[Clear-Site-Data] headers by default. The following Clear-Site-Data header: .Clear-Site-Data Example diff --git a/docs/modules/ROOT/pages/servlet/exploits/http.adoc b/docs/modules/ROOT/pages/servlet/exploits/http.adoc index f9d8c3275b..56e1cd16ab 100644 --- a/docs/modules/ROOT/pages/servlet/exploits/http.adoc +++ b/docs/modules/ROOT/pages/servlet/exploits/http.adoc @@ -1,7 +1,7 @@ [[servlet-http]] = HTTP -All HTTP based communication should be protected <>. +All HTTP based communication should be protected xref:overview/features/exploits/http.adoc#http[using TLS]. Below you can find details around Servlet specific features that assist with HTTPS usage. @@ -69,9 +69,9 @@ The following XML configuration will redirect all HTTP requests to HTTPS [[servlet-hsts]] == Strict Transport Security -Spring Security provides support for <> and enables it by default. +Spring Security provides support for xref:servlet/exploits/headers.adoc#servlet-headers-hsts[Strict Transport Security] and enables it by default. [[servlet-http-proxy-server]] == Proxy Server Configuration -Spring Security <>. +Spring Security xref:overview/features/exploits/http.adoc#http-proxy-server[integrates with proxy servers]. diff --git a/docs/modules/ROOT/pages/servlet/exploits/index.adoc b/docs/modules/ROOT/pages/servlet/exploits/index.adoc index b19171f9a6..e08b8eb72e 100644 --- a/docs/modules/ROOT/pages/servlet/exploits/index.adoc +++ b/docs/modules/ROOT/pages/servlet/exploits/index.adoc @@ -1,7 +1,7 @@ [[servlet-exploits]] = Protection Against Exploits -This section discusses Servlet specific support for <>. +This section discusses Servlet specific support for xref:overview/features/exploits/index.adoc#exploits[Spring Security's protection against common exploits]. * xref:servlet/exploits/csrf.adoc[] * xref:servlet/exploits/headers.adoc[] diff --git a/docs/modules/ROOT/pages/servlet/hello/index.adoc b/docs/modules/ROOT/pages/servlet/hello/index.adoc index 21d8497ad7..756fee3381 100644 --- a/docs/modules/ROOT/pages/servlet/hello/index.adoc +++ b/docs/modules/ROOT/pages/servlet/hello/index.adoc @@ -12,7 +12,7 @@ For your convenience, you can download a minimal Spring Boot + Spring Security a [[servlet-hello-dependencies]] == Updating Dependencies -The only step you need to do is update the dependencies by using <> or <>. +The only step you need to do is update the dependencies by using xref:overview/getting-spring-security.adoc#getting-maven-boot[Maven] or xref:overview/getting-spring-security.adoc#getting-gradle-boot[Gradle]. [[servlet-hello-starting]] == Starting Hello Spring Security Boot diff --git a/docs/modules/ROOT/pages/servlet/integrations/concurrency.adoc b/docs/modules/ROOT/pages/servlet/integrations/concurrency.adoc index 202b51ab09..ecda96b1f0 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/concurrency.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/concurrency.adoc @@ -5,7 +5,7 @@ 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. Spring Security provides some infrastructure to help make this much easier for users. 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 <>. +In fact, this is what Spring Security builds on to integration with xref:servlet/integrations/servlet-api.adoc#servletapi-start-runnable[] and xref:servlet/integrations/mvc.adoc#mvc-async[]. == DelegatingSecurityContextRunnable @@ -44,7 +44,7 @@ fun run() { While very simple, it makes it seamless to transfer the SecurityContext from one Thread to another. This is important since, in most cases, the SecurityContextHolder acts on a per Thread basis. -For example, you might have used Spring Security's <> support to secure one of your services. +For example, you might have used Spring Security's xref:servlet/appendix/namespace.adoc#nsa-global-method-security[] support to secure one of your services. You can now easily transfer the `SecurityContext` of the current `Thread` to the `Thread` that invokes the secured service. An example of how you might do this can be found below: diff --git a/docs/modules/ROOT/pages/servlet/integrations/data.adoc b/docs/modules/ROOT/pages/servlet/integrations/data.adoc index 9212daeda3..c37f96d302 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/data.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/data.adoc @@ -67,4 +67,4 @@ interface MessageRepository : PagingAndSortingRepository { This checks to see if the `Authentication.getPrincipal().getId()` is equal to the recipient of the `Message`. Note that this example assumes you have customized the principal to be an Object that has an id property. -By exposing the `SecurityEvaluationContextExtension` bean, all of the <> are available within the Query. +By exposing the `SecurityEvaluationContextExtension` bean, all of the xref:servlet/authorization/expression-based.adoc#common-expressions[Common Security Expressions] are available within the Query. diff --git a/docs/modules/ROOT/pages/servlet/integrations/jackson.adoc b/docs/modules/ROOT/pages/servlet/integrations/jackson.adoc index 1f26756b08..e1c407763b 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/jackson.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/jackson.adoc @@ -42,6 +42,6 @@ The following Spring Security modules provide Jackson support: - spring-security-core (`CoreJackson2Module`) - spring-security-web (`WebJackson2Module`, `WebServletJackson2Module`, `WebServerJackson2Module`) -- <> (`OAuth2ClientJackson2Module`) +- xref:servlet/oauth2/oauth2-client.adoc#oauth2client[ spring-security-oauth2-client] (`OAuth2ClientJackson2Module`) - spring-security-cas (`CasJackson2Module`) ==== diff --git a/docs/modules/ROOT/pages/servlet/integrations/jsp-taglibs.adoc b/docs/modules/ROOT/pages/servlet/integrations/jsp-taglibs.adoc index 28a02e34ac..05bd1ea02c 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/jsp-taglibs.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/jsp-taglibs.adoc @@ -17,7 +17,7 @@ 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. ]. -The first approach uses a <>, specified in the `access` attribute of the tag. +The first approach uses a xref:servlet/authorization/expression-based.adoc#el-access-web[web-security expression], specified in the `access` attribute of the tag. The expression evaluation will be delegated to the `SecurityExpressionHandler` defined in the application context (you should have web expressions enabled in your `` namespace configuration to make sure this service is available). So, for example, you might have diff --git a/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc b/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc index ef2d849355..d6b126a0a7 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc @@ -492,14 +492,14 @@ The `SecurityContext` that is used to process the `Callable` is the `SecurityCon There is no automatic integration with a `DeferredResult` that is returned by controllers. This is because `DeferredResult` is processed by the users and thus there is no way of automatically integrating with it. -However, you can still use <> to provide transparent integration with Spring Security. +However, you can still use xref:servlet/integrations/concurrency.adoc#concurrency[Concurrency Support] to provide transparent integration with Spring Security. [[mvc-csrf]] == Spring MVC and CSRF Integration === Automatic Token Inclusion -Spring Security will automatically <> within forms that use the https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/view.html#view-jsp-formtaglib-formtag[Spring MVC form tag]. +Spring Security will automatically xref:servlet/exploits/csrf.adoc#servlet-csrf-include[include the CSRF Token] within forms that use the https://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: [source,xml] @@ -544,7 +544,7 @@ Will output HTML that is similar to the following: === 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. +By using xref:servlet/java-configuration/index.adoc#jc-hello-wsca[@EnableWebSecurity] you will automatically have this added to your Spring MVC configuration. If you use XML based configuration, you must add this yourself. Once `CsrfTokenArgumentResolver` is properly configured, you can expose the `CsrfToken` to your static HTML based application. diff --git a/docs/modules/ROOT/pages/servlet/integrations/servlet-api.adoc b/docs/modules/ROOT/pages/servlet/integrations/servlet-api.adoc index 6bc74d8af5..741a0629f7 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/servlet-api.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/servlet-api.adoc @@ -270,4 +270,4 @@ The following section describes the Servlet 3.1 methods that Spring Security int [[servletapi-change-session-id]] === HttpServletRequest#changeSessionId() -The https://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. +The https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#changeSessionId()[HttpServletRequest.changeSessionId()] is the default method for protecting against xref:servlet/authentication/session-management.adoc#ns-session-fixation[Session Fixation] attacks in Servlet 3.1 and higher. diff --git a/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc b/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc index 2ef0312334..75a94b9ba4 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc @@ -51,7 +51,7 @@ This will ensure that: <2> The SecurityContextHolder is populated with the user within the simpUser header attribute for any inbound request. <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 <> -Spring Security also provides <> support for securing WebSockets. +Spring Security also provides xref:servlet/appendix/namespace.adoc#nsa-websocket-security[XML Namespace] support for securing WebSockets. A comparable XML based configuration looks like the following: [source,xml] @@ -132,7 +132,7 @@ This will ensure that: <5> Any other message of type MESSAGE or SUBSCRIBE is rejected. Due to 6 we do not need this step, but it illustrates how one can match on specific message types. <6> Any other Message is rejected. This is a good idea to ensure that you do not miss any messages. -Spring Security also provides <> support for securing WebSockets. +Spring Security also provides xref:servlet/appendix/namespace.adoc#nsa-websocket-security[XML Namespace] support for securing WebSockets. A comparable XML based configuration looks like the following: [source,xml] @@ -246,7 +246,7 @@ Spring Security adds an additional layer of protection to provide https://en.wik [[websocket-sameorigin-csrf]] === Adding CSRF to Stomp Headers -By default Spring Security requires the <> in any CONNECT message type. +By default Spring Security requires the xref:overview/features/exploits/csrf.adoc#csrf[CSRF token] in any CONNECT message type. This ensures that only a site that has access to the CSRF token can connect. Since only the *Same Origin* can access the CSRF token, external domains are not allowed to make a connection. @@ -254,7 +254,7 @@ Typically we need to include the CSRF token in an HTTP header or an HTTP paramet However, SockJS does not allow for these options. Instead, we must include the token in the Stomp headers -Applications can <> by accessing the request attribute named _csrf. +Applications can xref:servlet/exploits/csrf.adoc#servlet-csrf-include[obtain a CSRF token] by accessing the request attribute named _csrf. For example, the following will allow accessing the `CsrfToken` in a JSP: [source,javascript] @@ -357,10 +357,10 @@ When using the fallback options we need to relax a few security constraints to a === 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. +By default Spring Security will xref:overview/features/exploits/headers.adoc#headers-frame-options[deny] the site from being framed to prevent Clickjacking attacks. To allow SockJS frame based transports to work, we need to configure Spring Security to allow the same origin to frame the content. -You can customize X-Frame-Options with the <> element. +You can customize X-Frame-Options with the xref:servlet/appendix/namespace.adoc#nsa-frame-options[frame-options] element. For example, the following will instruct Spring Security to use "X-Frame-Options: SAMEORIGIN" which allows iframes within the same domain: [source,xml] @@ -486,7 +486,7 @@ open class WebSecurityConfig : WebSecurityConfigurerAdapter() { ---- ==== -If we are using XML based configuration, we can use the <>. +If we are using XML based configuration, we can use the xref:servlet/appendix/namespace.adoc#nsa-csrf-request-matcher-ref[csrf@request-matcher-ref]. For example: [source,xml] diff --git a/docs/modules/ROOT/pages/servlet/java-configuration/index.adoc b/docs/modules/ROOT/pages/servlet/java-configuration/index.adoc index e54df9bd7f..54cf54aa74 100644 --- a/docs/modules/ROOT/pages/servlet/java-configuration/index.adoc +++ b/docs/modules/ROOT/pages/servlet/java-configuration/index.adoc @@ -5,7 +5,7 @@ General support for https://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/beans.html#beans-java[Java Configuration] was added to Spring Framework in Spring 3.1. Since Spring Security 3.2 there has been Spring Security Java Configuration support which enables users to easily configure Spring Security without the use of any XML. -If you are familiar with the <> then you should find quite a few similarities between it and the Security Java Configuration support. +If you are familiar with the xref:servlet/namespace/index.adoc#ns-config[] then you should find quite a few similarities between it and the Security Java Configuration support. NOTE: Spring Security provides https://github.com/spring-projects/spring-security-samples/tree/main/servlet/java-configuration[lots of sample applications] which demonstrate the use of Spring Security Java Configuration. diff --git a/docs/modules/ROOT/pages/servlet/namespace/index.adoc b/docs/modules/ROOT/pages/servlet/namespace/index.adoc index f0d98efc6c..488c3743c7 100644 --- a/docs/modules/ROOT/pages/servlet/namespace/index.adoc +++ b/docs/modules/ROOT/pages/servlet/namespace/index.adoc @@ -18,7 +18,7 @@ For example, adding the following element from the security namespace to an appl 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[<>].]. +footnote:[You can find out more about the use of the `ldap-server` element in the chapter on pass:specialcharacters,macros[xref:servlet/authentication/unpwd/ldap.adoc#servlet-authentication-ldap[]].]. 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 https://spring.io/tools[Eclipse IDE with Spring Tools] as it has special features for working with standard Spring namespaces. @@ -126,7 +126,7 @@ All you need to enable web security to begin with is 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[<>] for more details on how matches are actually performed.]. +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[xref:servlet/exploits/firewall.adoc#servlet-httpfirewall[`HttpFirewall`]] 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. @@ -134,7 +134,7 @@ The prefix "ROLE_" is a marker which indicates that a simple comparison with the 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 <> which is used.]. -In Spring Security 3.0, the attribute can also be populated with an <>. +In Spring Security 3.0, the attribute can also be populated with an xref:servlet/authorization/expression-based.adoc#el-access[EL expression]. [NOTE] @@ -192,13 +192,13 @@ Common problems like incorrect filter ordering are no longer an issue as the fil 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 <>. +You can find more detailed information on the beans that are created in the xref:servlet/appendix/namespace.adoc#appendix-namespace[namespace appendix]. 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. +See the section on xref:servlet/authentication/unpwd/in-memory.adoc#servlet-authentication-inmemory[in-memory authentication] 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. @@ -353,7 +353,7 @@ Remove any elements which create filters whose functionality you want to replace Note that you can't replace filters which are created by the use of the `` element itself - `SecurityContextPersistenceFilter`, `ExceptionTranslationFilter` or `FilterSecurityInterceptor`. 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. +An `AnonymousAuthenticationFilter` is added by default and unless you have xref:servlet/authentication/session-management.adoc#ns-session-fixation[session-fixation protection] disabled, a `SessionManagementFilter` will also be added to the filter chain. ==== @@ -365,7 +365,7 @@ If you're replacing a namespace filter which requires an authentication entry po == 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 <>. +From 3.0 you can also make use of new xref:servlet/authorization/expression-based.adoc#el-access[expression-based annotations]. 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-access-manager]] @@ -376,7 +376,7 @@ If you don't you can skip it and come back to it later, as this section is only 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 <>. +You can find out more about these in the chapter on xref:servlet/authorization/architecture.adoc#authz-arch[authorization]. [[ns-custom-access-mgr]] diff --git a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-client.adoc b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-client.adoc index 31ac01c9f9..affccf9e55 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-client.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-client.adoc @@ -74,7 +74,7 @@ class OAuth2ClientSecurityConfig : WebSecurityConfigurerAdapter() { In addition to the `HttpSecurity.oauth2Client()` DSL, XML configuration is also supported. -The following code shows the complete configuration options available in the <>: +The following code shows the complete configuration options available in the xref:servlet/appendix/namespace.adoc#nsa-oauth2-client[ security namespace]: .OAuth2 Client XML Configuration Options ==== @@ -392,7 +392,7 @@ The default implementation of `OAuth2AuthorizedClientService` is `InMemoryOAuth2 Alternatively, the JDBC implementation `JdbcOAuth2AuthorizedClientService` may be configured for persisting `OAuth2AuthorizedClient`(s) in a database. [NOTE] -`JdbcOAuth2AuthorizedClientService` depends on the table definition described in <>. +`JdbcOAuth2AuthorizedClientService` depends on the table definition described in xref:servlet/appendix/database-schema.adoc#dbschema-oauth2-client[ OAuth 2.0 Client Schema]. [[oauth2Client-authorized-manager-provider]] @@ -702,7 +702,7 @@ spring: [NOTE] `+{baseUrl}+` resolves to `+{baseScheme}://{baseHost}{basePort}{basePath}+` -Configuring the `redirect-uri` with `URI` template variables is especially useful when the OAuth 2.0 Client is running behind a <>. +Configuring the `redirect-uri` with `URI` template variables is especially useful when the OAuth 2.0 Client is running behind a xref:overview/features/exploits/http.adoc#http-proxy-server[Proxy Server]. This ensures that the `X-Forwarded-*` headers are used when expanding the `redirect-uri`. ==== Customizing the Authorization Request diff --git a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-login.adoc b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-login.adoc index db121281b0..b1f7b736a3 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-login.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-login.adoc @@ -40,10 +40,10 @@ The redirect URI is the path in the application that the end-user's user-agent i 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 <>. +The *_registrationId_* is a unique identifier for the xref:servlet/oauth2/oauth2-client.adoc#oauth2Client-client-registration[ClientRegistration]. -IMPORTANT: If the OAuth Client is running behind a proxy server, it is recommended to check <> to ensure the application is correctly configured. -Also, see the supported <> for `redirect-uri`. +IMPORTANT: If the OAuth Client is running behind a proxy server, it is recommended to check xref:overview/features/exploits/http.adoc#http-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured. +Also, see the supported xref:servlet/oauth2/oauth2-client.adoc#oauth2Client-auth-code-redirect-uri[ `URI` template variables] for `redirect-uri`. [[oauth2login-sample-application-config]] @@ -69,7 +69,7 @@ spring: .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. +<2> Following the base property prefix is the ID for the xref:servlet/oauth2/oauth2-client.adoc#oauth2Client-client-registration[ClientRegistration], such as google. ==== . Replace the values in the `client-id` and `client-secret` property with the OAuth 2.0 credentials you created earlier. @@ -93,7 +93,7 @@ At this point, the OAuth Client retrieves your email address and basic profile i [[oauth2login-boot-property-mappings]] == Spring Boot 2.x Property Mappings -The following table outlines the mapping of the Spring Boot 2.x OAuth Client properties to the <> properties. +The following table outlines the mapping of the Spring Boot 2.x OAuth Client properties to the xref:servlet/oauth2/oauth2-client.adoc#oauth2Client-client-registration[ClientRegistration] properties. |=== |Spring Boot 2.x |ClientRegistration @@ -737,7 +737,7 @@ class OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() { In addition to the `oauth2Login()` DSL, XML configuration is also supported. -The following code shows the complete configuration options available in the <>: +The following code shows the complete configuration options available in the xref:servlet/appendix/namespace.adoc#nsa-oauth2-login[ security namespace]: .OAuth2 Login XML Configuration Options ==== diff --git a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc index b4565b3300..c9e00338ec 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc @@ -18,19 +18,19 @@ Working samples for both {gh-samples-url}/servlet/spring-boot/java/oauth2/resour ==== Let's take a look at how Bearer Token Authentication works within Spring Security. -First, we see that, like <>, the https://tools.ietf.org/html/rfc7235#section-4.1[WWW-Authenticate] header is sent back to an unauthenticated client. +First, we see that, like xref:servlet/authentication/unpwd/basic.adoc#servlet-authentication-basic[Basic Authentication], the https://tools.ietf.org/html/rfc7235#section-4.1[WWW-Authenticate] header is sent back to an unauthenticated client. .Sending WWW-Authenticate Header image::{figures}/bearerauthenticationentrypoint.png[] -The figure above builds off our <> diagram. +The figure above builds off our xref:servlet/architecture/index.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource `/private` for which it is not authorized. -image:{icondir}/number_2.png[] Spring Security's <> indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. +image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. -image:{icondir}/number_3.png[] Since the user is not authenticated, <> initiates __Start Authentication__. -The configured <> is an instance of {security-api-url}org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPoint.html[`BearerTokenAuthenticationEntryPoint`] which sends a WWW-Authenticate header. +image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture/index.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__. +The configured xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPoint.html[`BearerTokenAuthenticationEntryPoint`] which sends a WWW-Authenticate header. The `RequestCache` is typically a `NullRequestCache` that does not save the request since the client is capable of replaying the requests it originally requested. When a client receives the `WWW-Authenticate: Bearer` header, it knows it should retry with a bearer token. @@ -40,21 +40,21 @@ Below is the flow for the bearer token being processed. .Authenticating Bearer Token image::{figures}/bearertokenauthenticationfilter.png[] -The figure builds off our <> diagram. +The figure builds off our xref:servlet/architecture/index.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. -image:{icondir}/number_1.png[] When the user submits their bearer token, the `BearerTokenAuthenticationFilter` creates a `BearerTokenAuthenticationToken` which is a type of <> by extracting the token from the `HttpServletRequest`. +image:{icondir}/number_1.png[] When the user submits their bearer token, the `BearerTokenAuthenticationFilter` creates a `BearerTokenAuthenticationToken` which is a type of xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] by extracting the token from the `HttpServletRequest`. image:{icondir}/number_2.png[] Next, the `HttpServletRequest` is passed to the `AuthenticationManagerResolver`, which selects the `AuthenticationManager`. The `BearerTokenAuthenticationToken` is passed into the `AuthenticationManager` to be authenticated. The details of what `AuthenticationManager` looks like depends on whether you're configured for <> or <>. image:{icondir}/number_3.png[] If authentication fails, then __Failure__ -* The <> is cleared out. +* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[] is cleared out. * The `AuthenticationEntryPoint` is invoked to trigger the WWW-Authenticate header to be sent again. image:{icondir}/number_4.png[] If authentication is successful, then __Success__. -* The <> is set on the <>. +* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[] is set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[]. * The `BearerTokenAuthenticationFilter` invokes `FilterChain.doFilter(request,response)` to continue with the rest of the application logic. [[oauth2resourceserver-jwt-minimaldependencies]] @@ -142,17 +142,17 @@ From here, consider jumping to: Next, let's see the architectural components that Spring Security uses to support https://tools.ietf.org/html/rfc7519[JWT] Authentication in servlet-based applications, like the one we just saw. -{security-api-url}org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationProvider.html[`JwtAuthenticationProvider`] is an <> implementation that leverages a <> and <> to authenticate a JWT. +{security-api-url}org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationProvider.html[`JwtAuthenticationProvider`] is an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that leverages a <> and <> to authenticate a JWT. Let's take a look at how `JwtAuthenticationProvider` works within Spring Security. -The figure explains details of how the <> in figures from <> works. +The figure explains details of how the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] in figures from <> works. .`JwtAuthenticationProvider` Usage image::{figures}/jwtauthenticationprovider.png[] -image:{icondir}/number_1.png[] The authentication `Filter` from <> passes a `BearerTokenAuthenticationToken` to the `AuthenticationManager` which is implemented by <>. +image:{icondir}/number_1.png[] The authentication `Filter` from <> passes a `BearerTokenAuthenticationToken` to the `AuthenticationManager` which is implemented by xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`ProviderManager`]. -image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an <> of type `JwtAuthenticationProvider`. +image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[] of type `JwtAuthenticationProvider`. [[oauth2resourceserver-jwt-architecture-jwtdecoder]] image:{icondir}/number_3.png[] `JwtAuthenticationProvider` decodes, verifies, and validates the `Jwt` using a <>. @@ -160,8 +160,8 @@ image:{icondir}/number_3.png[] `JwtAuthenticationProvider` decodes, verifies, an [[oauth2resourceserver-jwt-architecture-jwtauthenticationconverter]] image:{icondir}/number_4.png[] `JwtAuthenticationProvider` then uses the <> to convert the `Jwt` into a `Collection` of granted authorities. -image:{icondir}/number_5.png[] When authentication is successful, the <> that is returned is of type `JwtAuthenticationToken` and has a principal that is the `Jwt` returned by the configured `JwtDecoder`. -Ultimately, the returned `JwtAuthenticationToken` will be set on the <> by the authentication `Filter`. +image:{icondir}/number_5.png[] When authentication is successful, the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `JwtAuthenticationToken` and has a principal that is the `Jwt` returned by the configured `JwtDecoder`. +Ultimately, the returned `JwtAuthenticationToken` will be set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`. [[oauth2resourceserver-jwt-jwkseturi]] == Specifying the Authorization Server JWK Set Uri Directly @@ -1513,22 +1513,22 @@ From here, you may want to jump to: Next, let's see the architectural components that Spring Security uses to support https://tools.ietf.org/html/rfc7662[opaque token] Authentication in servlet-based applications, like the one we just saw. -{security-api-url}org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.html[`OpaqueTokenAuthenticationProvider`] is an <> implementation that leverages a <> to authenticate an opaque token. +{security-api-url}org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.html[`OpaqueTokenAuthenticationProvider`] is an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that leverages a <> to authenticate an opaque token. Let's take a look at how `OpaqueTokenAuthenticationProvider` works within Spring Security. -The figure explains details of how the <> in figures from <> works. +The figure explains details of how the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] in figures from <> works. .`OpaqueTokenAuthenticationProvider` Usage image::{figures}/opaquetokenauthenticationprovider.png[] -image:{icondir}/number_1.png[] The authentication `Filter` from <> passes a `BearerTokenAuthenticationToken` to the `AuthenticationManager` which is implemented by <>. +image:{icondir}/number_1.png[] The authentication `Filter` from <> passes a `BearerTokenAuthenticationToken` to the `AuthenticationManager` which is implemented by xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`ProviderManager`]. -image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an <> of type `OpaqueTokenAuthenticationProvider`. +image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[] of type `OpaqueTokenAuthenticationProvider`. [[oauth2resourceserver-opaque-architecture-introspector]] image:{icondir}/number_3.png[] `OpaqueTokenAuthenticationProvider` introspects the opaque token and adds granted authorities using an <>. -When authentication is successful, the <> that is returned is of type `BearerTokenAuthentication` and has a principal that is the `OAuth2AuthenticatedPrincipal` returned by the configured <>. -Ultimately, the returned `BearerTokenAuthentication` will be set on the <> by the authentication `Filter`. +When authentication is successful, the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `BearerTokenAuthentication` and has a principal that is the `OAuth2AuthenticatedPrincipal` returned by the configured <>. +Ultimately, the returned `BearerTokenAuthentication` will be set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`. [[oauth2resourceserver-opaque-attributes]] == Looking Up Attributes Post-Authentication @@ -3007,7 +3007,7 @@ fun rest(): RestTemplate { [NOTE] Unlike the {security-api-url}org/springframework/security/oauth2/client/OAuth2AuthorizedClientManager.html[OAuth 2.0 Authorized Client Manager], this filter interceptor makes no attempt to renew the token, should it be expired. -To obtain this level of support, please create an interceptor using the <>. +To obtain this level of support, please create an interceptor using the xref:servlet/oauth2/oauth2-client.adoc#oauth2client[OAuth 2.0 Authorized Client Manager]. [[oauth2resourceserver-bearertoken-failure]] == Bearer Token Failure @@ -3023,7 +3023,7 @@ HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer error_code="invalid_token", error_description="Unsupported algorithm of none", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1" ---- -Additionally, it is published as an `AuthenticationFailureBadCredentialsEvent`, which you can <> like so: +Additionally, it is published as an `AuthenticationFailureBadCredentialsEvent`, which you can xref:servlet/authentication/events.adoc#servlet-events[listen for in your application] like so: ==== .Java diff --git a/docs/modules/ROOT/pages/servlet/saml2/index.adoc b/docs/modules/ROOT/pages/servlet/saml2/index.adoc index ec19543759..50cf0a5182 100644 --- a/docs/modules/ROOT/pages/servlet/saml2/index.adoc +++ b/docs/modules/ROOT/pages/servlet/saml2/index.adoc @@ -24,20 +24,20 @@ A working sample for {gh-samples-url}/servlet/spring-boot/java/saml2-login[SAML ==== Let's take a look at how SAML 2.0 Relying Party Authentication works within Spring Security. -First, we see that, like <>, Spring Security takes the user to a third-party for performing authentication. +First, we see that, like xref:servlet/oauth2/oauth2-login.adoc#oauth2login[ OAuth 2.0 Login], Spring Security takes the user to a third-party for performing authentication. It does this through a series of redirects. .Redirecting to Asserting Party Authentication image::{figures}/saml2webssoauthenticationrequestfilter.png[] -The figure above builds off our <> and <> diagrams: +The figure above builds off our xref:servlet/architecture/index.adoc#servlet-securityfilterchain[`SecurityFilterChain`] and xref:servlet/authentication/architecture/index.adoc#servlet-authentication-abstractprocessingfilter[ `AbstractAuthenticationProcessingFilter`] diagrams: image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource `/private` for which it is not authorized. -image:{icondir}/number_2.png[] Spring Security's <> indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. +image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. -image:{icondir}/number_3.png[] Since the user lacks authorization, the <> initiates __Start Authentication__. -The configured <> is an instance of {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[`LoginUrlAuthenticationEntryPoint`] which redirects to <` generating endpoint>>, `Saml2WebSsoAuthenticationRequestFilter`. +image:{icondir}/number_3.png[] Since the user lacks authorization, the xref:servlet/architecture/index.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__. +The configured xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[`LoginUrlAuthenticationEntryPoint`] which redirects to <` generating endpoint>>, `Saml2WebSsoAuthenticationRequestFilter`. Or, if you've <>, it will first redirect to a picker page. image:{icondir}/number_4.png[] Next, the `Saml2WebSsoAuthenticationRequestFilter` creates, signs, serializes, and encodes a `` using its configured <>. @@ -52,23 +52,23 @@ image:{icondir}/number_6.png[] The browser then POSTs the `` to .Authenticating a `` image::{figures}/saml2webssoauthenticationfilter.png[] -The figure builds off our <> diagram. +The figure builds off our xref:servlet/architecture/index.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. image:{icondir}/number_1.png[] When the browser submits a `` to the application, it <>. This filter calls its configured `AuthenticationConverter` to create a `Saml2AuthenticationToken` by extracting the response from the `HttpServletRequest`. This converter additionally resolves the <> and supplies it to `Saml2AuthenticationToken`. -image:{icondir}/number_2.png[] Next, the filter passes the token to its configured <>. +image:{icondir}/number_2.png[] Next, the filter passes the token to its configured xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`AuthenticationManager`]. By default, it will use the <>. image:{icondir}/number_3.png[] If authentication fails, then __Failure__ -* The <> is cleared out. -* The <> is invoked to restart the authentication process. +* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[ `SecurityContextHolder`] is cleared out. +* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is invoked to restart the authentication process. image:{icondir}/number_4.png[] If authentication is successful, then __Success__. -* The <> is set on the <>. +* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[ `Authentication`] is set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[ `SecurityContextHolder`]. * The `Saml2WebSsoAuthenticationFilter` invokes `FilterChain#doFilter(request,response)` to continue with the rest of the application logic. [[servlet-saml2login-minimaldependencies]] @@ -169,9 +169,9 @@ image:{figures}/opensamlauthenticationprovider.png[] This figure builds off of the <>. -image:{icondir}/number_1.png[] The `Saml2WebSsoAuthenticationFilter` formulates the `Saml2AuthenticationToken` and invokes the <>. +image:{icondir}/number_1.png[] The `Saml2WebSsoAuthenticationFilter` formulates the `Saml2AuthenticationToken` and invokes the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`AuthenticationManager`]. -image:{icondir}/number_2.png[] The <> invokes the OpenSAML authentication provider. +image:{icondir}/number_2.png[] The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`AuthenticationManager`] invokes the OpenSAML authentication provider. image:{icondir}/number_3.png[] The authentication provider deserializes the response into an OpenSAML `Response` and checks its signature. If the signature is invalid, authentication fails. @@ -1267,7 +1267,7 @@ open class SecurityConfig : WebSecurityConfigurerAdapter() { ---- ==== <1> First, call the default converter, which extracts attributes and authorities from the response -<2> Second, call the <> using the relevant information +<2> Second, call the xref:servlet/authentication/unpwd/user-details-service.adoc#servlet-authentication-userdetailsservice[ `UserDetailsService`] using the relevant information <3> Third, return a custom authentication that includes the user details [NOTE] diff --git a/docs/modules/ROOT/pages/servlet/test/mockmvc.adoc b/docs/modules/ROOT/pages/servlet/test/mockmvc.adoc index 5a58d3118d..dd712ec6cf 100644 --- a/docs/modules/ROOT/pages/servlet/test/mockmvc.adoc +++ b/docs/modules/ROOT/pages/servlet/test/mockmvc.adoc @@ -480,7 +480,7 @@ fun foo(user: Principal): String { ---- ==== -There's nothing OAuth2-specific about it, so you will likely be able to simply <> and be fine. +There's nothing OAuth2-specific about it, so you will likely be able to simply xref:servlet/test/method.adoc#test-method-withmockuser[use `@WithMockUser`] and be fine. But, in cases where your controllers are bound to some aspect of Spring Security's OAuth 2.0 support, like the following: @@ -583,7 +583,7 @@ assertThat(user.authorities).containsExactly(SimpleGrantedAuthority("SCOPE_read" ---- ==== -Spring Security does the necessary work to make sure that the `OidcUser` instance is available for <>. +Spring Security does the necessary work to make sure that the `OidcUser` instance is available for xref:servlet/integrations/mvc.adoc#mvc-authentication-principal[the `@AuthenticationPrincipal` annotation]. Further, it also links that `OidcUser` to a simple instance of `OAuth2AuthorizedClient` that it deposits into an mock `OAuth2AuthorizedClientRepository`. This can be handy if your tests <>.. @@ -806,7 +806,7 @@ assertThat(user.authorities).containsExactly(SimpleGrantedAuthority("SCOPE_read" ---- ==== -Spring Security does the necessary work to make sure that the `OAuth2User` instance is available for <>. +Spring Security does the necessary work to make sure that the `OAuth2User` instance is available for xref:servlet/integrations/mvc.adoc#mvc-authentication-principal[the `@AuthenticationPrincipal` annotation]. Further, it also links that `OAuth2User` to a simple instance of `OAuth2AuthorizedClient` that it deposits in a mock `OAuth2AuthorizedClientRepository`. This can be handy if your tests <>. From 29a76691017184f9bccef14352a25c7c252420fa Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 30 Jul 2021 17:02:44 -0500 Subject: [PATCH 09/34] rg "xref:\S+?#\S+\[\]" docs/modules -l -g "*.adoc" | while read adoc_file_to_replace; do echo "Replacing $adoc_file_to_replace" for id_file in build/ids/*.id; do id=$(basename $id_file | sed 's/\.id$//') xref_page=$(cat $id_file) if [[ "$adoc_file_to_replace" -ef "./docs/modules/ROOT/pages/$xref_page" ]] then echo " - Skipping same page refid $id " else text_file=$(echo $id_file | sed 's/\.id$/.text/') default_text=$(cat $text_file) sed -i -E "s%xref:${xref_page}#${id}\[\]%xref:${xref_page}#${id}[$default_text]%g" $adoc_file_to_replace fi done done --- .../ROOT/pages/overview/getting-spring-security.adoc | 10 +++++----- docs/modules/ROOT/pages/reactive/exploits/csrf.adoc | 2 +- docs/modules/ROOT/pages/reactive/index.adoc | 2 +- docs/modules/ROOT/pages/reactive/method.adoc | 2 +- docs/modules/ROOT/pages/reactive/test.adoc | 2 +- .../modules/ROOT/pages/servlet/appendix/namespace.adoc | 4 ++-- .../modules/ROOT/pages/servlet/architecture/index.adoc | 4 ++-- .../servlet/authentication/architecture/index.adoc | 2 +- .../ROOT/pages/servlet/authentication/index.adoc | 4 ++-- .../ROOT/pages/servlet/authentication/logout.adoc | 4 ++-- .../ROOT/pages/servlet/authentication/openid.adoc | 2 +- .../ROOT/pages/servlet/authentication/unpwd/basic.adoc | 4 ++-- .../unpwd/dao-authentication-provider.adoc | 2 +- .../ROOT/pages/servlet/authentication/unpwd/form.adoc | 6 +++--- .../ROOT/pages/servlet/authentication/unpwd/index.adoc | 8 ++++---- .../ROOT/pages/servlet/authentication/unpwd/ldap.adoc | 2 +- .../servlet/authorization/authorize-requests.adoc | 4 ++-- docs/modules/ROOT/pages/servlet/exploits/csrf.adoc | 2 +- .../ROOT/pages/servlet/integrations/concurrency.adoc | 4 ++-- .../ROOT/pages/servlet/java-configuration/index.adoc | 2 +- docs/modules/ROOT/pages/servlet/namespace/index.adoc | 2 +- .../pages/servlet/oauth2/oauth2-resourceserver.adoc | 8 ++++---- 22 files changed, 41 insertions(+), 41 deletions(-) diff --git a/docs/modules/ROOT/pages/overview/getting-spring-security.adoc b/docs/modules/ROOT/pages/overview/getting-spring-security.adoc index a9b2eef369..bb5c7b9f1c 100644 --- a/docs/modules/ROOT/pages/overview/getting-spring-security.adoc +++ b/docs/modules/ROOT/pages/overview/getting-spring-security.adoc @@ -2,7 +2,7 @@ = Getting Spring Security This section discusses all you need to know about getting the Spring Security binaries. -See xref:overview/community.adoc#community-source[] for how to obtain the source code. +See xref:overview/community.adoc#community-source[Source Code] for how to obtain the source code. == Release Numbering @@ -72,7 +72,7 @@ You can do so by adding a Maven property, as the following example shows: ---- ==== -If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:overview/modules.adoc#modules[]. +If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:overview/modules.adoc#modules[Project Modules and Dependencies]. [[getting-maven-no-boot]] === Maven Without Spring Boot @@ -118,7 +118,7 @@ A minimal Spring Security Maven set of dependencies typically looks like the fol ---- ==== -If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:overview/modules.adoc#modules[]. +If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:overview/modules.adoc#modules[Project Modules and Dependencies]. Spring Security builds against Spring Framework {spring-core-version} but should generally work with any newer version of Spring Framework 5.x. Many users are likely to run afoul of the fact that Spring Security's transitive dependencies resolve Spring Framework {spring-core-version}, which can cause strange classpath problems. @@ -236,7 +236,7 @@ ext['spring.version']='{spring-core-version}' ---- ==== -If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:overview/modules.adoc#modules[]. +If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:overview/modules.adoc#modules[Project Modules and Dependencies]. === Gradle Without Spring Boot @@ -274,7 +274,7 @@ dependencies { ---- ==== -If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:overview/modules.adoc#modules[]. +If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:overview/modules.adoc#modules[Project Modules and Dependencies]. Spring Security builds against Spring Framework {spring-core-version} but should generally work with any newer version of Spring Framework 5.x. Many users are likely to run afoul of the fact that Spring Security's transitive dependencies resolve Spring Framework {spring-core-version}, which can cause strange classpath problems. diff --git a/docs/modules/ROOT/pages/reactive/exploits/csrf.adoc b/docs/modules/ROOT/pages/reactive/exploits/csrf.adoc index d596977b40..a626b99554 100644 --- a/docs/modules/ROOT/pages/reactive/exploits/csrf.adoc +++ b/docs/modules/ROOT/pages/reactive/exploits/csrf.adoc @@ -272,7 +272,7 @@ An example of doing this with Thymeleaf is shown below: == CSRF Considerations There are a few special considerations to consider when implementing protection against CSRF attacks. This section discusses those considerations as it pertains to WebFlux environments. -Refer to xref:overview/features/exploits/csrf.adoc#csrf-considerations[] for a more general discussion. +Refer to xref:overview/features/exploits/csrf.adoc#csrf-considerations[CSRF Considerations] for a more general discussion. [[webflux-considerations-csrf-login]] diff --git a/docs/modules/ROOT/pages/reactive/index.adoc b/docs/modules/ROOT/pages/reactive/index.adoc index 8791a47c32..812fa7adef 100644 --- a/docs/modules/ROOT/pages/reactive/index.adoc +++ b/docs/modules/ROOT/pages/reactive/index.adoc @@ -1,4 +1,4 @@ = Reactive Applications -Reactive applications work very differently than xref:servlet/index.adoc#servlet-applications[]. +Reactive applications work very differently than xref:servlet/index.adoc#servlet-applications[Servlet Applications]. This section discusses how Spring Security works with reactive applications which are typically written using Spring's WebFlux. diff --git a/docs/modules/ROOT/pages/reactive/method.adoc b/docs/modules/ROOT/pages/reactive/method.adoc index fbb07d4d7e..46502fe1a3 100644 --- a/docs/modules/ROOT/pages/reactive/method.adoc +++ b/docs/modules/ROOT/pages/reactive/method.adoc @@ -162,7 +162,7 @@ It is important to note that any of the expressions in standard method security However, at this time we only support return type of `Boolean` or `boolean` of the expression. This means that the expression must not block. -When integrating with xref:reactive/webflux.adoc#jc-webflux[], the Reactor Context is automatically established by Spring Security according to the authenticated user. +When integrating with xref:reactive/webflux.adoc#jc-webflux[WebFlux Security], the Reactor Context is automatically established by Spring Security according to the authenticated user. ==== .Java diff --git a/docs/modules/ROOT/pages/reactive/test.adoc b/docs/modules/ROOT/pages/reactive/test.adoc index 632e58e18f..183558b136 100644 --- a/docs/modules/ROOT/pages/reactive/test.adoc +++ b/docs/modules/ROOT/pages/reactive/test.adoc @@ -4,7 +4,7 @@ [[test-erms]] == Testing Reactive Method Security -For example, we can test our example from xref:reactive/method.adoc#jc-erms[] using the same setup and annotations we did in xref:servlet/test/method.adoc#test-method[]. +For example, we can test our example from xref:reactive/method.adoc#jc-erms[EnableReactiveMethodSecurity] using the same setup and annotations we did in xref:servlet/test/method.adoc#test-method[Testing Method Security]. Here is a minimal sample of what we can do: ==== diff --git a/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc b/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc index ec3d417bde..00ef0b7772 100644 --- a/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc +++ b/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc @@ -1616,7 +1616,7 @@ By default, an instance of `PortMapperImpl` will be added to the configuration f This element can optionally be used to override the default mappings which that class defines. Each child `` element defines a pair of HTTP:HTTPS ports. The default mappings are 80:443 and 8080:8443. -An example of overriding these can be found in xref:servlet/exploits/http.adoc#servlet-http-redirect[]. +An example of overriding these can be found in xref:servlet/exploits/http.adoc#servlet-http-redirect[Redirect to HTTPS]. [[nsa-port-mappings-parents]] @@ -2278,7 +2278,7 @@ select username, password, enabled from users where username = ? [[nsa-password-encoder]] === -Authentication providers can optionally be configured to use a password encoder as described in the xref:overview/features/authentication/password-storage.adoc#authentication-password-storage[]. +Authentication providers can optionally be configured to use a password encoder as described in the xref:overview/features/authentication/password-storage.adoc#authentication-password-storage[Password Storage]. This will result in the bean being injected with the appropriate `PasswordEncoder` instance. diff --git a/docs/modules/ROOT/pages/servlet/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/architecture/index.adoc index 61fcb9f401..0ddea5d236 100644 --- a/docs/modules/ROOT/pages/servlet/architecture/index.adoc +++ b/docs/modules/ROOT/pages/servlet/architecture/index.adoc @@ -3,7 +3,7 @@ :figures: servlet/architecture This section discusses Spring Security's high level architecture within Servlet based applications. -We build on this high level understanding within xref:servlet/authentication/index.adoc#servlet-authentication[], xref:servlet/authorization/index.adoc#servlet-authorization[], xref:servlet/exploits/index.adoc#servlet-exploits[] sections of the reference. +We build on this high level understanding within xref:servlet/authentication/index.adoc#servlet-authentication[Authentication], xref:servlet/authorization/index.adoc#servlet-authorization[Authorization], xref:servlet/exploits/index.adoc#servlet-exploits[Protection Against Exploits] sections of the reference. // FIXME: Add links to other sections of architecture [[servlet-filters-review]] @@ -211,7 +211,7 @@ image::{figures}/exceptiontranslationfilter.png[] * image:{icondir}/number_1.png[] First, the `ExceptionTranslationFilter` invokes `FilterChain.doFilter(request, response)` to invoke the rest of the application. * image:{icondir}/number_2.png[] If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__. -** The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[] is cleared out. +** The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out. ** The `HttpServletRequest` is saved in the {security-api-url}org/springframework/security/web/savedrequest/RequestCache.html[`RequestCache`]. When the user successfully authenticates, the `RequestCache` is used to replay the original request. // FIXME: add link to authentication success diff --git a/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc index 07b8afac58..441b21fd74 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc @@ -2,7 +2,7 @@ = Servlet Authentication Architecture :figures: servlet/authentication/architecture -This discussion expands on xref:servlet/architecture/index.adoc#servlet-architecture[] to describe the main architectural components of Spring Security's used in Servlet authentication. +This discussion expands on xref:servlet/architecture/index.adoc#servlet-architecture[Servlet Security: The Big Picture] to describe the main architectural components of Spring Security's used in Servlet authentication. If you need concrete flows that explain how these pieces fit together, look at the xref:servlet/authentication/index.adoc#servlet-authentication-mechanisms[Authentication Mechanism] specific sections. * <> - The `SecurityContextHolder` is where Spring Security stores the details of who is xref:overview/features/authentication/index.adoc#authentication[authenticated]. diff --git a/docs/modules/ROOT/pages/servlet/authentication/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/index.adoc index 3d8053ac1d..a51390e95d 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/index.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/index.adoc @@ -1,7 +1,7 @@ [[servlet-authentication]] = Authentication -Spring Security provides comprehensive support for xref:overview/features/authentication/index.adoc#authentication[]. +Spring Security provides comprehensive support for xref:overview/features/authentication/index.adoc#authentication[Authentication]. We start by discussing the overall xref:servlet/architecture/index.adoc#servlet-architecture[Servlet Authentication Architecture]. As you might expect, this section is more abstract describing the architecture without much discussion on how it applies to concrete flows. @@ -20,5 +20,5 @@ These sections focus on specific ways you may want to authenticate and point bac * xref:servlet/authentication/rememberme.adoc#servlet-rememberme[Remember Me] - how to remember a user past session expiration * xref:servlet/authentication/jaas.adoc#servlet-jaas[JAAS Authentication] - authenticate with JAAS * xref:servlet/authentication/openid.adoc#servlet-openid[OpenID] - OpenID Authentication (not to be confused with OpenID Connect) -* xref:servlet/authentication/preauth.adoc#servlet-preauth[] - authenticate with an external mechanism such as https://www.siteminder.com/[SiteMinder] or Java EE security but still use Spring Security for authorization and protection against common exploits. +* xref:servlet/authentication/preauth.adoc#servlet-preauth[Pre-Authentication Scenarios] - authenticate with an external mechanism such as https://www.siteminder.com/[SiteMinder] or Java EE security but still use Spring Security for authorization and protection against common exploits. * xref:servlet/authentication/x509.adoc#servlet-x509[X509 Authentication] - X509 Authentication diff --git a/docs/modules/ROOT/pages/servlet/authentication/logout.adoc b/docs/modules/ROOT/pages/servlet/authentication/logout.adoc index c529c9afed..db4276e6df 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/logout.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/logout.adoc @@ -108,7 +108,7 @@ Various implementations are provided: - {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 xref:servlet/authentication/rememberme.adoc#remember-me-impls[] for details. +Please see xref:servlet/authentication/rememberme.adoc#remember-me-impls[Remember-Me Interfaces and Implementations] 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. @@ -142,7 +142,7 @@ If not configured a status code 200 will be returned by default. - <> - xref:servlet/test/mockmvc.adoc#test-logout[ Testing Logout] - xref:servlet/integrations/servlet-api.adoc#servletapi-logout[ HttpServletRequest.logout()] -- xref:servlet/authentication/rememberme.adoc#remember-me-impls[] +- xref:servlet/authentication/rememberme.adoc#remember-me-impls[Remember-Me Interfaces and Implementations] - xref:servlet/exploits/csrf.adoc#servlet-considerations-csrf-logout[ Logging Out] in section CSRF Caveats - Section xref:servlet/authentication/cas.adoc#cas-singlelogout[ Single Logout] (CAS protocol) - Documentation for the xref:servlet/appendix/namespace.adoc#nsa-logout[ logout element] in the Spring Security XML Namespace section diff --git a/docs/modules/ROOT/pages/servlet/authentication/openid.adoc b/docs/modules/ROOT/pages/servlet/authentication/openid.adoc index 9ada981dd5..d15acca793 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/openid.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/openid.adoc @@ -53,7 +53,7 @@ OpenIDAuthenticationToken token = List attributes = token.getAttributes(); ---- -We can obtain the `OpenIDAuthenticationToken` from the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[]. +We can obtain the `OpenIDAuthenticationToken` from the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder]. The `OpenIDAttribute` contains the attribute type and the retrieved value (or values in the case of multi-valued attributes). 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. diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc index 098d04672a..1796621b89 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc @@ -38,7 +38,7 @@ The details of what `AuthenticationManager` looks like depend on how the xref:se image:{icondir}/number_3.png[] If authentication fails, then __Failure__ -* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[] is cleared out. +* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out. * `RememberMeServices.loginFail` is invoked. If remember me is not configured, this is a no-op. // FIXME: link to rememberme @@ -46,7 +46,7 @@ If remember me is not configured, this is a no-op. image:{icondir}/number_4.png[] If authentication is successful, then __Success__. -* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[] is set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[]. +* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[Authentication] is set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder]. * `RememberMeServices.loginSuccess` is invoked. If remember me is not configured, this is a no-op. // FIXME: link to rememberme diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/dao-authentication-provider.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/dao-authentication-provider.adoc index 3d716d14c2..c25bf6c8e7 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/dao-authentication-provider.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/dao-authentication-provider.adoc @@ -12,7 +12,7 @@ image::{figures}/daoauthenticationprovider.png[] image:{icondir}/number_1.png[] The authentication `Filter` from xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd-input[Reading the Username & Password] passes a `UsernamePasswordAuthenticationToken` to the `AuthenticationManager` which is implemented by xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`ProviderManager`]. -image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[] of type `DaoAuthenticationProvider`. +image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[AuthenticationProvider] of type `DaoAuthenticationProvider`. image:{icondir}/number_3.png[] `DaoAuthenticationProvider` looks up the `UserDetails` from the `UserDetailsService`. diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc index 8ecd652912..368187c13f 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc @@ -27,7 +27,7 @@ image:{icondir}/number_5.png[] Something within the application, must <] support to secure one of your services. You can now easily transfer the `SecurityContext` of the current `Thread` to the `Thread` that invokes the secured service. An example of how you might do this can be found below: diff --git a/docs/modules/ROOT/pages/servlet/java-configuration/index.adoc b/docs/modules/ROOT/pages/servlet/java-configuration/index.adoc index 54cf54aa74..711ae532a9 100644 --- a/docs/modules/ROOT/pages/servlet/java-configuration/index.adoc +++ b/docs/modules/ROOT/pages/servlet/java-configuration/index.adoc @@ -5,7 +5,7 @@ General support for https://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/beans.html#beans-java[Java Configuration] was added to Spring Framework in Spring 3.1. Since Spring Security 3.2 there has been Spring Security Java Configuration support which enables users to easily configure Spring Security without the use of any XML. -If you are familiar with the xref:servlet/namespace/index.adoc#ns-config[] then you should find quite a few similarities between it and the Security Java Configuration support. +If you are familiar with the xref:servlet/namespace/index.adoc#ns-config[Security Namespace Configuration] then you should find quite a few similarities between it and the Security Java Configuration support. NOTE: Spring Security provides https://github.com/spring-projects/spring-security-samples/tree/main/servlet/java-configuration[lots of sample applications] which demonstrate the use of Spring Security Java Configuration. diff --git a/docs/modules/ROOT/pages/servlet/namespace/index.adoc b/docs/modules/ROOT/pages/servlet/namespace/index.adoc index 488c3743c7..e11eebde48 100644 --- a/docs/modules/ROOT/pages/servlet/namespace/index.adoc +++ b/docs/modules/ROOT/pages/servlet/namespace/index.adoc @@ -18,7 +18,7 @@ For example, adding the following element from the security namespace to an appl 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[xref:servlet/authentication/unpwd/ldap.adoc#servlet-authentication-ldap[]].]. +footnote:[You can find out more about the use of the `ldap-server` element in the chapter on pass:specialcharacters,macros[xref:servlet/authentication/unpwd/ldap.adoc#servlet-authentication-ldap[LDAP Authentication]].]. 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 https://spring.io/tools[Eclipse IDE with Spring Tools] as it has special features for working with standard Spring namespaces. diff --git a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc index c9e00338ec..e05e4402b6 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc @@ -49,12 +49,12 @@ The details of what `AuthenticationManager` looks like depends on whether you're image:{icondir}/number_3.png[] If authentication fails, then __Failure__ -* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[] is cleared out. +* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out. * The `AuthenticationEntryPoint` is invoked to trigger the WWW-Authenticate header to be sent again. image:{icondir}/number_4.png[] If authentication is successful, then __Success__. -* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[] is set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[]. +* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[Authentication] is set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder]. * The `BearerTokenAuthenticationFilter` invokes `FilterChain.doFilter(request,response)` to continue with the rest of the application logic. [[oauth2resourceserver-jwt-minimaldependencies]] @@ -152,7 +152,7 @@ image::{figures}/jwtauthenticationprovider.png[] image:{icondir}/number_1.png[] The authentication `Filter` from <> passes a `BearerTokenAuthenticationToken` to the `AuthenticationManager` which is implemented by xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`ProviderManager`]. -image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[] of type `JwtAuthenticationProvider`. +image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[AuthenticationProvider] of type `JwtAuthenticationProvider`. [[oauth2resourceserver-jwt-architecture-jwtdecoder]] image:{icondir}/number_3.png[] `JwtAuthenticationProvider` decodes, verifies, and validates the `Jwt` using a <>. @@ -1523,7 +1523,7 @@ image::{figures}/opaquetokenauthenticationprovider.png[] image:{icondir}/number_1.png[] The authentication `Filter` from <> passes a `BearerTokenAuthenticationToken` to the `AuthenticationManager` which is implemented by xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`ProviderManager`]. -image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[] of type `OpaqueTokenAuthenticationProvider`. +image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[AuthenticationProvider] of type `OpaqueTokenAuthenticationProvider`. [[oauth2resourceserver-opaque-architecture-introspector]] image:{icondir}/number_3.png[] `OpaqueTokenAuthenticationProvider` introspects the opaque token and adds granted authorities using an <>. From 32331185dc6dce4ae4e51a7a5ad43ae45b0b9f97 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 30 Jul 2021 17:06:45 -0500 Subject: [PATCH 10/34] Fix local anchor --- docs/modules/ROOT/pages/servlet/java-configuration/index.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/servlet/java-configuration/index.adoc b/docs/modules/ROOT/pages/servlet/java-configuration/index.adoc index 711ae532a9..cc5ebaa70a 100644 --- a/docs/modules/ROOT/pages/servlet/java-configuration/index.adoc +++ b/docs/modules/ROOT/pages/servlet/java-configuration/index.adoc @@ -65,7 +65,7 @@ This can be done in Java Configuration with https://docs.spring.io/spring/docs/3 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. -* xref:servlet/java-configuration/index.adoc#abstractsecuritywebapplicationinitializer-without-existing-spring[] - Use these instructions if you are not using Spring already +* <> - Use these instructions if you are not using Spring already * <> - Use these instructions if you are already using Spring === AbstractSecurityWebApplicationInitializer without Existing Spring From a64a694c6094c5915419e01606cb59a146baa248 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Mon, 9 Aug 2021 15:03:43 -0500 Subject: [PATCH 11/34] Indent testing subsections in nav.adoc --- docs/modules/ROOT/nav.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 7e28eb55af..f081962102 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -73,8 +73,8 @@ *** xref:servlet/kotlin-configuration/index.adoc[Kotlin Configuration] *** xref:servlet/namespace/index.adoc[Namespace Configuration] ** xref:servlet/test/index.adoc[Testing] -** xref:servlet/test/method.adoc[Method Security] -** xref:servlet/test/mockmvc.adoc[MockMvc Support] +*** xref:servlet/test/method.adoc[Method Security] +*** xref:servlet/test/mockmvc.adoc[MockMvc Support] ** xref:servlet/crypto/index.adoc[Cryptography] ** xref:servlet/appendix/index.adoc[Appendix] *** xref:servlet/appendix/database-schema.adoc[Database Schemas] From e681e44268a5f8483d229113d780816d384688e0 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Tue, 10 Aug 2021 15:21:42 -0500 Subject: [PATCH 12/34] overview/ -> ../ --- docs/modules/ROOT/nav.adoc | 26 +++++++++---------- .../ROOT/pages/{overview => }/community.adoc | 2 +- .../features/authentication/index.adoc | 0 .../authentication/password-storage.adoc | 0 .../features/exploits/csrf.adoc | 0 .../features/exploits/headers.adoc | 0 .../features/exploits/http.adoc | 2 +- .../features/exploits/index.adoc | 6 ++--- docs/modules/ROOT/pages/features/index.adoc | 6 +++++ .../getting-spring-security.adoc | 10 +++---- .../ROOT/pages/{overview => }/index.adoc | 0 .../ROOT/pages/{overview => }/modules.adoc | 0 .../ROOT/pages/overview/features/index.adoc | 6 ----- .../pages/{overview => }/prerequisites.adoc | 0 .../ROOT/pages/reactive/exploits/csrf.adoc | 24 ++++++++--------- .../ROOT/pages/reactive/exploits/headers.adoc | 22 ++++++++-------- .../ROOT/pages/reactive/exploits/http.adoc | 4 +-- .../ROOT/pages/reactive/oauth2/login.adoc | 2 +- .../ROOT/pages/{overview => }/samples.adoc | 0 .../pages/servlet/appendix/namespace.adoc | 6 ++--- .../authentication/architecture/index.adoc | 8 +++--- .../pages/servlet/authentication/cas.adoc | 4 +-- .../pages/servlet/authentication/index.adoc | 2 +- .../servlet/authentication/unpwd/digest.adoc | 4 +-- .../authentication/unpwd/in-memory.adoc | 6 ++--- .../servlet/authentication/unpwd/jdbc.adoc | 4 +-- .../unpwd/password-encoder.adoc | 4 +-- .../ROOT/pages/servlet/exploits/csrf.adoc | 22 ++++++++-------- .../ROOT/pages/servlet/exploits/headers.adoc | 26 +++++++++---------- .../ROOT/pages/servlet/exploits/http.adoc | 4 +-- .../ROOT/pages/servlet/exploits/index.adoc | 2 +- .../ROOT/pages/servlet/hello/index.adoc | 2 +- .../pages/servlet/integrations/websocket.adoc | 4 +-- .../pages/servlet/oauth2/oauth2-client.adoc | 2 +- .../pages/servlet/oauth2/oauth2-login.adoc | 2 +- .../ROOT/pages/{overview => }/whats-new.adoc | 0 36 files changed, 106 insertions(+), 106 deletions(-) rename docs/modules/ROOT/pages/{overview => }/community.adoc (95%) rename docs/modules/ROOT/pages/{overview => }/features/authentication/index.adoc (100%) rename docs/modules/ROOT/pages/{overview => }/features/authentication/password-storage.adoc (100%) rename docs/modules/ROOT/pages/{overview => }/features/exploits/csrf.adoc (100%) rename docs/modules/ROOT/pages/{overview => }/features/exploits/headers.adoc (100%) rename docs/modules/ROOT/pages/{overview => }/features/exploits/http.adoc (93%) rename docs/modules/ROOT/pages/{overview => }/features/exploits/index.adoc (61%) create mode 100644 docs/modules/ROOT/pages/features/index.adoc rename docs/modules/ROOT/pages/{overview => }/getting-spring-security.adoc (95%) rename docs/modules/ROOT/pages/{overview => }/index.adoc (100%) rename docs/modules/ROOT/pages/{overview => }/modules.adoc (100%) delete mode 100644 docs/modules/ROOT/pages/overview/features/index.adoc rename docs/modules/ROOT/pages/{overview => }/prerequisites.adoc (100%) rename docs/modules/ROOT/pages/{overview => }/samples.adoc (100%) rename docs/modules/ROOT/pages/{overview => }/whats-new.adoc (100%) diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index f081962102..31084773ca 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -1,17 +1,17 @@ * Overview -** xref:overview/prerequisites.adoc[Prerequisites] -** xref:overview/community.adoc[Community] -** xref:overview/whats-new.adoc[What's New] -** xref:overview/getting-spring-security.adoc[Getting Spring Security] -** xref:overview/features/index.adoc[Features] -*** xref:overview/features/authentication/index.adoc[Authentication] -**** xref:overview/features/authentication/password-storage.adoc[Password Storage] -*** xref:overview/features/exploits/index.adoc[Protection Against Exploits] -**** xref:overview/features/exploits/csrf.adoc[CSRF] -**** xref:overview/features/exploits/headers.adoc[HTTP Headers] -**** xref:overview/features/exploits/http.adoc[HTTP Requests] -** xref:overview/modules.adoc[Project Modules & Dependencies] -** xref:overview/samples.adoc[Samples] +** xref:prerequisites.adoc[Prerequisites] +** xref:community.adoc[Community] +** xref:whats-new.adoc[What's New] +** xref:getting-spring-security.adoc[Getting Spring Security] +** xref:features/index.adoc[Features] +*** xref:features/authentication/index.adoc[Authentication] +**** xref:features/authentication/password-storage.adoc[Password Storage] +*** xref:features/exploits/index.adoc[Protection Against Exploits] +**** xref:features/exploits/csrf.adoc[CSRF] +**** xref:features/exploits/headers.adoc[HTTP Headers] +**** xref:features/exploits/http.adoc[HTTP Requests] +** xref:modules.adoc[Project Modules & Dependencies] +** xref:samples.adoc[Samples] * xref:servlet/index.adoc[Servlet Applications] ** xref:servlet/hello/index.adoc[Hello Spring Security] ** xref:servlet/architecture/index.adoc[The Big Picture] diff --git a/docs/modules/ROOT/pages/overview/community.adoc b/docs/modules/ROOT/pages/community.adoc similarity index 95% rename from docs/modules/ROOT/pages/overview/community.adoc rename to docs/modules/ROOT/pages/community.adoc index 34cd5e504c..ee21f4ea20 100644 --- a/docs/modules/ROOT/pages/overview/community.adoc +++ b/docs/modules/ROOT/pages/community.adoc @@ -11,7 +11,7 @@ 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 xref:overview/samples.adoc#samples[sample applications]. +* Try one of our many xref:samples.adoc#samples[sample applications]. * 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 diff --git a/docs/modules/ROOT/pages/overview/features/authentication/index.adoc b/docs/modules/ROOT/pages/features/authentication/index.adoc similarity index 100% rename from docs/modules/ROOT/pages/overview/features/authentication/index.adoc rename to docs/modules/ROOT/pages/features/authentication/index.adoc diff --git a/docs/modules/ROOT/pages/overview/features/authentication/password-storage.adoc b/docs/modules/ROOT/pages/features/authentication/password-storage.adoc similarity index 100% rename from docs/modules/ROOT/pages/overview/features/authentication/password-storage.adoc rename to docs/modules/ROOT/pages/features/authentication/password-storage.adoc diff --git a/docs/modules/ROOT/pages/overview/features/exploits/csrf.adoc b/docs/modules/ROOT/pages/features/exploits/csrf.adoc similarity index 100% rename from docs/modules/ROOT/pages/overview/features/exploits/csrf.adoc rename to docs/modules/ROOT/pages/features/exploits/csrf.adoc diff --git a/docs/modules/ROOT/pages/overview/features/exploits/headers.adoc b/docs/modules/ROOT/pages/features/exploits/headers.adoc similarity index 100% rename from docs/modules/ROOT/pages/overview/features/exploits/headers.adoc rename to docs/modules/ROOT/pages/features/exploits/headers.adoc diff --git a/docs/modules/ROOT/pages/overview/features/exploits/http.adoc b/docs/modules/ROOT/pages/features/exploits/http.adoc similarity index 93% rename from docs/modules/ROOT/pages/overview/features/exploits/http.adoc rename to docs/modules/ROOT/pages/features/exploits/http.adoc index 579745c1aa..fcffae7a25 100644 --- a/docs/modules/ROOT/pages/overview/features/exploits/http.adoc +++ b/docs/modules/ROOT/pages/features/exploits/http.adoc @@ -14,7 +14,7 @@ When a client uses HTTP, Spring Security can be configured to redirect to HTTPS [[http-hsts]] == Strict Transport Security -Spring Security provides support for xref:overview/features/exploits/headers.adoc#headers-hsts[Strict Transport Security] and enables it by default. +Spring Security provides support for xref:features/exploits/headers.adoc#headers-hsts[Strict Transport Security] and enables it by default. [[http-proxy-server]] == Proxy Server Configuration diff --git a/docs/modules/ROOT/pages/overview/features/exploits/index.adoc b/docs/modules/ROOT/pages/features/exploits/index.adoc similarity index 61% rename from docs/modules/ROOT/pages/overview/features/exploits/index.adoc rename to docs/modules/ROOT/pages/features/exploits/index.adoc index b0e34907b8..6aa70077ee 100644 --- a/docs/modules/ROOT/pages/overview/features/exploits/index.adoc +++ b/docs/modules/ROOT/pages/features/exploits/index.adoc @@ -5,6 +5,6 @@ Spring Security provides protection against common exploits. Whenever possible, the protection is enabled by default. Below you will find high level description of the various exploits that Spring Security protects against. -* xref:overview/features/exploits/csrf.adoc[CSRF] -* xref:overview/features/exploits/headers.adoc[HTTP Headers] -* xref:overview/features/exploits/http.adoc[HTTP Requests] +* xref:features/exploits/csrf.adoc[CSRF] +* xref:features/exploits/headers.adoc[HTTP Headers] +* xref:features/exploits/http.adoc[HTTP Requests] diff --git a/docs/modules/ROOT/pages/features/index.adoc b/docs/modules/ROOT/pages/features/index.adoc new file mode 100644 index 0000000000..2e51981630 --- /dev/null +++ b/docs/modules/ROOT/pages/features/index.adoc @@ -0,0 +1,6 @@ +[[features]] += Features + +Spring Security provides comprehensive support for xref:features/authentication/index.adoc#authentication[authentication], authorization, and protection against xref:features/exploits/index.adoc#exploits[common exploits]. +It also provides integration with other libraries to simplify its usage. + diff --git a/docs/modules/ROOT/pages/overview/getting-spring-security.adoc b/docs/modules/ROOT/pages/getting-spring-security.adoc similarity index 95% rename from docs/modules/ROOT/pages/overview/getting-spring-security.adoc rename to docs/modules/ROOT/pages/getting-spring-security.adoc index bb5c7b9f1c..f99e18860f 100644 --- a/docs/modules/ROOT/pages/overview/getting-spring-security.adoc +++ b/docs/modules/ROOT/pages/getting-spring-security.adoc @@ -2,7 +2,7 @@ = Getting Spring Security This section discusses all you need to know about getting the Spring Security binaries. -See xref:overview/community.adoc#community-source[Source Code] for how to obtain the source code. +See xref:community.adoc#community-source[Source Code] for how to obtain the source code. == Release Numbering @@ -72,7 +72,7 @@ You can do so by adding a Maven property, as the following example shows: ---- ==== -If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:overview/modules.adoc#modules[Project Modules and Dependencies]. +If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:modules.adoc#modules[Project Modules and Dependencies]. [[getting-maven-no-boot]] === Maven Without Spring Boot @@ -118,7 +118,7 @@ A minimal Spring Security Maven set of dependencies typically looks like the fol ---- ==== -If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:overview/modules.adoc#modules[Project Modules and Dependencies]. +If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:modules.adoc#modules[Project Modules and Dependencies]. Spring Security builds against Spring Framework {spring-core-version} but should generally work with any newer version of Spring Framework 5.x. Many users are likely to run afoul of the fact that Spring Security's transitive dependencies resolve Spring Framework {spring-core-version}, which can cause strange classpath problems. @@ -236,7 +236,7 @@ ext['spring.version']='{spring-core-version}' ---- ==== -If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:overview/modules.adoc#modules[Project Modules and Dependencies]. +If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:modules.adoc#modules[Project Modules and Dependencies]. === Gradle Without Spring Boot @@ -274,7 +274,7 @@ dependencies { ---- ==== -If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:overview/modules.adoc#modules[Project Modules and Dependencies]. +If you use additional features (such as LDAP, OpenID, and others), you need to also include the appropriate xref:modules.adoc#modules[Project Modules and Dependencies]. Spring Security builds against Spring Framework {spring-core-version} but should generally work with any newer version of Spring Framework 5.x. Many users are likely to run afoul of the fact that Spring Security's transitive dependencies resolve Spring Framework {spring-core-version}, which can cause strange classpath problems. diff --git a/docs/modules/ROOT/pages/overview/index.adoc b/docs/modules/ROOT/pages/index.adoc similarity index 100% rename from docs/modules/ROOT/pages/overview/index.adoc rename to docs/modules/ROOT/pages/index.adoc diff --git a/docs/modules/ROOT/pages/overview/modules.adoc b/docs/modules/ROOT/pages/modules.adoc similarity index 100% rename from docs/modules/ROOT/pages/overview/modules.adoc rename to docs/modules/ROOT/pages/modules.adoc diff --git a/docs/modules/ROOT/pages/overview/features/index.adoc b/docs/modules/ROOT/pages/overview/features/index.adoc deleted file mode 100644 index 0edf1f1650..0000000000 --- a/docs/modules/ROOT/pages/overview/features/index.adoc +++ /dev/null @@ -1,6 +0,0 @@ -[[features]] -= Features - -Spring Security provides comprehensive support for xref:overview/features/authentication/index.adoc#authentication[authentication], authorization, and protection against xref:overview/features/exploits/index.adoc#exploits[common exploits]. -It also provides integration with other libraries to simplify its usage. - diff --git a/docs/modules/ROOT/pages/overview/prerequisites.adoc b/docs/modules/ROOT/pages/prerequisites.adoc similarity index 100% rename from docs/modules/ROOT/pages/overview/prerequisites.adoc rename to docs/modules/ROOT/pages/prerequisites.adoc diff --git a/docs/modules/ROOT/pages/reactive/exploits/csrf.adoc b/docs/modules/ROOT/pages/reactive/exploits/csrf.adoc index a626b99554..6762324171 100644 --- a/docs/modules/ROOT/pages/reactive/exploits/csrf.adoc +++ b/docs/modules/ROOT/pages/reactive/exploits/csrf.adoc @@ -1,7 +1,7 @@ [[webflux-csrf]] = Cross Site Request Forgery (CSRF) for WebFlux Environments -This section discusses Spring Security's xref:overview/features/exploits/csrf.adoc#csrf[Cross Site Request Forgery (CSRF)] support for WebFlux environments. +This section discusses Spring Security's xref:features/exploits/csrf.adoc#csrf[Cross Site Request Forgery (CSRF)] support for WebFlux environments. [[webflux-csrf-using]] == Using Spring Security CSRF Protection @@ -14,7 +14,7 @@ The steps to using Spring Security's CSRF protection are outlined below: [[webflux-csrf-idempotent]] === Use proper HTTP verbs The first step to protecting against CSRF attacks is to ensure your website uses proper HTTP verbs. -This is covered in detail in xref:overview/features/exploits/csrf.adoc#csrf-protection-idempotent[Safe Methods Must be Idempotent]. +This is covered in detail in xref:features/exploits/csrf.adoc#csrf-protection-idempotent[Safe Methods Must be Idempotent]. [[webflux-csrf-configure]] === Configure CSRF Protection @@ -73,7 +73,7 @@ If you do not need the ability to read the cookie with JavaScript directly, it i [[webflux-csrf-configure-disable]] ==== Disable CSRF Protection CSRF protection is enabled by default. -However, it is simple to disable CSRF protection if it xref:overview/features/exploits/csrf.adoc#csrf-when[makes sense for your application]. +However, it is simple to disable CSRF protection if it xref:features/exploits/csrf.adoc#csrf-when[makes sense for your application]. The Java configuration below will disable CSRF protection. @@ -109,7 +109,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain [[webflux-csrf-include]] === Include the CSRF Token -In order for the xref:overview/features/exploits/csrf.adoc#csrf-protection-stp[synchronizer token pattern] to protect against CSRF attacks, we must include the actual CSRF token in the HTTP request. +In order for the xref:features/exploits/csrf.adoc#csrf-protection-stp[synchronizer token pattern] to protect against CSRF attacks, we must include the actual CSRF token in the HTTP request. This must be included in a part of the request (i.e. form parameter, HTTP header, etc) that is not automatically included in the HTTP request by the browser. Spring Security's https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/server/csrf/CsrfWebFilter.html[CsrfWebFilter] exposes a https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/csrf/CsrfToken.html[Mono] as a `ServerWebExchange` attribute named `org.springframework.security.web.server.csrf.CsrfToken`. @@ -272,19 +272,19 @@ An example of doing this with Thymeleaf is shown below: == CSRF Considerations There are a few special considerations to consider when implementing protection against CSRF attacks. This section discusses those considerations as it pertains to WebFlux environments. -Refer to xref:overview/features/exploits/csrf.adoc#csrf-considerations[CSRF Considerations] for a more general discussion. +Refer to xref:features/exploits/csrf.adoc#csrf-considerations[CSRF Considerations] for a more general discussion. [[webflux-considerations-csrf-login]] === Logging In -It is important to xref:overview/features/exploits/csrf.adoc#csrf-considerations-login[require CSRF for log in] requests to protect against forging log in attempts. +It is important to xref:features/exploits/csrf.adoc#csrf-considerations-login[require CSRF for log in] requests to protect against forging log in attempts. Spring Security's WebFlux support does this out of the box. [[webflux-considerations-csrf-logout]] === Logging Out -It is important to xref:overview/features/exploits/csrf.adoc#csrf-considerations-logout[require CSRF for log out] requests to protect against forging log out attempts. +It is important to xref:features/exploits/csrf.adoc#csrf-considerations-logout[require CSRF for log out] requests to protect against forging log out attempts. By default Spring Security's `LogoutWebFilter` only processes HTTP post requests. This ensures that log out requires a CSRF token and that a malicious user cannot forcibly log out your users. @@ -333,7 +333,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain By default Spring Security stores the CSRF token in the `WebSession`. This can lead to a situation where the session expires which means there is not an expected CSRF token to validate against. -We've already discussed xref:overview/features/exploits/csrf.adoc#csrf-considerations-login[general solutions] to session timeouts. +We've already discussed xref:features/exploits/csrf.adoc#csrf-considerations-login[general solutions] to session timeouts. This section discusses the specifics of CSRF timeouts as it pertains to the WebFlux support. It is simple to change storage of the expected CSRF token to be in a cookie. @@ -344,7 +344,7 @@ For details, refer to the <> section. // FIXME: We need a WebFlux multipart body vs action story. WebFlux always has multipart enabled. [[webflux-csrf-considerations-multipart]] === Multipart (file upload) -We have xref:overview/features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] how protecting multipart requests (file uploads) from CSRF attacks causes a https://en.wikipedia.org/wiki/Chicken_or_the_egg[chicken and the egg] problem. +We have xref:features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] how protecting multipart requests (file uploads) from CSRF attacks causes a https://en.wikipedia.org/wiki/Chicken_or_the_egg[chicken and the egg] problem. This section discusses how to implement placing the CSRF token in the <> and <> within a WebFlux application. [NOTE] @@ -355,7 +355,7 @@ More information about using multipart forms with Spring can be found within the [[webflux-csrf-considerations-multipart-body]] ==== Place CSRF Token in the Body -We have xref:overview/features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] the trade-offs of placing the CSRF token in the body. +We have xref:features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] the trade-offs of placing the CSRF token in the body. In a WebFlux application, this can be configured with the following configuration: @@ -391,7 +391,7 @@ fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain [[webflux-csrf-considerations-multipart-url]] ==== Include CSRF Token in URL -We have xref:overview/features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] the trade-offs of placing the CSRF token in the URL. +We have xref:features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] the trade-offs of placing the CSRF token in the URL. Since the `CsrfToken` is exposed as an `ServerHttpRequest` <>, we can use that to create an `action` with the CSRF token in it. An example with Thymeleaf is shown below: @@ -407,6 +407,6 @@ An example with Thymeleaf is shown below: [[webflux-csrf-considerations-override-method]] === HiddenHttpMethodFilter -We have xref:overview/features/exploits/csrf.adoc#csrf-considerations-override-method[already discussed] overriding the HTTP method. +We have xref:features/exploits/csrf.adoc#csrf-considerations-override-method[already discussed] overriding the HTTP method. In a Spring WebFlux application, overriding the HTTP method is done using https://docs.spring.io/spring-framework/docs/5.2.x/javadoc-api/org/springframework/web/filter/reactive/HiddenHttpMethodFilter.html[HiddenHttpMethodFilter]. diff --git a/docs/modules/ROOT/pages/reactive/exploits/headers.adoc b/docs/modules/ROOT/pages/reactive/exploits/headers.adoc index ed916c6baf..9b61d12b3a 100644 --- a/docs/modules/ROOT/pages/reactive/exploits/headers.adoc +++ b/docs/modules/ROOT/pages/reactive/exploits/headers.adoc @@ -1,13 +1,13 @@ [[webflux-headers]] = Security HTTP Response Headers -xref:overview/features/exploits/headers.adoc#headers[Security HTTP Response Headers] can be used to increase the security of web applications. +xref:features/exploits/headers.adoc#headers[Security HTTP Response Headers] can be used to increase the security of web applications. This section is dedicated to WebFlux based support for Security HTTP Response Headers. [[webflux-headers-default]] == Default Security Headers -Spring Security provides a xref:overview/features/exploits/headers.adoc#headers-default[default set of Security HTTP Response Headers] to provide secure defaults. +Spring Security provides a xref:features/exploits/headers.adoc#headers-default[default set of Security HTTP Response Headers] to provide secure defaults. 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. @@ -85,7 +85,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { [[webflux-headers-cache-control]] == Cache Control -Spring Security includes xref:overview/features/exploits/headers.adoc#headers-cache-control[Cache Control] headers by default. +Spring Security includes xref:features/exploits/headers.adoc#headers-cache-control[Cache Control] headers by default. However, if you actually want to cache specific responses, your application can selectively add them to the https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/server/reactive/ServerHttpResponse.html[ServerHttpResponse] to override the header set by Spring Security. This is useful to ensure things like CSS, JavaScript, and images are properly cached. @@ -131,7 +131,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { [[webflux-headers-content-type-options]] == Content Type Options -Spring Security includes xref:overview/features/exploits/headers.adoc#headers-content-type-options[Content-Type] headers by default. +Spring Security includes xref:features/exploits/headers.adoc#headers-content-type-options[Content-Type] headers by default. However, you can disable it with: .Content Type Options Disabled @@ -169,7 +169,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { [[webflux-headers-hsts]] == HTTP Strict Transport Security (HSTS) -Spring Security provides the xref:overview/features/exploits/headers.adoc#headers-hsts[Strict Transport Security] header by default. +Spring Security provides the xref:features/exploits/headers.adoc#headers-hsts[Strict Transport Security] header by default. However, you can customize the results explicitly. For example, the following is an example of explicitly providing HSTS: @@ -214,7 +214,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { [[webflux-headers-frame-options]] == X-Frame-Options -By default, Spring Security disables rendering within an iframe using xref:overview/features/exploits/headers.adoc#headers-frame-options[X-Frame-Options]. +By default, Spring Security disables rendering within an iframe using xref:features/exploits/headers.adoc#headers-frame-options[X-Frame-Options]. You can customize frame options to use the same origin using the following: @@ -293,7 +293,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { [[webflux-headers-csp]] == Content Security Policy (CSP) -Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-csp[Content Security Policy] by default, because a reasonable default is impossible to know without context of the application. +Spring Security does not add xref:features/exploits/headers.adoc#headers-csp[Content Security Policy] by default, because a reasonable default is impossible to know without context of the application. 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: @@ -385,7 +385,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { [[webflux-headers-referrer]] == Referrer Policy -Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-referrer[Referrer Policy] headers by default. +Spring Security does not add xref:features/exploits/headers.adoc#headers-referrer[Referrer Policy] headers by default. You can enable the Referrer Policy header using configuration as shown below: .Referrer Policy Configuration @@ -427,7 +427,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { [[webflux-headers-feature]] == Feature Policy -Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-feature[Feature Policy] headers by default. +Spring Security does not add xref:features/exploits/headers.adoc#headers-feature[Feature Policy] headers by default. The following `Feature-Policy` header: .Feature-Policy Example @@ -475,7 +475,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { [[webflux-headers-permissions]] == Permissions Policy -Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-permissions[Permissions Policy] headers by default. +Spring Security does not add xref:features/exploits/headers.adoc#headers-permissions[Permissions Policy] headers by default. The following `Permissions-Policy` header: .Permissions-Policy Example @@ -527,7 +527,7 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { [[webflux-headers-clear-site-data]] == Clear Site Data -Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-clear-site-data[Clear-Site-Data] headers by default. +Spring Security does not add xref:features/exploits/headers.adoc#headers-clear-site-data[Clear-Site-Data] headers by default. The following Clear-Site-Data header: .Clear-Site-Data Example diff --git a/docs/modules/ROOT/pages/reactive/exploits/http.adoc b/docs/modules/ROOT/pages/reactive/exploits/http.adoc index 258d10f74f..e7a07bb20b 100644 --- a/docs/modules/ROOT/pages/reactive/exploits/http.adoc +++ b/docs/modules/ROOT/pages/reactive/exploits/http.adoc @@ -1,7 +1,7 @@ [[webflux-http]] = HTTP -All HTTP based communication should be protected xref:overview/features/exploits/http.adoc#http[using TLS]. +All HTTP based communication should be protected xref:features/exploits/http.adoc#http[using TLS]. Below you can find details around WebFlux specific features that assist with HTTPS usage. @@ -84,4 +84,4 @@ Spring Security provides support for xref:servlet/exploits/headers.adoc#servlet- [[webflux-http-proxy-server]] == Proxy Server Configuration -Spring Security xref:overview/features/exploits/http.adoc#http-proxy-server[integrates with proxy servers]. +Spring Security xref:features/exploits/http.adoc#http-proxy-server[integrates with proxy servers]. diff --git a/docs/modules/ROOT/pages/reactive/oauth2/login.adoc b/docs/modules/ROOT/pages/reactive/oauth2/login.adoc index 2f80da08b1..a16160c0ff 100644 --- a/docs/modules/ROOT/pages/reactive/oauth2/login.adoc +++ b/docs/modules/ROOT/pages/reactive/oauth2/login.adoc @@ -42,7 +42,7 @@ TIP: The default redirect URI template is `+{baseUrl}/login/oauth2/code/{registr The *_registrationId_* is a unique identifier for the xref:servlet/oauth2/oauth2-client.adoc#oauth2Client-client-registration[ClientRegistration]. For our example, the `registrationId` is `google`. -IMPORTANT: If the OAuth Client is running behind a proxy server, it is recommended to check xref:overview/features/exploits/http.adoc#http-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured. +IMPORTANT: If the OAuth Client is running behind a proxy server, it is recommended to check xref:features/exploits/http.adoc#http-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured. Also, see the supported xref:servlet/oauth2/oauth2-client.adoc#oauth2Client-auth-code-redirect-uri[ `URI` template variables] for `redirect-uri`. [[webflux-oauth2-login-sample-config]] diff --git a/docs/modules/ROOT/pages/overview/samples.adoc b/docs/modules/ROOT/pages/samples.adoc similarity index 100% rename from docs/modules/ROOT/pages/overview/samples.adoc rename to docs/modules/ROOT/pages/samples.adoc diff --git a/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc b/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc index 00ef0b7772..794f077c19 100644 --- a/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc +++ b/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc @@ -227,7 +227,7 @@ Optional attribute that specifies the bean name of a `CorsConfigurationSource` t === This element allows for configuring additional (security) headers to be send with the response. It enables easy configuration for several headers and also allows for setting custom headers through the <> element. -Additional information, can be found in the xref:overview/features/exploits/headers.adoc#headers[Security Headers] section of the reference. +Additional information, can be found in the xref:features/exploits/headers.adoc#headers[Security Headers] section of the reference. ** `Cache-Control`, `Pragma`, and `Expires` - Can be set using the <> element. This ensures that the browser does not cache your secured pages. @@ -674,7 +674,7 @@ if unset, defaults to `anonymousUser`. === This element will add https://en.wikipedia.org/wiki/Cross-site_request_forgery[Cross Site Request Forger (CSRF)] protection to the application. It also updates the default RequestCache to only replay "GET" requests upon successful authentication. -Additional information can be found in the xref:overview/features/exploits/csrf.adoc#csrf[Cross Site Request Forgery (CSRF)] section of the reference. +Additional information can be found in the xref:features/exploits/csrf.adoc#csrf[Cross Site Request Forgery (CSRF)] section of the reference. [[nsa-csrf-parents]] @@ -2278,7 +2278,7 @@ select username, password, enabled from users where username = ? [[nsa-password-encoder]] === -Authentication providers can optionally be configured to use a password encoder as described in the xref:overview/features/authentication/password-storage.adoc#authentication-password-storage[Password Storage]. +Authentication providers can optionally be configured to use a password encoder as described in the xref:features/authentication/password-storage.adoc#authentication-password-storage[Password Storage]. This will result in the bean being injected with the appropriate `PasswordEncoder` instance. diff --git a/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc index 441b21fd74..75808178cc 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc @@ -5,11 +5,11 @@ This discussion expands on xref:servlet/architecture/index.adoc#servlet-architecture[Servlet Security: The Big Picture] to describe the main architectural components of Spring Security's used in Servlet authentication. If you need concrete flows that explain how these pieces fit together, look at the xref:servlet/authentication/index.adoc#servlet-authentication-mechanisms[Authentication Mechanism] specific sections. -* <> - The `SecurityContextHolder` is where Spring Security stores the details of who is xref:overview/features/authentication/index.adoc#authentication[authenticated]. +* <> - The `SecurityContextHolder` is where Spring Security stores the details of who is xref:features/authentication/index.adoc#authentication[authenticated]. * <> - is obtained from the `SecurityContextHolder` and contains the `Authentication` of the currently authenticated user. * <> - Can be the input to `AuthenticationManager` to provide the credentials a user has provided to authenticate or the current user from the `SecurityContext`. * <> - An authority that is granted to the principal on the `Authentication` (i.e. roles, scopes, etc.) -* <> - the API that defines how Spring Security's Filters perform xref:overview/features/authentication/index.adoc#authentication[authentication]. +* <> - the API that defines how Spring Security's Filters perform xref:features/authentication/index.adoc#authentication[authentication]. * <> - the most common implementation of `AuthenticationManager`. * <> - used by `ProviderManager` to perform a specific type of authentication. * <> - used for requesting credentials from a client (i.e. redirecting to a log in page, sending a `WWW-Authenticate` response, etc.) @@ -26,7 +26,7 @@ It contains the <>. image::{figures}/securitycontextholder.png[] -The `SecurityContextHolder` is where Spring Security stores the details of who is xref:overview/features/authentication/index.adoc#authentication[authenticated]. +The `SecurityContextHolder` is where Spring Security stores the details of who is xref:features/authentication/index.adoc#authentication[authenticated]. Spring Security does not care how the `SecurityContextHolder` is populated. If it contains a value, then it is used as the currently authenticated user. @@ -151,7 +151,7 @@ Of course, Spring Security is expressly designed to handle this common requireme [[servlet-authentication-authenticationmanager]] == AuthenticationManager -{security-api-url}org/springframework/security/authentication/AuthenticationManager.html[`AuthenticationManager`] is the API that defines how Spring Security's Filters perform xref:overview/features/authentication/index.adoc#authentication[authentication]. +{security-api-url}org/springframework/security/authentication/AuthenticationManager.html[`AuthenticationManager`] is the API that defines how Spring Security's Filters perform xref:features/authentication/index.adoc#authentication[authentication]. The <> that is returned is then set on the <> by the controller (i.e. xref:servlet/architecture/index.adoc#servlet-security-filters[Spring Security's ``Filters``s]) that invoked the `AuthenticationManager`. If you are not integrating with __Spring Security's ``Filters``s__ you can set the `SecurityContextHolder` directly and are not required to use an `AuthenticationManager`. diff --git a/docs/modules/ROOT/pages/servlet/authentication/cas.adoc b/docs/modules/ROOT/pages/servlet/authentication/cas.adoc index 35f3ad8c23..5b6dd7617e 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/cas.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/cas.adoc @@ -92,7 +92,7 @@ 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. Each section builds upon the previous section. -A full CAS sample application can be found in the Spring Security xref:overview/samples.adoc#samples[Samples]. +A full CAS sample application can be found in the Spring Security xref:samples.adoc#samples[Samples]. [[cas-st]] @@ -337,7 +337,7 @@ An example configuration is shown below. [[cas-pt-client-sample]] ==== 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 CAS xref:overview/samples.adoc#samples[sample application] contains a working example in the `ProxyTicketSampleServlet`. +The CAS xref:samples.adoc#samples[sample application] contains a working example in the `ProxyTicketSampleServlet`. Example code can be found below: ==== diff --git a/docs/modules/ROOT/pages/servlet/authentication/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/index.adoc index a51390e95d..44694964b1 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/index.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/index.adoc @@ -1,7 +1,7 @@ [[servlet-authentication]] = Authentication -Spring Security provides comprehensive support for xref:overview/features/authentication/index.adoc#authentication[Authentication]. +Spring Security provides comprehensive support for xref:features/authentication/index.adoc#authentication[Authentication]. We start by discussing the overall xref:servlet/architecture/index.adoc#servlet-architecture[Servlet Authentication Architecture]. As you might expect, this section is more abstract describing the architecture without much discussion on how it applies to concrete flows. diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/digest.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/digest.adoc index 51763da932..92494d71b3 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/digest.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/digest.adoc @@ -18,7 +18,7 @@ The standard governing HTTP Digest Authentication is defined by https://tools.ie Most user agents implement RFC 2617. Spring Security's Digest Authentication support is compatible with the "`auth`" quality of protection (`qop`) prescribed by RFC 2617, which also provides backward compatibility with RFC 2069. Digest Authentication was seen as a more attractive option if you need to use unencrypted HTTP (i.e. no TLS/HTTPS) and wish to maximise security of the authentication process. -However, everyone should use xref:overview/features/exploits/http.adoc#http[HTTPS]. +However, everyone should use xref:features/exploits/http.adoc#http[HTTPS]. Central to Digest Authentication is a "nonce". This is a value the server generates. @@ -34,7 +34,7 @@ key: A private key to prevent modification of the nonce token ---- ==== -You will need to ensure you xref:overview/features/authentication/password-storage.adoc#authentication-password-storage-configuration[configure] insecure plain text xref:overview/features/authentication/password-storage.adoc#authentication-password-storage[Password Storage] using `NoOpPasswordEncoder`. +You will need to ensure you xref:features/authentication/password-storage.adoc#authentication-password-storage-configuration[configure] insecure plain text xref:features/authentication/password-storage.adoc#authentication-password-storage[Password Storage] using `NoOpPasswordEncoder`. The following provides an example of configuring Digest Authentication with Java Configuration: .Digest Authentication diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/in-memory.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/in-memory.adoc index b080006d50..3c74dd95fa 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/in-memory.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/in-memory.adoc @@ -5,7 +5,7 @@ Spring Security's `InMemoryUserDetailsManager` implements xref:servlet/authentic `InMemoryUserDetailsManager` provides management of `UserDetails` by implementing the `UserDetailsManager` interface. `UserDetails` based authentication is used by Spring Security when it is configured to xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd-input[accept a username/password] for authentication. -In this sample we use xref:overview/features/authentication/password-storage.adoc#authentication-password-storage-boot-cli[Spring Boot CLI] to encode the password of `password` and get the encoded password of `+{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW+`. +In this sample we use xref:features/authentication/password-storage.adoc#authentication-password-storage-boot-cli[Spring Boot CLI] to encode the password of `password` and get the encoded password of `+{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW+`. .InMemoryUserDetailsManager Java Configuration ==== @@ -64,7 +64,7 @@ fun users(): UserDetailsService { The samples above store the passwords in a secure format, but leave a lot to be desired in terms of getting started experience. -In the sample below we leverage xref:overview/features/authentication/password-storage.adoc#authentication-password-storage-dep-getting-started[User.withDefaultPasswordEncoder] to ensure that the password stored in memory is protected. +In the sample below we leverage xref:features/authentication/password-storage.adoc#authentication-password-storage-dep-getting-started[User.withDefaultPasswordEncoder] to ensure that the password stored in memory is protected. However, it does not protect against obtaining the password by decompiling the source code. For this reason, `User.withDefaultPasswordEncoder` should only be used for "getting started" and is not intended for production. @@ -114,7 +114,7 @@ fun users(): UserDetailsService { ==== There is no simple way to use `User.withDefaultPasswordEncoder` with XML based configuration. -For demos or just getting started, you can choose to prefix the password with `+{noop}+` to indicate xref:overview/features/authentication/password-storage.adoc#authentication-password-storage-dpe-format[no encoding should be used]. +For demos or just getting started, you can choose to prefix the password with `+{noop}+` to indicate xref:features/authentication/password-storage.adoc#authentication-password-storage-dpe-format[no encoding should be used]. . `+{noop}+` XML Configuration ==== diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/jdbc.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/jdbc.adoc index 75055a6a8f..3aa43ac7f2 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/jdbc.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/jdbc.adoc @@ -147,8 +147,8 @@ In a production environment, you will want to ensure you setup a connection to a [[servlet-authentication-jdbc-bean]] == JdbcUserDetailsManager Bean -In this sample we use xref:overview/features/authentication/password-storage.adoc#authentication-password-storage-boot-cli[Spring Boot CLI] to encode the password of `password` and get the encoded password of `+{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW+`. -See the xref:overview/features/authentication/password-storage.adoc#authentication-password-storage[PasswordEncoder] section for more details about how to store passwords. +In this sample we use xref:features/authentication/password-storage.adoc#authentication-password-storage-boot-cli[Spring Boot CLI] to encode the password of `password` and get the encoded password of `+{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW+`. +See the xref:features/authentication/password-storage.adoc#authentication-password-storage[PasswordEncoder] section for more details about how to store passwords. .JdbcUserDetailsManager ==== diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/password-encoder.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/password-encoder.adoc index aa443acdf6..de20d29004 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/password-encoder.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/password-encoder.adoc @@ -1,5 +1,5 @@ [[servlet-authentication-password-storage]] = PasswordEncoder -Spring Security's servlet support storing passwords securely by integrating with xref:overview/features/authentication/password-storage.adoc#authentication-password-storage[`PasswordEncoder`]. -Customizing the `PasswordEncoder` implementation used by Spring Security can be done by xref:overview/features/authentication/password-storage.adoc#authentication-password-storage-configuration[exposing a `PasswordEncoder` Bean]. +Spring Security's servlet support storing passwords securely by integrating with xref:features/authentication/password-storage.adoc#authentication-password-storage[`PasswordEncoder`]. +Customizing the `PasswordEncoder` implementation used by Spring Security can be done by xref:features/authentication/password-storage.adoc#authentication-password-storage-configuration[exposing a `PasswordEncoder` Bean]. diff --git a/docs/modules/ROOT/pages/servlet/exploits/csrf.adoc b/docs/modules/ROOT/pages/servlet/exploits/csrf.adoc index e6ee66f6d5..fc2d490735 100644 --- a/docs/modules/ROOT/pages/servlet/exploits/csrf.adoc +++ b/docs/modules/ROOT/pages/servlet/exploits/csrf.adoc @@ -1,7 +1,7 @@ [[servlet-csrf]] = Cross Site Request Forgery (CSRF) for Servlet Environments -This section discusses Spring Security's xref:overview/features/exploits/csrf.adoc#csrf[Cross Site Request Forgery (CSRF)] support for servlet environments. +This section discusses Spring Security's xref:features/exploits/csrf.adoc#csrf[Cross Site Request Forgery (CSRF)] support for servlet environments. [[servlet-csrf-using]] == Using Spring Security CSRF Protection @@ -14,7 +14,7 @@ The steps to using Spring Security's CSRF protection are outlined below: [[servlet-csrf-idempotent]] === Use proper HTTP verbs The first step to protecting against CSRF attacks is to ensure your website uses proper HTTP verbs. -This is covered in detail in xref:overview/features/exploits/csrf.adoc#csrf-protection-idempotent[Safe Methods Must be Idempotent]. +This is covered in detail in xref:features/exploits/csrf.adoc#csrf-protection-idempotent[Safe Methods Must be Idempotent]. [[servlet-csrf-configure]] === Configure CSRF Protection @@ -105,7 +105,7 @@ If you do not need the ability to read the cookie with JavaScript directly, it i [[servlet-csrf-configure-disable]] ==== Disable CSRF Protection CSRF protection is enabled by default. -However, it is simple to disable CSRF protection if it xref:overview/features/exploits/csrf.adoc#csrf-when[makes sense for your application]. +However, it is simple to disable CSRF protection if it xref:features/exploits/csrf.adoc#csrf-when[makes sense for your application]. The XML configuration below will disable CSRF protection. @@ -162,7 +162,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-csrf-include]] === Include the CSRF Token -In order for the xref:overview/features/exploits/csrf.adoc#csrf-protection-stp[synchronizer token pattern] to protect against CSRF attacks, we must include the actual CSRF token in the HTTP request. +In order for the xref:features/exploits/csrf.adoc#csrf-protection-stp[synchronizer token pattern] to protect against CSRF attacks, we must include the actual CSRF token in the HTTP request. This must be included in a part of the request (i.e. form parameter, HTTP header, etc) that is not automatically included in the HTTP request by the browser. Spring Security's https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/csrf/CsrfFilter.html[CsrfFilter] exposes a https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/csrf/CsrfToken.html[CsrfToken] as an `HttpServletRequest` attribute named `_csrf`. @@ -301,19 +301,19 @@ An example of doing this with a JSP is shown below: == CSRF Considerations There are a few special considerations to consider when implementing protection against CSRF attacks. This section discusses those considerations as it pertains to servlet environments. -Refer to xref:overview/features/exploits/csrf.adoc#csrf-considerations[CSRF Considerations] for a more general discussion. +Refer to xref:features/exploits/csrf.adoc#csrf-considerations[CSRF Considerations] for a more general discussion. [[servlet-considerations-csrf-login]] === Logging In -It is important to xref:overview/features/exploits/csrf.adoc#csrf-considerations-login[require CSRF for log in] requests to protect against forging log in attempts. +It is important to xref:features/exploits/csrf.adoc#csrf-considerations-login[require CSRF for log in] requests to protect against forging log in attempts. Spring Security's servlet support does this out of the box. [[servlet-considerations-csrf-logout]] === Logging Out -It is important to xref:overview/features/exploits/csrf.adoc#csrf-considerations-logout[require CSRF for log out] requests to protect against forging log out attempts. +It is important to xref:features/exploits/csrf.adoc#csrf-considerations-logout[require CSRF for log out] requests to protect against forging log out attempts. If CSRF protection is enabled (default), Spring Security's `LogoutFilter` to only process HTTP POST. This ensures that log out requires a CSRF token and that a malicious user cannot forcibly log out your users. @@ -367,7 +367,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { By default Spring Security stores the CSRF token in the `HttpSession`. This can lead to a situation where the session expires which means there is not an expected CSRF token to validate against. -We've already discussed xref:overview/features/exploits/csrf.adoc#csrf-considerations-login[general solutions] to session timeouts. +We've already discussed xref:features/exploits/csrf.adoc#csrf-considerations-login[general solutions] to session timeouts. This section discusses the specifics of CSRF timeouts as it pertains to the servlet support. It is simple to change storage of the expected CSRF token to be in a cookie. @@ -381,7 +381,7 @@ For an example of how to customize the `AccessDeniedHandler` refer to the provid [[servlet-csrf-considerations-multipart]] === Multipart (file upload) -We have xref:overview/features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] how protecting multipart requests (file uploads) from CSRF attacks causes a https://en.wikipedia.org/wiki/Chicken_or_the_egg[chicken and the egg] problem. +We have xref:features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] how protecting multipart requests (file uploads) from CSRF attacks causes a https://en.wikipedia.org/wiki/Chicken_or_the_egg[chicken and the egg] problem. This section discusses how to implement placing the CSRF token in the <> and <> within a servlet application. [NOTE] @@ -392,7 +392,7 @@ More information about using multipart forms with Spring can be found within the [[servlet-csrf-considerations-multipart-body]] ==== Place CSRF Token in the Body -We have xref:overview/features/exploits/csrf.adoc#csrf-considerations-multipart-body[already discussed] the tradeoffs of placing the CSRF token in the body. +We have xref:features/exploits/csrf.adoc#csrf-considerations-multipart-body[already discussed] the tradeoffs of placing the CSRF token in the body. In this section we will discuss how to configure Spring Security to read the CSRF from the body. In order to read the CSRF token from the body, the `MultipartFilter` is specified before the Spring Security filter. @@ -473,7 +473,7 @@ An example with a jsp is shown below [[servlet-csrf-considerations-override-method]] === HiddenHttpMethodFilter -We have xref:overview/features/exploits/csrf.adoc#csrf-considerations-multipart-body[already discussed] the trade-offs of placing the CSRF token in the body. +We have xref:features/exploits/csrf.adoc#csrf-considerations-multipart-body[already discussed] the trade-offs of placing the CSRF token in the body. In Spring's Servlet support, overriding the HTTP method is done using https://docs.spring.io/spring-framework/docs/5.2.x/javadoc-api/org/springframework/web/filter/reactive/HiddenHttpMethodFilter.html[HiddenHttpMethodFilter]. More information can be found in https://docs.spring.io/spring/docs/5.2.x/spring-framework-reference/web.html#mvc-rest-method-conversion[HTTP Method Conversion] section of the reference documentation. diff --git a/docs/modules/ROOT/pages/servlet/exploits/headers.adoc b/docs/modules/ROOT/pages/servlet/exploits/headers.adoc index 734271c6b0..535f3e976b 100644 --- a/docs/modules/ROOT/pages/servlet/exploits/headers.adoc +++ b/docs/modules/ROOT/pages/servlet/exploits/headers.adoc @@ -1,13 +1,13 @@ [[servlet-headers]] = Security HTTP Response Headers -xref:overview/features/exploits/headers.adoc#headers[Security HTTP Response Headers] can be used to increase the security of web applications. +xref:features/exploits/headers.adoc#headers[Security HTTP Response Headers] can be used to increase the security of web applications. This section is dedicated to servlet based support for Security HTTP Response Headers. [[servlet-headers-default]] == Default Security Headers -Spring Security provides a xref:overview/features/exploits/headers.adoc#headers-default[default set of Security HTTP Response Headers] to provide secure defaults. +Spring Security provides a xref:features/exploits/headers.adoc#headers-default[default set of Security HTTP Response Headers] to provide secure defaults. 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. @@ -71,7 +71,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { 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 is provided below: -If you are using Spring Security's Configuration the following will only add xref:overview/features/exploits/headers.adoc#headers-cache-control[Cache Control]. +If you are using Spring Security's Configuration the following will only add xref:features/exploits/headers.adoc#headers-cache-control[Cache Control]. .Customize Cache Control Headers ==== @@ -177,7 +177,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-cache-control]] == Cache Control -Spring Security includes xref:overview/features/exploits/headers.adoc#headers-cache-control[Cache Control] headers by default. +Spring Security includes xref:features/exploits/headers.adoc#headers-cache-control[Cache Control] headers by default. However, if you actually want to cache specific responses, your application can selectively invoke https://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. @@ -242,7 +242,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-content-type-options]] == Content Type Options -Spring Security includes xref:overview/features/exploits/headers.adoc#headers-content-type-options[Content-Type] headers by default. +Spring Security includes xref:features/exploits/headers.adoc#headers-content-type-options[Content-Type] headers by default. However, you can disable it with: .Content Type Options Disabled @@ -300,7 +300,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-hsts]] == HTTP Strict Transport Security (HSTS) -Spring Security provides the xref:overview/features/exploits/headers.adoc#headers-hsts[Strict Transport Security] header by default. +Spring Security provides the xref:features/exploits/headers.adoc#headers-hsts[Strict Transport Security] header by default. However, you can customize the results explicitly. For example, the following is an example of explicitly providing HSTS: @@ -366,7 +366,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-hpkp]] == HTTP Public Key Pinning (HPKP) -For passivity reasons, Spring Security provides servlet support for xref:overview/features/exploits/headers.adoc#headers-hpkp[HTTP Public Key Pinning] but it is xref:overview/features/exploits/headers.adoc#headers-hpkp-deprecated[no longer recommended]. +For passivity reasons, Spring Security provides servlet support for xref:features/exploits/headers.adoc#headers-hpkp[HTTP Public Key Pinning] but it is xref:features/exploits/headers.adoc#headers-hpkp-deprecated[no longer recommended]. You can enable HPKP headers with the following Configuration: @@ -437,7 +437,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-frame-options]] == X-Frame-Options -By default, Spring Security disables rendering within an iframe using xref:overview/features/exploits/headers.adoc#headers-frame-options[X-Frame-Options]. +By default, Spring Security disables rendering within an iframe using xref:features/exploits/headers.adoc#headers-frame-options[X-Frame-Options]. You can customize frame options to use the same origin within a Configuration using the following: @@ -560,7 +560,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-csp]] == Content Security Policy (CSP) -Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-csp[Content Security Policy] by default, because a reasonable default is impossible to know without context of the application. +Spring Security does not add xref:features/exploits/headers.adoc#headers-csp[Content Security Policy] by default, because a reasonable default is impossible to know without context of the application. 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: @@ -693,7 +693,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-referrer]] == Referrer Policy -Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-referrer[Referrer Policy] headers by default. +Spring Security does not add xref:features/exploits/headers.adoc#headers-referrer[Referrer Policy] headers by default. You can enable the Referrer Policy header using the configuration as shown below: .Referrer Policy @@ -753,7 +753,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-feature]] == Feature Policy -Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-feature[Feature Policy] headers by default. +Spring Security does not add xref:features/exploits/headers.adoc#headers-feature[Feature Policy] headers by default. The following `Feature-Policy` header: .Feature-Policy Example @@ -819,7 +819,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-permissions]] == Permissions Policy -Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-permissions[Permissions Policy] headers by default. +Spring Security does not add xref:features/exploits/headers.adoc#headers-permissions[Permissions Policy] headers by default. The following `Permissions-Policy` header: .Permissions-Policy Example @@ -889,7 +889,7 @@ class SecurityConfig : WebSecurityConfigurerAdapter() { [[servlet-headers-clear-site-data]] == Clear Site Data -Spring Security does not add xref:overview/features/exploits/headers.adoc#headers-clear-site-data[Clear-Site-Data] headers by default. +Spring Security does not add xref:features/exploits/headers.adoc#headers-clear-site-data[Clear-Site-Data] headers by default. The following Clear-Site-Data header: .Clear-Site-Data Example diff --git a/docs/modules/ROOT/pages/servlet/exploits/http.adoc b/docs/modules/ROOT/pages/servlet/exploits/http.adoc index 56e1cd16ab..3dc10d8ac7 100644 --- a/docs/modules/ROOT/pages/servlet/exploits/http.adoc +++ b/docs/modules/ROOT/pages/servlet/exploits/http.adoc @@ -1,7 +1,7 @@ [[servlet-http]] = HTTP -All HTTP based communication should be protected xref:overview/features/exploits/http.adoc#http[using TLS]. +All HTTP based communication should be protected xref:features/exploits/http.adoc#http[using TLS]. Below you can find details around Servlet specific features that assist with HTTPS usage. @@ -74,4 +74,4 @@ Spring Security provides support for xref:servlet/exploits/headers.adoc#servlet- [[servlet-http-proxy-server]] == Proxy Server Configuration -Spring Security xref:overview/features/exploits/http.adoc#http-proxy-server[integrates with proxy servers]. +Spring Security xref:features/exploits/http.adoc#http-proxy-server[integrates with proxy servers]. diff --git a/docs/modules/ROOT/pages/servlet/exploits/index.adoc b/docs/modules/ROOT/pages/servlet/exploits/index.adoc index e08b8eb72e..71c3a5eeb9 100644 --- a/docs/modules/ROOT/pages/servlet/exploits/index.adoc +++ b/docs/modules/ROOT/pages/servlet/exploits/index.adoc @@ -1,7 +1,7 @@ [[servlet-exploits]] = Protection Against Exploits -This section discusses Servlet specific support for xref:overview/features/exploits/index.adoc#exploits[Spring Security's protection against common exploits]. +This section discusses Servlet specific support for xref:features/exploits/index.adoc#exploits[Spring Security's protection against common exploits]. * xref:servlet/exploits/csrf.adoc[] * xref:servlet/exploits/headers.adoc[] diff --git a/docs/modules/ROOT/pages/servlet/hello/index.adoc b/docs/modules/ROOT/pages/servlet/hello/index.adoc index 756fee3381..deebb9bc46 100644 --- a/docs/modules/ROOT/pages/servlet/hello/index.adoc +++ b/docs/modules/ROOT/pages/servlet/hello/index.adoc @@ -12,7 +12,7 @@ For your convenience, you can download a minimal Spring Boot + Spring Security a [[servlet-hello-dependencies]] == Updating Dependencies -The only step you need to do is update the dependencies by using xref:overview/getting-spring-security.adoc#getting-maven-boot[Maven] or xref:overview/getting-spring-security.adoc#getting-gradle-boot[Gradle]. +The only step you need to do is update the dependencies by using xref:getting-spring-security.adoc#getting-maven-boot[Maven] or xref:getting-spring-security.adoc#getting-gradle-boot[Gradle]. [[servlet-hello-starting]] == Starting Hello Spring Security Boot diff --git a/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc b/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc index 75a94b9ba4..6ae9b57a86 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc @@ -246,7 +246,7 @@ Spring Security adds an additional layer of protection to provide https://en.wik [[websocket-sameorigin-csrf]] === Adding CSRF to Stomp Headers -By default Spring Security requires the xref:overview/features/exploits/csrf.adoc#csrf[CSRF token] in any CONNECT message type. +By default Spring Security requires the xref:features/exploits/csrf.adoc#csrf[CSRF token] in any CONNECT message type. This ensures that only a site that has access to the CSRF token can connect. Since only the *Same Origin* can access the CSRF token, external domains are not allowed to make a connection. @@ -357,7 +357,7 @@ When using the fallback options we need to relax a few security constraints to a === 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 xref:overview/features/exploits/headers.adoc#headers-frame-options[deny] the site from being framed to prevent Clickjacking attacks. +By default Spring Security will xref:features/exploits/headers.adoc#headers-frame-options[deny] the site from being framed to prevent Clickjacking attacks. To allow SockJS frame based transports to work, we need to configure Spring Security to allow the same origin to frame the content. You can customize X-Frame-Options with the xref:servlet/appendix/namespace.adoc#nsa-frame-options[frame-options] element. diff --git a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-client.adoc b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-client.adoc index affccf9e55..c02a4a0bc6 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-client.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-client.adoc @@ -702,7 +702,7 @@ spring: [NOTE] `+{baseUrl}+` resolves to `+{baseScheme}://{baseHost}{basePort}{basePath}+` -Configuring the `redirect-uri` with `URI` template variables is especially useful when the OAuth 2.0 Client is running behind a xref:overview/features/exploits/http.adoc#http-proxy-server[Proxy Server]. +Configuring the `redirect-uri` with `URI` template variables is especially useful when the OAuth 2.0 Client is running behind a xref:features/exploits/http.adoc#http-proxy-server[Proxy Server]. This ensures that the `X-Forwarded-*` headers are used when expanding the `redirect-uri`. ==== Customizing the Authorization Request diff --git a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-login.adoc b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-login.adoc index b1f7b736a3..db5ee9c506 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-login.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-login.adoc @@ -42,7 +42,7 @@ In the "Set a redirect URI" sub-section, ensure that the *Authorized redirect UR TIP: The default redirect URI template is `+{baseUrl}/login/oauth2/code/{registrationId}+`. The *_registrationId_* is a unique identifier for the xref:servlet/oauth2/oauth2-client.adoc#oauth2Client-client-registration[ClientRegistration]. -IMPORTANT: If the OAuth Client is running behind a proxy server, it is recommended to check xref:overview/features/exploits/http.adoc#http-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured. +IMPORTANT: If the OAuth Client is running behind a proxy server, it is recommended to check xref:features/exploits/http.adoc#http-proxy-server[Proxy Server Configuration] to ensure the application is correctly configured. Also, see the supported xref:servlet/oauth2/oauth2-client.adoc#oauth2Client-auth-code-redirect-uri[ `URI` template variables] for `redirect-uri`. diff --git a/docs/modules/ROOT/pages/overview/whats-new.adoc b/docs/modules/ROOT/pages/whats-new.adoc similarity index 100% rename from docs/modules/ROOT/pages/overview/whats-new.adoc rename to docs/modules/ROOT/pages/whats-new.adoc From 2da377ec889e64dc82ac9d897f531664c36b12d3 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Wed, 11 Aug 2021 08:48:40 -0500 Subject: [PATCH 13/34] Fix antora security version --- docs/antora.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/antora.yml b/docs/antora.yml index f1d4fefedb..8b2be6522e 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -1,6 +1,6 @@ name: security title: Spring Security -version: 5.6 +version: 6.0 start_page: ROOT:overview/index.adoc asciidoc: attributes: @@ -17,7 +17,7 @@ asciidoc: spring-core-version: 5.3.8 spring-framework-api-url: https://docs.spring.io/spring-framework/docs/current/javadoc-api/ spring-framework-reference-url: https://docs.spring.io/spring-framework/docs/current/reference/html/ - spring-security-version: 5.6.0 + spring-security-version: 6.0.0 unboundid-ldapsdk-version: 4.1 nav: - modules/ROOT/nav.adoc From 0b587dcef1178f1a5359de3a89dbfba18235ae1d Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Thu, 19 Aug 2021 14:17:49 -0500 Subject: [PATCH 14/34] Reduce folders --- docs/modules/ROOT/nav.adoc | 40 +- .../pages/servlet/appendix/namespace.adoc | 10 +- .../ROOT/pages/servlet/architecture.adoc | 248 +++++++++++ .../authentication/architecture/index.adoc | 8 +- .../pages/servlet/authentication/index.adoc | 2 +- .../pages/servlet/authentication/preauth.adoc | 2 +- .../servlet/authentication/rememberme.adoc | 2 +- .../servlet/authentication/unpwd/basic.adoc | 6 +- .../servlet/authentication/unpwd/form.adoc | 8 +- .../authorization/authorize-requests.adoc | 8 +- .../servlet/authorization/secure-objects.adoc | 2 +- .../ROOT/pages/servlet/cryptography.adoc | 264 ++++++++++++ .../ROOT/pages/servlet/getting-started.adoc | 74 ++++ .../ROOT/pages/servlet/integrations/mvc.adoc | 2 +- .../pages/servlet/java-configuration.adoc | 337 +++++++++++++++ .../pages/servlet/kotlin-configuration.adoc | 99 +++++ .../servlet/oauth2/oauth2-resourceserver.adoc | 6 +- .../ROOT/pages/servlet/saml2/index.adoc | 6 +- .../ROOT/pages/servlet/xml-namespace.adoc | 402 ++++++++++++++++++ 19 files changed, 1475 insertions(+), 51 deletions(-) create mode 100644 docs/modules/ROOT/pages/servlet/architecture.adoc create mode 100644 docs/modules/ROOT/pages/servlet/cryptography.adoc create mode 100644 docs/modules/ROOT/pages/servlet/getting-started.adoc create mode 100644 docs/modules/ROOT/pages/servlet/java-configuration.adoc create mode 100644 docs/modules/ROOT/pages/servlet/kotlin-configuration.adoc create mode 100644 docs/modules/ROOT/pages/servlet/xml-namespace.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 31084773ca..e45379856d 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -1,20 +1,20 @@ -* Overview -** xref:prerequisites.adoc[Prerequisites] -** xref:community.adoc[Community] -** xref:whats-new.adoc[What's New] -** xref:getting-spring-security.adoc[Getting Spring Security] -** xref:features/index.adoc[Features] -*** xref:features/authentication/index.adoc[Authentication] -**** xref:features/authentication/password-storage.adoc[Password Storage] -*** xref:features/exploits/index.adoc[Protection Against Exploits] -**** xref:features/exploits/csrf.adoc[CSRF] -**** xref:features/exploits/headers.adoc[HTTP Headers] -**** xref:features/exploits/http.adoc[HTTP Requests] -** xref:modules.adoc[Project Modules & Dependencies] -** xref:samples.adoc[Samples] +* xref:index.adoc[Overview] +* xref:prerequisites.adoc[Prerequisites] +* xref:community.adoc[Community] +* xref:whats-new.adoc[What's New] +* xref:getting-spring-security.adoc[Getting Spring Security] +* xref:features/index.adoc[Features] +** xref:features/authentication/index.adoc[Authentication] +*** xref:features/authentication/password-storage.adoc[Password Storage] +** xref:features/exploits/index.adoc[Protection Against Exploits] +*** xref:features/exploits/csrf.adoc[CSRF] +*** xref:features/exploits/headers.adoc[HTTP Headers] +*** xref:features/exploits/http.adoc[HTTP Requests] +* xref:modules.adoc[Project Modules & Dependencies] +* xref:samples.adoc[Samples] * xref:servlet/index.adoc[Servlet Applications] -** xref:servlet/hello/index.adoc[Hello Spring Security] -** xref:servlet/architecture/index.adoc[The Big Picture] +** xref:servlet/getting-started.adoc[Getting Started] +** xref:servlet/architecture.adoc[The Big Picture] ** Authentication *** xref:servlet/authentication/architecture/index.adoc[Authentication Architecture] *** xref:servlet/authentication/unpwd/index.adoc[Username/Password] @@ -69,13 +69,13 @@ *** xref:servlet/integrations/cors.adoc[Spring's CORS Support] *** xref:servlet/integrations/jsp-taglibs.adoc[JSP Taglib] ** Configuration -*** xref:servlet/java-configuration/index.adoc[Java Configuration] -*** xref:servlet/kotlin-configuration/index.adoc[Kotlin Configuration] -*** xref:servlet/namespace/index.adoc[Namespace Configuration] +*** xref:servlet/java-configuration.adoc[Java Configuration] +*** xref:servlet/kotlin-configuration.adoc[Kotlin Configuration] +*** xref:servlet/xml-namespace.adoc[Namespace Configuration] ** xref:servlet/test/index.adoc[Testing] *** xref:servlet/test/method.adoc[Method Security] *** xref:servlet/test/mockmvc.adoc[MockMvc Support] -** xref:servlet/crypto/index.adoc[Cryptography] +** xref:servlet/cryptography.adoc[Cryptography] ** xref:servlet/appendix/index.adoc[Appendix] *** xref:servlet/appendix/database-schema.adoc[Database Schemas] *** xref:servlet/appendix/namespace.adoc[XML Namespace] diff --git a/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc b/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc index 794f077c19..d6c2dae79f 100644 --- a/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc +++ b/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc @@ -1,7 +1,7 @@ [[appendix-namespace]] = The Security Namespace This appendix provides a reference to the elements available in the security namespace and information on the underlying beans they create (a knowledge of the individual classes and how they work together is assumed - you can find more information in the project Javadoc and elsewhere in this document). -If you haven't used the namespace before, please read the xref:servlet/namespace/index.adoc#ns-config[introductory chapter] on namespace configuration, as this is intended as a supplement to the information there. +If you haven't used the namespace before, please read the xref:servlet/xml-namespace.adoc#ns-config[introductory chapter] on namespace configuration, as this is intended as a supplement to the information there. Using a good quality XML editor while editing a configuration based on the schema is recommended as this will provide contextual information on which elements and attributes are available as well as comments explaining their purpose. The namespace is written in https://relaxng.org/[RELAX NG] Compact format and later converted into an XSD schema. If you are familiar with this format, you may wish to examine the https://raw.githubusercontent.com/spring-projects/spring-security/main/config/src/main/resources/org/springframework/security/config/spring-security-4.1.rnc[schema file] directly. @@ -19,10 +19,10 @@ This may include sensitive information, such as request parameters or headers, a === If you use an `` element within your application, a `FilterChainProxy` bean named "springSecurityFilterChain" is created and the configuration within the element is used to build a filter chain within `FilterChainProxy`. -As of Spring Security 3.1, additional `http` elements can be used to add extra filter chains footnote:[See the pass:specialcharacters,macros[xref:servlet/namespace/index.adoc#ns-web-xml[introductory chapter]] for how to set up the mapping from your `web.xml` ]. +As of Spring Security 3.1, additional `http` elements can be used to add extra filter chains footnote:[See the pass:specialcharacters,macros[xref:servlet/xml-namespace.adoc#ns-web-xml[introductory chapter]] for how to set up the mapping from your `web.xml` ]. Some core filters are always created in a filter chain and others will be added to the stack depending on the attributes and child elements which are present. The positions of the standard filters are fixed (see -xref:servlet/namespace/index.adoc#filter-stack[the filter order table] in the namespace introduction), removing a common source of errors with previous versions of the framework when users had to configure the filter chain explicitly in the +xref:servlet/xml-namespace.adoc#filter-stack[the filter order table] in the namespace introduction), removing a common source of errors with previous versions of the framework when users had to configure the filter chain explicitly in the `FilterChainProxy` bean. You can, of course, still do this if you need full control of the configuration. @@ -179,7 +179,7 @@ The default value is true. [[nsa-access-denied-handler]] === This element allows you to set the `errorPage` property for the default `AccessDeniedHandler` used by the `ExceptionTranslationFilter`, using the <> attribute, or to supply your own implementation using the<> attribute. -This is discussed in more detail in the section on the xref:servlet/architecture/index.adoc#servlet-exceptiontranslationfilter[ExceptionTranslationFilter]. +This is discussed in more detail in the section on the xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[ExceptionTranslationFilter]. [[nsa-access-denied-handler-parents]] @@ -710,7 +710,7 @@ Default is any HTTP method except "GET", "TRACE", "HEAD", "OPTIONS". === This element is used to add a filter to the filter chain. It doesn't create any additional beans but is used to select a bean of type `javax.servlet.Filter` which is already defined in the application context and add that at a particular position in the filter chain maintained by Spring Security. -Full details can be found in the xref:servlet/namespace/index.adoc#ns-custom-filters[ namespace chapter]. +Full details can be found in the xref:servlet/xml-namespace.adoc#ns-custom-filters[ namespace chapter]. [[nsa-custom-filter-parents]] diff --git a/docs/modules/ROOT/pages/servlet/architecture.adoc b/docs/modules/ROOT/pages/servlet/architecture.adoc new file mode 100644 index 0000000000..ff38d362a6 --- /dev/null +++ b/docs/modules/ROOT/pages/servlet/architecture.adoc @@ -0,0 +1,248 @@ +[[servlet-architecture]] += Servlet Security: The Big Picture +:figures: servlet/architecture + +This section discusses Spring Security's high level architecture within Servlet based applications. +We build on this high level understanding within xref:servlet/authentication/index.adoc#servlet-authentication[Authentication], xref:servlet/authorization/index.adoc#servlet-authorization[Authorization], xref:servlet/exploits/index.adoc#servlet-exploits[Protection Against Exploits] sections of the reference. +// FIXME: Add links to other sections of architecture + +[[servlet-filters-review]] +== A Review of ``Filter``s + +Spring Security's Servlet support is based on Servlet ``Filter``s, so it is helpful to look at the role of ``Filter``s generally first. +The picture below shows the typical layering of the handlers for a single HTTP request. + +.FilterChain +[[servlet-filterchain-figure]] +image::{figures}/filterchain.png[] + +The client sends a request to the application, and the container creates a `FilterChain` which contains the ``Filter``s and `Servlet` that should process the `HttpServletRequest` based on the path of the request URI. +In a Spring MVC application the `Servlet` is an instance of {spring-framework-reference-url}web.html#mvc-servlet[`DispatcherServlet`]. +At most one `Servlet` can handle a single `HttpServletRequest` and `HttpServletResponse`. +However, more than one `Filter` can be used to: + +* Prevent downstream ``Filter``s or the `Servlet` from being invoked. +In this instance the `Filter` will typically write the `HttpServletResponse`. +* Modify the `HttpServletRequest` or `HttpServletResponse` used by the downstream ``Filter``s and `Servlet` + +The power of the `Filter` comes from the `FilterChain` that is passed into it. + +.`FilterChain` Usage Example +==== +.Java +[source,java,role="primary"] +---- +public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { + // do something before the rest of the application + chain.doFilter(request, response); // invoke the rest of the application + // do something after the rest of the application +} +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { + // do something before the rest of the application + chain.doFilter(request, response) // invoke the rest of the application + // do something after the rest of the application +} +---- +==== + +Since a `Filter` only impacts downstream ``Filter``s and the `Servlet`, the order each `Filter` is invoked is extremely important. + + +[[servlet-delegatingfilterproxy]] +== DelegatingFilterProxy + +Spring provides a `Filter` implementation named {spring-framework-api-url}org/springframework/web/filter/DelegatingFilterProxy.html[`DelegatingFilterProxy`] that allows bridging between the Servlet container's lifecycle and Spring's `ApplicationContext`. +The Servlet container allows registering ``Filter``s using its own standards, but it is not aware of Spring defined Beans. +`DelegatingFilterProxy` can be registered via standard Servlet container mechanisms, but delegate all the work to a Spring Bean that implements `Filter`. + +Here is a picture of how `DelegatingFilterProxy` fits into the <>. + +.DelegatingFilterProxy +[[servlet-delegatingfilterproxy-figure]] +image::{figures}/delegatingfilterproxy.png[] + +`DelegatingFilterProxy` looks up __Bean Filter~0~__ from the `ApplicationContext` and then invokes __Bean Filter~0~__. +The pseudo code of `DelegatingFilterProxy` can be seen below. + +.`DelegatingFilterProxy` Pseudo Code +==== +.Java +[source,java,role="primary",subs="+quotes,+macros"] +---- +public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { + // Lazily get Filter that was registered as a Spring Bean + // For the example in <> `delegate` is an instance of __Bean Filter~0~__ + Filter delegate = getFilterBean(someBeanName); + // delegate work to the Spring Bean + delegate.doFilter(request, response); +} +---- + +.Kotlin +[source,kotlin,role="secondary",subs="+quotes,+macros"] +---- +fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { + // Lazily get Filter that was registered as a Spring Bean + // For the example in <> `delegate` is an instance of __Bean Filter~0~__ + val delegate: Filter = getFilterBean(someBeanName) + // delegate work to the Spring Bean + delegate.doFilter(request, response) +} +---- +==== + +Another benefit of `DelegatingFilterProxy` is that it allows delaying looking `Filter` bean instances. +This is important because the container needs to register the `Filter` instances before the container can startup. +However, Spring typically uses a `ContextLoaderListener` to load the Spring Beans which will not be done until after the `Filter` instances need to be registered. + +[[servlet-filterchainproxy]] +== FilterChainProxy + +Spring Security's Servlet support is contained within `FilterChainProxy`. +`FilterChainProxy` is a special `Filter` provided by Spring Security that allows delegating to many `Filter` instances through <>. +Since `FilterChainProxy` is a Bean, it is typically wrapped in a <>. + +.FilterChainProxy +[[servlet-filterchainproxy-figure]] +image::{figures}/filterchainproxy.png[] + +[[servlet-securityfilterchain]] +== SecurityFilterChain + +{security-api-url}org/springframework/security/web/SecurityFilterChain.html[`SecurityFilterChain`] is used by <> to determine which Spring Security ``Filter``s should be invoked for this request. + +.SecurityFilterChain +[[servlet-securityfilterchain-figure]] +image::{figures}/securityfilterchain.png[] + +The <> in `SecurityFilterChain` are typically Beans, but they are registered with `FilterChainProxy` instead of <>. +`FilterChainProxy` provides a number of advantages to registering directly with the Servlet container or <>. +First, it provides a starting point for all of Spring Security's Servlet support. +For that reason, if you are attempting to troubleshoot Spring Security's Servlet support, adding a debug point in `FilterChainProxy` is a great place to start. + +Second, since `FilterChainProxy` is central to Spring Security usage it can perform tasks that are not viewed as optional. +// FIXME: Add a link to SecurityContext +For example, it clears out the `SecurityContext` to avoid memory leaks. +It also applies Spring Security's xref:servlet/exploits/firewall.adoc#servlet-httpfirewall[`HttpFirewall`] to protect applications against certain types of attacks. + +In addition, it provides more flexibility in determining when a `SecurityFilterChain` should be invoked. +In a Servlet container, ``Filter``s are invoked based upon the URL alone. +// FIXME: Link to RequestMatcher +However, `FilterChainProxy` can determine invocation based upon anything in the `HttpServletRequest` by leveraging the `RequestMatcher` interface. + +In fact, `FilterChainProxy` can be used to determine which `SecurityFilterChain` should be used. +This allows providing a totally separate configuration for different _slices_ of your application. + +.Multiple SecurityFilterChain +[[servlet-multi-securityfilterchain-figure]] +image::{figures}/multi-securityfilterchain.png[] + +In the <> Figure `FilterChainProxy` decides which `SecurityFilterChain` should be used. +Only the first `SecurityFilterChain` that matches will be invoked. +If a URL of `/api/messages/` is requested, it will first match on ``SecurityFilterChain~0~``'s pattern of `+/api/**+`, so only `SecurityFilterChain~0~` will be invoked even though it also matches on ``SecurityFilterChain~n~``. +If a URL of `/messages/` is requested, it will not match on ``SecurityFilterChain~0~``'s pattern of `+/api/**+`, so `FilterChainProxy` will continue trying each `SecurityFilterChain`. +Assuming that no other, `SecurityFilterChain` instances match `SecurityFilterChain~n~` will be invoked. +// FIXME add link to pattern matching + +Notice that `SecurityFilterChain~0~` has only three security ``Filter``s instances configured. +However, `SecurityFilterChain~n~` has four security ``Filter``s configured. +It is important to note that each `SecurityFilterChain` can be unique and configured in isolation. +In fact, a `SecurityFilterChain` might have zero security ``Filter``s if the application wants Spring Security to ignore certain requests. +// FIXME: add link to configuring multiple `SecurityFilterChain` instances + +[[servlet-security-filters]] +== Security Filters + +The Security Filters are inserted into the <> with the <> API. +The <>s matters. +It is typically not necessary to know the ordering of Spring Security's ``Filter``s. +However, there are times that it is beneficial to know the ordering + +Below is a comprehensive list of Spring Security Filter ordering: + +* ChannelProcessingFilter +* WebAsyncManagerIntegrationFilter +* SecurityContextPersistenceFilter +* HeaderWriterFilter +* CorsFilter +* CsrfFilter +* LogoutFilter +* OAuth2AuthorizationRequestRedirectFilter +* Saml2WebSsoAuthenticationRequestFilter +* X509AuthenticationFilter +* AbstractPreAuthenticatedProcessingFilter +* CasAuthenticationFilter +* OAuth2LoginAuthenticationFilter +* Saml2WebSsoAuthenticationFilter +* xref:servlet/authentication/unpwd/form.adoc#servlet-authentication-usernamepasswordauthenticationfilter[`UsernamePasswordAuthenticationFilter`] +* OpenIDAuthenticationFilter +* DefaultLoginPageGeneratingFilter +* DefaultLogoutPageGeneratingFilter +* ConcurrentSessionFilter +* xref:servlet/authentication/unpwd/digest.adoc#servlet-authentication-digest[`DigestAuthenticationFilter`] +* BearerTokenAuthenticationFilter +* xref:servlet/authentication/unpwd/basic.adoc#servlet-authentication-basic[`BasicAuthenticationFilter`] +* RequestCacheAwareFilter +* SecurityContextHolderAwareRequestFilter +* JaasApiIntegrationFilter +* RememberMeAuthenticationFilter +* AnonymousAuthenticationFilter +* OAuth2AuthorizationCodeGrantFilter +* SessionManagementFilter +* <> +* xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] +* SwitchUserFilter + +[[servlet-exceptiontranslationfilter]] +== Handling Security Exceptions + + +The {security-api-url}org/springframework/security/web/access/ExceptionTranslationFilter.html[`ExceptionTranslationFilter`] allows translation of {security-api-url}org/springframework/security/access/AccessDeniedException.html[`AccessDeniedException`] and {security-api-url}/org/springframework/security/core/AuthenticationException.html[`AuthenticationException`] into HTTP responses. + +`ExceptionTranslationFilter` is inserted into the <> as one of the <>. + +image::{figures}/exceptiontranslationfilter.png[] + + +* image:{icondir}/number_1.png[] First, the `ExceptionTranslationFilter` invokes `FilterChain.doFilter(request, response)` to invoke the rest of the application. +* image:{icondir}/number_2.png[] If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__. +** The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out +** The `HttpServletRequest` is saved in the {security-api-url}org/springframework/security/web/savedrequest/RequestCache.html[`RequestCache`]. +When the user successfully authenticates, the `RequestCache` is used to replay the original request. +// FIXME: add link to authentication success +** The `AuthenticationEntryPoint` is used to request credentials from the client. +For example, it might redirect to a log in page or send a `WWW-Authenticate` header. +// FIXME: link to AuthenticationEntryPoint +* image:{icondir}/number_3.png[] Otherwise if it is an `AccessDeniedException`, then __Access Denied__. +The `AccessDeniedHandler` is invoked to handle access denied. +// FIXME: link to AccessDeniedHandler + +[NOTE] +==== +If the application does not throw an `AccessDeniedException` or an `AuthenticationException`, then `ExceptionTranslationFilter` does not do anything. +==== + +The pseudocode for `ExceptionTranslationFilter` looks something like this: + +.ExceptionTranslationFilter pseudocode +[source,java] +---- +try { + filterChain.doFilter(request, response); // <1> +} catch (AccessDeniedException | AuthenticationException ex) { + if (!authenticated || ex instanceof AuthenticationException) { + startAuthentication(); // <2> + } else { + accessDenied(); // <3> + } +} +---- +<1> You will recall from <> that invoking `FilterChain.doFilter(request, response)` is the equivalent of invoking the rest of the application. +This means that if another part of the application, (i.e. xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] or method security) throws an `AuthenticationException` or `AccessDeniedException` it will be caught and handled here. +<2> If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__. +<3> Otherwise, __Access Denied__ diff --git a/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc index 75808178cc..d37897800c 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc @@ -2,7 +2,7 @@ = Servlet Authentication Architecture :figures: servlet/authentication/architecture -This discussion expands on xref:servlet/architecture/index.adoc#servlet-architecture[Servlet Security: The Big Picture] to describe the main architectural components of Spring Security's used in Servlet authentication. +This discussion expands on xref:servlet/architecture.adoc#servlet-architecture[Servlet Security: The Big Picture] to describe the main architectural components of Spring Security's used in Servlet authentication. If you need concrete flows that explain how these pieces fit together, look at the xref:servlet/authentication/index.adoc#servlet-authentication-mechanisms[Authentication Mechanism] specific sections. * <> - The `SecurityContextHolder` is where Spring Security stores the details of who is xref:features/authentication/index.adoc#authentication[authenticated]. @@ -94,7 +94,7 @@ val authorities = authentication.authorities By default the `SecurityContextHolder` uses a `ThreadLocal` to store these details, which means that the `SecurityContext` is always available to methods in the same thread, even if the `SecurityContext` is not explicitly passed around as an argument to those methods. Using a `ThreadLocal` in this way is quite safe if care is taken to clear the thread after the present principal's request is processed. -Spring Security's xref:servlet/architecture/index.adoc#servlet-filterchainproxy[FilterChainProxy] ensures that the `SecurityContext` is always cleared. +Spring Security's xref:servlet/architecture.adoc#servlet-filterchainproxy[FilterChainProxy] ensures that the `SecurityContext` is always cleared. Some applications aren't entirely suitable for using a `ThreadLocal`, because of the specific way they work with threads. For example, a Swing client might want all threads in a Java Virtual Machine to use the same security context. @@ -152,7 +152,7 @@ Of course, Spring Security is expressly designed to handle this common requireme == AuthenticationManager {security-api-url}org/springframework/security/authentication/AuthenticationManager.html[`AuthenticationManager`] is the API that defines how Spring Security's Filters perform xref:features/authentication/index.adoc#authentication[authentication]. -The <> that is returned is then set on the <> by the controller (i.e. xref:servlet/architecture/index.adoc#servlet-security-filters[Spring Security's ``Filters``s]) that invoked the `AuthenticationManager`. +The <> that is returned is then set on the <> by the controller (i.e. xref:servlet/architecture.adoc#servlet-security-filters[Spring Security's ``Filters``s]) that invoked the `AuthenticationManager`. If you are not integrating with __Spring Security's ``Filters``s__ you can set the `SecurityContextHolder` directly and are not required to use an `AuthenticationManager`. While the implementation of `AuthenticationManager` could be anything, the most common implementation is <>. @@ -179,7 +179,7 @@ The parent can be any type of `AuthenticationManager`, but it is often an instan image::{figures}/providermanager-parent.png[] In fact, multiple `ProviderManager` instances might share the same parent `AuthenticationManager`. -This is somewhat common in scenarios where there are multiple xref:servlet/architecture/index.adoc#servlet-securityfilterchain[`SecurityFilterChain`] instances that have some authentication in common (the shared parent `AuthenticationManager`), but also different authentication mechanisms (the different `ProviderManager` instances). +This is somewhat common in scenarios where there are multiple xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] instances that have some authentication in common (the shared parent `AuthenticationManager`), but also different authentication mechanisms (the different `ProviderManager` instances). image::{figures}/providermanagers-parent.png[] diff --git a/docs/modules/ROOT/pages/servlet/authentication/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/index.adoc index 44694964b1..bcb81431d1 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/index.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/index.adoc @@ -2,7 +2,7 @@ = Authentication Spring Security provides comprehensive support for xref:features/authentication/index.adoc#authentication[Authentication]. -We start by discussing the overall xref:servlet/architecture/index.adoc#servlet-architecture[Servlet Authentication Architecture]. +We start by discussing the overall xref:servlet/architecture.adoc#servlet-architecture[Servlet Authentication Architecture]. As you might expect, this section is more abstract describing the architecture without much discussion on how it applies to concrete flows. If you prefer, you can refer to <> for concrete ways in which users can authenticate. diff --git a/docs/modules/ROOT/pages/servlet/authentication/preauth.adoc b/docs/modules/ROOT/pages/servlet/authentication/preauth.adoc index b6ad3ad06e..f6d105c462 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/preauth.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/preauth.adoc @@ -139,7 +139,7 @@ A typical configuration using this filter would look like this: ---- -We've assumed here that the xref:servlet/namespace/index.adoc#ns-config[security namespace] is being used for configuration. +We've assumed here that the xref:servlet/xml-namespace.adoc#ns-config[security namespace] is being used for configuration. It's also assumed that you have added a `UserDetailsService` (called "userDetailsService") to your configuration to load the user's roles. diff --git a/docs/modules/ROOT/pages/servlet/authentication/rememberme.adoc b/docs/modules/ROOT/pages/servlet/authentication/rememberme.adoc index e954e72ac7..87433fd297 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/rememberme.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/rememberme.adoc @@ -36,7 +36,7 @@ If a principal is aware a token has been captured, they can easily change their If more significant security is needed you should use the approach described in the next section. Alternatively, remember-me services should simply not be used at all. -If you are familiar with the topics discussed in the chapter on xref:servlet/namespace/index.adoc#ns-config[namespace configuration], you can enable remember-me authentication just by adding the `` element: +If you are familiar with the topics discussed in the chapter on xref:servlet/xml-namespace.adoc#ns-config[namespace configuration], you can enable remember-me authentication just by adding the `` element: [source,xml] ---- diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc index 1796621b89..2040aec005 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc @@ -11,13 +11,13 @@ First, we see the https://tools.ietf.org/html/rfc7235#section-4.1[WWW-Authentica .Sending WWW-Authenticate Header image::{figures}/basicauthenticationentrypoint.png[] -The figure builds off our xref:servlet/architecture/index.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. +The figure builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource `/private` for which it is not authorized. image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. -image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture/index.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__. +image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__. The configured xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/web/authentication/www/BasicAuthenticationEntryPoint.html[`BasicAuthenticationEntryPoint`] which sends a WWW-Authenticate header. The `RequestCache` is typically a `NullRequestCache` that does not save the request since the client is capable of replaying the requests it originally requested. @@ -28,7 +28,7 @@ Below is the flow for the username and password being processed. .Authenticating Username and Password image::{figures}/basicauthenticationfilter.png[] -The figure builds off our xref:servlet/architecture/index.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. +The figure builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. image:{icondir}/number_1.png[] When the user submits their username and password, the `BasicAuthenticationFilter` creates a `UsernamePasswordAuthenticationToken` which is a type of xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] by extracting the username and password from the `HttpServletRequest`. diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc index 368187c13f..2bee66af93 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc @@ -12,13 +12,13 @@ First, we see how the user is redirected to the log in form. .Redirecting to the Log In Page image::{figures}/loginurlauthenticationentrypoint.png[] -The figure builds off our xref:servlet/architecture/index.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. +The figure builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource `/private` for which it is not authorized. image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. -image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture/index.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__ and sends a redirect to the log in page with the configured xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`]. +image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__ and sends a redirect to the log in page with the configured xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`]. In most cases the `AuthenticationEntryPoint` is an instance of {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[`LoginUrlAuthenticationEntryPoint`]. image:{icondir}/number_4.png[] The browser will then request the log in page that it was redirected to. @@ -32,7 +32,7 @@ The `UsernamePasswordAuthenticationFilter` extends xref:servlet/authentication/a .Authenticating Username and Password image::{figures}/usernamepasswordauthenticationfilter.png[] -The figure builds off our xref:servlet/architecture/index.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. +The figure builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. image:{icondir}/number_1.png[] When the user submits their username and password, the `UsernamePasswordAuthenticationFilter` creates a `UsernamePasswordAuthenticationToken` which is a type of xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] by extracting the username and password from the `HttpServletRequest`. @@ -59,7 +59,7 @@ image:{icondir}/number_4.png[] If authentication is successful, then __Success__ If remember me is not configured, this is a no-op. // FIXME: link to rememberme * `ApplicationEventPublisher` publishes an `InteractiveAuthenticationSuccessEvent`. -* The `AuthenticationSuccessHandler` is invoked. Typically this is a `SimpleUrlAuthenticationSuccessHandler` which will redirect to a request saved by xref:servlet/architecture/index.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] when we redirect to the log in page. +* The `AuthenticationSuccessHandler` is invoked. Typically this is a `SimpleUrlAuthenticationSuccessHandler` which will redirect to a request saved by xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] when we redirect to the log in page. [[servlet-authentication-form-min]] Spring Security form log in is enabled by default. diff --git a/docs/modules/ROOT/pages/servlet/authorization/authorize-requests.adoc b/docs/modules/ROOT/pages/servlet/authorization/authorize-requests.adoc index 1018d00324..bfeba777f8 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/authorize-requests.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/authorize-requests.adoc @@ -2,10 +2,10 @@ = Authorize HttpServletRequest with FilterSecurityInterceptor :figures: servlet/authorization -This section builds on xref:servlet/architecture/index.adoc#servlet-architecture[Servlet Architecture and Implementation] by digging deeper into how xref:servlet/authorization/index.adoc#servlet-authorization[authorization] works within Servlet based applications. +This section builds on xref:servlet/architecture.adoc#servlet-architecture[Servlet Architecture and Implementation] by digging deeper into how xref:servlet/authorization/index.adoc#servlet-authorization[authorization] works within Servlet based applications. The {security-api-url}org/springframework/security/web/access/intercept/FilterSecurityInterceptor.html[`FilterSecurityInterceptor`] provides xref:servlet/authorization/index.adoc#servlet-authorization[authorization] for ``HttpServletRequest``s. -It is inserted into the xref:servlet/architecture/index.adoc#servlet-filterchainproxy[FilterChainProxy] as one of the xref:servlet/architecture/index.adoc#servlet-security-filters[Security Filters]. +It is inserted into the xref:servlet/architecture.adoc#servlet-filterchainproxy[FilterChainProxy] as one of the xref:servlet/architecture.adoc#servlet-security-filters[Security Filters]. .Authorize HttpServletRequest image::{figures}/filtersecurityinterceptor.png[] @@ -16,8 +16,8 @@ image::{figures}/filtersecurityinterceptor.png[] * image:{icondir}/number_3.png[] Next, it passes the `FilterInvocation` to `SecurityMetadataSource` to get the ``ConfigAttribute``s. * image:{icondir}/number_4.png[] Finally, it passes the `Authentication`, `FilterInvocation`, and ``ConfigAttribute``s to the `AccessDecisionManager`. ** image:{icondir}/number_5.png[] If authorization is denied, an `AccessDeniedException` is thrown. -In this case the xref:servlet/architecture/index.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] handles the `AccessDeniedException`. -** image:{icondir}/number_6.png[] If access is granted, `FilterSecurityInterceptor` continues with the xref:servlet/architecture/index.adoc#servlet-filters-review[FilterChain] which allows the application to process normally. +In this case the xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] handles the `AccessDeniedException`. +** image:{icondir}/number_6.png[] If access is granted, `FilterSecurityInterceptor` continues with the xref:servlet/architecture.adoc#servlet-filters-review[FilterChain] which allows the application to process normally. // configuration (xml/java) diff --git a/docs/modules/ROOT/pages/servlet/authorization/secure-objects.adoc b/docs/modules/ROOT/pages/servlet/authorization/secure-objects.adoc index 59d2f96e4b..0e915f5a40 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/secure-objects.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/secure-objects.adoc @@ -5,7 +5,7 @@ [[aop-alliance]] == AOP Alliance (MethodInvocation) Security Interceptor Prior to Spring Security 2.0, securing ``MethodInvocation``s needed quite a lot of boiler plate configuration. -Now the recommended approach for method security is to use xref:servlet/namespace/index.adoc#ns-method-security[namespace configuration]. +Now the recommended approach for method security is to use xref:servlet/xml-namespace.adoc#ns-method-security[namespace configuration]. This way the method security infrastructure beans are configured automatically for you so you don't really need to know about the implementation classes. We'll just provide a quick overview of the classes that are involved here. diff --git a/docs/modules/ROOT/pages/servlet/cryptography.adoc b/docs/modules/ROOT/pages/servlet/cryptography.adoc new file mode 100644 index 0000000000..137b3e8b69 --- /dev/null +++ b/docs/modules/ROOT/pages/servlet/cryptography.adoc @@ -0,0 +1,264 @@ +[[crypto]] += Spring Security Crypto Module + + +[[spring-security-crypto-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 +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 +Use the `Encryptors.stronger` factory method to construct a BytesEncryptor: + +.BytesEncryptor +==== +.Java +[source,java,role="primary"] +---- +Encryptors.stronger("password", "salt"); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +Encryptors.stronger("password", "salt") +---- +==== + +The "stronger" encryption method creates an encryptor using 256 bit AES encryption with +Galois Counter Mode (GCM). +It derives the secret key using PKCS #5's PBKDF2 (Password-Based Key Derivation Function #2). +This method requires Java 6. +The password used to generate the SecretKey should be kept in a secure place and not be shared. +The salt is used to prevent dictionary attacks against the key in the event your encrypted data is compromised. +A 16-byte random initialization vector is also applied so each encrypted message is unique. + +The provided salt should be in hex-encoded String form, be random, and be at least 8 bytes in length. +Such a salt may be generated using a KeyGenerator: + +.Generating a key +==== +.Java +[source,java,role="primary"] +---- +String salt = KeyGenerators.string().generateKey(); // generates a random 8-byte salt that is then hex-encoded +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +val salt = KeyGenerators.string().generateKey() // generates a random 8-byte salt that is then hex-encoded +---- +==== + +Users may also use the `standard` encryption method, which is 256-bit AES in Cipher Block Chaining (CBC) Mode. +This mode is not https://en.wikipedia.org/wiki/Authenticated_encryption[authenticated] and does not provide any +guarantees about the authenticity of the data. +For a more secure alternative, users should prefer `Encryptors.stronger`. + +[[spring-security-crypto-encryption-text]] +=== TextEncryptor +Use the Encryptors.text factory method to construct a standard TextEncryptor: + +.TextEncryptor +==== +.Java +[source,java,role="primary"] +---- +Encryptors.text("password", "salt"); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +Encryptors.text("password", "salt") +---- +==== + +A TextEncryptor uses a standard BytesEncryptor to encrypt text data. +Encrypted results are returned as hex-encoded strings for easy storage on the filesystem or in the database. + +Use the Encryptors.queryableText factory method to construct a "queryable" TextEncryptor: + +.Queryable TextEncryptor +==== +.Java +[source,java,role="primary"] +---- +Encryptors.queryableText("password", "salt"); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +Encryptors.queryableText("password", "salt") +---- +==== + +The difference between a queryable TextEncryptor and a standard TextEncryptor has to do with initialization vector (iv) handling. +The iv used in a queryable TextEncryptor#encrypt operation is shared, or constant, and is not randomly generated. +This means the same text encrypted multiple times will always produce the same encryption result. +This is less secure, but necessary for encrypted data that needs to be queried against. +An example of queryable encrypted text would be an OAuth apiKey. + +[[spring-security-crypto-keygenerators]] +== 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 +Use the KeyGenerators.secureRandom factory methods to generate a BytesKeyGenerator backed by a SecureRandom instance: + +.BytesKeyGenerator +==== +.Java +[source,java,role="primary"] +---- +BytesKeyGenerator generator = KeyGenerators.secureRandom(); +byte[] key = generator.generateKey(); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +val generator = KeyGenerators.secureRandom() +val key = generator.generateKey() +---- +==== + +The default key length is 8 bytes. +There is also a KeyGenerators.secureRandom variant that provides control over the key length: + +.KeyGenerators.secureRandom +==== +.Java +[source,java,role="primary"] +---- +KeyGenerators.secureRandom(16); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +KeyGenerators.secureRandom(16) +---- +==== + +Use the KeyGenerators.shared factory method to construct a BytesKeyGenerator that always returns the same key on every invocation: + +.KeyGenerators.shared +==== +.Java +[source,java,role="primary"] +---- +KeyGenerators.shared(16); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +KeyGenerators.shared(16) +---- +==== + +=== StringKeyGenerator +Use the KeyGenerators.string factory method to construct a 8-byte, SecureRandom KeyGenerator that hex-encodes each key as a String: + +.StringKeyGenerator +==== +.Java +[source,java,role="primary"] +---- +KeyGenerators.string(); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +KeyGenerators.string() +---- +==== + +[[spring-security-crypto-passwordencoders]] +== 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: + +[source,java] +---- +public interface PasswordEncoder { + +String encode(String rawPassword); + +boolean matches(String rawPassword, String encodedPassword); +} +---- + +The matches method returns true if the rawPassword, once encoded, equals the encodedPassword. +This method is designed to support password-based authentication schemes. + +The `BCryptPasswordEncoder` implementation uses the widely supported "bcrypt" algorithm to hash the passwords. +Bcrypt uses a random 16 byte salt value and is a deliberately slow algorithm, in order to hinder password crackers. +The amount of work it does can be tuned using the "strength" parameter which takes values from 4 to 31. +The higher the value, the more work has to be done to calculate the hash. +The default value is 10. +You can change this value in your deployed system without affecting existing passwords, as the value is also stored in the encoded hash. + +.BCryptPasswordEncoder +==== +.Java +[source,java,role="primary"] +---- + +// Create an encoder with strength 16 +BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16); +String result = encoder.encode("myPassword"); +assertTrue(encoder.matches("myPassword", result)); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- + +// Create an encoder with strength 16 +val encoder = BCryptPasswordEncoder(16) +val result: String = encoder.encode("myPassword") +assertTrue(encoder.matches("myPassword", result)) +---- +==== + +The `Pbkdf2PasswordEncoder` implementation uses PBKDF2 algorithm to hash the passwords. +In order to defeat password cracking PBKDF2 is a deliberately slow algorithm and should be tuned to take about .5 seconds to verify a password on your system. + + +.Pbkdf2PasswordEncoder +==== +.Java +[source,java,role="primary"] +---- +// Create an encoder with all the defaults +Pbkdf2PasswordEncoder encoder = new Pbkdf2PasswordEncoder(); +String result = encoder.encode("myPassword"); +assertTrue(encoder.matches("myPassword", result)); +---- + +.Kotlin +[source,kotlin,role="secondary"] +---- +// Create an encoder with all the defaults +val encoder = Pbkdf2PasswordEncoder() +val result: String = encoder.encode("myPassword") +assertTrue(encoder.matches("myPassword", result)) +---- +==== diff --git a/docs/modules/ROOT/pages/servlet/getting-started.adoc b/docs/modules/ROOT/pages/servlet/getting-started.adoc new file mode 100644 index 0000000000..adf4aa555f --- /dev/null +++ b/docs/modules/ROOT/pages/servlet/getting-started.adoc @@ -0,0 +1,74 @@ +[[servlet-hello]] += Hello Spring Security + +This section covers the minimum setup for how to use Spring Security with Spring Boot. + +[NOTE] +==== +The completed application can be found {gh-samples-url}/servlet/spring-boot/java/hello-security[in our samples repository]. +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&packaging=jar&jvmVersion=1.8&groupId=example&artifactId=hello-security&name=hello-security&description=Hello%20Security&packageName=example.hello-security&dependencies=web,security[clicking here]. +==== + +[[servlet-hello-dependencies]] +== Updating Dependencies + +The only step you need to do is update the dependencies by using xref:getting-spring-security.adoc#getting-maven-boot[Maven] or xref:getting-spring-security.adoc#getting-gradle-boot[Gradle]. + +[[servlet-hello-starting]] +== 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-auto-configuration]] +== Spring Boot Auto Configuration + +// FIXME: Link to relevant portions of documentation +// FIXME: Link to Spring Boot's Security Auto configuration classes +// FIXME: Add a links for what user's should do next + +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 +* https://en.wikipedia.org/wiki/Cross-site_request_forgery[CSRF attack] prevention +* https://en.wikipedia.org/wiki/Session_fixation[Session Fixation] protection +* Security Header integration +** https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security[HTTP Strict Transport Security] for secure requests +** https://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) +** https://msdn.microsoft.com/en-us/library/dd565647(v=vs.85).aspx[X-XSS-Protection] integration +** X-Frame-Options integration to help prevent https://en.wikipedia.org/wiki/Clickjacking[Clickjacking] +* Integrate with the following Servlet API methods: +** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[`HttpServletRequest#getRemoteUser()`] +** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[`HttpServletRequest.html#getUserPrincipal()`] +** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[`HttpServletRequest.html#isUserInRole(java.lang.String)`] +** https://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)`] +** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout()[`HttpServletRequest.html#logout()`] + diff --git a/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc b/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc index d6b126a0a7..b473543bf2 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc @@ -544,7 +544,7 @@ Will output HTML that is similar to the following: === Resolving the CsrfToken Spring Security provides `CsrfTokenArgumentResolver` which can automatically resolve the current `CsrfToken` for Spring MVC arguments. -By using xref:servlet/java-configuration/index.adoc#jc-hello-wsca[@EnableWebSecurity] you will automatically have this added to your Spring MVC configuration. +By using xref:servlet/java-configuration.adoc#jc-hello-wsca[@EnableWebSecurity] you will automatically have this added to your Spring MVC configuration. If you use XML based configuration, you must add this yourself. Once `CsrfTokenArgumentResolver` is properly configured, you can expose the `CsrfToken` to your static HTML based application. diff --git a/docs/modules/ROOT/pages/servlet/java-configuration.adoc b/docs/modules/ROOT/pages/servlet/java-configuration.adoc new file mode 100644 index 0000000000..f0e8f14049 --- /dev/null +++ b/docs/modules/ROOT/pages/servlet/java-configuration.adoc @@ -0,0 +1,337 @@ + +[[jc]] += Java Configuration + +General support for https://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/beans.html#beans-java[Java Configuration] was added to Spring Framework in Spring 3.1. +Since Spring Security 3.2 there has been Spring Security Java Configuration support which enables users to easily configure Spring Security without the use of any XML. + +If you are familiar with the xref:servlet/xml-namespace.adoc#ns-config[Security Namespace Configuration] then you should find quite a few similarities between it and the Security Java Configuration support. + +NOTE: Spring Security provides https://github.com/spring-projects/spring-security-samples/tree/main/servlet/java-configuration[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 { + + @Bean + public UserDetailsService userDetailsService() { + 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 +* https://en.wikipedia.org/wiki/Cross-site_request_forgery[CSRF attack] prevention +* https://en.wikipedia.org/wiki/Session_fixation[Session Fixation] protection +* Security Header integration +** https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security[HTTP Strict Transport Security] for secure requests +** https://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) +** https://msdn.microsoft.com/en-us/library/dd565647(v=vs.85).aspx[X-XSS-Protection] integration +** X-Frame-Options integration to help prevent https://en.wikipedia.org/wiki/Clickjacking[Clickjacking] +* Integrate with the following Servlet API methods +** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[HttpServletRequest#getRemoteUser()] +** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[HttpServletRequest#getUserPrincipal()] +** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[HttpServletRequest#isUserInRole(java.lang.String)] +** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login(java.lang.String,%20java.lang.String)[HttpServletRequest#login(java.lang.String, java.lang.String)] +** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout()[HttpServletRequest#logout()] + +=== AbstractSecurityWebApplicationInitializer + +The next step is to register the `springSecurityFilterChain` with the war. +This can be done in Java Configuration with https://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 + +Thus far our <> only contains information about how to authenticate our users. +How does Spring Security know that we want to require all users to be authenticated? +How does Spring Security know we want to support form based authentication? +Actually, there is a configuration class that is being invoked behind the scenes called `WebSecurityConfigurerAdapter`. +It has a method called `configure` with the following default implementation: + +[source,java] +---- +protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests(authorize -> authorize + .anyRequest().authenticated() + ) + .formLogin(withDefaults()) + .httpBasic(withDefaults()); +} +---- + +The default configuration above: + +* Ensures that any request to our application requires the user to be authenticated +* Allows users to authenticate with form based login +* Allows users to authenticate with HTTP Basic authentication + +You will notice that this configuration is quite similar the XML Namespace configuration: + +[source,xml] +---- + + + + + +---- + +== Multiple HttpSecurity + +We can configure multiple HttpSecurity instances just as we can have multiple `` blocks. +The key is to extend the `WebSecurityConfigurerAdapter` multiple times. +For example, the following is an example of having a different configuration for URL's that start with `/api/`. + +[source,java] +---- +@EnableWebSecurity +public class MultiHttpSecurityConfig { + @Bean <1> + 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; + } + + @Configuration + @Order(1) <2> + public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { + protected void configure(HttpSecurity http) throws Exception { + http + .antMatcher("/api/**") <3> + .authorizeRequests(authorize -> authorize + .anyRequest().hasRole("ADMIN") + ) + .httpBasic(withDefaults()); + } + } + + @Configuration <4> + public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests(authorize -> authorize + .anyRequest().authenticated() + ) + .formLogin(withDefaults()); + } + } +} +---- + +<1> Configure Authentication as normal +<2> Create an instance of `WebSecurityConfigurerAdapter` that contains `@Order` to specify which `WebSecurityConfigurerAdapter` should be considered first. +<3> The `http.antMatcher` states that this `HttpSecurity` will only be applicable to URLs that start with `/api/` +<4> Create another instance of `WebSecurityConfigurerAdapter`. +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-custom-dsls]] +== Custom DSLs + +You can provide your own custom DSLs in Spring Security. +For example, you might have something that looks like this: + +[source,java] +---- +public class MyCustomDsl extends AbstractHttpConfigurer { + private boolean flag; + + @Override + public void init(HttpSecurity http) throws Exception { + // any method that adds another configurer + // must be done in the init method + http.csrf().disable(); + } + + @Override + public void configure(HttpSecurity http) throws Exception { + ApplicationContext context = http.getSharedObject(ApplicationContext.class); + + // here we lookup from the ApplicationContext. You can also just create a new instance. + MyFilter myFilter = context.getBean(MyFilter.class); + myFilter.setFlag(flag); + http.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class); + } + + public MyCustomDsl flag(boolean value) { + this.flag = value; + return this; + } + + public static MyCustomDsl customDsl() { + return new MyCustomDsl(); + } +} +---- + +NOTE: This is actually how methods like `HttpSecurity.authorizeRequests()` are implemented. + +The custom DSL can then be used like this: + +[source,java] +---- +@EnableWebSecurity +public class Config extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .apply(customDsl()) + .flag(true) + .and() + ...; + } +} +---- + +The code is invoked in the following order: + +* Code in `Config`s configure method is invoked +* Code in `MyCustomDsl`s init method is invoked +* Code in `MyCustomDsl`s configure method is invoked + +If you want, you can have `WebSecurityConfigurerAdapter` add `MyCustomDsl` by default by using `SpringFactories`. +For example, you would create a resource on the classpath named `META-INF/spring.factories` with the following contents: + +.META-INF/spring.factories +---- +org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyCustomDsl +---- + +Users wishing to disable the default can do so explicitly. + +[source,java] +---- +@EnableWebSecurity +public class Config extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .apply(customDsl()).disable() + ...; + } +} +---- + +[[post-processing-configured-objects]] +== 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(authorize -> authorize + .anyRequest().authenticated() + .withObjectPostProcessor(new ObjectPostProcessor() { + public O postProcess( + O fsi) { + fsi.setPublishAuthorizationSuccess(true); + return fsi; + } + }) + ); +} +---- diff --git a/docs/modules/ROOT/pages/servlet/kotlin-configuration.adoc b/docs/modules/ROOT/pages/servlet/kotlin-configuration.adoc new file mode 100644 index 0000000000..767ab7ed80 --- /dev/null +++ b/docs/modules/ROOT/pages/servlet/kotlin-configuration.adoc @@ -0,0 +1,99 @@ + +[[kotlin-config]] += Kotlin Configuration +Spring Security Kotlin Configuration support has been available since Spring Security 5.3. +It enables users to easily configure Spring Security using a native Kotlin DSL. + +NOTE: Spring Security provides https://github.com/spring-projects/spring-security-samples/tree/main/servlet/spring-boot/kotlin/hello-security[a sample application] which demonstrates the use of Spring Security Kotlin Configuration. + +[[kotlin-config-httpsecurity]] +== HttpSecurity + +How does Spring Security know that we want to require all users to be authenticated? +How does Spring Security know we want to support form based authentication? +There is a configuration class that is being invoked behind the scenes called `WebSecurityConfigurerAdapter`. +It has a method called `configure` with the following default implementation: + +[source,kotlin] +---- +fun configure(http: HttpSecurity) { + http { + authorizeRequests { + authorize(anyRequest, authenticated) + } + formLogin { } + httpBasic { } + } +} +---- + +The default configuration above: + +* Ensures that any request to our application requires the user to be authenticated +* Allows users to authenticate with form based login +* Allows users to authenticate with HTTP Basic authentication + +You will notice that this configuration is quite similar the XML Namespace configuration: + +[source,xml] +---- + + + + + +---- + +== Multiple HttpSecurity + +We can configure multiple HttpSecurity instances just as we can have multiple `` blocks. +The key is to extend the `WebSecurityConfigurerAdapter` multiple times. +For example, the following is an example of having a different configuration for URL's that start with `/api/`. + +[source,kotlin] +---- +@EnableWebSecurity +class MultiHttpSecurityConfig { + @Bean <1> + public fun userDetailsService(): UserDetailsService { + val users: User.UserBuilder = User.withDefaultPasswordEncoder() + val manager = InMemoryUserDetailsManager() + manager.createUser(users.username("user").password("password").roles("USER").build()) + manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build()) + return manager + } + + @Configuration + @Order(1) <2> + class ApiWebSecurityConfigurationAdapter: WebSecurityConfigurerAdapter() { + override fun configure(http: HttpSecurity) { + http { + securityMatcher("/api/**") <3> + authorizeRequests { + authorize(anyRequest, hasRole("ADMIN")) + } + httpBasic { } + } + } + } + + @Configuration <4> + class FormLoginWebSecurityConfigurerAdapter: WebSecurityConfigurerAdapter() { + override fun configure(http: HttpSecurity) { + http { + authorizeRequests { + authorize(anyRequest, authenticated) + } + formLogin { } + } + } + } +} +---- + +<1> Configure Authentication as normal +<2> Create an instance of `WebSecurityConfigurerAdapter` that contains `@Order` to specify which `WebSecurityConfigurerAdapter` should be considered first. +<3> The `http.antMatcher` states that this `HttpSecurity` will only be applicable to URLs that start with `/api/` +<4> Create another instance of `WebSecurityConfigurerAdapter`. +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). diff --git a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc index e05e4402b6..31f481910d 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc @@ -23,13 +23,13 @@ First, we see that, like xref:servlet/authentication/unpwd/basic.adoc#servlet-au .Sending WWW-Authenticate Header image::{figures}/bearerauthenticationentrypoint.png[] -The figure above builds off our xref:servlet/architecture/index.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. +The figure above builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource `/private` for which it is not authorized. image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. -image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture/index.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__. +image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__. The configured xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPoint.html[`BearerTokenAuthenticationEntryPoint`] which sends a WWW-Authenticate header. The `RequestCache` is typically a `NullRequestCache` that does not save the request since the client is capable of replaying the requests it originally requested. @@ -40,7 +40,7 @@ Below is the flow for the bearer token being processed. .Authenticating Bearer Token image::{figures}/bearertokenauthenticationfilter.png[] -The figure builds off our xref:servlet/architecture/index.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. +The figure builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. image:{icondir}/number_1.png[] When the user submits their bearer token, the `BearerTokenAuthenticationFilter` creates a `BearerTokenAuthenticationToken` which is a type of xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] by extracting the token from the `HttpServletRequest`. diff --git a/docs/modules/ROOT/pages/servlet/saml2/index.adoc b/docs/modules/ROOT/pages/servlet/saml2/index.adoc index 50cf0a5182..7b40f8d156 100644 --- a/docs/modules/ROOT/pages/servlet/saml2/index.adoc +++ b/docs/modules/ROOT/pages/servlet/saml2/index.adoc @@ -30,13 +30,13 @@ It does this through a series of redirects. .Redirecting to Asserting Party Authentication image::{figures}/saml2webssoauthenticationrequestfilter.png[] -The figure above builds off our xref:servlet/architecture/index.adoc#servlet-securityfilterchain[`SecurityFilterChain`] and xref:servlet/authentication/architecture/index.adoc#servlet-authentication-abstractprocessingfilter[ `AbstractAuthenticationProcessingFilter`] diagrams: +The figure above builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] and xref:servlet/authentication/architecture/index.adoc#servlet-authentication-abstractprocessingfilter[ `AbstractAuthenticationProcessingFilter`] diagrams: image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource `/private` for which it is not authorized. image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. -image:{icondir}/number_3.png[] Since the user lacks authorization, the xref:servlet/architecture/index.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__. +image:{icondir}/number_3.png[] Since the user lacks authorization, the xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__. The configured xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[`LoginUrlAuthenticationEntryPoint`] which redirects to <` generating endpoint>>, `Saml2WebSsoAuthenticationRequestFilter`. Or, if you've <>, it will first redirect to a picker page. @@ -52,7 +52,7 @@ image:{icondir}/number_6.png[] The browser then POSTs the `` to .Authenticating a `` image::{figures}/saml2webssoauthenticationfilter.png[] -The figure builds off our xref:servlet/architecture/index.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. +The figure builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. image:{icondir}/number_1.png[] When the browser submits a `` to the application, it <>. This filter calls its configured `AuthenticationConverter` to create a `Saml2AuthenticationToken` by extracting the response from the `HttpServletRequest`. diff --git a/docs/modules/ROOT/pages/servlet/xml-namespace.adoc b/docs/modules/ROOT/pages/servlet/xml-namespace.adoc new file mode 100644 index 0000000000..e11eebde48 --- /dev/null +++ b/docs/modules/ROOT/pages/servlet/xml-namespace.adoc @@ -0,0 +1,402 @@ + +[[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 https://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[xref:servlet/authentication/unpwd/ldap.adoc#servlet-authentication-ldap[LDAP Authentication]].]. +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 https://spring.io/tools[Eclipse IDE with Spring Tools] 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[xref:servlet/exploits/firewall.adoc#servlet-httpfirewall[`HttpFirewall`]] 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 <> which is used.]. +In Spring Security 3.0, the attribute can also be populated with an xref:servlet/authorization/expression-based.adoc#el-access[EL expression]. + + +[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,attrs="-attributes"] +---- + + + + + + + + + +---- + +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,attrs="-attributes"] +---- + + + + + + + + + + +---- + + + +[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 xref:servlet/appendix/namespace.adoc#appendix-namespace[namespace appendix]. +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 xref:servlet/authentication/unpwd/in-memory.adoc#servlet-authentication-inmemory[in-memory authentication] 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-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`. + +[[ns-web-advanced]] +== Advanced Web Features + +[[ns-custom-filters]] +=== Adding in Your Own Filters +If you've used Spring Security before, you'll know that the framework maintains a chain of filters in order to apply its services. +You may want to add your own filters to the stack at particular locations or use a Spring Security filter for which there isn't currently a namespace configuration option (CAS, for example). +Or you might want to use a customized version of a standard namespace filter, such as the `UsernamePasswordAuthenticationFilter` which is created by the `` element, taking advantage of some of the extra configuration options which are available by using the bean explicitly. +How can you do this with namespace configuration, since the filter chain is not directly exposed? + +The order of the filters is always strictly enforced when using the namespace. +When the application context is being created, the filter beans are sorted by the namespace handling code and the standard Spring Security filters each have an alias in the namespace and a well-known position. + +[NOTE] +==== +In previous versions, the sorting took place after the filter instances had been created, during post-processing of the application context. +In version 3.0+ the sorting is now done at the bean metadata level, before the classes have been instantiated. +This has implications for how you add your own filters to the stack as the entire filter list must be known during the parsing of the `` element, so the syntax has changed slightly in 3.0. +==== + +The filters, aliases and namespace elements/attributes which create the filters are shown in <>. +The filters are listed in the order in which they occur in the filter chain. + +[[filter-stack]] +.Standard Filter Aliases and Ordering +|=== +| Alias | Filter Class | Namespace Element or Attribute + +| CHANNEL_FILTER +| `ChannelProcessingFilter` +| `http/intercept-url@requires-channel` + +| SECURITY_CONTEXT_FILTER +| `SecurityContextPersistenceFilter` +| `http` + +| CONCURRENT_SESSION_FILTER +| `ConcurrentSessionFilter` +| `session-management/concurrency-control` + +| HEADERS_FILTER +| `HeaderWriterFilter` +| `http/headers` + +| CSRF_FILTER +| `CsrfFilter` +| `http/csrf` + +| LOGOUT_FILTER +| `LogoutFilter` +| `http/logout` + +| X509_FILTER +| `X509AuthenticationFilter` +| `http/x509` + +| PRE_AUTH_FILTER +| `AbstractPreAuthenticatedProcessingFilter` Subclasses +| N/A + +| CAS_FILTER +| `CasAuthenticationFilter` +| N/A + +| FORM_LOGIN_FILTER +| `UsernamePasswordAuthenticationFilter` +| `http/form-login` + +| BASIC_AUTH_FILTER +| `BasicAuthenticationFilter` +| `http/http-basic` + +| SERVLET_API_SUPPORT_FILTER +| `SecurityContextHolderAwareRequestFilter` +| `http/@servlet-api-provision` + +| JAAS_API_SUPPORT_FILTER +| `JaasApiIntegrationFilter` +| `http/@jaas-api-provision` + +| REMEMBER_ME_FILTER +| `RememberMeAuthenticationFilter` +| `http/remember-me` + +| ANONYMOUS_FILTER +| `AnonymousAuthenticationFilter` +| `http/anonymous` + +| SESSION_MANAGEMENT_FILTER +| `SessionManagementFilter` +| `session-management` + +| EXCEPTION_TRANSLATION_FILTER +| `ExceptionTranslationFilter` +| `http` + +| FILTER_SECURITY_INTERCEPTOR +| `FilterSecurityInterceptor` +| `http` + +| SWITCH_USER_FILTER +| `SwitchUserFilter` +| N/A +|=== + +You can add your own filter to the stack, using the `custom-filter` element and one of these names to specify the position your filter should appear at: + +[source,xml] +---- + + + + + +---- + +You can also use the `after` or `before` attributes if you want your filter to be inserted before or after another filter in the stack. +The names "FIRST" and "LAST" can be used with the `position` attribute to indicate that you want your filter to appear before or after the entire stack, respectively. + +.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. + +Note that you can't replace filters which are created by the use of the `` element itself - `SecurityContextPersistenceFilter`, `ExceptionTranslationFilter` or `FilterSecurityInterceptor`. +Some other filters are added by default, but you can disable them. +An `AnonymousAuthenticationFilter` is added by default and unless you have xref:servlet/authentication/session-management.adoc#ns-session-fixation[session-fixation protection] 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-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 xref:servlet/authorization/expression-based.adoc#el-access[expression-based annotations]. +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-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 xref:servlet/authorization/architecture.adoc#authz-arch[authorization]. + + +[[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] +---- + +... + +---- From a0e9c6748245b5c5f3c67e3c32c3cb1592539bd7 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Thu, 19 Aug 2021 14:18:50 -0500 Subject: [PATCH 15/34] Fix antora version --- docs/antora.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/antora.yml b/docs/antora.yml index 8b2be6522e..9abbfe3d35 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -1,6 +1,7 @@ name: security title: Spring Security -version: 6.0 +version: ~ +display_version: 6.0 start_page: ROOT:overview/index.adoc asciidoc: attributes: From e4ce7249ccb68bb7083d1ddcaef975556bab7675 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 20 Aug 2021 17:08:39 -0500 Subject: [PATCH 16/34] Generate antora.yml and push to spring-security-docs-generated --- .github/workflows/build-reference.yml | 12 ++++- docs/antora.yml | 19 +------- docs/spring-security-docs.gradle | 67 +++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build-reference.yml b/.github/workflows/build-reference.yml index 3e3f714e06..8e7b9ce30c 100644 --- a/.github/workflows/build-reference.yml +++ b/.github/workflows/build-reference.yml @@ -7,6 +7,7 @@ on: env: GH_TOKEN_DISPATCH: ${{ secrets.GH_TOKEN_DISPATCH }} + GH_TOKEN_PUSH: ${{ secrets.GH_TOKEN_PUSH }} jobs: build: @@ -14,5 +15,14 @@ jobs: steps: - name: Checkout Source uses: actions/checkout@v2 + - name: Generate antora.yml + run: ./gradlew :spring-security-docs:generateAntora + - name: Push generated antora files to the spring-security-docs-generated + uses: JamesIves/github-pages-deploy-action@4.1.4 + with: + branch: "spring-security/main" # The branch the action should deploy to. + folder: "docs/build/generateAntora" # The folder the action should deploy. + repository-name: "rwinch/spring-security-docs-generated" + token: ${{ secrets.GH_TOKEN_PUSH }} - name: Dispatch Build Request - run: ${GITHUB_WORKSPACE}/.github/actions/dispatch.sh 'rwinch/spring-reference' "$GH_TOKEN_DISPATCH" \ No newline at end of file + run: ${GITHUB_WORKSPACE}/.github/actions/dispatch.sh 'rwinch/spring-reference' "$GH_TOKEN_DISPATCH" diff --git a/docs/antora.yml b/docs/antora.yml index 9abbfe3d35..16d009128e 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -2,23 +2,6 @@ name: security title: Spring Security version: ~ display_version: 6.0 -start_page: ROOT:overview/index.adoc -asciidoc: - attributes: - page-pagination: true - idprefix: '' - idseparator: '-' - apacheds-core-version: 1.5.5 - gh-old-samples-url: https://github.com/spring-projects/spring-security/tree/5.4.x/samples - gh-samples-url: https://github.com/spring-projects/spring-security-samples/tree/main - gh-url: https://github.com/spring-projects/spring-security/tree/main - icondir: icons - security-api-url: https://docs.spring.io/spring-security/site/docs/current/api/ - security-reference-url: https://docs.spring.io/spring-security/site/docs/reference/html5/ - spring-core-version: 5.3.8 - spring-framework-api-url: https://docs.spring.io/spring-framework/docs/current/javadoc-api/ - spring-framework-reference-url: https://docs.spring.io/spring-framework/docs/current/reference/html/ - spring-security-version: 6.0.0 - unboundid-ldapsdk-version: 4.1 +start_page: ROOT:index.adoc nav: - modules/ROOT/nav.adoc diff --git a/docs/spring-security-docs.gradle b/docs/spring-security-docs.gradle index e69de29bb2..5f7b36614c 100644 --- a/docs/spring-security-docs.gradle +++ b/docs/spring-security-docs.gradle @@ -0,0 +1,67 @@ +apply plugin: 'java' + +tasks.register("generateAntora") { + group = "Documentation" + description = "Generates the antora.yml for dynamic properties" + doLast { + def docsTag = snapshotBuild ? 'current' : project.version + def ghTag = snapshotBuild ? 'main' : project.version + def ghUrl = "https://github.com/spring-projects/spring-security/tree/$ghTag" + def ghOldSamplesUrl = "https://github.com/spring-projects/spring-security/tree/5.4.x/samples" + def ghSamplesUrl = "https://github.com/spring-projects/spring-security-samples/tree/$samplesBranch" + def securityDocsUrl = "https://docs.spring.io/spring-security/site/docs/$docsTag" + def securityApiUrl = "$securityDocsUrl/api/" + def securityReferenceUrl = "$securityDocsUrl/reference/html5/" + def springFrameworkApiUrl = "https://docs.spring.io/spring-framework/docs/$springFrameworkVersion/javadoc-api/" + def springFrameworkReferenceUrl = "https://docs.spring.io/spring-framework/docs/$springFrameworkVersion/reference/html/" + def versions = resolvedVersions(project.configurations.testRuntimeClasspath) + def ymlVersions = "" + versions.call().each { name, version -> + ymlVersions += """ + ${name}: ${version}""" + } + def outputFile = new File("$buildDir/generateAntora/antora.yml") + outputFile.getParentFile().mkdirs() + outputFile.createNewFile() + outputFile.setText("""name: security +title: Spring Security +version: ~ +display_version: 6.0 +start_page: ROOT:index.adoc +asciidoc: + attributes: + icondir: icons + gh-old-samples-url: $ghOldSamplesUrl + gh-samples-url: $ghSamplesUrl + gh-url: $ghUrl + security-api-url: $securityApiUrl + security-reference-url: $securityReferenceUrl + spring-framework-api-url: $springFrameworkApiUrl + spring-framework-reference-url: $springFrameworkReferenceUrl + spring-security-version: ${project.version} +${ymlVersions} +""") + } +} + +dependencies { + testImplementation platform(project(":spring-security-dependencies")) + testImplementation "com.unboundid:unboundid-ldapsdk" + testImplementation "org.apache.directory.server:apacheds-core" + testImplementation "org.springframework:spring-core" +} + +def resolvedVersions(Configuration configuration) { + return { + configuration.resolvedConfiguration + .resolvedArtifacts + .collectEntries { [(it.name + "-version"): it.moduleVersion.id.version] } + } +} + +repositories { + mavenCentral() + maven { url "https://repo.spring.io/release" } + maven { url "https://repo.spring.io/milestone" } + maven { url "https://repo.spring.io/snapshot" } +} From 583761f91609a2100c4dd299a753b8a48470c125 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Wed, 25 Aug 2021 13:31:00 -0500 Subject: [PATCH 17/34] unpwd->passwords folder --- docs/modules/ROOT/nav.adoc | 22 +++++++-------- .../pages/servlet/appendix/namespace.adoc | 4 +-- .../ROOT/pages/servlet/architecture.adoc | 6 ++--- .../pages/servlet/architecture/index.adoc | 6 ++--- .../authentication/architecture/index.adoc | 10 +++---- .../pages/servlet/authentication/index.adoc | 2 +- .../{unpwd => passwords}/basic.adoc | 2 +- .../dao-authentication-provider.adoc | 8 +++--- .../{unpwd => passwords}/digest.adoc | 2 +- .../{unpwd => passwords}/form.adoc | 2 +- .../{unpwd => passwords}/in-memory.adoc | 4 +-- .../authentication/passwords/index.adoc | 27 +++++++++++++++++++ .../{unpwd => passwords}/jdbc.adoc | 6 ++--- .../{unpwd => passwords}/ldap.adoc | 4 +-- .../password-encoder.adoc | 0 .../user-details-service.adoc | 4 +-- .../passwords/user-details.adoc | 5 ++++ .../servlet/authentication/unpwd/index.adoc | 27 ------------------- .../authentication/unpwd/user-details.adoc | 5 ---- .../ROOT/pages/servlet/namespace/index.adoc | 2 +- .../servlet/oauth2/oauth2-resourceserver.adoc | 2 +- .../ROOT/pages/servlet/saml2/index.adoc | 2 +- .../ROOT/pages/servlet/xml-namespace.adoc | 2 +- 23 files changed, 77 insertions(+), 77 deletions(-) rename docs/modules/ROOT/pages/servlet/authentication/{unpwd => passwords}/basic.adoc (97%) rename docs/modules/ROOT/pages/servlet/authentication/{unpwd => passwords}/dao-authentication-provider.adoc (65%) rename docs/modules/ROOT/pages/servlet/authentication/{unpwd => passwords}/digest.adoc (95%) rename docs/modules/ROOT/pages/servlet/authentication/{unpwd => passwords}/form.adoc (98%) rename docs/modules/ROOT/pages/servlet/authentication/{unpwd => passwords}/in-memory.adoc (92%) create mode 100644 docs/modules/ROOT/pages/servlet/authentication/passwords/index.adoc rename docs/modules/ROOT/pages/servlet/authentication/{unpwd => passwords}/jdbc.adoc (94%) rename docs/modules/ROOT/pages/servlet/authentication/{unpwd => passwords}/ldap.adoc (97%) rename docs/modules/ROOT/pages/servlet/authentication/{unpwd => passwords}/password-encoder.adoc (100%) rename docs/modules/ROOT/pages/servlet/authentication/{unpwd => passwords}/user-details-service.adoc (67%) create mode 100644 docs/modules/ROOT/pages/servlet/authentication/passwords/user-details.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/authentication/unpwd/index.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index e45379856d..e1f08f69e0 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -17,19 +17,19 @@ ** xref:servlet/architecture.adoc[The Big Picture] ** Authentication *** xref:servlet/authentication/architecture/index.adoc[Authentication Architecture] -*** xref:servlet/authentication/unpwd/index.adoc[Username/Password] +*** xref:servlet/authentication/passwords/index.adoc[Username/Password] **** Reading Username/Password -***** xref:servlet/authentication/unpwd/form.adoc[Form] -***** xref:servlet/authentication/unpwd/basic.adoc[Basic] -***** xref:servlet/authentication/unpwd/digest.adoc[Digest] +***** xref:servlet/authentication/passwords/form.adoc[Form] +***** xref:servlet/authentication/passwords/basic.adoc[Basic] +***** xref:servlet/authentication/passwords/digest.adoc[Digest] **** Password Storage -***** xref:servlet/authentication/unpwd/in-memory.adoc[In Memory] -***** xref:servlet/authentication/unpwd/jdbc.adoc[JDBC] -***** xref:servlet/authentication/unpwd/user-details.adoc[UserDetails] -***** xref:servlet/authentication/unpwd/user-details-service.adoc[UserDetailsService] -***** xref:servlet/authentication/unpwd/password-encoder.adoc[PasswordEncoder] -***** xref:servlet/authentication/unpwd/dao-authentication-provider.adoc[DaoAuthenticationProvider] -***** xref:servlet/authentication/unpwd/ldap.adoc[LDAP] +***** xref:servlet/authentication/passwords/in-memory.adoc[In Memory] +***** xref:servlet/authentication/passwords/jdbc.adoc[JDBC] +***** xref:servlet/authentication/passwords/user-details.adoc[UserDetails] +***** xref:servlet/authentication/passwords/user-details-service.adoc[UserDetailsService] +***** xref:servlet/authentication/passwords/password-encoder.adoc[PasswordEncoder] +***** xref:servlet/authentication/passwords/dao-authentication-provider.adoc[DaoAuthenticationProvider] +***** xref:servlet/authentication/passwords/ldap.adoc[LDAP] *** xref:servlet/authentication/session-management.adoc[Session Management] *** xref:servlet/authentication/rememberme.adoc[Remember Me] *** xref:servlet/authentication/openid.adoc[OpenID] diff --git a/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc b/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc index d6c2dae79f..f143661568 100644 --- a/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc +++ b/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc @@ -2736,7 +2736,7 @@ A method name [[nsa-ldap]] == LDAP Namespace Options -LDAP is covered in some details in xref:servlet/authentication/unpwd/ldap.adoc#servlet-authentication-ldap[its own chapter]. +LDAP is covered in some details in xref:servlet/authentication/passwords/ldap.adoc#servlet-authentication-ldap[its own chapter]. We will expand on that here with some explanation of how the namespace options map to Spring beans. The LDAP implementation uses Spring LDAP extensively, so some familiarity with that project's API may be useful. @@ -2746,7 +2746,7 @@ The LDAP implementation uses Spring LDAP extensively, so some familiarity with t `` Element This element sets up a Spring LDAP `ContextSource` for use by the other LDAP beans, defining the location of the LDAP server and other information (such as a username and password, if it doesn't allow anonymous access) for connecting to it. It can also be used to create an embedded server for testing. -Details of the syntax for both options are covered in the xref:servlet/authentication/unpwd/ldap.adoc#servlet-authentication-ldap[LDAP chapter]. +Details of the syntax for both options are covered in the xref:servlet/authentication/passwords/ldap.adoc#servlet-authentication-ldap[LDAP chapter]. The actual `ContextSource` implementation is `DefaultSpringSecurityContextSource` which extends Spring LDAP's `LdapContextSource` class. The `manager-dn` and `manager-password` attributes map to the latter's `userDn` and `password` properties respectively. diff --git a/docs/modules/ROOT/pages/servlet/architecture.adoc b/docs/modules/ROOT/pages/servlet/architecture.adoc index ff38d362a6..4ff33d61e0 100644 --- a/docs/modules/ROOT/pages/servlet/architecture.adoc +++ b/docs/modules/ROOT/pages/servlet/architecture.adoc @@ -179,14 +179,14 @@ Below is a comprehensive list of Spring Security Filter ordering: * CasAuthenticationFilter * OAuth2LoginAuthenticationFilter * Saml2WebSsoAuthenticationFilter -* xref:servlet/authentication/unpwd/form.adoc#servlet-authentication-usernamepasswordauthenticationfilter[`UsernamePasswordAuthenticationFilter`] +* xref:servlet/authentication/passwords/form.adoc#servlet-authentication-usernamepasswordauthenticationfilter[`UsernamePasswordAuthenticationFilter`] * OpenIDAuthenticationFilter * DefaultLoginPageGeneratingFilter * DefaultLogoutPageGeneratingFilter * ConcurrentSessionFilter -* xref:servlet/authentication/unpwd/digest.adoc#servlet-authentication-digest[`DigestAuthenticationFilter`] +* xref:servlet/authentication/passwords/digest.adoc#servlet-authentication-digest[`DigestAuthenticationFilter`] * BearerTokenAuthenticationFilter -* xref:servlet/authentication/unpwd/basic.adoc#servlet-authentication-basic[`BasicAuthenticationFilter`] +* xref:servlet/authentication/passwords/basic.adoc#servlet-authentication-basic[`BasicAuthenticationFilter`] * RequestCacheAwareFilter * SecurityContextHolderAwareRequestFilter * JaasApiIntegrationFilter diff --git a/docs/modules/ROOT/pages/servlet/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/architecture/index.adoc index 0ddea5d236..8771a8f5be 100644 --- a/docs/modules/ROOT/pages/servlet/architecture/index.adoc +++ b/docs/modules/ROOT/pages/servlet/architecture/index.adoc @@ -179,14 +179,14 @@ Below is a comprehensive list of Spring Security Filter ordering: * CasAuthenticationFilter * OAuth2LoginAuthenticationFilter * Saml2WebSsoAuthenticationFilter -* xref:servlet/authentication/unpwd/form.adoc#servlet-authentication-usernamepasswordauthenticationfilter[`UsernamePasswordAuthenticationFilter`] +* xref:servlet/authentication/passwords/form.adoc#servlet-authentication-usernamepasswordauthenticationfilter[`UsernamePasswordAuthenticationFilter`] * OpenIDAuthenticationFilter * DefaultLoginPageGeneratingFilter * DefaultLogoutPageGeneratingFilter * ConcurrentSessionFilter -* xref:servlet/authentication/unpwd/digest.adoc#servlet-authentication-digest[`DigestAuthenticationFilter`] +* xref:servlet/authentication/passwords/digest.adoc#servlet-authentication-digest[`DigestAuthenticationFilter`] * BearerTokenAuthenticationFilter -* xref:servlet/authentication/unpwd/basic.adoc#servlet-authentication-basic[`BasicAuthenticationFilter`] +* xref:servlet/authentication/passwords/basic.adoc#servlet-authentication-basic[`BasicAuthenticationFilter`] * RequestCacheAwareFilter * SecurityContextHolderAwareRequestFilter * JaasApiIntegrationFilter diff --git a/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc index d37897800c..ea61488c57 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc @@ -125,7 +125,7 @@ The current `Authentication` can be obtained from the <> are high level permissions the user is granted. @@ -141,7 +141,7 @@ A `GrantedAuthority` is, not surprisingly, an authority that is granted to the p Such authorities are usually "roles", such as `ROLE_ADMINISTRATOR` or `ROLE_HR_SUPERVISOR`. These roles are later on configured for web authorization, method authorization and domain object authorization. Other parts of Spring Security are capable of interpreting these authorities, and expect them to be present. -When using username/password based authentication ``GrantedAuthority``s are usually loaded by the xref:servlet/authentication/unpwd/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`]. +When using username/password based authentication ``GrantedAuthority``s are usually loaded by the xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`]. Usually the `GrantedAuthority` objects are application-wide permissions. They are not specific to a given domain object. @@ -199,7 +199,7 @@ See the {security-api-url}org/springframework/security/authentication/ProviderMa Multiple {security-api-url}org/springframework/security/authentication/AuthenticationProvider.html[``AuthenticationProvider``s] can be injected into <>. Each `AuthenticationProvider` performs a specific type of authentication. -For example, xref:servlet/authentication/unpwd/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] supports username/password based authentication while `JwtAuthenticationProvider` supports authenticating a JWT token. +For example, xref:servlet/authentication/passwords/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] supports username/password based authentication while `JwtAuthenticationProvider` supports authenticating a JWT token. [[servlet-authentication-authenticationentrypoint]] == Request Credentials with `AuthenticationEntryPoint` @@ -211,7 +211,7 @@ In these cases, Spring Security does not need to provide an HTTP response that r In other cases, a client will make an unauthenticated request to a resource that they are not authorized to access. In this case, an implementation of `AuthenticationEntryPoint` is used to request credentials from the client. -The `AuthenticationEntryPoint` implementation might perform a xref:servlet/authentication/unpwd/form.adoc#servlet-authentication-form[redirect to a log in page], respond with an xref:servlet/authentication/unpwd/basic.adoc#servlet-authentication-basic[WWW-Authenticate] header, etc. +The `AuthenticationEntryPoint` implementation might perform a xref:servlet/authentication/passwords/form.adoc#servlet-authentication-form[redirect to a log in page], respond with an xref:servlet/authentication/passwords/basic.adoc#servlet-authentication-basic[WWW-Authenticate] header, etc. @@ -230,7 +230,7 @@ image::{figures}/abstractauthenticationprocessingfilter.png[] image:{icondir}/number_1.png[] When the user submits their credentials, the `AbstractAuthenticationProcessingFilter` creates an <> from the `HttpServletRequest` to be authenticated. The type of `Authentication` created depends on the subclass of `AbstractAuthenticationProcessingFilter`. -For example, xref:servlet/authentication/unpwd/form.adoc#servlet-authentication-usernamepasswordauthenticationfilter[`UsernamePasswordAuthenticationFilter`] creates a `UsernamePasswordAuthenticationToken` from a __username__ and __password__ that are submitted in the `HttpServletRequest`. +For example, xref:servlet/authentication/passwords/form.adoc#servlet-authentication-usernamepasswordauthenticationfilter[`UsernamePasswordAuthenticationFilter`] creates a `UsernamePasswordAuthenticationToken` from a __username__ and __password__ that are submitted in the `HttpServletRequest`. image:{icondir}/number_2.png[] Next, the <> is passed into the <> to be authenticated. diff --git a/docs/modules/ROOT/pages/servlet/authentication/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/index.adoc index bcb81431d1..ceb08df6cb 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/index.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/index.adoc @@ -13,7 +13,7 @@ These sections focus on specific ways you may want to authenticate and point bac // FIXME: brief description -* xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd[Username and Password] - how to authenticate with a username/password +* xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd[Username and Password] - how to authenticate with a username/password * xref:servlet/oauth2/oauth2-login.adoc#oauth2login[OAuth 2.0 Login] - OAuth 2.0 Log In with OpenID Connect and non-standard OAuth 2.0 Login (i.e. GitHub) * xref:servlet/saml2/index.adoc#servlet-saml2[SAML 2.0 Login] - SAML 2.0 Log In * xref:servlet/authentication/cas.adoc#servlet-cas[Central Authentication Server (CAS)] - Central Authentication Server (CAS) Support diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc b/docs/modules/ROOT/pages/servlet/authentication/passwords/basic.adoc similarity index 97% rename from docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc rename to docs/modules/ROOT/pages/servlet/authentication/passwords/basic.adoc index 2040aec005..321f64459b 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/basic.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/passwords/basic.adoc @@ -34,7 +34,7 @@ The figure builds off our xref:servlet/architecture.adoc#servlet-securityfilterc image:{icondir}/number_1.png[] When the user submits their username and password, the `BasicAuthenticationFilter` creates a `UsernamePasswordAuthenticationToken` which is a type of xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] by extracting the username and password from the `HttpServletRequest`. image:{icondir}/number_2.png[] Next, the `UsernamePasswordAuthenticationToken` is passed into the `AuthenticationManager` to be authenticated. -The details of what `AuthenticationManager` looks like depend on how the xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd-storage[user information is stored]. +The details of what `AuthenticationManager` looks like depend on how the xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-storage[user information is stored]. image:{icondir}/number_3.png[] If authentication fails, then __Failure__ diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/dao-authentication-provider.adoc b/docs/modules/ROOT/pages/servlet/authentication/passwords/dao-authentication-provider.adoc similarity index 65% rename from docs/modules/ROOT/pages/servlet/authentication/unpwd/dao-authentication-provider.adoc rename to docs/modules/ROOT/pages/servlet/authentication/passwords/dao-authentication-provider.adoc index c25bf6c8e7..2f696514c6 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/dao-authentication-provider.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/passwords/dao-authentication-provider.adoc @@ -2,21 +2,21 @@ = DaoAuthenticationProvider :figures: servlet/authentication/unpwd -{security-api-url}org/springframework/security/authentication/dao/DaoAuthenticationProvider.html[`DaoAuthenticationProvider`] is an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that leverages a xref:servlet/authentication/unpwd/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`] and xref:servlet/authentication/unpwd/password-encoder.adoc#servlet-authentication-password-storage[`PasswordEncoder`] to authenticate a username and password. +{security-api-url}org/springframework/security/authentication/dao/DaoAuthenticationProvider.html[`DaoAuthenticationProvider`] is an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that leverages a xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`] and xref:servlet/authentication/passwords/password-encoder.adoc#servlet-authentication-password-storage[`PasswordEncoder`] to authenticate a username and password. Let's take a look at how `DaoAuthenticationProvider` works within Spring Security. -The figure explains details of how the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] in figures from xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd-input[Reading the Username & Password] works. +The figure explains details of how the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] in figures from xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-input[Reading the Username & Password] works. .`DaoAuthenticationProvider` Usage image::{figures}/daoauthenticationprovider.png[] -image:{icondir}/number_1.png[] The authentication `Filter` from xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd-input[Reading the Username & Password] passes a `UsernamePasswordAuthenticationToken` to the `AuthenticationManager` which is implemented by xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`ProviderManager`]. +image:{icondir}/number_1.png[] The authentication `Filter` from xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-input[Reading the Username & Password] passes a `UsernamePasswordAuthenticationToken` to the `AuthenticationManager` which is implemented by xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`ProviderManager`]. image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[AuthenticationProvider] of type `DaoAuthenticationProvider`. image:{icondir}/number_3.png[] `DaoAuthenticationProvider` looks up the `UserDetails` from the `UserDetailsService`. -image:{icondir}/number_4.png[] `DaoAuthenticationProvider` then uses the xref:servlet/authentication/unpwd/password-encoder.adoc#servlet-authentication-password-storage[`PasswordEncoder`] to validate the password on the `UserDetails` returned in the previous step. +image:{icondir}/number_4.png[] `DaoAuthenticationProvider` then uses the xref:servlet/authentication/passwords/password-encoder.adoc#servlet-authentication-password-storage[`PasswordEncoder`] to validate the password on the `UserDetails` returned in the previous step. image:{icondir}/number_5.png[] When authentication is successful, the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `UsernamePasswordAuthenticationToken` and has a principal that is the `UserDetails` returned by the configured `UserDetailsService`. Ultimately, the returned `UsernamePasswordAuthenticationToken` will be set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`. diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/digest.adoc b/docs/modules/ROOT/pages/servlet/authentication/passwords/digest.adoc similarity index 95% rename from docs/modules/ROOT/pages/servlet/authentication/unpwd/digest.adoc rename to docs/modules/ROOT/pages/servlet/authentication/passwords/digest.adoc index 92494d71b3..913f175e36 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/digest.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/passwords/digest.adoc @@ -11,7 +11,7 @@ All of these storage formats are considered insecure. Instead, you should store credentials using a one way adaptive password hash (i.e. bCrypt, PBKDF2, SCrypt, etc) which is not supported by Digest Authentication. ==== -Digest Authentication attempts to solve many of the weaknesses of xref:servlet/authentication/unpwd/basic.adoc#servlet-authentication-basic[Basic authentication], specifically by ensuring credentials are never sent in clear text across the wire. +Digest Authentication attempts to solve many of the weaknesses of xref:servlet/authentication/passwords/basic.adoc#servlet-authentication-basic[Basic authentication], specifically by ensuring credentials are never sent in clear text across the wire. Many https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Digest#Browser_compatibility[browsers support Digest Authentication]. The standard governing HTTP Digest Authentication is defined by https://tools.ietf.org/html/rfc2617[RFC 2617], which updates an earlier version of the Digest Authentication standard prescribed by https://tools.ietf.org/html/rfc2069[RFC 2069]. diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc b/docs/modules/ROOT/pages/servlet/authentication/passwords/form.adoc similarity index 98% rename from docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc rename to docs/modules/ROOT/pages/servlet/authentication/passwords/form.adoc index 2bee66af93..62c8575688 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/form.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/passwords/form.adoc @@ -38,7 +38,7 @@ The figure builds off our xref:servlet/architecture.adoc#servlet-securityfilterc image:{icondir}/number_1.png[] When the user submits their username and password, the `UsernamePasswordAuthenticationFilter` creates a `UsernamePasswordAuthenticationToken` which is a type of xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] by extracting the username and password from the `HttpServletRequest`. image:{icondir}/number_2.png[] Next, the `UsernamePasswordAuthenticationToken` is passed into the `AuthenticationManager` to be authenticated. -The details of what `AuthenticationManager` looks like depend on how the xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd-storage[user information is stored]. +The details of what `AuthenticationManager` looks like depend on how the xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-storage[user information is stored]. image:{icondir}/number_3.png[] If authentication fails, then __Failure__ diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/in-memory.adoc b/docs/modules/ROOT/pages/servlet/authentication/passwords/in-memory.adoc similarity index 92% rename from docs/modules/ROOT/pages/servlet/authentication/unpwd/in-memory.adoc rename to docs/modules/ROOT/pages/servlet/authentication/passwords/in-memory.adoc index 3c74dd95fa..62fb993c0d 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/in-memory.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/passwords/in-memory.adoc @@ -1,9 +1,9 @@ [[servlet-authentication-inmemory]] = In-Memory Authentication -Spring Security's `InMemoryUserDetailsManager` implements xref:servlet/authentication/unpwd/user-details-service.adoc#servlet-authentication-userdetailsservice[UserDetailsService] to provide support for username/password based authentication that is stored in memory. +Spring Security's `InMemoryUserDetailsManager` implements xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[UserDetailsService] to provide support for username/password based authentication that is stored in memory. `InMemoryUserDetailsManager` provides management of `UserDetails` by implementing the `UserDetailsManager` interface. -`UserDetails` based authentication is used by Spring Security when it is configured to xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd-input[accept a username/password] for authentication. +`UserDetails` based authentication is used by Spring Security when it is configured to xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-input[accept a username/password] for authentication. In this sample we use xref:features/authentication/password-storage.adoc#authentication-password-storage-boot-cli[Spring Boot CLI] to encode the password of `password` and get the encoded password of `+{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW+`. diff --git a/docs/modules/ROOT/pages/servlet/authentication/passwords/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/passwords/index.adoc new file mode 100644 index 0000000000..49efe3d2da --- /dev/null +++ b/docs/modules/ROOT/pages/servlet/authentication/passwords/index.adoc @@ -0,0 +1,27 @@ +[[servlet-authentication-unpwd]] += Username/Password Authentication +:figures: images/servlet/authentication/unpwd +:icondir: images/icons + +One of the most common ways to authenticate a user is by validating a username and password. +As such, Spring Security provides comprehensive support for authenticating with a username and password. + +[[servlet-authentication-unpwd-input]] +== Reading the Username & Password + +Spring Security provides the following built in mechanisms for reading a username and password from the `HttpServletRequest`: + +* xref:servlet/authentication/passwords/form.adoc#servlet-authentication-form[Form Login] +* xref:servlet/authentication/passwords/basic.adoc#servlet-authentication-basic[Basic Authentication] +* xref:servlet/authentication/passwords/digest.adoc#servlet-authentication-digest[Digest Authentication] + +[[servlet-authentication-unpwd-storage]] +== Storage Mechanisms + +Each of the supported mechanisms for reading a username and password can leverage any of the supported storage mechanisms: + +* Simple Storage with xref:servlet/authentication/passwords/in-memory.adoc#servlet-authentication-inmemory[In-Memory Authentication] +* Relational Databases with xref:servlet/authentication/passwords/jdbc.adoc#servlet-authentication-jdbc[JDBC Authentication] +* Custom data stores with xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[UserDetailsService] +* LDAP storage with xref:servlet/authentication/passwords/ldap.adoc#servlet-authentication-ldap[LDAP Authentication] + diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/jdbc.adoc b/docs/modules/ROOT/pages/servlet/authentication/passwords/jdbc.adoc similarity index 94% rename from docs/modules/ROOT/pages/servlet/authentication/unpwd/jdbc.adoc rename to docs/modules/ROOT/pages/servlet/authentication/passwords/jdbc.adoc index 3aa43ac7f2..d4462eaaf8 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/jdbc.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/passwords/jdbc.adoc @@ -1,9 +1,9 @@ [[servlet-authentication-jdbc]] = JDBC Authentication -Spring Security's `JdbcDaoImpl` implements xref:servlet/authentication/unpwd/user-details-service.adoc#servlet-authentication-userdetailsservice[UserDetailsService] to provide support for username/password based authentication that is retrieved using JDBC. +Spring Security's `JdbcDaoImpl` implements xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[UserDetailsService] to provide support for username/password based authentication that is retrieved using JDBC. `JdbcUserDetailsManager` extends `JdbcDaoImpl` to provide management of `UserDetails` through the `UserDetailsManager` interface. -`UserDetails` based authentication is used by Spring Security when it is configured to xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd-input[accept a username/password] for authentication. +`UserDetails` based authentication is used by Spring Security when it is configured to xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-input[accept a username/password] for authentication. In the following sections we will discuss: @@ -171,7 +171,7 @@ UserDetailsManager users(DataSource dataSource) { JdbcUserDetailsManager users = new JdbcUserDetailsManager(dataSource); users.createUser(user); users.createUser(admin); - return users; + return users; } ---- diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/ldap.adoc b/docs/modules/ROOT/pages/servlet/authentication/passwords/ldap.adoc similarity index 97% rename from docs/modules/ROOT/pages/servlet/authentication/unpwd/ldap.adoc rename to docs/modules/ROOT/pages/servlet/authentication/passwords/ldap.adoc index 5df913cf90..aa4ad900ff 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/ldap.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/passwords/ldap.adoc @@ -4,7 +4,7 @@ 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. -Spring Security's LDAP based authentication is used by Spring Security when it is configured to xref:servlet/authentication/unpwd/index.adoc#servlet-authentication-unpwd-input[accept a username/password] for authentication. +Spring Security's LDAP based authentication is used by Spring Security when it is configured to xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-input[accept a username/password] for authentication. However, despite leveraging a username/password for authentication it does not integrate using `UserDetailsService` because in <> the LDAP server does not return the password so the application cannot perform validation of the password. There are many different scenarios for how an LDAP server may be configured so Spring Security's LDAP provider is fully configurable. @@ -258,7 +258,7 @@ fun contextSource(container: UnboundIdContainer): ContextSource { [[servlet-authentication-ldap-authentication]] == Authentication -Spring Security's LDAP support does not use the xref:servlet/authentication/unpwd/user-details-service.adoc#servlet-authentication-userdetailsservice[UserDetailsService] because LDAP bind authentication does not allow clients to read the password or even a hashed version of the password. +Spring Security's LDAP support does not use the xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[UserDetailsService] because LDAP bind authentication does not allow clients to read the password or even a hashed version of the password. This means there is no way a password to be read and then authenticated by Spring Security. For this reason, LDAP support is implemented using the `LdapAuthenticator` interface. diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/password-encoder.adoc b/docs/modules/ROOT/pages/servlet/authentication/passwords/password-encoder.adoc similarity index 100% rename from docs/modules/ROOT/pages/servlet/authentication/unpwd/password-encoder.adoc rename to docs/modules/ROOT/pages/servlet/authentication/passwords/password-encoder.adoc diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details-service.adoc b/docs/modules/ROOT/pages/servlet/authentication/passwords/user-details-service.adoc similarity index 67% rename from docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details-service.adoc rename to docs/modules/ROOT/pages/servlet/authentication/passwords/user-details-service.adoc index 92a1d3519f..d557c04041 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details-service.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/passwords/user-details-service.adoc @@ -1,8 +1,8 @@ [[servlet-authentication-userdetailsservice]] = UserDetailsService -{security-api-url}org/springframework/security/core/userdetails/UserDetailsService.html[`UserDetailsService`] is used by xref:servlet/authentication/unpwd/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] for retrieving a username, password, and other attributes for authenticating with a username and password. -Spring Security provides xref:servlet/authentication/unpwd/in-memory.adoc#servlet-authentication-inmemory[in-memory] and xref:servlet/authentication/unpwd/jdbc.adoc#servlet-authentication-jdbc[JDBC] implementations of `UserDetailsService`. +{security-api-url}org/springframework/security/core/userdetails/UserDetailsService.html[`UserDetailsService`] is used by xref:servlet/authentication/passwords/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] for retrieving a username, password, and other attributes for authenticating with a username and password. +Spring Security provides xref:servlet/authentication/passwords/in-memory.adoc#servlet-authentication-inmemory[in-memory] and xref:servlet/authentication/passwords/jdbc.adoc#servlet-authentication-jdbc[JDBC] implementations of `UserDetailsService`. You can define custom authentication by exposing a custom `UserDetailsService` as a bean. For example, the following will customize authentication assuming that `CustomUserDetailsService` implements `UserDetailsService`: diff --git a/docs/modules/ROOT/pages/servlet/authentication/passwords/user-details.adoc b/docs/modules/ROOT/pages/servlet/authentication/passwords/user-details.adoc new file mode 100644 index 0000000000..bbdcd1bf8d --- /dev/null +++ b/docs/modules/ROOT/pages/servlet/authentication/passwords/user-details.adoc @@ -0,0 +1,5 @@ +[[servlet-authentication-userdetails]] += UserDetails + +{security-api-url}org/springframework/security/core/userdetails/UserDetails.html[`UserDetails`] is returned by the xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`]. +The xref:servlet/authentication/passwords/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] validates the `UserDetails` and then returns an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] that has a principal that is the `UserDetails` returned by the configured `UserDetailsService`. diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/index.adoc deleted file mode 100644 index 4dc2f6ca38..0000000000 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/index.adoc +++ /dev/null @@ -1,27 +0,0 @@ -[[servlet-authentication-unpwd]] -= Username/Password Authentication -:figures: images/servlet/authentication/unpwd -:icondir: images/icons - -One of the most common ways to authenticate a user is by validating a username and password. -As such, Spring Security provides comprehensive support for authenticating with a username and password. - -[[servlet-authentication-unpwd-input]] -== Reading the Username & Password - -Spring Security provides the following built in mechanisms for reading a username and password from the `HttpServletRequest`: - -* xref:servlet/authentication/unpwd/form.adoc#servlet-authentication-form[Form Login] -* xref:servlet/authentication/unpwd/basic.adoc#servlet-authentication-basic[Basic Authentication] -* xref:servlet/authentication/unpwd/digest.adoc#servlet-authentication-digest[Digest Authentication] - -[[servlet-authentication-unpwd-storage]] -== Storage Mechanisms - -Each of the supported mechanisms for reading a username and password can leverage any of the supported storage mechanisms: - -* Simple Storage with xref:servlet/authentication/unpwd/in-memory.adoc#servlet-authentication-inmemory[In-Memory Authentication] -* Relational Databases with xref:servlet/authentication/unpwd/jdbc.adoc#servlet-authentication-jdbc[JDBC Authentication] -* Custom data stores with xref:servlet/authentication/unpwd/user-details-service.adoc#servlet-authentication-userdetailsservice[UserDetailsService] -* LDAP storage with xref:servlet/authentication/unpwd/ldap.adoc#servlet-authentication-ldap[LDAP Authentication] - diff --git a/docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details.adoc b/docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details.adoc deleted file mode 100644 index 1b55e00a0b..0000000000 --- a/docs/modules/ROOT/pages/servlet/authentication/unpwd/user-details.adoc +++ /dev/null @@ -1,5 +0,0 @@ -[[servlet-authentication-userdetails]] -= UserDetails - -{security-api-url}org/springframework/security/core/userdetails/UserDetails.html[`UserDetails`] is returned by the xref:servlet/authentication/unpwd/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`]. -The xref:servlet/authentication/unpwd/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] validates the `UserDetails` and then returns an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] that has a principal that is the `UserDetails` returned by the configured `UserDetailsService`. diff --git a/docs/modules/ROOT/pages/servlet/namespace/index.adoc b/docs/modules/ROOT/pages/servlet/namespace/index.adoc index e11eebde48..0168114c1b 100644 --- a/docs/modules/ROOT/pages/servlet/namespace/index.adoc +++ b/docs/modules/ROOT/pages/servlet/namespace/index.adoc @@ -198,7 +198,7 @@ It's worth cross-checking this if you want to start understanding what the impor 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 xref:servlet/authentication/unpwd/in-memory.adoc#servlet-authentication-inmemory[in-memory authentication] for more details on the file format. +See the section on xref:servlet/authentication/passwords/in-memory.adoc#servlet-authentication-inmemory[in-memory authentication] 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. diff --git a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc index 31f481910d..0128f6de88 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/oauth2-resourceserver.adoc @@ -18,7 +18,7 @@ Working samples for both {gh-samples-url}/servlet/spring-boot/java/oauth2/resour ==== Let's take a look at how Bearer Token Authentication works within Spring Security. -First, we see that, like xref:servlet/authentication/unpwd/basic.adoc#servlet-authentication-basic[Basic Authentication], the https://tools.ietf.org/html/rfc7235#section-4.1[WWW-Authenticate] header is sent back to an unauthenticated client. +First, we see that, like xref:servlet/authentication/passwords/basic.adoc#servlet-authentication-basic[Basic Authentication], the https://tools.ietf.org/html/rfc7235#section-4.1[WWW-Authenticate] header is sent back to an unauthenticated client. .Sending WWW-Authenticate Header image::{figures}/bearerauthenticationentrypoint.png[] diff --git a/docs/modules/ROOT/pages/servlet/saml2/index.adoc b/docs/modules/ROOT/pages/servlet/saml2/index.adoc index 7b40f8d156..d302b1f70c 100644 --- a/docs/modules/ROOT/pages/servlet/saml2/index.adoc +++ b/docs/modules/ROOT/pages/servlet/saml2/index.adoc @@ -1267,7 +1267,7 @@ open class SecurityConfig : WebSecurityConfigurerAdapter() { ---- ==== <1> First, call the default converter, which extracts attributes and authorities from the response -<2> Second, call the xref:servlet/authentication/unpwd/user-details-service.adoc#servlet-authentication-userdetailsservice[ `UserDetailsService`] using the relevant information +<2> Second, call the xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[ `UserDetailsService`] using the relevant information <3> Third, return a custom authentication that includes the user details [NOTE] diff --git a/docs/modules/ROOT/pages/servlet/xml-namespace.adoc b/docs/modules/ROOT/pages/servlet/xml-namespace.adoc index e11eebde48..0168114c1b 100644 --- a/docs/modules/ROOT/pages/servlet/xml-namespace.adoc +++ b/docs/modules/ROOT/pages/servlet/xml-namespace.adoc @@ -198,7 +198,7 @@ It's worth cross-checking this if you want to start understanding what the impor 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 xref:servlet/authentication/unpwd/in-memory.adoc#servlet-authentication-inmemory[in-memory authentication] for more details on the file format. +See the section on xref:servlet/authentication/passwords/in-memory.adoc#servlet-authentication-inmemory[in-memory authentication] 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. From a7f3f54a3388d90112941f0c4d3fa7d0f617db35 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Thu, 26 Aug 2021 10:37:46 -0500 Subject: [PATCH 18/34] architecture/index.adoc -> ../architecture.adoc BASE_DIR=docs/modules/ROOT/pages git --no-pager diff HEAD~1 --diff-filter=R -M | sed -Ez "s%(\nrename to|rename from |similarity index [^\n]+|diff[^\n]+|$BASE_DIR/)%%g" | grep "\S" | while read rename_from_to; do from=$(echo $rename_from_to | cut -f 1 -d " ") to=$(echo $rename_from_to | cut -f 2 -d " ") echo "processing rename from $from to $to" find "$BASE_DIR/../" -name "*.adoc" | while read adoc_file; do sed -i -E "s%xref:$from%xref:$to%g" "$adoc_file" done done --- docs/modules/ROOT/nav.adoc | 2 +- .../pages/servlet/appendix/namespace.adoc | 4 +-- .../ROOT/pages/servlet/architecture.adoc | 2 +- .../pages/servlet/architecture/index.adoc | 2 +- .../index.adoc => architecture.adoc} | 0 .../pages/servlet/authentication/openid.adoc | 2 +- .../authentication/passwords/basic.adoc | 8 ++--- .../dao-authentication-provider.adoc | 12 +++---- .../authentication/passwords/form.adoc | 10 +++--- .../passwords/user-details.adoc | 2 +- .../pages/servlet/authentication/preauth.adoc | 2 +- .../servlet/authorization/architecture.adoc | 2 +- .../authorization/authorize-requests.adoc | 2 +- .../servlet/oauth2/oauth2-resourceserver.adoc | 32 +++++++++---------- .../ROOT/pages/servlet/saml2/index.adoc | 16 +++++----- 15 files changed, 49 insertions(+), 49 deletions(-) rename docs/modules/ROOT/pages/servlet/authentication/{architecture/index.adoc => architecture.adoc} (100%) diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index e1f08f69e0..e9a3b3ccf6 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -16,7 +16,7 @@ ** xref:servlet/getting-started.adoc[Getting Started] ** xref:servlet/architecture.adoc[The Big Picture] ** Authentication -*** xref:servlet/authentication/architecture/index.adoc[Authentication Architecture] +*** xref:servlet/authentication/architecture.adoc[Authentication Architecture] *** xref:servlet/authentication/passwords/index.adoc[Username/Password] **** Reading Username/Password ***** xref:servlet/authentication/passwords/form.adoc[Form] diff --git a/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc b/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc index f143661568..1a54033a3c 100644 --- a/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc +++ b/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc @@ -27,7 +27,7 @@ xref:servlet/xml-namespace.adoc#filter-stack[the filter order table] in the name You can, of course, still do this if you need full control of the configuration. -All filters which require a reference to the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] will be automatically injected with the internal instance created by the namespace configuration. +All filters which require a reference to the xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] will be automatically injected with the internal instance created by the namespace configuration. Each `` namespace block always creates an `SecurityContextPersistenceFilter`, an `ExceptionTranslationFilter` and a `FilterSecurityInterceptor`. These are fixed and cannot be replaced with alternatives. @@ -2124,7 +2124,7 @@ This attribute allows you to define an alias name for the internal instance for [[nsa-authentication-manager-erase-credentials]] * **erase-credentials** If set to true, the AuthenticationManager will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated. -Literally it maps to the `eraseCredentialsAfterAuthentication` property of the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`ProviderManager`]. +Literally it maps to the `eraseCredentialsAfterAuthentication` property of the xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager[`ProviderManager`]. [[nsa-authentication-manager-id]] diff --git a/docs/modules/ROOT/pages/servlet/architecture.adoc b/docs/modules/ROOT/pages/servlet/architecture.adoc index 4ff33d61e0..ea457e03bb 100644 --- a/docs/modules/ROOT/pages/servlet/architecture.adoc +++ b/docs/modules/ROOT/pages/servlet/architecture.adoc @@ -211,7 +211,7 @@ image::{figures}/exceptiontranslationfilter.png[] * image:{icondir}/number_1.png[] First, the `ExceptionTranslationFilter` invokes `FilterChain.doFilter(request, response)` to invoke the rest of the application. * image:{icondir}/number_2.png[] If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__. -** The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out +** The xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out ** The `HttpServletRequest` is saved in the {security-api-url}org/springframework/security/web/savedrequest/RequestCache.html[`RequestCache`]. When the user successfully authenticates, the `RequestCache` is used to replay the original request. // FIXME: add link to authentication success diff --git a/docs/modules/ROOT/pages/servlet/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/architecture/index.adoc index 8771a8f5be..a1120e1585 100644 --- a/docs/modules/ROOT/pages/servlet/architecture/index.adoc +++ b/docs/modules/ROOT/pages/servlet/architecture/index.adoc @@ -211,7 +211,7 @@ image::{figures}/exceptiontranslationfilter.png[] * image:{icondir}/number_1.png[] First, the `ExceptionTranslationFilter` invokes `FilterChain.doFilter(request, response)` to invoke the rest of the application. * image:{icondir}/number_2.png[] If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__. -** The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out. +** The xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out. ** The `HttpServletRequest` is saved in the {security-api-url}org/springframework/security/web/savedrequest/RequestCache.html[`RequestCache`]. When the user successfully authenticates, the `RequestCache` is used to replay the original request. // FIXME: add link to authentication success diff --git a/docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/authentication/architecture.adoc similarity index 100% rename from docs/modules/ROOT/pages/servlet/authentication/architecture/index.adoc rename to docs/modules/ROOT/pages/servlet/authentication/architecture.adoc diff --git a/docs/modules/ROOT/pages/servlet/authentication/openid.adoc b/docs/modules/ROOT/pages/servlet/authentication/openid.adoc index d15acca793..8451b82d4d 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/openid.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/openid.adoc @@ -53,7 +53,7 @@ OpenIDAuthenticationToken token = List attributes = token.getAttributes(); ---- -We can obtain the `OpenIDAuthenticationToken` from the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder]. +We can obtain the `OpenIDAuthenticationToken` from the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder]. The `OpenIDAttribute` contains the attribute type and the retrieved value (or values in the case of multi-valued attributes). 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. diff --git a/docs/modules/ROOT/pages/servlet/authentication/passwords/basic.adoc b/docs/modules/ROOT/pages/servlet/authentication/passwords/basic.adoc index 321f64459b..f83ead53ac 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/passwords/basic.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/passwords/basic.adoc @@ -18,7 +18,7 @@ image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__. -The configured xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/web/authentication/www/BasicAuthenticationEntryPoint.html[`BasicAuthenticationEntryPoint`] which sends a WWW-Authenticate header. +The configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/web/authentication/www/BasicAuthenticationEntryPoint.html[`BasicAuthenticationEntryPoint`] which sends a WWW-Authenticate header. The `RequestCache` is typically a `NullRequestCache` that does not save the request since the client is capable of replaying the requests it originally requested. When a client receives the WWW-Authenticate header it knows it should retry with a username and password. @@ -31,14 +31,14 @@ image::{figures}/basicauthenticationfilter.png[] The figure builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] diagram. -image:{icondir}/number_1.png[] When the user submits their username and password, the `BasicAuthenticationFilter` creates a `UsernamePasswordAuthenticationToken` which is a type of xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] by extracting the username and password from the `HttpServletRequest`. +image:{icondir}/number_1.png[] When the user submits their username and password, the `BasicAuthenticationFilter` creates a `UsernamePasswordAuthenticationToken` which is a type of xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] by extracting the username and password from the `HttpServletRequest`. image:{icondir}/number_2.png[] Next, the `UsernamePasswordAuthenticationToken` is passed into the `AuthenticationManager` to be authenticated. The details of what `AuthenticationManager` looks like depend on how the xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-storage[user information is stored]. image:{icondir}/number_3.png[] If authentication fails, then __Failure__ -* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out. +* The xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out. * `RememberMeServices.loginFail` is invoked. If remember me is not configured, this is a no-op. // FIXME: link to rememberme @@ -46,7 +46,7 @@ If remember me is not configured, this is a no-op. image:{icondir}/number_4.png[] If authentication is successful, then __Success__. -* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[Authentication] is set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder]. +* The xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[Authentication] is set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder]. * `RememberMeServices.loginSuccess` is invoked. If remember me is not configured, this is a no-op. // FIXME: link to rememberme diff --git a/docs/modules/ROOT/pages/servlet/authentication/passwords/dao-authentication-provider.adoc b/docs/modules/ROOT/pages/servlet/authentication/passwords/dao-authentication-provider.adoc index 2f696514c6..18631cb9c4 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/passwords/dao-authentication-provider.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/passwords/dao-authentication-provider.adoc @@ -2,21 +2,21 @@ = DaoAuthenticationProvider :figures: servlet/authentication/unpwd -{security-api-url}org/springframework/security/authentication/dao/DaoAuthenticationProvider.html[`DaoAuthenticationProvider`] is an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that leverages a xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`] and xref:servlet/authentication/passwords/password-encoder.adoc#servlet-authentication-password-storage[`PasswordEncoder`] to authenticate a username and password. +{security-api-url}org/springframework/security/authentication/dao/DaoAuthenticationProvider.html[`DaoAuthenticationProvider`] is an xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that leverages a xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`] and xref:servlet/authentication/passwords/password-encoder.adoc#servlet-authentication-password-storage[`PasswordEncoder`] to authenticate a username and password. Let's take a look at how `DaoAuthenticationProvider` works within Spring Security. -The figure explains details of how the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] in figures from xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-input[Reading the Username & Password] works. +The figure explains details of how the xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] in figures from xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-input[Reading the Username & Password] works. .`DaoAuthenticationProvider` Usage image::{figures}/daoauthenticationprovider.png[] -image:{icondir}/number_1.png[] The authentication `Filter` from xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-input[Reading the Username & Password] passes a `UsernamePasswordAuthenticationToken` to the `AuthenticationManager` which is implemented by xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`ProviderManager`]. +image:{icondir}/number_1.png[] The authentication `Filter` from xref:servlet/authentication/passwords/index.adoc#servlet-authentication-unpwd-input[Reading the Username & Password] passes a `UsernamePasswordAuthenticationToken` to the `AuthenticationManager` which is implemented by xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager[`ProviderManager`]. -image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[AuthenticationProvider] of type `DaoAuthenticationProvider`. +image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationprovider[AuthenticationProvider] of type `DaoAuthenticationProvider`. image:{icondir}/number_3.png[] `DaoAuthenticationProvider` looks up the `UserDetails` from the `UserDetailsService`. image:{icondir}/number_4.png[] `DaoAuthenticationProvider` then uses the xref:servlet/authentication/passwords/password-encoder.adoc#servlet-authentication-password-storage[`PasswordEncoder`] to validate the password on the `UserDetails` returned in the previous step. -image:{icondir}/number_5.png[] When authentication is successful, the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `UsernamePasswordAuthenticationToken` and has a principal that is the `UserDetails` returned by the configured `UserDetailsService`. -Ultimately, the returned `UsernamePasswordAuthenticationToken` will be set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`. +image:{icondir}/number_5.png[] When authentication is successful, the xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `UsernamePasswordAuthenticationToken` and has a principal that is the `UserDetails` returned by the configured `UserDetailsService`. +Ultimately, the returned `UsernamePasswordAuthenticationToken` will be set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`. diff --git a/docs/modules/ROOT/pages/servlet/authentication/passwords/form.adoc b/docs/modules/ROOT/pages/servlet/authentication/passwords/form.adoc index 62c8575688..941cbb037f 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/passwords/form.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/passwords/form.adoc @@ -18,7 +18,7 @@ image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. -image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__ and sends a redirect to the log in page with the configured xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`]. +image:{icondir}/number_3.png[] Since the user is not authenticated, xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__ and sends a redirect to the log in page with the configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`]. In most cases the `AuthenticationEntryPoint` is an instance of {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[`LoginUrlAuthenticationEntryPoint`]. image:{icondir}/number_4.png[] The browser will then request the log in page that it was redirected to. @@ -27,7 +27,7 @@ image:{icondir}/number_5.png[] Something within the application, must <> or <>. image:{icondir}/number_3.png[] If authentication fails, then __Failure__ -* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out. +* The xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out. * The `AuthenticationEntryPoint` is invoked to trigger the WWW-Authenticate header to be sent again. image:{icondir}/number_4.png[] If authentication is successful, then __Success__. -* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[Authentication] is set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder]. +* The xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[Authentication] is set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder]. * The `BearerTokenAuthenticationFilter` invokes `FilterChain.doFilter(request,response)` to continue with the rest of the application logic. [[oauth2resourceserver-jwt-minimaldependencies]] @@ -142,17 +142,17 @@ From here, consider jumping to: Next, let's see the architectural components that Spring Security uses to support https://tools.ietf.org/html/rfc7519[JWT] Authentication in servlet-based applications, like the one we just saw. -{security-api-url}org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationProvider.html[`JwtAuthenticationProvider`] is an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that leverages a <> and <> to authenticate a JWT. +{security-api-url}org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationProvider.html[`JwtAuthenticationProvider`] is an xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that leverages a <> and <> to authenticate a JWT. Let's take a look at how `JwtAuthenticationProvider` works within Spring Security. -The figure explains details of how the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] in figures from <> works. +The figure explains details of how the xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] in figures from <> works. .`JwtAuthenticationProvider` Usage image::{figures}/jwtauthenticationprovider.png[] -image:{icondir}/number_1.png[] The authentication `Filter` from <> passes a `BearerTokenAuthenticationToken` to the `AuthenticationManager` which is implemented by xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`ProviderManager`]. +image:{icondir}/number_1.png[] The authentication `Filter` from <> passes a `BearerTokenAuthenticationToken` to the `AuthenticationManager` which is implemented by xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager[`ProviderManager`]. -image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[AuthenticationProvider] of type `JwtAuthenticationProvider`. +image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationprovider[AuthenticationProvider] of type `JwtAuthenticationProvider`. [[oauth2resourceserver-jwt-architecture-jwtdecoder]] image:{icondir}/number_3.png[] `JwtAuthenticationProvider` decodes, verifies, and validates the `Jwt` using a <>. @@ -160,8 +160,8 @@ image:{icondir}/number_3.png[] `JwtAuthenticationProvider` decodes, verifies, an [[oauth2resourceserver-jwt-architecture-jwtauthenticationconverter]] image:{icondir}/number_4.png[] `JwtAuthenticationProvider` then uses the <> to convert the `Jwt` into a `Collection` of granted authorities. -image:{icondir}/number_5.png[] When authentication is successful, the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `JwtAuthenticationToken` and has a principal that is the `Jwt` returned by the configured `JwtDecoder`. -Ultimately, the returned `JwtAuthenticationToken` will be set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`. +image:{icondir}/number_5.png[] When authentication is successful, the xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `JwtAuthenticationToken` and has a principal that is the `Jwt` returned by the configured `JwtDecoder`. +Ultimately, the returned `JwtAuthenticationToken` will be set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`. [[oauth2resourceserver-jwt-jwkseturi]] == Specifying the Authorization Server JWK Set Uri Directly @@ -1513,22 +1513,22 @@ From here, you may want to jump to: Next, let's see the architectural components that Spring Security uses to support https://tools.ietf.org/html/rfc7662[opaque token] Authentication in servlet-based applications, like the one we just saw. -{security-api-url}org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.html[`OpaqueTokenAuthenticationProvider`] is an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that leverages a <> to authenticate an opaque token. +{security-api-url}org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.html[`OpaqueTokenAuthenticationProvider`] is an xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationprovider[`AuthenticationProvider`] implementation that leverages a <> to authenticate an opaque token. Let's take a look at how `OpaqueTokenAuthenticationProvider` works within Spring Security. -The figure explains details of how the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] in figures from <> works. +The figure explains details of how the xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationmanager[`AuthenticationManager`] in figures from <> works. .`OpaqueTokenAuthenticationProvider` Usage image::{figures}/opaquetokenauthenticationprovider.png[] -image:{icondir}/number_1.png[] The authentication `Filter` from <> passes a `BearerTokenAuthenticationToken` to the `AuthenticationManager` which is implemented by xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`ProviderManager`]. +image:{icondir}/number_1.png[] The authentication `Filter` from <> passes a `BearerTokenAuthenticationToken` to the `AuthenticationManager` which is implemented by xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager[`ProviderManager`]. -image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationprovider[AuthenticationProvider] of type `OpaqueTokenAuthenticationProvider`. +image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationprovider[AuthenticationProvider] of type `OpaqueTokenAuthenticationProvider`. [[oauth2resourceserver-opaque-architecture-introspector]] image:{icondir}/number_3.png[] `OpaqueTokenAuthenticationProvider` introspects the opaque token and adds granted authorities using an <>. -When authentication is successful, the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `BearerTokenAuthentication` and has a principal that is the `OAuth2AuthenticatedPrincipal` returned by the configured <>. -Ultimately, the returned `BearerTokenAuthentication` will be set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`. +When authentication is successful, the xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `BearerTokenAuthentication` and has a principal that is the `OAuth2AuthenticatedPrincipal` returned by the configured <>. +Ultimately, the returned `BearerTokenAuthentication` will be set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`. [[oauth2resourceserver-opaque-attributes]] == Looking Up Attributes Post-Authentication diff --git a/docs/modules/ROOT/pages/servlet/saml2/index.adoc b/docs/modules/ROOT/pages/servlet/saml2/index.adoc index d302b1f70c..43a9baf4d1 100644 --- a/docs/modules/ROOT/pages/servlet/saml2/index.adoc +++ b/docs/modules/ROOT/pages/servlet/saml2/index.adoc @@ -30,14 +30,14 @@ It does this through a series of redirects. .Redirecting to Asserting Party Authentication image::{figures}/saml2webssoauthenticationrequestfilter.png[] -The figure above builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] and xref:servlet/authentication/architecture/index.adoc#servlet-authentication-abstractprocessingfilter[ `AbstractAuthenticationProcessingFilter`] diagrams: +The figure above builds off our xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] and xref:servlet/authentication/architecture.adoc#servlet-authentication-abstractprocessingfilter[ `AbstractAuthenticationProcessingFilter`] diagrams: image:{icondir}/number_1.png[] First, a user makes an unauthenticated request to the resource `/private` for which it is not authorized. image:{icondir}/number_2.png[] Spring Security's xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] indicates that the unauthenticated request is __Denied__ by throwing an `AccessDeniedException`. image:{icondir}/number_3.png[] Since the user lacks authorization, the xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] initiates __Start Authentication__. -The configured xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[`LoginUrlAuthenticationEntryPoint`] which redirects to <` generating endpoint>>, `Saml2WebSsoAuthenticationRequestFilter`. +The configured xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is an instance of {security-api-url}org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.html[`LoginUrlAuthenticationEntryPoint`] which redirects to <` generating endpoint>>, `Saml2WebSsoAuthenticationRequestFilter`. Or, if you've <>, it will first redirect to a picker page. image:{icondir}/number_4.png[] Next, the `Saml2WebSsoAuthenticationRequestFilter` creates, signs, serializes, and encodes a `` using its configured <>. @@ -58,17 +58,17 @@ image:{icondir}/number_1.png[] When the browser submits a `` to This filter calls its configured `AuthenticationConverter` to create a `Saml2AuthenticationToken` by extracting the response from the `HttpServletRequest`. This converter additionally resolves the <> and supplies it to `Saml2AuthenticationToken`. -image:{icondir}/number_2.png[] Next, the filter passes the token to its configured xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`AuthenticationManager`]. +image:{icondir}/number_2.png[] Next, the filter passes the token to its configured xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager[`AuthenticationManager`]. By default, it will use the <>. image:{icondir}/number_3.png[] If authentication fails, then __Failure__ -* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[ `SecurityContextHolder`] is cleared out. -* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is invoked to restart the authentication process. +* The xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[ `SecurityContextHolder`] is cleared out. +* The xref:servlet/authentication/architecture.adoc#servlet-authentication-authenticationentrypoint[`AuthenticationEntryPoint`] is invoked to restart the authentication process. image:{icondir}/number_4.png[] If authentication is successful, then __Success__. -* The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-authentication[ `Authentication`] is set on the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-securitycontextholder[ `SecurityContextHolder`]. +* The xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[ `Authentication`] is set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[ `SecurityContextHolder`]. * The `Saml2WebSsoAuthenticationFilter` invokes `FilterChain#doFilter(request,response)` to continue with the rest of the application logic. [[servlet-saml2login-minimaldependencies]] @@ -169,9 +169,9 @@ image:{figures}/opensamlauthenticationprovider.png[] This figure builds off of the <>. -image:{icondir}/number_1.png[] The `Saml2WebSsoAuthenticationFilter` formulates the `Saml2AuthenticationToken` and invokes the xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`AuthenticationManager`]. +image:{icondir}/number_1.png[] The `Saml2WebSsoAuthenticationFilter` formulates the `Saml2AuthenticationToken` and invokes the xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager[`AuthenticationManager`]. -image:{icondir}/number_2.png[] The xref:servlet/authentication/architecture/index.adoc#servlet-authentication-providermanager[`AuthenticationManager`] invokes the OpenSAML authentication provider. +image:{icondir}/number_2.png[] The xref:servlet/authentication/architecture.adoc#servlet-authentication-providermanager[`AuthenticationManager`] invokes the OpenSAML authentication provider. image:{icondir}/number_3.png[] The authentication provider deserializes the response into an OpenSAML `Response` and checks its signature. If the signature is invalid, authentication fails. From 52bfae5e27114ea8fac774788340004272c50735 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Thu, 26 Aug 2021 10:50:27 -0500 Subject: [PATCH 19/34] Servlet Authentication no a link in nav --- docs/modules/ROOT/nav.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index e9a3b3ccf6..260ad49b3e 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -15,7 +15,7 @@ * xref:servlet/index.adoc[Servlet Applications] ** xref:servlet/getting-started.adoc[Getting Started] ** xref:servlet/architecture.adoc[The Big Picture] -** Authentication +** xref:servlet/authentication/index.adoc[Authentication] *** xref:servlet/authentication/architecture.adoc[Authentication Architecture] *** xref:servlet/authentication/passwords/index.adoc[Username/Password] **** Reading Username/Password From 7e2eb6894f169329dffc825831a9559a1ed422e2 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Mon, 30 Aug 2021 15:59:23 -0500 Subject: [PATCH 20/34] Use component name of '' This makes it so that the URL contains security in the root of the deploy folder. We will do this until we decide to add projects other than the security related projects. --- docs/antora.yml | 2 +- docs/spring-security-docs.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/antora.yml b/docs/antora.yml index 16d009128e..eb7bde66fa 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -1,4 +1,4 @@ -name: security +name: '' title: Spring Security version: ~ display_version: 6.0 diff --git a/docs/spring-security-docs.gradle b/docs/spring-security-docs.gradle index 5f7b36614c..a0db3dc911 100644 --- a/docs/spring-security-docs.gradle +++ b/docs/spring-security-docs.gradle @@ -23,7 +23,7 @@ tasks.register("generateAntora") { def outputFile = new File("$buildDir/generateAntora/antora.yml") outputFile.getParentFile().mkdirs() outputFile.createNewFile() - outputFile.setText("""name: security + outputFile.setText("""name: '' title: Spring Security version: ~ display_version: 6.0 From 871bc1c42c6c6b109a484ebbe2064f4ae6d0ad59 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Mon, 30 Aug 2021 16:00:03 -0500 Subject: [PATCH 21/34] The next version of Security is 5.6 --- docs/antora.yml | 2 +- docs/spring-security-docs.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/antora.yml b/docs/antora.yml index eb7bde66fa..2519066807 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -1,7 +1,7 @@ name: '' title: Spring Security version: ~ -display_version: 6.0 +display_version: 5.6 start_page: ROOT:index.adoc nav: - modules/ROOT/nav.adoc diff --git a/docs/spring-security-docs.gradle b/docs/spring-security-docs.gradle index a0db3dc911..b888fc0023 100644 --- a/docs/spring-security-docs.gradle +++ b/docs/spring-security-docs.gradle @@ -26,7 +26,7 @@ tasks.register("generateAntora") { outputFile.setText("""name: '' title: Spring Security version: ~ -display_version: 6.0 +display_version: 5.6 start_page: ROOT:index.adoc asciidoc: attributes: From be9ff16583e3b171992490df1858d64e99e36d64 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Thu, 2 Sep 2021 14:38:19 -0500 Subject: [PATCH 22/34] Remove Asciidoctor from the build in favor of Antora --- buildSrc/build.gradle | 4 - .../gradle/convention/DocsPlugin.groovy | 45 +--- .../AsciidoctorConventionPlugin.java | 208 ------------------ .../gradle/convention/DocsPluginITest.java | 87 -------- .../docs/missing-attribute/build.gradle | 6 - .../docs/missing-attribute/settings.gradle | 1 - .../src/docs/asciidoc/index.adoc | 3 - .../docs/missing-cross-reference/build.gradle | 6 - .../missing-cross-reference/settings.gradle | 1 - .../src/docs/asciidoc/index.adoc | 3 - .../samples/docs/missing-include/build.gradle | 6 - .../docs/missing-include/settings.gradle | 1 - .../src/docs/asciidoc/index.adoc | 5 - .../samples/docs/simple/build.gradle | 13 -- .../samples/docs/simple/settings.gradle | 1 - .../simple/src/docs/asciidoc/docinfo.html | 1 - .../src/docs/asciidoc/images/sunset.jpg | Bin 122404 -> 0 bytes .../docs/simple/src/docs/asciidoc/index.adoc | 60 ----- .../simple/src/docs/asciidoc/subdir/_b.adoc | 7 - .../simple/src/docs/asciidoc/subdir/_c.adoc | 1 - .../src/main/java/example/StringUtils.java | 9 - .../showcase/sgbcs-docs/sgbcs-docs.gradle | 7 - docs/spring-security-docs.gradle | 1 + 23 files changed, 4 insertions(+), 472 deletions(-) delete mode 100644 buildSrc/src/main/java/io/spring/gradle/convention/AsciidoctorConventionPlugin.java delete mode 100644 buildSrc/src/test/java/io/spring/gradle/convention/DocsPluginITest.java delete mode 100644 buildSrc/src/test/resources/samples/docs/missing-attribute/build.gradle delete mode 100644 buildSrc/src/test/resources/samples/docs/missing-attribute/settings.gradle delete mode 100644 buildSrc/src/test/resources/samples/docs/missing-attribute/src/docs/asciidoc/index.adoc delete mode 100644 buildSrc/src/test/resources/samples/docs/missing-cross-reference/build.gradle delete mode 100644 buildSrc/src/test/resources/samples/docs/missing-cross-reference/settings.gradle delete mode 100644 buildSrc/src/test/resources/samples/docs/missing-cross-reference/src/docs/asciidoc/index.adoc delete mode 100644 buildSrc/src/test/resources/samples/docs/missing-include/build.gradle delete mode 100644 buildSrc/src/test/resources/samples/docs/missing-include/settings.gradle delete mode 100644 buildSrc/src/test/resources/samples/docs/missing-include/src/docs/asciidoc/index.adoc delete mode 100644 buildSrc/src/test/resources/samples/docs/simple/build.gradle delete mode 100644 buildSrc/src/test/resources/samples/docs/simple/settings.gradle delete mode 100644 buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/docinfo.html delete mode 100644 buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/images/sunset.jpg delete mode 100644 buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/index.adoc delete mode 100644 buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/subdir/_b.adoc delete mode 100644 buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/subdir/_c.adoc delete mode 100644 buildSrc/src/test/resources/samples/docs/simple/src/main/java/example/StringUtils.java diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index fd47234f40..b0555da6c7 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -76,13 +76,9 @@ dependencies { implementation 'gradle.plugin.org.gretty:gretty:3.0.1' implementation 'com.apollographql.apollo:apollo-runtime:2.4.5' implementation 'com.github.ben-manes:gradle-versions-plugin:0.38.0' - implementation 'io.spring.gradle:docbook-reference-plugin:0.3.1' implementation 'io.spring.gradle:propdeps-plugin:0.0.10.RELEASE' implementation 'io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.15' implementation 'io.spring.nohttp:nohttp-gradle:0.0.10' - implementation 'org.aim42:htmlSanityCheck:1.1.6' - implementation 'org.asciidoctor:asciidoctor-gradle-jvm:3.1.0' - implementation 'org.asciidoctor:asciidoctor-gradle-jvm-pdf:3.1.0' implementation 'org.hidetake:gradle-ssh-plugin:2.10.1' implementation 'org.jfrog.buildinfo:build-info-extractor-gradle:4.9.10' implementation 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1' diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy index a527d719e6..d0a64ab85b 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy @@ -1,12 +1,9 @@ package io.spring.gradle.convention -import org.aim42.htmlsanitycheck.HtmlSanityCheckPlugin -import org.aim42.htmlsanitycheck.HtmlSanityCheckTask -import org.aim42.htmlsanitycheck.check.BrokenHttpLinksChecker -import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.Task +import org.gradle.api.plugins.BasePlugin import org.gradle.api.plugins.PluginManager import org.gradle.api.tasks.bundling.Zip @@ -19,54 +16,18 @@ public class DocsPlugin implements Plugin { public void apply(Project project) { PluginManager pluginManager = project.getPluginManager(); - pluginManager.apply("org.asciidoctor.jvm.convert"); - pluginManager.apply("org.asciidoctor.jvm.pdf"); - pluginManager.apply(AsciidoctorConventionPlugin); + pluginManager.apply(BasePlugin); pluginManager.apply(DeployDocsPlugin); pluginManager.apply(JavadocApiPlugin); - pluginManager.apply(HtmlSanityCheckPlugin) - - String projectName = Utils.getProjectName(project); - String pdfFilename = projectName + "-reference.pdf"; - - project.tasks.withType(AbstractAsciidoctorTask) { t -> - project.configure(t) { - sources { - include "**/*.adoc" - exclude '_*/**' - } - } - } - - project.tasks.withType(HtmlSanityCheckTask) { HtmlSanityCheckTask t -> - project.configure(t) { - t.dependsOn 'asciidoctor' - t.checkerClasses = [BrokenHttpLinksChecker] - t.checkingResultsDir = new File(project.getBuildDir(), "/report/htmlchecks") - t.failOnErrors = false - t.httpConnectionTimeout = 3000 - t.sourceDir = new File(project.getBuildDir(), "/docs/asciidoc/") - t.sourceDocuments = project.files(new File(project.getBuildDir(), "/docs/asciidoc/index.html")) - } - } Task docsZip = project.tasks.create('docsZip', Zip) { - dependsOn 'api', 'asciidoctor' + dependsOn 'api' group = 'Distribution' archiveBaseName = project.rootProject.name archiveClassifier = 'docs' description = "Builds -${classifier} archive containing all " + "Docs for deployment at docs.spring.io" - from(project.tasks.asciidoctor.outputs) { - into 'reference/html5' - include '**' - } - from(project.tasks.asciidoctorPdf.outputs) { - into 'reference/pdf' - include '**' - rename "index.pdf", pdfFilename - } from(project.tasks.api.outputs) { into 'api' } diff --git a/buildSrc/src/main/java/io/spring/gradle/convention/AsciidoctorConventionPlugin.java b/buildSrc/src/main/java/io/spring/gradle/convention/AsciidoctorConventionPlugin.java deleted file mode 100644 index 548dfbca51..0000000000 --- a/buildSrc/src/main/java/io/spring/gradle/convention/AsciidoctorConventionPlugin.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright 2019-2020 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 - * - * https://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 io.spring.gradle.convention; - -import org.asciidoctor.gradle.base.AsciidoctorAttributeProvider; -import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask; -import org.asciidoctor.gradle.jvm.AsciidoctorJExtension; -import org.asciidoctor.gradle.jvm.AsciidoctorJPlugin; -import org.asciidoctor.gradle.jvm.AsciidoctorTask; -import org.gradle.api.Action; -import org.gradle.api.Plugin; -import org.gradle.api.Project; -import org.gradle.api.artifacts.Configuration; -import org.gradle.api.artifacts.DependencySet; -import org.gradle.api.artifacts.dsl.RepositoryHandler; -import org.gradle.api.file.CopySpec; -import org.gradle.api.file.FileTree; -import org.gradle.api.tasks.Sync; - -import java.io.File; -import java.net.URI; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.function.Consumer; - -/** - * Conventions that are applied in the presence of the {@link AsciidoctorJPlugin}. When - * the plugin is applied: - * - *
    - *
  • All warnings are made fatal. - *
  • A task is created to resolve and unzip our documentation resources (CSS and - * Javascript). - *
  • For each {@link AsciidoctorTask} (HTML only): - *
      - *
    • A configuration named asciidoctorExtensions is ued to add the - * block - * switch extension - *
    • {@code doctype} {@link AsciidoctorTask#options(Map) option} is configured. - *
    • {@link AsciidoctorTask#attributes(Map) Attributes} are configured for syntax - * highlighting, CSS styling, docinfo, etc. - *
    - *
  • For each {@link AbstractAsciidoctorTask} (HTML and PDF): - *
      - *
    • {@link AsciidoctorTask#attributes(Map) Attributes} are configured to enable - * warnings for references to missing attributes, the year is added as @{code today-year}, - * etc - *
    • {@link AbstractAsciidoctorTask#baseDirFollowsSourceDir() baseDirFollowsSourceDir()} - * is enabled. - *
    - *
- * - * @author Andy Wilkinson - * @author Rob Winch - */ -public class AsciidoctorConventionPlugin implements Plugin { - - public void apply(Project project) { - project.getPlugins().withType(AsciidoctorJPlugin.class, (asciidoctorPlugin) -> { - createDefaultAsciidoctorRepository(project); - makeAllWarningsFatal(project); - Sync unzipResources = createUnzipDocumentationResourcesTask(project); - project.getTasks().withType(AbstractAsciidoctorTask.class, (asciidoctorTask) -> { - asciidoctorTask.dependsOn(unzipResources); - configureExtensions(project, asciidoctorTask); - configureCommonAttributes(project, asciidoctorTask); - configureOptions(asciidoctorTask); - asciidoctorTask.baseDirFollowsSourceDir(); - asciidoctorTask.useIntermediateWorkDir(); - asciidoctorTask.resources(new Action() { - @Override - public void execute(CopySpec resourcesSpec) { - resourcesSpec.from(unzipResources); - resourcesSpec.from(asciidoctorTask.getSourceDir(), new Action() { - @Override - public void execute(CopySpec resourcesSrcDirSpec) { - // https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/523 - // For now copy the entire sourceDir over so that include files are - // available in the intermediateWorkDir - // resourcesSrcDirSpec.include("images/**"); - } - }); - } - }); - if (asciidoctorTask instanceof AsciidoctorTask) { - configureHtmlOnlyAttributes(project, asciidoctorTask); - } - }); - }); - } - - private void createDefaultAsciidoctorRepository(Project project) { - project.getGradle().afterProject(new Action() { - @Override - public void execute(Project project) { - RepositoryHandler repositories = project.getRepositories(); - if (repositories.isEmpty()) { - repositories.mavenCentral(); - repositories.maven(repo -> { - repo.setUrl(URI.create("https://repo.spring.io/release")); - }); - } - } - }); - } - - private void makeAllWarningsFatal(Project project) { - project.getExtensions().getByType(AsciidoctorJExtension.class).fatalWarnings(".*"); - } - - private void configureExtensions(Project project, AbstractAsciidoctorTask asciidoctorTask) { - Configuration extensionsConfiguration = project.getConfigurations().maybeCreate("asciidoctorExtensions"); - extensionsConfiguration.defaultDependencies(new Action() { - @Override - public void execute(DependencySet dependencies) { - dependencies.add(project.getDependencies().create("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.4.2.RELEASE")); - } - }); - asciidoctorTask.configurations(extensionsConfiguration); - } - - private Sync createUnzipDocumentationResourcesTask(Project project) { - Configuration documentationResources = project.getConfigurations().maybeCreate("documentationResources"); - documentationResources.getDependencies() - .add(project.getDependencies().create("io.spring.docresources:spring-doc-resources:0.2.5")); - Sync unzipResources = project.getTasks().create("unzipDocumentationResources", - Sync.class, new Action() { - @Override - public void execute(Sync sync) { - sync.dependsOn(documentationResources); - sync.from(new Callable>() { - @Override - public List call() throws Exception { - List result = new ArrayList<>(); - documentationResources.getAsFileTree().forEach(new Consumer() { - @Override - public void accept(File file) { - result.add(project.zipTree(file)); - } - }); - return result; - } - }); - File destination = new File(project.getBuildDir(), "docs/resources"); - sync.into(project.relativePath(destination)); - } - }); - return unzipResources; - } - - private void configureOptions(AbstractAsciidoctorTask asciidoctorTask) { - asciidoctorTask.options(Collections.singletonMap("doctype", "book")); - } - - private void configureHtmlOnlyAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) { - Map attributes = new HashMap<>(); - attributes.put("source-highlighter", "highlight.js"); - attributes.put("highlightjsdir", "js/highlight"); - attributes.put("highlightjs-theme", "github"); - attributes.put("linkcss", true); - attributes.put("icons", "font"); - attributes.put("stylesheet", "css/spring.css"); - asciidoctorTask.getAttributeProviders().add(new AsciidoctorAttributeProvider() { - @Override - public Map getAttributes() { - Object version = project.getVersion(); - Map attrs = new HashMap<>(); - if (version != null && version.toString() != Project.DEFAULT_VERSION) { - attrs.put("revnumber", version); - } - return attrs; - } - }); - asciidoctorTask.attributes(attributes); - } - - private void configureCommonAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) { - Map attributes = new HashMap<>(); - attributes.put("attribute-missing", "warn"); - attributes.put("icons", "font"); - attributes.put("idprefix", ""); - attributes.put("idseparator", "-"); - attributes.put("docinfo", "shared"); - attributes.put("sectanchors", ""); - attributes.put("sectnums", ""); - attributes.put("today-year", LocalDate.now().getYear()); - asciidoctorTask.attributes(attributes); - } -} diff --git a/buildSrc/src/test/java/io/spring/gradle/convention/DocsPluginITest.java b/buildSrc/src/test/java/io/spring/gradle/convention/DocsPluginITest.java deleted file mode 100644 index badd126a82..0000000000 --- a/buildSrc/src/test/java/io/spring/gradle/convention/DocsPluginITest.java +++ /dev/null @@ -1,87 +0,0 @@ -package io.spring.gradle.convention; - -import io.spring.gradle.TestKit; -import org.codehaus.groovy.runtime.ResourceGroovyMethods; -import org.gradle.testkit.runner.BuildResult; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import java.io.File; -import java.nio.file.Path; -import java.util.Collections; -import java.util.List; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.gradle.testkit.runner.TaskOutcome.FAILED; -import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; - -public class DocsPluginITest { - private TestKit testKit; - - @BeforeEach - void setup(@TempDir Path tempDir) { - this.testKit = new TestKit(tempDir.toFile()); - } - - @Test - public void buildTriggersDocs() throws Exception { - BuildResult result = testKit.withProjectResource("samples/docs/simple/") - .withArguments("build") - .build(); - assertThat(result.task(":build").getOutcome()).isEqualTo(SUCCESS); - assertThat(result.task(":docs").getOutcome()).isEqualTo(SUCCESS); - assertThat(result.task(":docsZip").getOutcome()).isEqualTo(SUCCESS); - File zip = new File(testKit.getRootDir(), "build/distributions/simple-1.0.0.BUILD-SNAPSHOT-docs.zip"); - try (ZipFile file = new ZipFile(zip)) { - List entries = Collections.list(file.entries()); - assertThat(entries) - .extracting(ZipEntry::getName) - .contains("docs/reference/html5/index.html") - .contains("docs/reference/pdf/simple-reference.pdf"); - } - } - - @Test - public void asciidocCopiesImages() throws Exception { - BuildResult result = testKit.withProjectResource("samples/docs/simple/").withArguments("asciidoctor").build(); - assertThat(result.task(":asciidoctor").getOutcome()).isEqualTo(SUCCESS); - assertThat(new File(testKit.getRootDir(), "build/docs/asciidoc/images")).exists(); - } - - @Test - public void asciidocDocInfoFromResourcesUsed() throws Exception { - BuildResult result = this.testKit.withProjectResource("samples/docs/simple/") - .withArguments("asciidoctor") - .build(); - assertThat(result.task(":asciidoctor").getOutcome()).isEqualTo(SUCCESS); - assertThat(ResourceGroovyMethods.getText(new File(testKit.getRootDir(), "build/docs/asciidoc/index.html"))) - .contains(""); - } - - @Test - public void missingAttributeFails() throws Exception { - BuildResult result = this.testKit.withProjectResource("samples/docs/missing-attribute/") - .withArguments(":asciidoctor") - .buildAndFail(); - assertThat(result.task(":asciidoctor").getOutcome()).isEqualTo(FAILED); - } - - @Test - public void missingInclude() throws Exception { - BuildResult result = this.testKit.withProjectResource("samples/docs/missing-include/") - .withArguments(":asciidoctor") - .buildAndFail(); - assertThat(result.task(":asciidoctor").getOutcome()).isEqualTo(FAILED); - } - - @Test - public void missingCrossReference() throws Exception { - BuildResult result = this.testKit.withProjectResource("samples/docs/missing-cross-reference/") - .withArguments(":asciidoctor") - .buildAndFail(); - assertThat(result.task(":asciidoctor").getOutcome()).isEqualTo(FAILED); - } -} diff --git a/buildSrc/src/test/resources/samples/docs/missing-attribute/build.gradle b/buildSrc/src/test/resources/samples/docs/missing-attribute/build.gradle deleted file mode 100644 index 91d6d7e151..0000000000 --- a/buildSrc/src/test/resources/samples/docs/missing-attribute/build.gradle +++ /dev/null @@ -1,6 +0,0 @@ -plugins { - id 'io.spring.convention.docs' - id 'java' -} - -version = '1.0.0.BUILD-SNAPSHOT' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/missing-attribute/settings.gradle b/buildSrc/src/test/resources/samples/docs/missing-attribute/settings.gradle deleted file mode 100644 index 7378d4a349..0000000000 --- a/buildSrc/src/test/resources/samples/docs/missing-attribute/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'simple' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/missing-attribute/src/docs/asciidoc/index.adoc b/buildSrc/src/test/resources/samples/docs/missing-attribute/src/docs/asciidoc/index.adoc deleted file mode 100644 index ba2fbbe84b..0000000000 --- a/buildSrc/src/test/resources/samples/docs/missing-attribute/src/docs/asciidoc/index.adoc +++ /dev/null @@ -1,3 +0,0 @@ -= Example Manual - -This will fail due to {missing} attribute \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/missing-cross-reference/build.gradle b/buildSrc/src/test/resources/samples/docs/missing-cross-reference/build.gradle deleted file mode 100644 index 91d6d7e151..0000000000 --- a/buildSrc/src/test/resources/samples/docs/missing-cross-reference/build.gradle +++ /dev/null @@ -1,6 +0,0 @@ -plugins { - id 'io.spring.convention.docs' - id 'java' -} - -version = '1.0.0.BUILD-SNAPSHOT' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/missing-cross-reference/settings.gradle b/buildSrc/src/test/resources/samples/docs/missing-cross-reference/settings.gradle deleted file mode 100644 index 1731930901..0000000000 --- a/buildSrc/src/test/resources/samples/docs/missing-cross-reference/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'missing-include' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/missing-cross-reference/src/docs/asciidoc/index.adoc b/buildSrc/src/test/resources/samples/docs/missing-cross-reference/src/docs/asciidoc/index.adoc deleted file mode 100644 index 2a41dd8a8c..0000000000 --- a/buildSrc/src/test/resources/samples/docs/missing-cross-reference/src/docs/asciidoc/index.adoc +++ /dev/null @@ -1,3 +0,0 @@ -= Example Manual - -This will fail due to <> cross reference \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/missing-include/build.gradle b/buildSrc/src/test/resources/samples/docs/missing-include/build.gradle deleted file mode 100644 index 91d6d7e151..0000000000 --- a/buildSrc/src/test/resources/samples/docs/missing-include/build.gradle +++ /dev/null @@ -1,6 +0,0 @@ -plugins { - id 'io.spring.convention.docs' - id 'java' -} - -version = '1.0.0.BUILD-SNAPSHOT' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/missing-include/settings.gradle b/buildSrc/src/test/resources/samples/docs/missing-include/settings.gradle deleted file mode 100644 index 1731930901..0000000000 --- a/buildSrc/src/test/resources/samples/docs/missing-include/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'missing-include' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/missing-include/src/docs/asciidoc/index.adoc b/buildSrc/src/test/resources/samples/docs/missing-include/src/docs/asciidoc/index.adoc deleted file mode 100644 index ecf2133e9a..0000000000 --- a/buildSrc/src/test/resources/samples/docs/missing-include/src/docs/asciidoc/index.adoc +++ /dev/null @@ -1,5 +0,0 @@ -= Example Manual - -This will fail due to missing include - -include::missing.adoc[] \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/simple/build.gradle b/buildSrc/src/test/resources/samples/docs/simple/build.gradle deleted file mode 100644 index 9deefc7e1a..0000000000 --- a/buildSrc/src/test/resources/samples/docs/simple/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -plugins { - id 'io.spring.convention.docs' - id 'java' -} - -version = '1.0.0.BUILD-SNAPSHOT' - -asciidoctorj { - attributes \ - 'build-gradle': project.buildFile, - 'sourcedir': project.sourceSets.main.java.srcDirs[0], - 'endpoint-url': 'https://example.org' -} \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/simple/settings.gradle b/buildSrc/src/test/resources/samples/docs/simple/settings.gradle deleted file mode 100644 index 7378d4a349..0000000000 --- a/buildSrc/src/test/resources/samples/docs/simple/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'simple' \ No newline at end of file diff --git a/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/docinfo.html b/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/docinfo.html deleted file mode 100644 index f399886516..0000000000 --- a/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/docinfo.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/images/sunset.jpg b/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/images/sunset.jpg deleted file mode 100644 index 48c9129b301398f736bf7714dc8c425904b2ef49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 122404 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⪙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 diff --git a/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/index.adoc b/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/index.adoc deleted file mode 100644 index ea4087750a..0000000000 --- a/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/index.adoc +++ /dev/null @@ -1,60 +0,0 @@ -= Example Manual -Doc Writer -2014-09-09 -:example-caption!: -ifndef::imagesdir[:imagesdir: images] -ifndef::sourcedir[:sourcedir: ../java] - -This is a user manual for an example project. - -== Introduction - -This project does something. -We just haven't decided what that is yet. - -== Source Code - -[source,java] -.Java code from project ----- -include::{sourcedir}/example/StringUtils.java[tags=contains,indent=0] ----- - -This page was built by the following command: - - $ ./gradlew asciidoctor - -== Images - -[.thumb] -image::sunset.jpg[scaledwidth=75%] - -== Attributes - -.Built-in -asciidoctor-version:: {asciidoctor-version} -safe-mode-name:: {safe-mode-name} -docdir:: {docdir} -docfile:: {docfile} -imagesdir:: {imagesdir} -revnumber:: {revnumber} - -.Custom -sourcedir:: {sourcedir} -endpoint-url:: {endpoint-url} - -== Includes - -.include::subdir/_b.adoc[] -==== -include::subdir/_b.adoc[] -==== - -WARNING: Includes can be tricky! - -== build.gradle - -[source,groovy] ----- -include::{build-gradle}[] ----- diff --git a/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/subdir/_b.adoc b/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/subdir/_b.adoc deleted file mode 100644 index 422eb5ee04..0000000000 --- a/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/subdir/_b.adoc +++ /dev/null @@ -1,7 +0,0 @@ -content from _src/docs/asciidoc/subdir/_b.adoc_. - -.include::_c.adoc[] -[example] --- -include::_c.adoc[] --- diff --git a/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/subdir/_c.adoc b/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/subdir/_c.adoc deleted file mode 100644 index 3aca173636..0000000000 --- a/buildSrc/src/test/resources/samples/docs/simple/src/docs/asciidoc/subdir/_c.adoc +++ /dev/null @@ -1 +0,0 @@ -content from _src/docs/asciidoc/subdir/c.adoc_. diff --git a/buildSrc/src/test/resources/samples/docs/simple/src/main/java/example/StringUtils.java b/buildSrc/src/test/resources/samples/docs/simple/src/main/java/example/StringUtils.java deleted file mode 100644 index 3884f862bb..0000000000 --- a/buildSrc/src/test/resources/samples/docs/simple/src/main/java/example/StringUtils.java +++ /dev/null @@ -1,9 +0,0 @@ -package example; - -public class StringUtils { - // tag::contains[] - public boolean contains(String haystack, String needle) { - return haystack.contains(needle); - } - // end::contains[] -} diff --git a/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/sgbcs-docs.gradle b/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/sgbcs-docs.gradle index f3d52b8c1f..88d66263de 100644 --- a/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/sgbcs-docs.gradle +++ b/buildSrc/src/test/resources/samples/showcase/sgbcs-docs/sgbcs-docs.gradle @@ -2,10 +2,3 @@ apply plugin: 'java' apply plugin: 'io.spring.convention.docs' version = "1.0.0.BUILD-SNAPSHOT" - -asciidoctorj { - attributes \ - 'build-gradle': project.buildFile, - 'sourcedir': project.sourceSets.main.java.srcDirs[0], - 'endpoint-url': 'https://example.org' -} \ No newline at end of file diff --git a/docs/spring-security-docs.gradle b/docs/spring-security-docs.gradle index b888fc0023..57a95e9145 100644 --- a/docs/spring-security-docs.gradle +++ b/docs/spring-security-docs.gradle @@ -1,3 +1,4 @@ +apply plugin: 'io.spring.convention.docs' apply plugin: 'java' tasks.register("generateAntora") { From 7f5b008266acf5a98cb62608ac1d3813c2310b7d Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 17 Sep 2021 10:33:43 -0500 Subject: [PATCH 23/34] Add spring-security-docs-generated to local-antora-playbook.yml --- local-antora-playbook.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/local-antora-playbook.yml b/local-antora-playbook.yml index cdcb52c474..061bad7e96 100644 --- a/local-antora-playbook.yml +++ b/local-antora-playbook.yml @@ -9,6 +9,8 @@ content: - url: ./ branches: [HEAD] start_path: docs + - url: ../../rwinch/spring-security-docs-generated + branches: [HEAD] ui: bundle: url: https://github.com/rwinch/antora-ui-spring/releases/download/latest/ui-bundle.zip From d52aab88af88c413701a5ff90b2b783fac96341f Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Mon, 20 Sep 2021 14:55:23 -0500 Subject: [PATCH 24/34] Update nav text Project Modules --- docs/modules/ROOT/nav.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 260ad49b3e..d82e2177e5 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -10,7 +10,7 @@ *** xref:features/exploits/csrf.adoc[CSRF] *** xref:features/exploits/headers.adoc[HTTP Headers] *** xref:features/exploits/http.adoc[HTTP Requests] -* xref:modules.adoc[Project Modules & Dependencies] +* xref:modules.adoc[Project Modules] * xref:samples.adoc[Samples] * xref:servlet/index.adoc[Servlet Applications] ** xref:servlet/getting-started.adoc[Getting Started] From a1c7a39bd3062c8859281a319c11df36f1194c61 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Mon, 20 Sep 2021 16:50:46 -0500 Subject: [PATCH 25/34] Fix Firewall link in nav --- docs/modules/ROOT/nav.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index d82e2177e5..86e69a6013 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -57,7 +57,7 @@ *** xref:servlet/exploits/csrf.adoc[] *** xref:servlet/exploits/headers.adoc[] *** xref:servlet/exploits/http.adoc[] -*** xref:servlet/exploits/firewall.adoc[ +*** xref:servlet/exploits/firewall.adoc[] ** xref:servlet/integrations/index.adoc[Integrations] *** xref:servlet/integrations/servlet-api.adoc[Servlet APIs] *** xref:servlet/integrations/data.adoc[Spring Data] From 7537aa5124e4f6d74820449311e50da6ade10f16 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Mon, 20 Sep 2021 16:51:01 -0500 Subject: [PATCH 26/34] Add generic authorization page --- .../ROOT/pages/features/authorization/index.adoc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 docs/modules/ROOT/pages/features/authorization/index.adoc diff --git a/docs/modules/ROOT/pages/features/authorization/index.adoc b/docs/modules/ROOT/pages/features/authorization/index.adoc new file mode 100644 index 0000000000..d3c1d39328 --- /dev/null +++ b/docs/modules/ROOT/pages/features/authorization/index.adoc @@ -0,0 +1,14 @@ +[[authorization]] += Authorization + +Spring Security provides comprehensive support for https://en.wikipedia.org/wiki/Authorization[authorization]. +Authorization is determining who is allowed to access a particular resource. +Spring Security provides https://en.wikipedia.org/wiki/Defense_in_depth_(computing)[defense in depth] by allowing for request based authorization and method based authorization. + +[[authorization-request]] +== Request Based Authorization + +Spring Security provides authorization based upon the request for both xref:servlet/authorization/index.adoc[Servlet] and WebFlux environments. + +[[authorization-method]] +== Method Based Authorization From 0d339aeda61b558a2e45e2e688bc0eaf2ecc399d Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Mon, 20 Sep 2021 16:51:25 -0500 Subject: [PATCH 27/34] Add Reactive Getting Started Page --- docs/modules/ROOT/nav.adoc | 1 + .../ROOT/pages/reactive/getting-started.adoc | 80 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 docs/modules/ROOT/pages/reactive/getting-started.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 86e69a6013..c5a915faa9 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -81,6 +81,7 @@ *** xref:servlet/appendix/namespace.adoc[XML Namespace] *** xref:servlet/appendix/faq.adoc[FAQ] * xref:reactive/index.adoc[Reactive Applications] +** xref:reactive/getting-started.adoc[Getting Started] ** xref:reactive/webflux.adoc[WebFlux Security] ** xref:reactive/exploits/index.adoc[Protection Against Exploits] *** xref:reactive/exploits/csrf.adoc[CSRF] diff --git a/docs/modules/ROOT/pages/reactive/getting-started.adoc b/docs/modules/ROOT/pages/reactive/getting-started.adoc new file mode 100644 index 0000000000..aac4095a4c --- /dev/null +++ b/docs/modules/ROOT/pages/reactive/getting-started.adoc @@ -0,0 +1,80 @@ +[[getting-started]] += Getting Started with WebFlux Applications + +This section covers the minimum setup for how to use Spring Security with Spring Boot in a reactive application. + +[NOTE] +==== +The completed application can be found {gh-samples-url}/reactive/webflux/java/hello-security[in our samples repository]. +For your convenience, you can download a minimal Reactive Spring Boot + Spring Security application by https://start.spring.io/starter.zip?type=maven-project&language=java&packaging=jar&jvmVersion=1.8&groupId=example&artifactId=hello-security&name=hello-security&description=Hello%20Security&packageName=example.hello-security&dependencies=webflux,security[clicking here]. +==== + +[[dependencies]] +== Updating Dependencies + +You can add Spring Security to your Spring Boot project by adding `spring-boot-starter-security`. + +==== +.Maven +[source,xml,role="primary"] +---- + + org.springframework.boot + spring-boot-starter-security + +---- + +.Gradle +[source,groovy,role="secondary"] +---- + implementation 'org.springframework.boot:spring-boot-starter-security' +---- +==== + + +[[servlet-hello-starting]] +== 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 + +==== +.Maven +[source,bash,role="primary"] +---- +$ ./mvnw spring-boot:run +... +INFO 23689 --- [ restartedMain] .s.s.UserDetailsServiceAutoConfiguration : + +Using generated security password: 8e557245-73e2-4286-969a-ff57fe326336 + +... +---- + +.Gradle +[source,bash,role="secondary"] +---- +$ ./gradlew bootRun +... +INFO 23689 --- [ restartedMain] .s.s.UserDetailsServiceAutoConfiguration : + +Using generated security password: 8e557245-73e2-4286-969a-ff57fe326336 + +... +---- +==== + +[[authenticating]] +== Authenticating + +You can access the application at http://localhost:8080/ which will redirect the browser to the default log in page. You can provide the default username of `user` with the randomly generated password that is logged to the console. The browser is then taken to the orginally requested page. + +To log out you can visit http://localhost:8080/logout and then confirming you wish to log out. + +[[auto-configuration]] +== Spring Boot Auto Configuration + +Spring Boot automatically adds Spring Security which requires all requests be authenticated. It also generates a user with a randomly generated password that is logged to the console which can be used to authenticate using form or basic authentication. + From e368734557e4c8bf67db3be02b28977fd3011470 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Mon, 20 Sep 2021 16:57:37 -0500 Subject: [PATCH 28/34] Update "Big Picture" to Architecture --- docs/modules/ROOT/nav.adoc | 2 +- .../ROOT/pages/servlet/architecture.adoc | 2 +- .../pages/servlet/architecture/index.adoc | 248 ------------------ 3 files changed, 2 insertions(+), 250 deletions(-) delete mode 100644 docs/modules/ROOT/pages/servlet/architecture/index.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index c5a915faa9..b6819592eb 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -14,7 +14,7 @@ * xref:samples.adoc[Samples] * xref:servlet/index.adoc[Servlet Applications] ** xref:servlet/getting-started.adoc[Getting Started] -** xref:servlet/architecture.adoc[The Big Picture] +** xref:servlet/architecture.adoc[Architecture] ** xref:servlet/authentication/index.adoc[Authentication] *** xref:servlet/authentication/architecture.adoc[Authentication Architecture] *** xref:servlet/authentication/passwords/index.adoc[Username/Password] diff --git a/docs/modules/ROOT/pages/servlet/architecture.adoc b/docs/modules/ROOT/pages/servlet/architecture.adoc index ea457e03bb..40b62a1a3c 100644 --- a/docs/modules/ROOT/pages/servlet/architecture.adoc +++ b/docs/modules/ROOT/pages/servlet/architecture.adoc @@ -1,5 +1,5 @@ [[servlet-architecture]] -= Servlet Security: The Big Picture += Architecture :figures: servlet/architecture This section discusses Spring Security's high level architecture within Servlet based applications. diff --git a/docs/modules/ROOT/pages/servlet/architecture/index.adoc b/docs/modules/ROOT/pages/servlet/architecture/index.adoc deleted file mode 100644 index a1120e1585..0000000000 --- a/docs/modules/ROOT/pages/servlet/architecture/index.adoc +++ /dev/null @@ -1,248 +0,0 @@ -[[servlet-architecture]] -= Servlet Security: The Big Picture -:figures: servlet/architecture - -This section discusses Spring Security's high level architecture within Servlet based applications. -We build on this high level understanding within xref:servlet/authentication/index.adoc#servlet-authentication[Authentication], xref:servlet/authorization/index.adoc#servlet-authorization[Authorization], xref:servlet/exploits/index.adoc#servlet-exploits[Protection Against Exploits] sections of the reference. -// FIXME: Add links to other sections of architecture - -[[servlet-filters-review]] -== A Review of ``Filter``s - -Spring Security's Servlet support is based on Servlet ``Filter``s, so it is helpful to look at the role of ``Filter``s generally first. -The picture below shows the typical layering of the handlers for a single HTTP request. - -.FilterChain -[[servlet-filterchain-figure]] -image::{figures}/filterchain.png[] - -The client sends a request to the application, and the container creates a `FilterChain` which contains the ``Filter``s and `Servlet` that should process the `HttpServletRequest` based on the path of the request URI. -In a Spring MVC application the `Servlet` is an instance of {spring-framework-reference-url}web.html#mvc-servlet[`DispatcherServlet`]. -At most one `Servlet` can handle a single `HttpServletRequest` and `HttpServletResponse`. -However, more than one `Filter` can be used to: - -* Prevent downstream ``Filter``s or the `Servlet` from being invoked. -In this instance the `Filter` will typically write the `HttpServletResponse`. -* Modify the `HttpServletRequest` or `HttpServletResponse` used by the downstream ``Filter``s and `Servlet` - -The power of the `Filter` comes from the `FilterChain` that is passed into it. - -.`FilterChain` Usage Example -==== -.Java -[source,java,role="primary"] ----- -public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { - // do something before the rest of the application - chain.doFilter(request, response); // invoke the rest of the application - // do something after the rest of the application -} ----- - -.Kotlin -[source,kotlin,role="secondary"] ----- -fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { - // do something before the rest of the application - chain.doFilter(request, response) // invoke the rest of the application - // do something after the rest of the application -} ----- -==== - -Since a `Filter` only impacts downstream ``Filter``s and the `Servlet`, the order each `Filter` is invoked is extremely important. - - -[[servlet-delegatingfilterproxy]] -== DelegatingFilterProxy - -Spring provides a `Filter` implementation named {spring-framework-api-url}org/springframework/web/filter/DelegatingFilterProxy.html[`DelegatingFilterProxy`] that allows bridging between the Servlet container's lifecycle and Spring's `ApplicationContext`. -The Servlet container allows registering ``Filter``s using its own standards, but it is not aware of Spring defined Beans. -`DelegatingFilterProxy` can be registered via standard Servlet container mechanisms, but delegate all the work to a Spring Bean that implements `Filter`. - -Here is a picture of how `DelegatingFilterProxy` fits into the <>. - -.DelegatingFilterProxy -[[servlet-delegatingfilterproxy-figure]] -image::{figures}/delegatingfilterproxy.png[] - -`DelegatingFilterProxy` looks up __Bean Filter~0~__ from the `ApplicationContext` and then invokes __Bean Filter~0~__. -The pseudo code of `DelegatingFilterProxy` can be seen below. - -.`DelegatingFilterProxy` Pseudo Code -==== -.Java -[source,java,role="primary",subs="+quotes,+macros"] ----- -public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { - // Lazily get Filter that was registered as a Spring Bean - // For the example in <> `delegate` is an instance of __Bean Filter~0~__ - Filter delegate = getFilterBean(someBeanName); - // delegate work to the Spring Bean - delegate.doFilter(request, response); -} ----- - -.Kotlin -[source,kotlin,role="secondary",subs="+quotes,+macros"] ----- -fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { - // Lazily get Filter that was registered as a Spring Bean - // For the example in <> `delegate` is an instance of __Bean Filter~0~__ - val delegate: Filter = getFilterBean(someBeanName) - // delegate work to the Spring Bean - delegate.doFilter(request, response) -} ----- -==== - -Another benefit of `DelegatingFilterProxy` is that it allows delaying looking `Filter` bean instances up. -This is important because the container needs to register the `Filter` instances before the container can startup. -However, Spring typically uses a `ContextLoaderListener` to load the Spring Beans which will not be done until after the `Filter` instances need to be registered. - -[[servlet-filterchainproxy]] -== FilterChainProxy - -Spring Security's Servlet support is contained within `FilterChainProxy`. -`FilterChainProxy` is a special `Filter` provided by Spring Security that allows delegating to many `Filter` instances through <>. -Since `FilterChainProxy` is a Bean, it is typically wrapped in a <>. - -.FilterChainProxy -[[servlet-filterchainproxy-figure]] -image::{figures}/filterchainproxy.png[] - -[[servlet-securityfilterchain]] -== SecurityFilterChain - -{security-api-url}org/springframework/security/web/SecurityFilterChain.html[`SecurityFilterChain`] is used by <> to determine which Spring Security ``Filter``s should be invoked for this request. - -.SecurityFilterChain -[[servlet-securityfilterchain-figure]] -image::{figures}/securityfilterchain.png[] - -The <> in `SecurityFilterChain` are typically Beans, but they are registered with `FilterChainProxy` instead of <>. -`FilterChainProxy` provides a number of advantages to registering directly with the Servlet container or <>. -First, it provides a starting point for all of Spring Security's Servlet support. -For that reason, if you are attempting to troubleshoot Spring Security's Servlet support, adding a debug point in `FilterChainProxy` is a great place to start. - -Second, since `FilterChainProxy` is central to Spring Security usage it can perform tasks that are not viewed as optional. -// FIXME: Add a link to SecurityContext -For example, it clears out the `SecurityContext` to avoid memory leaks. -It also applies Spring Security's xref:servlet/exploits/firewall.adoc#servlet-httpfirewall[`HttpFirewall`] to protect applications against certain types of attacks. - -In addition, it provides more flexibility in determining when a `SecurityFilterChain` should be invoked. -In a Servlet container, ``Filter``s are invoked based upon the URL alone. -// FIXME: Link to RequestMatcher -However, `FilterChainProxy` can determine invocation based upon anything in the `HttpServletRequest` by leveraging the `RequestMatcher` interface. - -In fact, `FilterChainProxy` can be used to determine which `SecurityFilterChain` should be used. -This allows providing a totally separate configuration for different _slices_ of your application. - -.Multiple SecurityFilterChain -[[servlet-multi-securityfilterchain-figure]] -image::{figures}/multi-securityfilterchain.png[] - -In the <> Figure `FilterChainProxy` decides which `SecurityFilterChain` should be used. -Only the first `SecurityFilterChain` that matches will be invoked. -If a URL of `/api/messages/` is requested, it will first match on ``SecurityFilterChain~0~``'s pattern of `+/api/**+`, so only `SecurityFilterChain~0~` will be invoked even though it also matches on ``SecurityFilterChain~n~``. -If a URL of `/messages/` is requested, it will not match on ``SecurityFilterChain~0~``'s pattern of `+/api/**+`, so `FilterChainProxy` will continue trying each `SecurityFilterChain`. -Assuming that no other, `SecurityFilterChain` instances match `SecurityFilterChain~n~` will be invoked. -// FIXME add link to pattern matching - -Notice that `SecurityFilterChain~0~` has only three security ``Filter``s instances configured. -However, `SecurityFilterChain~n~` has four security ``Filter``s configured. -It is important to note that each `SecurityFilterChain` can be unique and configured in isolation. -In fact, a `SecurityFilterChain` might have zero security ``Filter``s if the application wants Spring Security to ignore certain requests. -// FIXME: add link to configuring multiple `SecurityFilterChain` instances - -[[servlet-security-filters]] -== Security Filters - -The Security Filters are inserted into the <> with the <> API. -The <>s matters. -It is typically not necessary to know the ordering of Spring Security's ``Filter``s. -However, there are times that it is beneficial to know the ordering - -Below is a comprehensive list of Spring Security Filter ordering: - -* ChannelProcessingFilter -* WebAsyncManagerIntegrationFilter -* SecurityContextPersistenceFilter -* HeaderWriterFilter -* CorsFilter -* CsrfFilter -* LogoutFilter -* OAuth2AuthorizationRequestRedirectFilter -* Saml2WebSsoAuthenticationRequestFilter -* X509AuthenticationFilter -* AbstractPreAuthenticatedProcessingFilter -* CasAuthenticationFilter -* OAuth2LoginAuthenticationFilter -* Saml2WebSsoAuthenticationFilter -* xref:servlet/authentication/passwords/form.adoc#servlet-authentication-usernamepasswordauthenticationfilter[`UsernamePasswordAuthenticationFilter`] -* OpenIDAuthenticationFilter -* DefaultLoginPageGeneratingFilter -* DefaultLogoutPageGeneratingFilter -* ConcurrentSessionFilter -* xref:servlet/authentication/passwords/digest.adoc#servlet-authentication-digest[`DigestAuthenticationFilter`] -* BearerTokenAuthenticationFilter -* xref:servlet/authentication/passwords/basic.adoc#servlet-authentication-basic[`BasicAuthenticationFilter`] -* RequestCacheAwareFilter -* SecurityContextHolderAwareRequestFilter -* JaasApiIntegrationFilter -* RememberMeAuthenticationFilter -* AnonymousAuthenticationFilter -* OAuth2AuthorizationCodeGrantFilter -* SessionManagementFilter -* <> -* xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] -* SwitchUserFilter - -[[servlet-exceptiontranslationfilter]] -== Handling Security Exceptions - - -The {security-api-url}org/springframework/security/web/access/ExceptionTranslationFilter.html[`ExceptionTranslationFilter`] allows translation of {security-api-url}org/springframework/security/access/AccessDeniedException.html[`AccessDeniedException`] and {security-api-url}/org/springframework/security/core/AuthenticationException.html[`AuthenticationException`] into HTTP responses. - -`ExceptionTranslationFilter` is inserted into the <> as one of the <>. - -image::{figures}/exceptiontranslationfilter.png[] - - -* image:{icondir}/number_1.png[] First, the `ExceptionTranslationFilter` invokes `FilterChain.doFilter(request, response)` to invoke the rest of the application. -* image:{icondir}/number_2.png[] If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__. -** The xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder] is cleared out. -** The `HttpServletRequest` is saved in the {security-api-url}org/springframework/security/web/savedrequest/RequestCache.html[`RequestCache`]. -When the user successfully authenticates, the `RequestCache` is used to replay the original request. -// FIXME: add link to authentication success -** The `AuthenticationEntryPoint` is used to request credentials from the client. -For example, it might redirect to a log in page or send a `WWW-Authenticate` header. -// FIXME: link to AuthenticationEntryPoint -* image:{icondir}/number_3.png[] Otherwise if it is an `AccessDeniedException`, then __Access Denied__. -The `AccessDeniedHandler` is invoked to handle access denied. -// FIXME: link to AccessDeniedHandler - -[NOTE] -==== -If the application does not throw an `AccessDeniedException` or an `AuthenticationException`, then `ExceptionTranslationFilter` does not do anything. -==== - -The pseudocode for `ExceptionTranslationFilter` looks something like this: - -.ExceptionTranslationFilter pseudocode -[source,java] ----- -try { - filterChain.doFilter(request, response); // <1> -} catch (AccessDeniedException | AuthenticationException ex) { - if (!authenticated || ex instanceof AuthenticationException) { - startAuthentication(); // <2> - } else { - accessDenied(); // <3> - } -} ----- -<1> You will recall from <> that invoking `FilterChain.doFilter(request, response)` is the equivalent of invoking the rest of the application. -This means that if another part of the application, (i.e. xref:servlet/authorization/authorize-requests.adoc#servlet-authorization-filtersecurityinterceptor[`FilterSecurityInterceptor`] or method security) throws an `AuthenticationException` or `AccessDeniedException` it will be caught and handled here. -<2> If the user is not authenticated or it is an `AuthenticationException`, then __Start Authentication__. -<3> Otherwise, __Access Denied__ From d6ada7fb18505cfd9f6cb095e792d54da00e1eea Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Tue, 21 Sep 2021 10:07:35 -0500 Subject: [PATCH 29/34] Remove /servlet/hello/index.adoc This is now /servlet/getting-started.adoc --- .../ROOT/pages/servlet/hello/index.adoc | 74 ------------------- 1 file changed, 74 deletions(-) delete mode 100644 docs/modules/ROOT/pages/servlet/hello/index.adoc diff --git a/docs/modules/ROOT/pages/servlet/hello/index.adoc b/docs/modules/ROOT/pages/servlet/hello/index.adoc deleted file mode 100644 index deebb9bc46..0000000000 --- a/docs/modules/ROOT/pages/servlet/hello/index.adoc +++ /dev/null @@ -1,74 +0,0 @@ -[[servlet-hello]] -= Hello Spring Security - -This section covers the minimum setup for how to use Spring Security with Spring Boot. - -[NOTE] -==== -The completed application can be found {gh-samples-url}/servlet/spring-boot/java/hello-security[in our samples repository]. -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&packaging=jar&jvmVersion=1.8&groupId=example&artifactId=hello-security&name=hello-security&description=Hello%20Security&packageName=example.hello-security&dependencies=web,security[clicking here]. -==== - -[[servlet-hello-dependencies]] -== Updating Dependencies - -The only step you need to do is update the dependencies by using xref:getting-spring-security.adoc#getting-maven-boot[Maven] or xref:getting-spring-security.adoc#getting-gradle-boot[Gradle]. - -[[servlet-hello-starting]] -== 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-auto-configuration]] -== Spring Boot Auto Configuration - -// FIXME: Link to relevant portions of documentation -// FIXME: Link to Spring Boot's Security Auto configuration classes -// FIXME: Add a links for what user's should do next - -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 -* https://en.wikipedia.org/wiki/Cross-site_request_forgery[CSRF attack] prevention -* https://en.wikipedia.org/wiki/Session_fixation[Session Fixation] protection -* Security Header integration -** https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security[HTTP Strict Transport Security] for secure requests -** https://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) -** https://msdn.microsoft.com/en-us/library/dd565647(v=vs.85).aspx[X-XSS-Protection] integration -** X-Frame-Options integration to help prevent https://en.wikipedia.org/wiki/Clickjacking[Clickjacking] -* Integrate with the following Servlet API methods: -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[`HttpServletRequest#getRemoteUser()`] -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[`HttpServletRequest#getUserPrincipal()`] -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[`HttpServletRequest#isUserInRole(java.lang.String)`] -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login(java.lang.String,%20java.lang.String)[`HttpServletRequest#login(java.lang.String, java.lang.String)`] -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout()[`HttpServletRequest#logout()`] - From 176e6b60003f14179dfecd8ff96fe5294676807a Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Tue, 21 Sep 2021 10:25:24 -0500 Subject: [PATCH 30/34] Add additional info to overview --- docs/modules/ROOT/pages/index.adoc | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 7441dadf05..84f9f83499 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -1,5 +1,15 @@ -= Overview += Spring Security -Spring Security is a framework that provides authentication, authorization, and protection against common attacks. -With first class support for both imperative and reactive applications, it is the de-facto standard for securing Spring-based applications. +Spring Security is a framework that provides xref:features/authentication/index.adoc[authentication], xref:features/authorization/index.adoc[authorization], and xref:features/exploits/index.adoc[protection against common attacks]. +With first class support for securing both xref:servlet/index.adoc[imperative] and xref:reactive/index.adoc[reactive] applications, it is the de-facto standard for securing Spring-based applications. +For a complete list of features, see the xref:features/index.adoc[Features] section of the reference. + +== Getting Started + +If you are ready to start securing an application see the Getting Started sections for xref:servlet/getting-started.adoc[servlet] and xref:reactive/getting-started.adoc[reactive]. These sections will walk you through creating your first Spring Security applications. + +If you want to understand how Spring Security works, you can refer to the xref:servlet/architecture.adoc[Architecture] section. +// FIXME add link to reactive architecture + +If you have any questions, there is a wonderful xref:community.adoc[community] that would love to help you! From ab63ebbbee4348c538fdfdae1683feb484d586ef Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Tue, 21 Sep 2021 14:37:49 -0500 Subject: [PATCH 31/34] Add link to authorization from features. --- docs/modules/ROOT/pages/features/authentication/index.adoc | 2 -- docs/modules/ROOT/pages/features/index.adoc | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/modules/ROOT/pages/features/authentication/index.adoc b/docs/modules/ROOT/pages/features/authentication/index.adoc index 991b671e92..fb4cc5cab9 100644 --- a/docs/modules/ROOT/pages/features/authentication/index.adoc +++ b/docs/modules/ROOT/pages/features/authentication/index.adoc @@ -6,8 +6,6 @@ Authentication is how we verify the identity of who is trying to access a partic A common way to authenticate users is by requiring the user to enter a username and password. Once authentication is performed we know the identity and can perform authorization. -== Authentication Support - Spring Security provides built in support for authenticating users. This section is dedicated to generic authentication support that applies in both Servlet and WebFlux environments. Refer to the sections on authentication for xref:servlet/authentication/index.adoc#servlet-authentication[Servlet] and WebFlux for details on what is supported for each stack. diff --git a/docs/modules/ROOT/pages/features/index.adoc b/docs/modules/ROOT/pages/features/index.adoc index 2e51981630..5775de1d1f 100644 --- a/docs/modules/ROOT/pages/features/index.adoc +++ b/docs/modules/ROOT/pages/features/index.adoc @@ -1,6 +1,6 @@ [[features]] = Features -Spring Security provides comprehensive support for xref:features/authentication/index.adoc#authentication[authentication], authorization, and protection against xref:features/exploits/index.adoc#exploits[common exploits]. +Spring Security provides comprehensive support for xref:features/authentication/index.adoc[authentication], xref:features/authorization/index.adoc[authorization], and protection against xref:features/exploits/index.adoc#exploits[common exploits]. It also provides integration with other libraries to simplify its usage. From ca2bc958df1aa01b48861143ad50b534a1bfcf44 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Tue, 21 Sep 2021 14:53:38 -0500 Subject: [PATCH 32/34] Create features/integrations --- docs/modules/ROOT/nav.adoc | 11 +- .../integrations/concurrency.adoc | 0 .../integrations}/cryptography.adoc | 0 .../integrations/data.adoc | 0 .../pages/features/integrations/index.adoc | 13 + .../integrations/jackson.adoc | 0 .../integrations/localization.adoc | 0 .../ROOT/pages/servlet/crypto/index.adoc | 264 ------------------ .../pages/servlet/integrations/index.adoc | 4 - .../ROOT/pages/servlet/integrations/mvc.adoc | 2 +- 10 files changed, 20 insertions(+), 274 deletions(-) rename docs/modules/ROOT/pages/{servlet => features}/integrations/concurrency.adoc (100%) rename docs/modules/ROOT/pages/{servlet => features/integrations}/cryptography.adoc (100%) rename docs/modules/ROOT/pages/{servlet => features}/integrations/data.adoc (100%) create mode 100644 docs/modules/ROOT/pages/features/integrations/index.adoc rename docs/modules/ROOT/pages/{servlet => features}/integrations/jackson.adoc (100%) rename docs/modules/ROOT/pages/{servlet => features}/integrations/localization.adoc (100%) delete mode 100644 docs/modules/ROOT/pages/servlet/crypto/index.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index b6819592eb..381e6d8044 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -10,6 +10,12 @@ *** xref:features/exploits/csrf.adoc[CSRF] *** xref:features/exploits/headers.adoc[HTTP Headers] *** xref:features/exploits/http.adoc[HTTP Requests] +** xref:features/integrations/index.adoc[Integrations] +*** xref:features/integrations/cryptography.adoc[Cryptography] +*** xref:features/integrations/data.adoc[Spring Data] +*** xref:features/integrations/concurrency.adoc[Java's Concurrency APIs] +*** xref:features/integrations/jackson.adoc[Jackson] +*** xref:features/integrations/localization.adoc[Localization] * xref:modules.adoc[Project Modules] * xref:samples.adoc[Samples] * xref:servlet/index.adoc[Servlet Applications] @@ -60,10 +66,6 @@ *** xref:servlet/exploits/firewall.adoc[] ** xref:servlet/integrations/index.adoc[Integrations] *** xref:servlet/integrations/servlet-api.adoc[Servlet APIs] -*** xref:servlet/integrations/data.adoc[Spring Data] -*** xref:servlet/integrations/concurrency.adoc[Java's Concurrency APIs] -*** xref:servlet/integrations/jackson.adoc[Jackson] -*** xref:servlet/integrations/localization.adoc[Localization] *** xref:servlet/integrations/mvc.adoc[Spring MVC] *** xref:servlet/integrations/websocket.adoc[WebSocket] *** xref:servlet/integrations/cors.adoc[Spring's CORS Support] @@ -75,7 +77,6 @@ ** xref:servlet/test/index.adoc[Testing] *** xref:servlet/test/method.adoc[Method Security] *** xref:servlet/test/mockmvc.adoc[MockMvc Support] -** xref:servlet/cryptography.adoc[Cryptography] ** xref:servlet/appendix/index.adoc[Appendix] *** xref:servlet/appendix/database-schema.adoc[Database Schemas] *** xref:servlet/appendix/namespace.adoc[XML Namespace] diff --git a/docs/modules/ROOT/pages/servlet/integrations/concurrency.adoc b/docs/modules/ROOT/pages/features/integrations/concurrency.adoc similarity index 100% rename from docs/modules/ROOT/pages/servlet/integrations/concurrency.adoc rename to docs/modules/ROOT/pages/features/integrations/concurrency.adoc diff --git a/docs/modules/ROOT/pages/servlet/cryptography.adoc b/docs/modules/ROOT/pages/features/integrations/cryptography.adoc similarity index 100% rename from docs/modules/ROOT/pages/servlet/cryptography.adoc rename to docs/modules/ROOT/pages/features/integrations/cryptography.adoc diff --git a/docs/modules/ROOT/pages/servlet/integrations/data.adoc b/docs/modules/ROOT/pages/features/integrations/data.adoc similarity index 100% rename from docs/modules/ROOT/pages/servlet/integrations/data.adoc rename to docs/modules/ROOT/pages/features/integrations/data.adoc diff --git a/docs/modules/ROOT/pages/features/integrations/index.adoc b/docs/modules/ROOT/pages/features/integrations/index.adoc new file mode 100644 index 0000000000..96f151b5fc --- /dev/null +++ b/docs/modules/ROOT/pages/features/integrations/index.adoc @@ -0,0 +1,13 @@ +[[integrations]] += Integrations + +Spring Security provides integrations with numerous frameworks and APIs. +In this section, we discuss generic integrations that are not specific to Servlet or Reactive environments. +To see specific integrations, refer to the xref:servlet/integrations/index.adoc[Servlet] and xref:servlet/integrations/index.adoc[Reactive] Integrations sections. +// FIXME add link to reactive integrations + +* xref:features/integrations/cryptography.adoc[Cryptography] +* xref:features/integrations/data.adoc[Spring Data] +* xref:features/integrations/concurrency.adoc[Java's Concurrency APIs] +* xref:features/integrations/jackson.adoc[Jackson] +* xref:features/integrations/localization.adoc[Localization] diff --git a/docs/modules/ROOT/pages/servlet/integrations/jackson.adoc b/docs/modules/ROOT/pages/features/integrations/jackson.adoc similarity index 100% rename from docs/modules/ROOT/pages/servlet/integrations/jackson.adoc rename to docs/modules/ROOT/pages/features/integrations/jackson.adoc diff --git a/docs/modules/ROOT/pages/servlet/integrations/localization.adoc b/docs/modules/ROOT/pages/features/integrations/localization.adoc similarity index 100% rename from docs/modules/ROOT/pages/servlet/integrations/localization.adoc rename to docs/modules/ROOT/pages/features/integrations/localization.adoc diff --git a/docs/modules/ROOT/pages/servlet/crypto/index.adoc b/docs/modules/ROOT/pages/servlet/crypto/index.adoc deleted file mode 100644 index 137b3e8b69..0000000000 --- a/docs/modules/ROOT/pages/servlet/crypto/index.adoc +++ /dev/null @@ -1,264 +0,0 @@ -[[crypto]] -= Spring Security Crypto Module - - -[[spring-security-crypto-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 -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 -Use the `Encryptors.stronger` factory method to construct a BytesEncryptor: - -.BytesEncryptor -==== -.Java -[source,java,role="primary"] ----- -Encryptors.stronger("password", "salt"); ----- - -.Kotlin -[source,kotlin,role="secondary"] ----- -Encryptors.stronger("password", "salt") ----- -==== - -The "stronger" encryption method creates an encryptor using 256 bit AES encryption with -Galois Counter Mode (GCM). -It derives the secret key using PKCS #5's PBKDF2 (Password-Based Key Derivation Function #2). -This method requires Java 6. -The password used to generate the SecretKey should be kept in a secure place and not be shared. -The salt is used to prevent dictionary attacks against the key in the event your encrypted data is compromised. -A 16-byte random initialization vector is also applied so each encrypted message is unique. - -The provided salt should be in hex-encoded String form, be random, and be at least 8 bytes in length. -Such a salt may be generated using a KeyGenerator: - -.Generating a key -==== -.Java -[source,java,role="primary"] ----- -String salt = KeyGenerators.string().generateKey(); // generates a random 8-byte salt that is then hex-encoded ----- - -.Kotlin -[source,kotlin,role="secondary"] ----- -val salt = KeyGenerators.string().generateKey() // generates a random 8-byte salt that is then hex-encoded ----- -==== - -Users may also use the `standard` encryption method, which is 256-bit AES in Cipher Block Chaining (CBC) Mode. -This mode is not https://en.wikipedia.org/wiki/Authenticated_encryption[authenticated] and does not provide any -guarantees about the authenticity of the data. -For a more secure alternative, users should prefer `Encryptors.stronger`. - -[[spring-security-crypto-encryption-text]] -=== TextEncryptor -Use the Encryptors.text factory method to construct a standard TextEncryptor: - -.TextEncryptor -==== -.Java -[source,java,role="primary"] ----- -Encryptors.text("password", "salt"); ----- - -.Kotlin -[source,kotlin,role="secondary"] ----- -Encryptors.text("password", "salt") ----- -==== - -A TextEncryptor uses a standard BytesEncryptor to encrypt text data. -Encrypted results are returned as hex-encoded strings for easy storage on the filesystem or in the database. - -Use the Encryptors.queryableText factory method to construct a "queryable" TextEncryptor: - -.Queryable TextEncryptor -==== -.Java -[source,java,role="primary"] ----- -Encryptors.queryableText("password", "salt"); ----- - -.Kotlin -[source,kotlin,role="secondary"] ----- -Encryptors.queryableText("password", "salt") ----- -==== - -The difference between a queryable TextEncryptor and a standard TextEncryptor has to do with initialization vector (iv) handling. -The iv used in a queryable TextEncryptor#encrypt operation is shared, or constant, and is not randomly generated. -This means the same text encrypted multiple times will always produce the same encryption result. -This is less secure, but necessary for encrypted data that needs to be queried against. -An example of queryable encrypted text would be an OAuth apiKey. - -[[spring-security-crypto-keygenerators]] -== 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 -Use the KeyGenerators.secureRandom factory methods to generate a BytesKeyGenerator backed by a SecureRandom instance: - -.BytesKeyGenerator -==== -.Java -[source,java,role="primary"] ----- -BytesKeyGenerator generator = KeyGenerators.secureRandom(); -byte[] key = generator.generateKey(); ----- - -.Kotlin -[source,kotlin,role="secondary"] ----- -val generator = KeyGenerators.secureRandom() -val key = generator.generateKey() ----- -==== - -The default key length is 8 bytes. -There is also a KeyGenerators.secureRandom variant that provides control over the key length: - -.KeyGenerators.secureRandom -==== -.Java -[source,java,role="primary"] ----- -KeyGenerators.secureRandom(16); ----- - -.Kotlin -[source,kotlin,role="secondary"] ----- -KeyGenerators.secureRandom(16) ----- -==== - -Use the KeyGenerators.shared factory method to construct a BytesKeyGenerator that always returns the same key on every invocation: - -.KeyGenerators.shared -==== -.Java -[source,java,role="primary"] ----- -KeyGenerators.shared(16); ----- - -.Kotlin -[source,kotlin,role="secondary"] ----- -KeyGenerators.shared(16) ----- -==== - -=== StringKeyGenerator -Use the KeyGenerators.string factory method to construct a 8-byte, SecureRandom KeyGenerator that hex-encodes each key as a String: - -.StringKeyGenerator -==== -.Java -[source,java,role="primary"] ----- -KeyGenerators.string(); ----- - -.Kotlin -[source,kotlin,role="secondary"] ----- -KeyGenerators.string() ----- -==== - -[[spring-security-crypto-passwordencoders]] -== 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: - -[source,java] ----- -public interface PasswordEncoder { - -String encode(String rawPassword); - -boolean matches(String rawPassword, String encodedPassword); -} ----- - -The matches method returns true if the rawPassword, once encoded, equals the encodedPassword. -This method is designed to support password-based authentication schemes. - -The `BCryptPasswordEncoder` implementation uses the widely supported "bcrypt" algorithm to hash the passwords. -Bcrypt uses a random 16 byte salt value and is a deliberately slow algorithm, in order to hinder password crackers. -The amount of work it does can be tuned using the "strength" parameter which takes values from 4 to 31. -The higher the value, the more work has to be done to calculate the hash. -The default value is 10. -You can change this value in your deployed system without affecting existing passwords, as the value is also stored in the encoded hash. - -.BCryptPasswordEncoder -==== -.Java -[source,java,role="primary"] ----- - -// Create an encoder with strength 16 -BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16); -String result = encoder.encode("myPassword"); -assertTrue(encoder.matches("myPassword", result)); ----- - -.Kotlin -[source,kotlin,role="secondary"] ----- - -// Create an encoder with strength 16 -val encoder = BCryptPasswordEncoder(16) -val result: String = encoder.encode("myPassword") -assertTrue(encoder.matches("myPassword", result)) ----- -==== - -The `Pbkdf2PasswordEncoder` implementation uses PBKDF2 algorithm to hash the passwords. -In order to defeat password cracking PBKDF2 is a deliberately slow algorithm and should be tuned to take about .5 seconds to verify a password on your system. - - -.Pbkdf2PasswordEncoder -==== -.Java -[source,java,role="primary"] ----- -// Create an encoder with all the defaults -Pbkdf2PasswordEncoder encoder = new Pbkdf2PasswordEncoder(); -String result = encoder.encode("myPassword"); -assertTrue(encoder.matches("myPassword", result)); ----- - -.Kotlin -[source,kotlin,role="secondary"] ----- -// Create an encoder with all the defaults -val encoder = Pbkdf2PasswordEncoder() -val result: String = encoder.encode("myPassword") -assertTrue(encoder.matches("myPassword", result)) ----- -==== diff --git a/docs/modules/ROOT/pages/servlet/integrations/index.adoc b/docs/modules/ROOT/pages/servlet/integrations/index.adoc index 5bfff0a49c..0e549ff221 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/index.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/index.adoc @@ -4,10 +4,6 @@ Spring Security integrates with numerous frameworks and APIs. In this section, we discuss Spring Security integration with: * xref:servlet/integrations/servlet-api.adoc[Servlet APIs] -* xref:servlet/integrations/data.adoc[Spring Data] -* xref:servlet/integrations/concurrency.adoc[Java's Concurrency APIs] -* xref:servlet/integrations/jackson.adoc[Jackson] -* xref:servlet/integrations/localization.adoc[Localization] * xref:servlet/integrations/mvc.adoc[Spring MVC] * xref:servlet/integrations/websocket.adoc[WebSocket] * xref:servlet/integrations/cors.adoc[Spring's CORS Support] diff --git a/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc b/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc index b473543bf2..5198ffc905 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc @@ -492,7 +492,7 @@ The `SecurityContext` that is used to process the `Callable` is the `SecurityCon There is no automatic integration with a `DeferredResult` that is returned by controllers. This is because `DeferredResult` is processed by the users and thus there is no way of automatically integrating with it. -However, you can still use xref:servlet/integrations/concurrency.adoc#concurrency[Concurrency Support] to provide transparent integration with Spring Security. +However, you can still use xref:features/integrations/concurrency.adoc#concurrency[Concurrency Support] to provide transparent integration with Spring Security. [[mvc-csrf]] == Spring MVC and CSRF Integration From 77dc3d1a0ea091404fe95954fb0e62b8c4b4cb6d Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Tue, 21 Sep 2021 14:56:09 -0500 Subject: [PATCH 33/34] Move to servlet/configurations/* --- docs/modules/ROOT/nav.adoc | 6 +- .../pages/servlet/appendix/namespace.adoc | 8 +- .../pages/servlet/authentication/preauth.adoc | 2 +- .../servlet/authentication/rememberme.adoc | 2 +- .../servlet/authorization/secure-objects.adoc | 2 +- .../java.adoc} | 2 +- .../kotlin.adoc} | 0 .../{ => configuration}/xml-namespace.adoc | 0 .../ROOT/pages/servlet/integrations/mvc.adoc | 2 +- .../servlet/java-configuration/index.adoc | 337 --------------- .../servlet/kotlin-configuration/index.adoc | 99 ----- .../ROOT/pages/servlet/namespace/index.adoc | 402 ------------------ 12 files changed, 12 insertions(+), 850 deletions(-) rename docs/modules/ROOT/pages/servlet/{java-configuration.adoc => configuration/java.adoc} (98%) rename docs/modules/ROOT/pages/servlet/{kotlin-configuration.adoc => configuration/kotlin.adoc} (100%) rename docs/modules/ROOT/pages/servlet/{ => configuration}/xml-namespace.adoc (100%) delete mode 100644 docs/modules/ROOT/pages/servlet/java-configuration/index.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/kotlin-configuration/index.adoc delete mode 100644 docs/modules/ROOT/pages/servlet/namespace/index.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 381e6d8044..f71de72317 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -71,9 +71,9 @@ *** xref:servlet/integrations/cors.adoc[Spring's CORS Support] *** xref:servlet/integrations/jsp-taglibs.adoc[JSP Taglib] ** Configuration -*** xref:servlet/java-configuration.adoc[Java Configuration] -*** xref:servlet/kotlin-configuration.adoc[Kotlin Configuration] -*** xref:servlet/xml-namespace.adoc[Namespace Configuration] +*** xref:servlet/configuration/java.adoc[Java Configuration] +*** xref:servlet/configuration/kotlin.adoc[Kotlin Configuration] +*** xref:servlet/configuration/xml-namespace.adoc[Namespace Configuration] ** xref:servlet/test/index.adoc[Testing] *** xref:servlet/test/method.adoc[Method Security] *** xref:servlet/test/mockmvc.adoc[MockMvc Support] diff --git a/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc b/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc index 1a54033a3c..a6518b4ed1 100644 --- a/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc +++ b/docs/modules/ROOT/pages/servlet/appendix/namespace.adoc @@ -1,7 +1,7 @@ [[appendix-namespace]] = The Security Namespace This appendix provides a reference to the elements available in the security namespace and information on the underlying beans they create (a knowledge of the individual classes and how they work together is assumed - you can find more information in the project Javadoc and elsewhere in this document). -If you haven't used the namespace before, please read the xref:servlet/xml-namespace.adoc#ns-config[introductory chapter] on namespace configuration, as this is intended as a supplement to the information there. +If you haven't used the namespace before, please read the xref:servlet/configuration/xml-namespace.adoc#ns-config[introductory chapter] on namespace configuration, as this is intended as a supplement to the information there. Using a good quality XML editor while editing a configuration based on the schema is recommended as this will provide contextual information on which elements and attributes are available as well as comments explaining their purpose. The namespace is written in https://relaxng.org/[RELAX NG] Compact format and later converted into an XSD schema. If you are familiar with this format, you may wish to examine the https://raw.githubusercontent.com/spring-projects/spring-security/main/config/src/main/resources/org/springframework/security/config/spring-security-4.1.rnc[schema file] directly. @@ -19,10 +19,10 @@ This may include sensitive information, such as request parameters or headers, a === If you use an `` element within your application, a `FilterChainProxy` bean named "springSecurityFilterChain" is created and the configuration within the element is used to build a filter chain within `FilterChainProxy`. -As of Spring Security 3.1, additional `http` elements can be used to add extra filter chains footnote:[See the pass:specialcharacters,macros[xref:servlet/xml-namespace.adoc#ns-web-xml[introductory chapter]] for how to set up the mapping from your `web.xml` ]. +As of Spring Security 3.1, additional `http` elements can be used to add extra filter chains footnote:[See the pass:specialcharacters,macros[xref:servlet/configuration/xml-namespace.adoc#ns-web-xml[introductory chapter]] for how to set up the mapping from your `web.xml` ]. Some core filters are always created in a filter chain and others will be added to the stack depending on the attributes and child elements which are present. The positions of the standard filters are fixed (see -xref:servlet/xml-namespace.adoc#filter-stack[the filter order table] in the namespace introduction), removing a common source of errors with previous versions of the framework when users had to configure the filter chain explicitly in the +xref:servlet/configuration/xml-namespace.adoc#filter-stack[the filter order table] in the namespace introduction), removing a common source of errors with previous versions of the framework when users had to configure the filter chain explicitly in the `FilterChainProxy` bean. You can, of course, still do this if you need full control of the configuration. @@ -710,7 +710,7 @@ Default is any HTTP method except "GET", "TRACE", "HEAD", "OPTIONS". === This element is used to add a filter to the filter chain. It doesn't create any additional beans but is used to select a bean of type `javax.servlet.Filter` which is already defined in the application context and add that at a particular position in the filter chain maintained by Spring Security. -Full details can be found in the xref:servlet/xml-namespace.adoc#ns-custom-filters[ namespace chapter]. +Full details can be found in the xref:servlet/configuration/xml-namespace.adoc#ns-custom-filters[ namespace chapter]. [[nsa-custom-filter-parents]] diff --git a/docs/modules/ROOT/pages/servlet/authentication/preauth.adoc b/docs/modules/ROOT/pages/servlet/authentication/preauth.adoc index 0fd4bde781..406c3f871f 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/preauth.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/preauth.adoc @@ -139,7 +139,7 @@ A typical configuration using this filter would look like this: ---- -We've assumed here that the xref:servlet/xml-namespace.adoc#ns-config[security namespace] is being used for configuration. +We've assumed here that the xref:servlet/configuration/xml-namespace.adoc#ns-config[security namespace] is being used for configuration. It's also assumed that you have added a `UserDetailsService` (called "userDetailsService") to your configuration to load the user's roles. diff --git a/docs/modules/ROOT/pages/servlet/authentication/rememberme.adoc b/docs/modules/ROOT/pages/servlet/authentication/rememberme.adoc index 87433fd297..5e75fc219e 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/rememberme.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/rememberme.adoc @@ -36,7 +36,7 @@ If a principal is aware a token has been captured, they can easily change their If more significant security is needed you should use the approach described in the next section. Alternatively, remember-me services should simply not be used at all. -If you are familiar with the topics discussed in the chapter on xref:servlet/xml-namespace.adoc#ns-config[namespace configuration], you can enable remember-me authentication just by adding the `` element: +If you are familiar with the topics discussed in the chapter on xref:servlet/configuration/xml-namespace.adoc#ns-config[namespace configuration], you can enable remember-me authentication just by adding the `` element: [source,xml] ---- diff --git a/docs/modules/ROOT/pages/servlet/authorization/secure-objects.adoc b/docs/modules/ROOT/pages/servlet/authorization/secure-objects.adoc index 0e915f5a40..b85afb3a5a 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/secure-objects.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/secure-objects.adoc @@ -5,7 +5,7 @@ [[aop-alliance]] == AOP Alliance (MethodInvocation) Security Interceptor Prior to Spring Security 2.0, securing ``MethodInvocation``s needed quite a lot of boiler plate configuration. -Now the recommended approach for method security is to use xref:servlet/xml-namespace.adoc#ns-method-security[namespace configuration]. +Now the recommended approach for method security is to use xref:servlet/configuration/xml-namespace.adoc#ns-method-security[namespace configuration]. This way the method security infrastructure beans are configured automatically for you so you don't really need to know about the implementation classes. We'll just provide a quick overview of the classes that are involved here. diff --git a/docs/modules/ROOT/pages/servlet/java-configuration.adoc b/docs/modules/ROOT/pages/servlet/configuration/java.adoc similarity index 98% rename from docs/modules/ROOT/pages/servlet/java-configuration.adoc rename to docs/modules/ROOT/pages/servlet/configuration/java.adoc index f0e8f14049..bf100524cc 100644 --- a/docs/modules/ROOT/pages/servlet/java-configuration.adoc +++ b/docs/modules/ROOT/pages/servlet/configuration/java.adoc @@ -5,7 +5,7 @@ General support for https://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/beans.html#beans-java[Java Configuration] was added to Spring Framework in Spring 3.1. Since Spring Security 3.2 there has been Spring Security Java Configuration support which enables users to easily configure Spring Security without the use of any XML. -If you are familiar with the xref:servlet/xml-namespace.adoc#ns-config[Security Namespace Configuration] then you should find quite a few similarities between it and the Security Java Configuration support. +If you are familiar with the xref:servlet/configuration/xml-namespace.adoc#ns-config[Security Namespace Configuration] then you should find quite a few similarities between it and the Security Java Configuration support. NOTE: Spring Security provides https://github.com/spring-projects/spring-security-samples/tree/main/servlet/java-configuration[lots of sample applications] which demonstrate the use of Spring Security Java Configuration. diff --git a/docs/modules/ROOT/pages/servlet/kotlin-configuration.adoc b/docs/modules/ROOT/pages/servlet/configuration/kotlin.adoc similarity index 100% rename from docs/modules/ROOT/pages/servlet/kotlin-configuration.adoc rename to docs/modules/ROOT/pages/servlet/configuration/kotlin.adoc diff --git a/docs/modules/ROOT/pages/servlet/xml-namespace.adoc b/docs/modules/ROOT/pages/servlet/configuration/xml-namespace.adoc similarity index 100% rename from docs/modules/ROOT/pages/servlet/xml-namespace.adoc rename to docs/modules/ROOT/pages/servlet/configuration/xml-namespace.adoc diff --git a/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc b/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc index 5198ffc905..23bb16ccac 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc @@ -544,7 +544,7 @@ Will output HTML that is similar to the following: === Resolving the CsrfToken Spring Security provides `CsrfTokenArgumentResolver` which can automatically resolve the current `CsrfToken` for Spring MVC arguments. -By using xref:servlet/java-configuration.adoc#jc-hello-wsca[@EnableWebSecurity] you will automatically have this added to your Spring MVC configuration. +By using xref:servlet/configuration/java.adoc#jc-hello-wsca[@EnableWebSecurity] you will automatically have this added to your Spring MVC configuration. If you use XML based configuration, you must add this yourself. Once `CsrfTokenArgumentResolver` is properly configured, you can expose the `CsrfToken` to your static HTML based application. diff --git a/docs/modules/ROOT/pages/servlet/java-configuration/index.adoc b/docs/modules/ROOT/pages/servlet/java-configuration/index.adoc deleted file mode 100644 index cc5ebaa70a..0000000000 --- a/docs/modules/ROOT/pages/servlet/java-configuration/index.adoc +++ /dev/null @@ -1,337 +0,0 @@ - -[[jc]] -= Java Configuration - -General support for https://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/beans.html#beans-java[Java Configuration] was added to Spring Framework in Spring 3.1. -Since Spring Security 3.2 there has been Spring Security Java Configuration support which enables users to easily configure Spring Security without the use of any XML. - -If you are familiar with the xref:servlet/namespace/index.adoc#ns-config[Security Namespace Configuration] then you should find quite a few similarities between it and the Security Java Configuration support. - -NOTE: Spring Security provides https://github.com/spring-projects/spring-security-samples/tree/main/servlet/java-configuration[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 { - - @Bean - public UserDetailsService userDetailsService() { - 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 -* https://en.wikipedia.org/wiki/Cross-site_request_forgery[CSRF attack] prevention -* https://en.wikipedia.org/wiki/Session_fixation[Session Fixation] protection -* Security Header integration -** https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security[HTTP Strict Transport Security] for secure requests -** https://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) -** https://msdn.microsoft.com/en-us/library/dd565647(v=vs.85).aspx[X-XSS-Protection] integration -** X-Frame-Options integration to help prevent https://en.wikipedia.org/wiki/Clickjacking[Clickjacking] -* Integrate with the following Servlet API methods -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[HttpServletRequest#getRemoteUser()] -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[HttpServletRequest#getUserPrincipal()] -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[HttpServletRequest#isUserInRole(java.lang.String)] -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login(java.lang.String,%20java.lang.String)[HttpServletRequest#login(java.lang.String, java.lang.String)] -** https://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout()[HttpServletRequest#logout()] - -=== AbstractSecurityWebApplicationInitializer - -The next step is to register the `springSecurityFilterChain` with the war. -This can be done in Java Configuration with https://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 - -Thus far our <> only contains information about how to authenticate our users. -How does Spring Security know that we want to require all users to be authenticated? -How does Spring Security know we want to support form based authentication? -Actually, there is a configuration class that is being invoked behind the scenes called `WebSecurityConfigurerAdapter`. -It has a method called `configure` with the following default implementation: - -[source,java] ----- -protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests(authorize -> authorize - .anyRequest().authenticated() - ) - .formLogin(withDefaults()) - .httpBasic(withDefaults()); -} ----- - -The default configuration above: - -* Ensures that any request to our application requires the user to be authenticated -* Allows users to authenticate with form based login -* Allows users to authenticate with HTTP Basic authentication - -You will notice that this configuration is quite similar the XML Namespace configuration: - -[source,xml] ----- - - - - - ----- - -== Multiple HttpSecurity - -We can configure multiple HttpSecurity instances just as we can have multiple `` blocks. -The key is to extend the `WebSecurityConfigurerAdapter` multiple times. -For example, the following is an example of having a different configuration for URL's that start with `/api/`. - -[source,java] ----- -@EnableWebSecurity -public class MultiHttpSecurityConfig { - @Bean <1> - 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; - } - - @Configuration - @Order(1) <2> - public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { - protected void configure(HttpSecurity http) throws Exception { - http - .antMatcher("/api/**") <3> - .authorizeRequests(authorize -> authorize - .anyRequest().hasRole("ADMIN") - ) - .httpBasic(withDefaults()); - } - } - - @Configuration <4> - public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests(authorize -> authorize - .anyRequest().authenticated() - ) - .formLogin(withDefaults()); - } - } -} ----- - -<1> Configure Authentication as normal -<2> Create an instance of `WebSecurityConfigurerAdapter` that contains `@Order` to specify which `WebSecurityConfigurerAdapter` should be considered first. -<3> The `http.antMatcher` states that this `HttpSecurity` will only be applicable to URLs that start with `/api/` -<4> Create another instance of `WebSecurityConfigurerAdapter`. -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-custom-dsls]] -== Custom DSLs - -You can provide your own custom DSLs in Spring Security. -For example, you might have something that looks like this: - -[source,java] ----- -public class MyCustomDsl extends AbstractHttpConfigurer { - private boolean flag; - - @Override - public void init(HttpSecurity http) throws Exception { - // any method that adds another configurer - // must be done in the init method - http.csrf().disable(); - } - - @Override - public void configure(HttpSecurity http) throws Exception { - ApplicationContext context = http.getSharedObject(ApplicationContext.class); - - // here we lookup from the ApplicationContext. You can also just create a new instance. - MyFilter myFilter = context.getBean(MyFilter.class); - myFilter.setFlag(flag); - http.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class); - } - - public MyCustomDsl flag(boolean value) { - this.flag = value; - return this; - } - - public static MyCustomDsl customDsl() { - return new MyCustomDsl(); - } -} ----- - -NOTE: This is actually how methods like `HttpSecurity.authorizeRequests()` are implemented. - -The custom DSL can then be used like this: - -[source,java] ----- -@EnableWebSecurity -public class Config extends WebSecurityConfigurerAdapter { - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .apply(customDsl()) - .flag(true) - .and() - ...; - } -} ----- - -The code is invoked in the following order: - -* Code in `Config`s configure method is invoked -* Code in `MyCustomDsl`s init method is invoked -* Code in `MyCustomDsl`s configure method is invoked - -If you want, you can have `WebSecurityConfigurerAdapter` add `MyCustomDsl` by default by using `SpringFactories`. -For example, you would create a resource on the classpath named `META-INF/spring.factories` with the following contents: - -.META-INF/spring.factories ----- -org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyCustomDsl ----- - -Users wishing to disable the default can do so explicitly. - -[source,java] ----- -@EnableWebSecurity -public class Config extends WebSecurityConfigurerAdapter { - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .apply(customDsl()).disable() - ...; - } -} ----- - -[[post-processing-configured-objects]] -== 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. -After all, 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(authorize -> authorize - .anyRequest().authenticated() - .withObjectPostProcessor(new ObjectPostProcessor() { - public O postProcess( - O fsi) { - fsi.setPublishAuthorizationSuccess(true); - return fsi; - } - }) - ); -} ----- diff --git a/docs/modules/ROOT/pages/servlet/kotlin-configuration/index.adoc b/docs/modules/ROOT/pages/servlet/kotlin-configuration/index.adoc deleted file mode 100644 index 767ab7ed80..0000000000 --- a/docs/modules/ROOT/pages/servlet/kotlin-configuration/index.adoc +++ /dev/null @@ -1,99 +0,0 @@ - -[[kotlin-config]] -= Kotlin Configuration -Spring Security Kotlin Configuration support has been available since Spring Security 5.3. -It enables users to easily configure Spring Security using a native Kotlin DSL. - -NOTE: Spring Security provides https://github.com/spring-projects/spring-security-samples/tree/main/servlet/spring-boot/kotlin/hello-security[a sample application] which demonstrates the use of Spring Security Kotlin Configuration. - -[[kotlin-config-httpsecurity]] -== HttpSecurity - -How does Spring Security know that we want to require all users to be authenticated? -How does Spring Security know we want to support form based authentication? -There is a configuration class that is being invoked behind the scenes called `WebSecurityConfigurerAdapter`. -It has a method called `configure` with the following default implementation: - -[source,kotlin] ----- -fun configure(http: HttpSecurity) { - http { - authorizeRequests { - authorize(anyRequest, authenticated) - } - formLogin { } - httpBasic { } - } -} ----- - -The default configuration above: - -* Ensures that any request to our application requires the user to be authenticated -* Allows users to authenticate with form based login -* Allows users to authenticate with HTTP Basic authentication - -You will notice that this configuration is quite similar the XML Namespace configuration: - -[source,xml] ----- - - - - - ----- - -== Multiple HttpSecurity - -We can configure multiple HttpSecurity instances just as we can have multiple `` blocks. -The key is to extend the `WebSecurityConfigurerAdapter` multiple times. -For example, the following is an example of having a different configuration for URL's that start with `/api/`. - -[source,kotlin] ----- -@EnableWebSecurity -class MultiHttpSecurityConfig { - @Bean <1> - public fun userDetailsService(): UserDetailsService { - val users: User.UserBuilder = User.withDefaultPasswordEncoder() - val manager = InMemoryUserDetailsManager() - manager.createUser(users.username("user").password("password").roles("USER").build()) - manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build()) - return manager - } - - @Configuration - @Order(1) <2> - class ApiWebSecurityConfigurationAdapter: WebSecurityConfigurerAdapter() { - override fun configure(http: HttpSecurity) { - http { - securityMatcher("/api/**") <3> - authorizeRequests { - authorize(anyRequest, hasRole("ADMIN")) - } - httpBasic { } - } - } - } - - @Configuration <4> - class FormLoginWebSecurityConfigurerAdapter: WebSecurityConfigurerAdapter() { - override fun configure(http: HttpSecurity) { - http { - authorizeRequests { - authorize(anyRequest, authenticated) - } - formLogin { } - } - } - } -} ----- - -<1> Configure Authentication as normal -<2> Create an instance of `WebSecurityConfigurerAdapter` that contains `@Order` to specify which `WebSecurityConfigurerAdapter` should be considered first. -<3> The `http.antMatcher` states that this `HttpSecurity` will only be applicable to URLs that start with `/api/` -<4> Create another instance of `WebSecurityConfigurerAdapter`. -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). diff --git a/docs/modules/ROOT/pages/servlet/namespace/index.adoc b/docs/modules/ROOT/pages/servlet/namespace/index.adoc deleted file mode 100644 index 0168114c1b..0000000000 --- a/docs/modules/ROOT/pages/servlet/namespace/index.adoc +++ /dev/null @@ -1,402 +0,0 @@ - -[[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 https://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[xref:servlet/authentication/unpwd/ldap.adoc#servlet-authentication-ldap[LDAP Authentication]].]. -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 https://spring.io/tools[Eclipse IDE with Spring Tools] 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[xref:servlet/exploits/firewall.adoc#servlet-httpfirewall[`HttpFirewall`]] 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 <> which is used.]. -In Spring Security 3.0, the attribute can also be populated with an xref:servlet/authorization/expression-based.adoc#el-access[EL expression]. - - -[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,attrs="-attributes"] ----- - - - - - - - - - ----- - -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,attrs="-attributes"] ----- - - - - - - - - - - ----- - - - -[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 xref:servlet/appendix/namespace.adoc#appendix-namespace[namespace appendix]. -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 xref:servlet/authentication/passwords/in-memory.adoc#servlet-authentication-inmemory[in-memory authentication] 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-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`. - -[[ns-web-advanced]] -== Advanced Web Features - -[[ns-custom-filters]] -=== Adding in Your Own Filters -If you've used Spring Security before, you'll know that the framework maintains a chain of filters in order to apply its services. -You may want to add your own filters to the stack at particular locations or use a Spring Security filter for which there isn't currently a namespace configuration option (CAS, for example). -Or you might want to use a customized version of a standard namespace filter, such as the `UsernamePasswordAuthenticationFilter` which is created by the `` element, taking advantage of some of the extra configuration options which are available by using the bean explicitly. -How can you do this with namespace configuration, since the filter chain is not directly exposed? - -The order of the filters is always strictly enforced when using the namespace. -When the application context is being created, the filter beans are sorted by the namespace handling code and the standard Spring Security filters each have an alias in the namespace and a well-known position. - -[NOTE] -==== -In previous versions, the sorting took place after the filter instances had been created, during post-processing of the application context. -In version 3.0+ the sorting is now done at the bean metadata level, before the classes have been instantiated. -This has implications for how you add your own filters to the stack as the entire filter list must be known during the parsing of the `` element, so the syntax has changed slightly in 3.0. -==== - -The filters, aliases and namespace elements/attributes which create the filters are shown in <>. -The filters are listed in the order in which they occur in the filter chain. - -[[filter-stack]] -.Standard Filter Aliases and Ordering -|=== -| Alias | Filter Class | Namespace Element or Attribute - -| CHANNEL_FILTER -| `ChannelProcessingFilter` -| `http/intercept-url@requires-channel` - -| SECURITY_CONTEXT_FILTER -| `SecurityContextPersistenceFilter` -| `http` - -| CONCURRENT_SESSION_FILTER -| `ConcurrentSessionFilter` -| `session-management/concurrency-control` - -| HEADERS_FILTER -| `HeaderWriterFilter` -| `http/headers` - -| CSRF_FILTER -| `CsrfFilter` -| `http/csrf` - -| LOGOUT_FILTER -| `LogoutFilter` -| `http/logout` - -| X509_FILTER -| `X509AuthenticationFilter` -| `http/x509` - -| PRE_AUTH_FILTER -| `AbstractPreAuthenticatedProcessingFilter` Subclasses -| N/A - -| CAS_FILTER -| `CasAuthenticationFilter` -| N/A - -| FORM_LOGIN_FILTER -| `UsernamePasswordAuthenticationFilter` -| `http/form-login` - -| BASIC_AUTH_FILTER -| `BasicAuthenticationFilter` -| `http/http-basic` - -| SERVLET_API_SUPPORT_FILTER -| `SecurityContextHolderAwareRequestFilter` -| `http/@servlet-api-provision` - -| JAAS_API_SUPPORT_FILTER -| `JaasApiIntegrationFilter` -| `http/@jaas-api-provision` - -| REMEMBER_ME_FILTER -| `RememberMeAuthenticationFilter` -| `http/remember-me` - -| ANONYMOUS_FILTER -| `AnonymousAuthenticationFilter` -| `http/anonymous` - -| SESSION_MANAGEMENT_FILTER -| `SessionManagementFilter` -| `session-management` - -| EXCEPTION_TRANSLATION_FILTER -| `ExceptionTranslationFilter` -| `http` - -| FILTER_SECURITY_INTERCEPTOR -| `FilterSecurityInterceptor` -| `http` - -| SWITCH_USER_FILTER -| `SwitchUserFilter` -| N/A -|=== - -You can add your own filter to the stack, using the `custom-filter` element and one of these names to specify the position your filter should appear at: - -[source,xml] ----- - - - - - ----- - -You can also use the `after` or `before` attributes if you want your filter to be inserted before or after another filter in the stack. -The names "FIRST" and "LAST" can be used with the `position` attribute to indicate that you want your filter to appear before or after the entire stack, respectively. - -.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. - -Note that you can't replace filters which are created by the use of the `` element itself - `SecurityContextPersistenceFilter`, `ExceptionTranslationFilter` or `FilterSecurityInterceptor`. -Some other filters are added by default, but you can disable them. -An `AnonymousAuthenticationFilter` is added by default and unless you have xref:servlet/authentication/session-management.adoc#ns-session-fixation[session-fixation protection] 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-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 xref:servlet/authorization/expression-based.adoc#el-access[expression-based annotations]. -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-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 xref:servlet/authorization/architecture.adoc#authz-arch[authorization]. - - -[[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] ----- - -... - ----- From 14fd21355773962e2aba36067b37aae5b4bdc0c0 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Tue, 21 Sep 2021 15:54:30 -0500 Subject: [PATCH 34/34] Better consistency between reactive and servlet --- docs/modules/ROOT/nav.adoc | 27 ++++++++++--------- .../reactive/{ => authentication}/logout.adoc | 0 .../reactive/{ => authentication}/x509.adoc | 0 .../reactive/{ => authorization}/method.adoc | 2 +- .../reactive/{ => configuration}/webflux.adoc | 0 .../reactive/{ => integrations}/cors.adoc | 0 .../reactive/{ => integrations}/rsocket.adoc | 0 .../{ => integrations}/webclient.adoc | 0 docs/modules/ROOT/pages/reactive/test.adoc | 2 +- 9 files changed, 17 insertions(+), 14 deletions(-) rename docs/modules/ROOT/pages/reactive/{ => authentication}/logout.adoc (100%) rename docs/modules/ROOT/pages/reactive/{ => authentication}/x509.adoc (100%) rename docs/modules/ROOT/pages/reactive/{ => authorization}/method.adoc (96%) rename docs/modules/ROOT/pages/reactive/{ => configuration}/webflux.adoc (100%) rename docs/modules/ROOT/pages/reactive/{ => integrations}/cors.adoc (100%) rename docs/modules/ROOT/pages/reactive/{ => integrations}/rsocket.adoc (100%) rename docs/modules/ROOT/pages/reactive/{ => integrations}/webclient.adoc (100%) diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index f71de72317..7445e37651 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -83,20 +83,23 @@ *** xref:servlet/appendix/faq.adoc[FAQ] * xref:reactive/index.adoc[Reactive Applications] ** xref:reactive/getting-started.adoc[Getting Started] -** xref:reactive/webflux.adoc[WebFlux Security] -** xref:reactive/exploits/index.adoc[Protection Against Exploits] -*** xref:reactive/exploits/csrf.adoc[CSRF] -*** xref:reactive/exploits/headers.adoc[Headers] -*** xref:reactive/exploits/http.adoc[HTTP Requests] +** Authentication +*** xref:reactive/authentication/x509.adoc[X.509 Authentication] +*** xref:reactive/authentication/logout.adoc[Logout] +** Authorization +*** xref:reactive/authorization/method.adoc[EnableReactiveMethodSecurity] ** xref:reactive/oauth2/index.adoc[OAuth2] *** xref:reactive/oauth2/login.adoc[OAuth 2.0 Login] *** xref:reactive/oauth2/access-token.adoc[OAuth2 Client] *** xref:reactive/oauth2/resource-server.adoc[OAuth 2.0 Resource Server] -** xref:reactive/registered-oauth2-authorized-client.adoc[@RegisteredOAuth2AuthorizedClient] -** xref:reactive/x509.adoc[X.509 Authentication] -** xref:reactive/logout.adoc[Logout] -** xref:reactive/webclient.adoc[WebClient] -** xref:reactive/method.adoc[EnableReactiveMethodSecurity] -** xref:reactive/cors.adoc[CORS] +*** xref:reactive/registered-oauth2-authorized-client.adoc[@RegisteredOAuth2AuthorizedClient] +** xref:reactive/exploits/index.adoc[Protection Against Exploits] +*** xref:reactive/exploits/csrf.adoc[CSRF] +*** xref:reactive/exploits/headers.adoc[Headers] +*** xref:reactive/exploits/http.adoc[HTTP Requests] +** Integrations +*** xref:reactive/integrations/cors.adoc[CORS] +*** xref:reactive/integrations/rsocket.adoc[RSocket] +*** xref:reactive/integrations/webclient.adoc[WebClient] ** xref:reactive/test.adoc[Testing] -** xref:reactive/rsocket.adoc[RSocket] +** xref:reactive/configuration/webflux.adoc[WebFlux Security] diff --git a/docs/modules/ROOT/pages/reactive/logout.adoc b/docs/modules/ROOT/pages/reactive/authentication/logout.adoc similarity index 100% rename from docs/modules/ROOT/pages/reactive/logout.adoc rename to docs/modules/ROOT/pages/reactive/authentication/logout.adoc diff --git a/docs/modules/ROOT/pages/reactive/x509.adoc b/docs/modules/ROOT/pages/reactive/authentication/x509.adoc similarity index 100% rename from docs/modules/ROOT/pages/reactive/x509.adoc rename to docs/modules/ROOT/pages/reactive/authentication/x509.adoc diff --git a/docs/modules/ROOT/pages/reactive/method.adoc b/docs/modules/ROOT/pages/reactive/authorization/method.adoc similarity index 96% rename from docs/modules/ROOT/pages/reactive/method.adoc rename to docs/modules/ROOT/pages/reactive/authorization/method.adoc index 46502fe1a3..dc04d46be6 100644 --- a/docs/modules/ROOT/pages/reactive/method.adoc +++ b/docs/modules/ROOT/pages/reactive/authorization/method.adoc @@ -162,7 +162,7 @@ It is important to note that any of the expressions in standard method security However, at this time we only support return type of `Boolean` or `boolean` of the expression. This means that the expression must not block. -When integrating with xref:reactive/webflux.adoc#jc-webflux[WebFlux Security], the Reactor Context is automatically established by Spring Security according to the authenticated user. +When integrating with xref:reactive/configuration/webflux.adoc#jc-webflux[WebFlux Security], the Reactor Context is automatically established by Spring Security according to the authenticated user. ==== .Java diff --git a/docs/modules/ROOT/pages/reactive/webflux.adoc b/docs/modules/ROOT/pages/reactive/configuration/webflux.adoc similarity index 100% rename from docs/modules/ROOT/pages/reactive/webflux.adoc rename to docs/modules/ROOT/pages/reactive/configuration/webflux.adoc diff --git a/docs/modules/ROOT/pages/reactive/cors.adoc b/docs/modules/ROOT/pages/reactive/integrations/cors.adoc similarity index 100% rename from docs/modules/ROOT/pages/reactive/cors.adoc rename to docs/modules/ROOT/pages/reactive/integrations/cors.adoc diff --git a/docs/modules/ROOT/pages/reactive/rsocket.adoc b/docs/modules/ROOT/pages/reactive/integrations/rsocket.adoc similarity index 100% rename from docs/modules/ROOT/pages/reactive/rsocket.adoc rename to docs/modules/ROOT/pages/reactive/integrations/rsocket.adoc diff --git a/docs/modules/ROOT/pages/reactive/webclient.adoc b/docs/modules/ROOT/pages/reactive/integrations/webclient.adoc similarity index 100% rename from docs/modules/ROOT/pages/reactive/webclient.adoc rename to docs/modules/ROOT/pages/reactive/integrations/webclient.adoc diff --git a/docs/modules/ROOT/pages/reactive/test.adoc b/docs/modules/ROOT/pages/reactive/test.adoc index 183558b136..53156d110a 100644 --- a/docs/modules/ROOT/pages/reactive/test.adoc +++ b/docs/modules/ROOT/pages/reactive/test.adoc @@ -4,7 +4,7 @@ [[test-erms]] == Testing Reactive Method Security -For example, we can test our example from xref:reactive/method.adoc#jc-erms[EnableReactiveMethodSecurity] using the same setup and annotations we did in xref:servlet/test/method.adoc#test-method[Testing Method Security]. +For example, we can test our example from xref:reactive/authorization/method.adoc#jc-erms[EnableReactiveMethodSecurity] using the same setup and annotations we did in xref:servlet/test/method.adoc#test-method[Testing Method Security]. Here is a minimal sample of what we can do: ====