Skip to content

Commit e6d7d57

Browse files
authored
Merge pull request #101 from xdev-software/spring-boot-3.4
Spring boot 3.4+ Support
2 parents 978df35 + c8aca47 commit e6d7d57

File tree

15 files changed

+903
-222
lines changed

15 files changed

+903
-222
lines changed

CHANGELOG.md

+26
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,29 @@
1+
# 2.0.0
2+
* Added support for Spring Security 6.4+ / Spring Boot 3.4+ #100
3+
* Spring now
4+
* uses a Regex-based templating system
5+
* no longer uses bootstrap
6+
* provides One-Time token/OTT and Passkey logins
7+
* Changes to ``Extendable``-subsystem
8+
* Now uses the new Regex-based templating system
9+
* Correct a bunch of problems in Spring Security including
10+
* One-Time token/OTT and Passkeys are ignored when computing if the whole filter is enabled
11+
* [Passkeys] Removed invalid XML comment in scripts block
12+
* [Passkeys] Fixed incorrectly closed HTML-form/div-tag
13+
* [HtmlTemplating] Compile ``UNUSED_PLACEHOLDER_PATTERN`` regex once and not for each request
14+
* [HtmlTemplating] Render: Optimization: Use entrySet instead of keySet + getValue
15+
* Add correct setter for ``generateOneTimeTokenUrl``
16+
* Improved naming of methods
17+
* Changes to ``Advanced``-subsystem
18+
* Keeps using Bootstrap
19+
* By default bootstrap is still loaded from ``cdn.jsdelivr.net`` but you can (and should) provide your own version
20+
* Keeps using the old templating system (without Regex)
21+
* Not all values are escaped by default as is with Spring's Regex based system
22+
* Usually they don't need to be escaped in the first place as they are set on the server side and can't be modified by a user
23+
* This is A LOT FASTER (in tests around 50x) than Spring's new Regex based system
24+
* Adopted changes; Added new configuration options
25+
* [Passkeys] Fixed a problem where more than one header results in invalid generated JavaScript code
26+
127
# 1.0.3
228
* Updated dependencies
329
* Abstracted code

README.md

+11
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@ public SecurityFilterChain configure(final HttpSecurity http) throws Exception
3232

3333
A more detailed scenario is available in the [demo](./spring-security-advanced-authentication-ui-demo/).
3434

35+
> [!NOTE]
36+
> By default [Bootstrap](https://github.com/twbs/bootstrap) is loaded from ``cdn.jsdelivr.net``.<br/>
37+
> Due to privacy and stability reasons you should ship your own version!<br/>
38+
> An example how this can be done is shown in the demo.
39+
40+
> [!NOTE]
41+
> The ``Advanced``-subsystem uses the pre-``Spring Security 6.4`` / ``Spring Boot 3.4`` templating system (without Regex).<br/>
42+
> * In contrast to Spring's new Regex based system not all values are escaped by default
43+
> * Usually they don't need to be escaped in the first place as they are set on the server side and can't be modified by a user
44+
> * This is A LOT FASTER (in tests around 50x) than Spring's new Regex based system
45+
3546
## Installation
3647
[Installation guide for the latest release](https://github.com/xdev-software/spring-security-advanced-authentication-ui/releases/latest#Installation)
3748

dev_infra/docker-compose.yml

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
version: "3"
2-
31
services:
42
# Docs: https://docs.duendesoftware.com
53
oidc-server-mock:

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>software.xdev</groupId>
88
<artifactId>spring-security-advanced-authentication-ui-root</artifactId>
9-
<version>1.0.4-SNAPSHOT</version>
9+
<version>2.0.0-SNAPSHOT</version>
1010
<packaging>pom</packaging>
1111

1212
<organization>

spring-security-advanced-authentication-ui-demo/README.md

+14
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,17 @@
33
* Start the [development infrastructure](../dev_infra/)
44
* Run the application
55
* Open ``http://localhost:8080``
6+
7+
## Special Login information
8+
9+
### Username + Password
10+
11+
Example user:
12+
* Username: ``test``
13+
* Password: ``test``
14+
15+
### Passkeys
16+
17+
The browser needs to support passkeys and you also need an appropriate store (usually the OS handles this).
18+
19+
NOTE: Passkeys are lost when rebooting the server

spring-security-advanced-authentication-ui-demo/pom.xml

+16-3
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
<parent>
88
<groupId>software.xdev</groupId>
99
<artifactId>spring-security-advanced-authentication-ui-root</artifactId>
10-
<version>1.0.4-SNAPSHOT</version>
10+
<version>2.0.0-SNAPSHOT</version>
1111
</parent>
1212

1313
<artifactId>spring-security-advanced-authentication-ui-demo</artifactId>
14-
<version>1.0.4-SNAPSHOT</version>
14+
<version>2.0.0-SNAPSHOT</version>
1515
<packaging>jar</packaging>
1616

1717
<organization>
@@ -28,7 +28,7 @@
2828

2929
<mainClass>software.xdev.Application</mainClass>
3030

31-
<org.springframework.boot.version>3.3.5</org.springframework.boot.version>
31+
<org.springframework.boot.version>3.4.2</org.springframework.boot.version>
3232
</properties>
3333

3434
<dependencyManagement>
@@ -63,6 +63,19 @@
6363
<groupId>org.springframework.boot</groupId>
6464
<artifactId>spring-boot-starter-oauth2-client</artifactId>
6565
</dependency>
66+
67+
<!-- Required to showcase passkeys -->
68+
<dependency>
69+
<groupId>com.webauthn4j</groupId>
70+
<artifactId>webauthn4j-core</artifactId>
71+
<version>0.28.5.RELEASE</version>
72+
</dependency>
73+
74+
<dependency>
75+
<groupId>org.springframework.boot</groupId>
76+
<artifactId>spring-boot-devtools</artifactId>
77+
<optional>true</optional>
78+
</dependency>
6679
</dependencies>
6780

6881
<build>

spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/controllers/RootController.java

+11-11
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package software.xdev.controllers;
22

3+
import org.springframework.security.core.Authentication;
34
import org.springframework.security.core.context.SecurityContextHolder;
4-
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
55
import org.springframework.web.bind.annotation.GetMapping;
66
import org.springframework.web.bind.annotation.RequestMapping;
77
import org.springframework.web.bind.annotation.RestController;
@@ -14,19 +14,19 @@ public class RootController
1414
@GetMapping
1515
public Result respond()
1616
{
17-
if(SecurityContextHolder.getContext()
18-
.getAuthentication()
19-
.getPrincipal() instanceof final DefaultOidcUser oidcUser)
20-
{
21-
return new Result(oidcUser.getFullName(), oidcUser.getEmail(), "/logout");
22-
}
23-
return null;
17+
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
18+
return new Result(
19+
"/logout",
20+
"/webauthn/register",
21+
authentication != null ? authentication.getClass().getName() : null,
22+
authentication);
2423
}
2524

2625
public record Result(
27-
String name,
28-
String email,
29-
String logoutUrl
26+
String logoutUrl,
27+
String passKeyRegistrationUrl,
28+
String authenticationClass,
29+
Object authentication
3030
)
3131
{
3232

spring-security-advanced-authentication-ui-demo/src/main/java/software/xdev/security/MainWebSecurity.java

+41-4
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,17 @@
88
import java.util.List;
99
import java.util.Map;
1010

11+
import org.slf4j.Logger;
12+
import org.slf4j.LoggerFactory;
1113
import org.springframework.boot.context.properties.EnableConfigurationProperties;
1214
import org.springframework.context.annotation.Bean;
1315
import org.springframework.context.annotation.Configuration;
1416
import org.springframework.security.config.Customizer;
1517
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
1618
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
19+
import org.springframework.security.core.userdetails.User;
20+
import org.springframework.security.core.userdetails.UserDetailsService;
21+
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
1722
import org.springframework.security.web.SecurityFilterChain;
1823
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
1924
import org.springframework.security.web.savedrequest.NullRequestCache;
@@ -28,12 +33,14 @@
2833
@EnableConfigurationProperties(AdditionalOAuth2ClientProperties.class)
2934
public class MainWebSecurity
3035
{
31-
@Bean(name = "mainSecurityFilterChainBean")
32-
public SecurityFilterChain configure(
36+
private static final Logger LOG = LoggerFactory.getLogger(MainWebSecurity.class);
37+
38+
protected void customizeLogin(
3339
final HttpSecurity http,
3440
final AdditionalOAuth2ClientProperties additionalOAuth2ClientProperties) throws Exception
3541
{
36-
http.with(new AdvancedLoginPageAdapter<>(http), c -> c
42+
http.with(
43+
new AdvancedLoginPageAdapter<>(http), c -> c
3744
.customizePages(p -> p
3845
// No remote communication -> Use local resources
3946
.setHeaderElements(List.of(
@@ -60,18 +67,48 @@ public SecurityFilterChain configure(
6067
+ " href='https://xdev.software' target='_blank'>"
6168
+ " XDEV Software"
6269
+ " </a>"
63-
+ "</p>")))
70+
+ "</p>")));
71+
}
72+
73+
@Bean(name = "mainSecurityFilterChainBean")
74+
public SecurityFilterChain configure(
75+
final HttpSecurity http,
76+
final AdditionalOAuth2ClientProperties additionalOAuth2ClientProperties) throws Exception
77+
{
78+
this.customizeLogin(http, additionalOAuth2ClientProperties);
79+
80+
http
6481
.headers(h -> h
6582
.referrerPolicy(r -> r.policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.SAME_ORIGIN))
6683
.contentSecurityPolicy(csp -> csp.policyDirectives(this.getCSP())))
6784
.formLogin(Customizer.withDefaults())
85+
.oneTimeTokenLogin(c -> c.tokenGenerationSuccessHandler(
86+
(request, response, oneTimeToken) ->
87+
LOG.info(
88+
"OneTimeToken should be sent for {} with value {}",
89+
oneTimeToken.getUsername(),
90+
oneTimeToken.getTokenValue())))
91+
.webAuthn(c -> c.rpName("Spring Security Localhost Relying Party")
92+
.rpId("localhost")
93+
.allowedOrigins("http://localhost:8080"))
6894
.oauth2Login(c -> c.defaultSuccessUrl("/"))
6995
.authorizeHttpRequests(urlRegistry -> urlRegistry.anyRequest().authenticated())
7096
.requestCache(c -> c.requestCache(new NullRequestCache()));
7197

7298
return http.build();
7399
}
74100

101+
@Bean
102+
@SuppressWarnings({"java:S6437", "deprecation"})
103+
public UserDetailsService userDetailsService()
104+
{
105+
return new InMemoryUserDetailsManager(User.withDefaultPasswordEncoder()
106+
.username("test")
107+
.password("test")
108+
.roles("USER")
109+
.build());
110+
}
111+
75112
// Example CSP
76113
protected String getCSP()
77114
{

spring-security-advanced-authentication-ui/pom.xml

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>software.xdev</groupId>
88
<artifactId>spring-security-advanced-authentication-ui</artifactId>
9-
<version>1.0.4-SNAPSHOT</version>
9+
<version>2.0.0-SNAPSHOT</version>
1010
<packaging>jar</packaging>
1111

1212
<name>spring-security-advanced-authentication-ui</name>
@@ -88,13 +88,13 @@
8888
<dependency>
8989
<groupId>org.springframework.boot</groupId>
9090
<artifactId>spring-boot-starter-web</artifactId>
91-
<version>3.3.5</version>
91+
<version>3.4.2</version>
9292
<scope>provided</scope>
9393
</dependency>
9494
<dependency>
9595
<groupId>org.springframework.boot</groupId>
9696
<artifactId>spring-boot-starter-security</artifactId>
97-
<version>3.3.5</version>
97+
<version>3.4.2</version>
9898
<scope>provided</scope>
9999
</dependency>
100100

0 commit comments

Comments
 (0)