1) Let’s jump right in with a Spring Boot demo secured by an OpenID Connect server
a) The dependencies: pom.xml
<!-- Spring Boot web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- pac4j implementation for Spring MVC so for Spring Boot as well -->
<dependency>
<groupId>org.pac4j</groupId>
<artifactId>spring-webmvc-pac4j</artifactId>
<version>8.0.2</version>
</dependency>
<!-- pac4j support for OpenID Connect -->
<dependency>
<groupId>org.pac4j</groupId>
<artifactId>pac4j-oidc</artifactId>
<version>6.4.0</version>
</dependency>
b) The SpringBootDemo
@SpringBootApplication
public class SpringBootDemo {
public static void main(final String[] args) {
SpringApplication.run(SpringBootDemo.class, args);
}
}
Just the usual, nothing to say. Run this class.
c) The SecurityConfig for OpenID Connect
@Configuration
public class SecurityConfig extends Pac4jSecurityConfig {
@Value("${app.base-url:http://localhost:8080}")
private String baseUri;
@Bean
public Config config() {
// configuration of the authentication via the OpenID Connect protocol
final var config = new OidcConfiguration()
.setDiscoveryURI("https://casserverpac4j.herokuapp.com/oidc/.well-known/openid-configuration")
.setClientId("myclient")
.setSecret("mysecret")
.setAllowUnsignedIdTokens(true);
return new Config(baseUri + "/callback", new OidcClient(config));
}
@Override
public void addInterceptors(final InterceptorRegistry registry) {
// the /protected/** URLs require the OIDC authentication
addSecurity(registry, "OidcClient").addPathPatterns("/protected/**");
}
}
Notice the extends Pac4jSecurityConfig.
Two definitions are required:
- the authentication mechanism which is a client (built from a config when needed)
- the protected URLs.
Automatically, the /callback and /logout endpoints are created.
d) The controller
@Autowired
private ProfileManager profileManager;
@RequestMapping("/")
@ResponseBody
public String index() {
return "<h1>Public area</h1><p><a href='/protected/index'>Protected area</a></p>"
+ "<p><a href='/logout'>Logout</a></p>" + profileManager.getProfiles();
}
@RequestMapping("/protected/index")
@ResponseBody
public String secure() {
return "<h1>Protected area</h1><a href='/'>Home</a><p/>"
+ "<p><a href='/logout'>Logout</a></p>" + profileManager.getProfiles();
}
The injected ProfileManager gives you access to the authenticated user.
2) Let’s switch to a Spring Boot demo secured by a CAS server
Nothing to change, except one dependency:
<!-- pac4j support for CAS -->
<dependency>
<groupId>org.pac4j</groupId>
<artifactId>pac4j-cas</artifactId>
<version>6.4.0</version>
</dependency>
(instead of pac4j-oidc) and the security configuration:
@Value("${cas.login-url:https://casserverpac4j.herokuapp.com/login}")
private String casLoginUrl;
@Bean
public Config config() {
// configuration of the authentication via the CAS protocol
return new Config(baseUri + "/callback", new CasClient(new CasConfiguration(casLoginUrl)));
}
@Override
public void addInterceptors(final InterceptorRegistry registry) {
// the /protected/** URLs require the CAS authentication
addSecurity(registry, "CasClient").addPathPatterns("/protected/**");
}
A new client (= authn mechanism): CasClient for the CAS protocol (like OidcClient for the OIDC protocol).
The CasConfiguration class is optional on such an easy configuration: return new Config(baseUri + "/callback", new CasClient(casLoginUrl));.
3) Let’s update to a Spring Boot demo secured by a SAML2 IdP
We need a new dependency for the SAML2 protocol support:
<!-- pac4j support for SAML2 -->
<dependency>
<groupId>org.pac4j</groupId>
<artifactId>pac4j-saml</artifactId>
<version>6.4.0</version>
</dependency>
and a new SecurityConfig:
@Bean
public Config config() {
// configuration of the authentication via the SAML2 protocol
final var cfg = new SAML2Configuration();
cfg.getKeystore().setKeystorePath("classpath:samlKeystore.jks");
cfg.getKeystore().setKeystorePassword("pac4j-demo-passwd");
cfg.getKeystore().setPrivateKeyPassword("pac4j-demo-passwd");
cfg.setIdentityProviderMetadataPath("https://casserverpac4j.herokuapp.com/idp/metadata");
cfg.setServiceProviderEntityId(baseUri + "/callback?client_name=SAML2Client");
cfg.setServiceProviderMetadataPath("file:metadata/sp-metadata-8080.xml");
return new Config(baseUri + "/callback", new SAML2Client(cfg));
}
@Override
public void addInterceptors(final InterceptorRegistry registry) {
// the /protected/** URLs require the SAML2 authentication
addSecurity(registry, "SAML2Client").addPathPatterns("/protected/**");
}
The SAML2Configuration defines the keystore (in the classpath), its passwords, the SAML2 server metadata, the SP (= pac4j = client) identifier as well as the location of its generated metadata.
A little more complicated but the SAML2 protocol requires that and generally a lot more :-(
Discover more pac4j frameworks and more authentication mechanisms…