I am trying to upgrade my project to spring boot 3. For this, I need to upgrade the whole security part of my project, since the old code is not compatible with spring security 6 and therefore spring boot 3. I am no expert in spring security and have a lot of difficulties to migrate my code, more so since I find the documentation a bit light.
Enough talk, here is my new code I already migrated :
@Configuration
@EnableWebSecurity
@Slf4j
@Order(3)
@ConditionalOnProperty(name = SamlSettings.ENABLE_PROPERTY, havingValue = "true")
@NoLazy
@AllArgsConstructor
public class SamlSecurityConfiguration {
@Nonnull
private final SamlSettings samlSettings;
@Bean
SecurityFilterChain app(HttpSecurity http) throws Exception {
if (samlSettings.isDisableSSLVerification()) {
disableSSLVerification();
}
OpenSaml4AuthenticationProvider authenticationProvider =
new OpenSaml4AuthenticationProvider();
authenticationProvider.setAssertionValidator(
OpenSaml4AuthenticationProvider.createDefaultAssertionValidatorWithParameters(p -> {
p.put(CLOCK_SKEW, samlSettings.getResponseSkew());
p.put(LIFETIME, samlSettings.getMaxAuthenticationAge());
p.put(SIGNATURE_REQUIRED, false);
})
);
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.saml2Login(withDefaults());
return http.build();
}
@Bean
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {
var credentials = loadCredential();
RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations
.fromMetadataLocation(samlSettings.getIdpMetadataLocation())
.entityId(samlSettings.getEntityId())
.registrationId(samlSettings.getEntityId())
.decryptionX509Credentials(c -> c.add(credentials))
.signingX509Credentials(c -> c.add(credentials))
.assertingPartyDetails(details ->
details
.singleSignOnServiceLocation(
samlSettings.getBaseEndPoint() + samlSettings.getAcs() + "/**"
)
)
.build();
return new InMemoryRelyingPartyRegistrationRepository(relyingPartyRegistration);
}
private Saml2X509Credential loadCredential() {
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(
new FileSystemResource(samlSettings.getKeystoreFile()).getInputStream(),
samlSettings.getStorePass().toCharArray()
);
PrivateKey privateKey = (PrivateKey) keyStore.getKey(
samlSettings.getKeyAlias(),
samlSettings.getKeyPass().toCharArray()
);
X509Certificate certificate = (X509Certificate) keyStore.getCertificate(
samlSettings.getKeyAlias()
);
return new Saml2X509Credential(privateKey, certificate, DECRYPTION, SIGNING);
} catch (Exception e) {
log.error("Could not load credentials for SAML", e);
throw new RuntimeException(e);
}
}
public static void disableSSLVerification() {
try {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
// Install the all-trusting trust manager
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}
}
The last part for SSL disabling I had to add since for testing purposes my shibboleth uses a self signed certificat which seems not valid. If there is something to do about this that's more appropriate I would like to know, but this is not my main concern.
The problem I have is that prior to the migration, this code redirects to /saml/login :
function getRedirectUrl(webParams) {
var suffix =
'sourceUrl=' +
(webParams.samlEnabled
? encodeURIComponent('/' + $window.location.hash)
: encodeURIComponent(
$window.location.pathname + $window.location.hash
))
return webParams.ssoLoginUrl.match(/\?/)
? webParams.ssoLoginUrl + '&' + suffix
: webParams.ssoLoginUrl + '?' + suffix
}
It worked perfectly before, the redirection to /saml/login redirected to the shibboleth login page and I could connect without any trouble.
Now the problem is that I get a 404 NOT FOUND on /saml/login. The redirection to shibboleth does not work. I am stuck here.
I suspect that this class did the redirection to shibboleth and the rest of the saml authentication process but I do not know if that's the case, and if it is, how to migrate it :
@Slf4j
public class TargetUrlSamlEntryPoint
implements AuthenticationEntryPoint {
private String sourceUrl;
private boolean expectsJson(HttpServletRequest request) {
return request.getHeader("accept").contains("application/json");
}
@Override
public void commence(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException e
) throws IOException, ServletException {
this.sourceUrl = request.getParameter("sourceUrl");
if (expectsJson(request) && e instanceof InsufficientAuthenticationException) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
}
}
@Override
protected void initializeSSO(SAMLMessageContext context, AuthenticationException e)
throws MetadataProviderException, SAMLException, MessageEncodingException {
WebSSOProfileOptions options = this.getProfileOptions(context, e);
options.setRelayState(this.sourceUrl);
if (log.isDebugEnabled()) {
log.debug("Set relay state to redirect the user to {}", this.sourceUrl);
}
this.setDefaultProfileOptions(options);
super.initializeSSO(context, e);
}
}
It seems that SAMLEntrypoint is no longer available. I don't know if spring security is supposed to do the job itself or if I have to add more code.