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
This commit is contained in:
337
docs/modules/ROOT/pages/servlet/java-configuration/index.adoc
Normal file
337
docs/modules/ROOT/pages/servlet/java-configuration/index.adoc
Normal file
@@ -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 <<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.
|
||||
|
||||
== 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.
|
||||
|
||||
* xref:servlet/java-configuration/index.adoc#abstractsecuritywebapplicationinitializer-without-existing-spring[] - Use these instructions if you are not using Spring already
|
||||
* <<abstractsecuritywebapplicationinitializer-with-spring-mvc>> - 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 <<jc-hello-wsca,WebSecurityConfig>>.
|
||||
|
||||
=== 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 <<jc-hello-wsca,WebSecurityConfig>> 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]
|
||||
----
|
||||
<http>
|
||||
<intercept-url pattern="/**" access="authenticated"/>
|
||||
<form-login />
|
||||
<http-basic />
|
||||
</http>
|
||||
----
|
||||
|
||||
== Multiple HttpSecurity
|
||||
|
||||
We can configure multiple HttpSecurity instances just as we can have multiple `<http>` 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<MyCustomDsl, HttpSecurity> {
|
||||
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<FilterSecurityInterceptor>() {
|
||||
public <O extends FilterSecurityInterceptor> O postProcess(
|
||||
O fsi) {
|
||||
fsi.setPublishAuthorizationSuccess(true);
|
||||
return fsi;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
----
|
||||
Reference in New Issue
Block a user