Skip to content

Commit 87efe45

Browse files
sarahAbdulibeltagy
authored andcommitted
All updates needed for spring security 6
Signed-off-by: Sarah Abdulsalam <[email protected]>
1 parent c827fc8 commit 87efe45

File tree

16 files changed

+695
-395
lines changed

16 files changed

+695
-395
lines changed

.mvn/wrapper/maven-wrapper.jar

-48.3 KB
Binary file not shown.
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.3/apache-maven-3.3.3-bin.zip
1+
wrapperVersion=3.3.4
2+
distributionType=only-script
3+
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip

click/README.adoc

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -79,36 +79,38 @@ WARNING: It's not a great idea to return a whole `OAuth2User` in an endpoint sin
7979
There's one final change you'll need to make.
8080

8181
This app will now work fine and authenticate as before, but it's still going to redirect before showing the page.
82-
To make the link visible, we also need to switch off the security on the home page by extending `WebSecurityConfigurerAdapter`:
82+
To make the link visible, we also need to switch off the security on the home page by registering a SecurityFilterChain bean:
8383

8484
.SocialApplication
8585
[source,java]
8686
----
8787
@SpringBootApplication
8888
@RestController
89-
public class SocialApplication extends WebSecurityConfigurerAdapter {
89+
public class SocialApplication {
9090
9191
// ...
9292
93-
@Override
94-
protected void configure(HttpSecurity http) throws Exception {
95-
// @formatter:off
96-
http
97-
.authorizeRequests(a -> a
98-
.antMatchers("/", "/error", "/webjars/**").permitAll()
99-
.anyRequest().authenticated()
100-
)
101-
.exceptionHandling(e -> e
102-
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
103-
)
104-
.oauth2Login();
105-
// @formatter:on
106-
}
93+
@Bean
94+
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
95+
// @formatter:off
96+
http
97+
.authorizeHttpRequests(auth -> auth
98+
.requestMatchers("/", "/index.html", "/error", "/webjars/**").permitAll()
99+
.anyRequest().authenticated()
100+
)
101+
.oauth2Login(oauth -> oauth
102+
.defaultSuccessUrl("/", true) // Always redirect to home after login
103+
)
104+
.exceptionHandling(e -> e
105+
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
106+
);
107+
// @formatter:on
108+
return http.build();
109+
}
107110
108111
}
109112
----
110113

111-
Spring Boot attaches special meaning to a `WebSecurityConfigurerAdapter` on the class annotated with `@SpringBootApplication`:
112114
It uses it to configure the security filter chain that carries the OAuth 2.0 authentication processor.
113115

114116
The above configuration indicates a whitelist of permitted endpoints, with every other endpoint requiring authentication.

click/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
<parent>
1515
<groupId>org.springframework.boot</groupId>
1616
<artifactId>spring-boot-starter-parent</artifactId>
17-
<version>2.2.2.RELEASE</version>
17+
<version>3.5.6</version>
1818
<relativePath /> <!-- lookup parent from repository -->
1919
</parent>
2020

2121
<properties>
2222
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
23-
<java.version>1.8</java.version>
23+
<java.version>17</java.version>
2424
</properties>
2525

2626
<dependencies>

click/src/main/java/com/example/SocialApplication.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,37 +20,41 @@
2020

2121
import org.springframework.boot.SpringApplication;
2222
import org.springframework.boot.autoconfigure.SpringBootApplication;
23+
import org.springframework.context.annotation.Bean;
2324
import org.springframework.http.HttpStatus;
2425
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
25-
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
2626
import org.springframework.security.core.annotation.AuthenticationPrincipal;
2727
import org.springframework.security.oauth2.core.user.OAuth2User;
28+
import org.springframework.security.web.SecurityFilterChain;
2829
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
2930
import org.springframework.web.bind.annotation.GetMapping;
3031
import org.springframework.web.bind.annotation.RestController;
3132

3233
@SpringBootApplication
3334
@RestController
34-
public class SocialApplication extends WebSecurityConfigurerAdapter {
35+
public class SocialApplication {
3536

3637
@GetMapping("/user")
3738
public Map<String, Object> user(@AuthenticationPrincipal OAuth2User principal) {
3839
return Collections.singletonMap("name", principal.getAttribute("name"));
3940
}
4041

41-
@Override
42-
protected void configure(HttpSecurity http) throws Exception {
42+
@Bean
43+
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
4344
// @formatter:off
4445
http
45-
.authorizeRequests(a -> a
46-
.antMatchers("/", "/error", "/webjars/**").permitAll()
46+
.authorizeHttpRequests(auth -> auth
47+
.requestMatchers("/", "/index.html", "/error", "/webjars/**").permitAll()
4748
.anyRequest().authenticated()
4849
)
50+
.oauth2Login(oauth -> oauth
51+
.defaultSuccessUrl("/", true) // Always redirect to home after login
52+
)
4953
.exceptionHandling(e -> e
5054
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
51-
)
52-
.oauth2Login();
55+
);
5356
// @formatter:on
57+
return http.build();
5458
}
5559

5660
public static void main(String[] args) {

custom-error/README.adoc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,14 @@ To achieve this, you can configure an `AuthenticationFailureHandler`, like so:
5656

5757
[source,java]
5858
----
59-
protected void configure(HttpSecurity http) throws Exception {
59+
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
60+
SimpleUrlAuthenticationFailureHandler handler = new SimpleUrlAuthenticationFailureHandler("/");
61+
6062
// @formatter:off
6163
http
6264
// ... existing configuration
6365
.oauth2Login(o -> o
66+
.defaultSuccessUrl("/", true) // Always redirect to home after login
6467
.failureHandler((request, response, exception) -> {
6568
request.getSession().setAttribute("error.message", exception.getMessage());
6669
handler.onAuthenticationFailure(request, response, exception);

custom-error/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
<parent>
1515
<groupId>org.springframework.boot</groupId>
1616
<artifactId>spring-boot-starter-parent</artifactId>
17-
<version>2.2.2.RELEASE</version>
17+
<version>3.5.6</version>
1818
<relativePath /> <!-- lookup parent from repository -->
1919
</parent>
2020

2121
<properties>
2222
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
23-
<java.version>1.8</java.version>
23+
<java.version>17</java.version>
2424
</properties>
2525

2626
<dependencies>

custom-error/src/main/java/com/example/SocialApplication.java

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,16 @@
1515
*/
1616
package com.example;
1717

18+
import java.util.function.Supplier;
19+
1820
import java.util.Collections;
1921
import java.util.List;
2022
import java.util.Map;
21-
import javax.servlet.http.HttpServletRequest;
22-
2323
import org.springframework.boot.SpringApplication;
2424
import org.springframework.boot.autoconfigure.SpringBootApplication;
2525
import org.springframework.context.annotation.Bean;
2626
import org.springframework.http.HttpStatus;
2727
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
28-
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
2928
import org.springframework.security.core.annotation.AuthenticationPrincipal;
3029
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
3130
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
@@ -37,19 +36,28 @@
3736
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
3837
import org.springframework.security.oauth2.core.OAuth2Error;
3938
import org.springframework.security.oauth2.core.user.OAuth2User;
39+
import org.springframework.security.web.SecurityFilterChain;
4040
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
4141
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
4242
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
43+
import org.springframework.security.web.csrf.CsrfToken;
44+
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
45+
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
46+
import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;
4347
import org.springframework.stereotype.Controller;
48+
import org.springframework.util.StringUtils;
4449
import org.springframework.web.bind.annotation.GetMapping;
4550
import org.springframework.web.bind.annotation.ResponseBody;
4651
import org.springframework.web.reactive.function.client.WebClient;
4752

53+
import jakarta.servlet.http.HttpServletRequest;
54+
import jakarta.servlet.http.HttpServletResponse;
55+
4856
import static org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient;
4957

5058
@SpringBootApplication
5159
@Controller
52-
public class SocialApplication extends WebSecurityConfigurerAdapter {
60+
public class SocialApplication {
5361

5462
@Bean
5563
public WebClient rest(ClientRegistrationRepository clients, OAuth2AuthorizedClientRepository authz) {
@@ -100,36 +108,76 @@ public String error(HttpServletRequest request) {
100108
return message;
101109
}
102110

103-
@Override
104-
protected void configure(HttpSecurity http) throws Exception {
111+
112+
@Bean
113+
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
105114
SimpleUrlAuthenticationFailureHandler handler = new SimpleUrlAuthenticationFailureHandler("/");
106115

107116
// @formatter:off
108-
http.antMatcher("/**")
109-
.authorizeRequests(a -> a
110-
.antMatchers("/", "/error", "/webjars/**").permitAll()
117+
http
118+
.authorizeHttpRequests(auth -> auth
119+
.requestMatchers("/", "/index.html", "/error", "/webjars/**").permitAll()
111120
.anyRequest().authenticated()
112121
)
113122
.exceptionHandling(e -> e
114123
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
115124
)
116-
.csrf(c -> c
117-
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
125+
.csrf(csrf -> csrf
126+
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
127+
.csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler())
118128
)
119129
.logout(l -> l
120130
.logoutSuccessUrl("/").permitAll()
121131
)
122-
.oauth2Login(o -> o
132+
.oauth2Login(oauth -> oauth
133+
.defaultSuccessUrl("/", true) // Always redirect to home after login
123134
.failureHandler((request, response, exception) -> {
124135
request.getSession().setAttribute("error.message", exception.getMessage());
125136
handler.onAuthenticationFailure(request, response, exception);
126137
})
127-
);
138+
);
128139
// @formatter:on
140+
return http.build();
129141
}
130142

131143
public static void main(String[] args) {
132144
SpringApplication.run(SocialApplication.class, args);
133145
}
134146

135147
}
148+
149+
final class SpaCsrfTokenRequestHandler implements CsrfTokenRequestHandler {
150+
private final CsrfTokenRequestHandler plain = new CsrfTokenRequestAttributeHandler();
151+
private final CsrfTokenRequestHandler xor = new XorCsrfTokenRequestAttributeHandler();
152+
153+
@Override
154+
public void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {
155+
/*
156+
* Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
157+
* the CsrfToken when it is rendered in the response body.
158+
*/
159+
this.xor.handle(request, response, csrfToken);
160+
/*
161+
* Render the token value to a cookie by causing the deferred token to be loaded.
162+
*/
163+
csrfToken.get();
164+
}
165+
166+
@Override
167+
public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
168+
String headerValue = request.getHeader(csrfToken.getHeaderName());
169+
/*
170+
* If the request contains a request header, use CsrfTokenRequestAttributeHandler
171+
* to resolve the CsrfToken. This applies when a single-page application includes
172+
* the header value automatically, which was obtained via a cookie containing the
173+
* raw CsrfToken.
174+
*
175+
* In all other cases (e.g. if the request contains a request parameter), use
176+
* XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
177+
* when a server-side rendered form includes the _csrf request parameter as a
178+
* hidden input.
179+
*/
180+
return (StringUtils.hasText(headerValue) ? this.plain : this.xor).resolveCsrfTokenValue(request, csrfToken);
181+
}
182+
}
183+

logout/README.adoc

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,14 @@ Now we can switch over to the server side to implement that endpoint.
4040
== Adding a Logout Endpoint
4141

4242
Spring Security has built in support for a `/logout` endpoint which will do the right thing for us (clear the session and invalidate the cookie).
43-
To configure the endpoint we simply extend the existing `configure()` method in our `WebSecurityConfigurerAdapter`:
43+
To configure the endpoint we simply extend the existing `securityFilterChain()` method in our `SocialApplication`:
4444

4545
.SocialApplication.java
4646
[source,java]
4747
----
48-
@Override
49-
protected void configure(HttpSecurity http) throws Exception {
50-
// @formatter:off
48+
@Bean
49+
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
50+
// @formatter:off
5151
http
5252
// ... existing code here
5353
.logout(l -> l
@@ -66,24 +66,59 @@ For instance, in Angular, the front end would like the server to send it a cooki
6666
We can implement the same behaviour with our simple jQuery client, and then the server-side changes will work with other front end implementations with no or very few changes.
6767
To teach Spring Security about this we need to add a filter that creates the cookie.
6868

69-
In the `WebSecurityConfigurerAdapter` we do the following:
69+
In the `SecurityFilterChain` we do the following:
7070

7171
.SocialApplication.java
7272
[source,java]
7373
----
74-
@Override
75-
protected void configure(HttpSecurity http) throws Exception {
74+
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
7675
// @formatter:off
7776
http
7877
// ... existing code here
7978
.csrf(c -> c
8079
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
80+
.csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler())
8181
)
8282
// ... existing code here
8383
// @formatter:on
8484
}
85-
----
8685
86+
final class SpaCsrfTokenRequestHandler implements CsrfTokenRequestHandler {
87+
private final CsrfTokenRequestHandler plain = new CsrfTokenRequestAttributeHandler();
88+
private final CsrfTokenRequestHandler xor = new XorCsrfTokenRequestAttributeHandler();
89+
90+
@Override
91+
public void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {
92+
/*
93+
* Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
94+
* the CsrfToken when it is rendered in the response body.
95+
*/
96+
this.xor.handle(request, response, csrfToken);
97+
/*
98+
* Render the token value to a cookie by causing the deferred token to be loaded.
99+
*/
100+
csrfToken.get();
101+
}
102+
103+
@Override
104+
public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
105+
String headerValue = request.getHeader(csrfToken.getHeaderName());
106+
/*
107+
* If the request contains a request header, use CsrfTokenRequestAttributeHandler
108+
* to resolve the CsrfToken. This applies when a single-page application includes
109+
* the header value automatically, which was obtained via a cookie containing the
110+
* raw CsrfToken.
111+
*
112+
* In all other cases (e.g. if the request contains a request parameter), use
113+
* XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
114+
* when a server-side rendered form includes the _csrf request parameter as a
115+
* hidden input.
116+
*/
117+
return (StringUtils.hasText(headerValue) ? this.plain : this.xor).resolveCsrfTokenValue(request, csrfToken);
118+
}
119+
}
120+
----
121+
Refer to https://docs.spring.io/spring-security/reference/servlet/exploits/csrf.html#csrf-integration-javascript-spa[spring security documentation for logout] for Single Page Application (SPA).
87122
== Adding the CSRF Token in the Client
88123

89124
Since we are not using a higher level framework in this sample, you'll need to explicitly add the CSRF token, which you just made available as a cookie from the backend.

logout/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
<parent>
1515
<groupId>org.springframework.boot</groupId>
1616
<artifactId>spring-boot-starter-parent</artifactId>
17-
<version>2.2.2.RELEASE</version>
17+
<version>3.5.6</version>
1818
<relativePath /> <!-- lookup parent from repository -->
1919
</parent>
2020

2121
<properties>
2222
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
23-
<java.version>1.8</java.version>
23+
<java.version>17</java.version>
2424
</properties>
2525

2626
<dependencies>

0 commit comments

Comments
 (0)