LuciadFusion comes pre-configured with HTTP Security which restricts user access to critical resources within the application. This article describes how to use the Spring Framework to integrate custom HTTP Security logic that overrides the built-in default HTTP Security authorization configuration.
In this article, we explain how to implement the custom HTTP Security authorization logic to restrict user access to critical
resources within LuciadFusion. The complete source code of this tutorial is also available as a sample application in the
package samples.fusion.platform.security.http
.
See the Spring Security documentation to learn how to configure application security in a Spring-based application.
Create a YAML-based authorization configuration
The configuration file application-authorization.yml
defines HTTP Security authorization related properties that are used in configuring restrictions on resources from unauthorized
access.
It stores properties such as the login URL, the logout URL, protected/unprotected endpoint URLs.
The properties from this file will be injected into the CustomHttpSecurityEndpointProperties
bean, created in the next step.The configuration properties bean then exposes the properties to the HTTP Security authorization configuration that you want
to plug in.See the sample YAML configuration file:
Expand this section to see the HTTP Security authorization configuration YAML properties file.
samples/fusion/platform/security/application-authorization
)
# Custom authorization sample configuration profile #
# Custom authorization profile configuration.
# This profile must be combined with the "fusion.single" & "fusion.development" profiles as active profile. For example, by using a vm property:
# -Dspring.profiles.active=fusion.single,fusion.development,authorization
# or a program argument:
# --spring.profiles.active=fusion.single,fusion.development,authorization
# This Configuration defines authorization configuration properties picked up by spring security.
fusion.samples.custom.security:
# Endpoint for logging the user into LuciadFusion server.
loginUrl: /studio/service/login
# Endpoint for logging the user out of LuciadFusion server.
logoutUrl: /studio/service/logout
# This section defines endpoints that need protection & need authorization.
authenticatedUrlList:
# Endpoint used to check whether there is a user currently logged in.
- /studio/service/api/app-user/current
# Used to access the specific actuator endpoint.
- /actuators/configurationInfo
# This section defines endpoints that are open to access for anyone without authentication/authorization.
permittedUrlList:
# Endpoint for obtaining a list of properties that can be used to query Data.
- /api/data/queryable-properties
# Endpoint for obtaining a list of Products with an optional search and sort criteria.
- /api/products
# Endpoint for obtaining a list of Service types supported by this server.
- /api/services/types
# Sample implementation for granting access to actuator endpoints to users having an 'ADMIN' role.
roleBasedAccessUrlList:
# Endpoint used to access the available actuators. This endpoint pattern is accessible to authenticated users with 'ADMIN' roles only
- endpointPattern: /actuators/**
roles:
- ADMIN
Create dedicated Spring property and configuration class for HTTP Security authorization
Next, you create a CustomHttpSecurityEndpointProperties
bean to load the values from the YAML file.
These bean classes must read the externalized configuration properties described in Create a YAML-based authorization configuration.
These configuration properties beans will be automatically picked up and injected into the Spring Security configuration bean
as one of its dependencies.
The Spring AuthorizationFilter
provides authorization for HttpServletRequests
.
It is inserted into the FilterChainProxy as one of the Security Filters.
You can override the default when you define your own SecurityFilterChain
bean.
Define a SecurityFilterChain
bean to configure HTTP Security using the HttpSecurity
class of the Spring framework.
This SecurityFilterChain
is executed in addition to LuciadFusion’s SecurityFilterChain.
This section defines rules that affect how your application handles incoming HTTP requests.
You can configure attributes such as URL patterns, form-based authentication properties such as login/logout URLs, and you
can restrict access to a resource.
Expand this section to view the HTTP Security Properties
bean definition.
samples/fusion/platform/security/http/config/CustomHttpSecurityEndpointProperties
)
/**
* A bean class exposing the properties defined in {@code application-authorization.yml}.
* The properties are injected into the fields of this class by the spring container.
*/
@Component
@ConfigurationProperties(prefix = "fusion.samples.custom.security")
public class CustomHttpSecurityEndpointProperties {
private static final ILcdLogger LOGGER = TLcdLoggerFactory.getLogger(CustomHttpSecurityEndpointProperties.class);
private String fLoginUrl;
private String fLogoutUrl;
private List<String> fAuthenticatedUrlList;
private List<String> fPermittedUrlList;
private String fLoginPage;
private Set<RoleBasedAccessibleEndpoints> fRoleBasedAccessUrlList;
public static final class RoleBasedAccessibleEndpoints {
private String fEndpointPattern;
private Set<String> fRoles;
public String getEndpointPattern() {
return fEndpointPattern;
}
public void setEndpointPattern(String aEndpointPattern) {
fEndpointPattern = aEndpointPattern;
}
public Set<String> getRoles() {
return fRoles;
}
public void setRoles(Set<String> aRoles) {
fRoles = aRoles;
}
@Override
public boolean equals(Object aO) {
if (this == aO) {
return true;
}
if (aO == null || getClass() != aO.getClass()) {
return false;
}
RoleBasedAccessibleEndpoints that = (RoleBasedAccessibleEndpoints) aO;
return Objects.equals(fEndpointPattern, that.fEndpointPattern) && Objects.equals(fRoles, that.fRoles);
}
@Override
public int hashCode() {
return Objects.hash(fEndpointPattern, fRoles);
}
}
}
Expand this section to see the HTTP Security configuration
.
samples/fusion/platform/security/http/config/CustomHttpSecurityConfiguration
)
// Spring security kicks in when it intercepts an incoming request matching the pattern "/**".
aHttp.securityMatcher("/**");
// Disables cache header
aHttp.headers(headers -> headers.cacheControl(HeadersConfigurer.CacheControlConfig::disable));
// Configures access to URL list allowed to be accessed by anyone without needing to authenticate.
aHttp.authorizeHttpRequests(authorizedRequests ->
authorizedRequests.requestMatchers(fSecurityProperties.getPermittedUrlList()
.stream()
.map(aBuilder::pattern)
.toArray(MvcRequestMatcher[]::new))
.permitAll());
// Configures access to URL list allowed to be accessed by authenticated users.
aHttp.authorizeHttpRequests(authorizedRequests ->
authorizedRequests.requestMatchers(fSecurityProperties.getAuthenticatedUrlList()
.stream()
.map(aBuilder::pattern)
.toArray(MvcRequestMatcher[]::new))
.authenticated());
// Grant access to actuator endpoints to users having an 'ADMIN' role.
for (CustomHttpSecurityEndpointProperties.RoleBasedAccessibleEndpoints roleBasedAccessibleEndpoints : fSecurityProperties.getRoleBasedAccessUrlList()) {
aHttp.authorizeHttpRequests(authorizedRequests -> authorizedRequests.requestMatchers(aBuilder.pattern(roleBasedAccessibleEndpoints.getEndpointPattern()))
.hasAnyRole(roleBasedAccessibleEndpoints.getRoles()
.toArray(new String[0])));
}
// Enables user login by accepting username and password provided through an HTML form.
aHttp.formLogin(formLogin -> formLogin.loginProcessingUrl(fSecurityProperties.getLoginUrl())
.successHandler(successHandler)
.permitAll());
// Specifies including the logout support and provide associated configuration parameters like logout url, session invalidation etc.
aHttp.logout(logout -> logout.invalidateHttpSession(true)
.logoutUrl(fSecurityProperties.getLogoutUrl())
.permitAll());
// Configures the CORS filter and associated parameters.
aHttp.cors(cors -> cors.configurationSource(getCorsConfigurationSource()));
// CSRF support is added by default. Needs to be explicitly disabled.
aHttp.csrf(AbstractHttpConfigurer::disable);
Full sample code for the HTTP Security authorization configuration
Expand this section to see the full sample.
samples/fusion/platform/security/http/config/CustomHttpSecurityConfiguration
)
/**
* <p>
* A sample spring security configuration class. This configuration class configures parameters needed to implement
* security of Endpoint Urls exposed by LuciadFusion Studio.
* </p>
*/
@Configuration
@EnableWebSecurity
public class CustomHttpSecurityConfiguration {
private static final ILcdLogger LOGGER = TLcdLoggerFactory.getLogger(CustomHttpSecurityConfiguration.class);
private final CustomHttpSecurityEndpointProperties fSecurityProperties;
public CustomHttpSecurityConfiguration(CustomHttpSecurityEndpointProperties aSecurityProperties) {
fSecurityProperties = aSecurityProperties;
}
@PostConstruct
private void init() {
LOGGER.info("Fusion sample: loading security configuration for Endpoint urls");
}
@Bean
public MvcRequestMatcher.Builder mvcRequestBuilder(HandlerMappingIntrospector aIntrospector) {
return new MvcRequestMatcher.Builder(aIntrospector);
}
/**
* This method configures {@link HttpSecurity}
*
* @param aHttp the {@link HttpSecurity} to be configured.
*/
@Bean
@Order(1)
@Autowired
public SecurityFilterChain fusionFilterChain(HttpSecurity aHttp, MvcRequestMatcher.Builder aBuilder) throws Exception {
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter("redirect");
// Spring security kicks in when it intercepts an incoming request matching the pattern "/**".
aHttp.securityMatcher("/**");
// Disables cache header
aHttp.headers(headers -> headers.cacheControl(HeadersConfigurer.CacheControlConfig::disable));
// Configures access to URL list allowed to be accessed by anyone without needing to authenticate.
aHttp.authorizeHttpRequests(authorizedRequests ->
authorizedRequests.requestMatchers(fSecurityProperties.getPermittedUrlList()
.stream()
.map(aBuilder::pattern)
.toArray(MvcRequestMatcher[]::new))
.permitAll());
// Configures access to URL list allowed to be accessed by authenticated users.
aHttp.authorizeHttpRequests(authorizedRequests ->
authorizedRequests.requestMatchers(fSecurityProperties.getAuthenticatedUrlList()
.stream()
.map(aBuilder::pattern)
.toArray(MvcRequestMatcher[]::new))
.authenticated());
// Grant access to actuator endpoints to users having an 'ADMIN' role.
for (CustomHttpSecurityEndpointProperties.RoleBasedAccessibleEndpoints roleBasedAccessibleEndpoints : fSecurityProperties.getRoleBasedAccessUrlList()) {
aHttp.authorizeHttpRequests(authorizedRequests -> authorizedRequests.requestMatchers(aBuilder.pattern(roleBasedAccessibleEndpoints.getEndpointPattern()))
.hasAnyRole(roleBasedAccessibleEndpoints.getRoles()
.toArray(new String[0])));
}
// Enables user login by accepting username and password provided through an HTML form.
aHttp.formLogin(formLogin -> formLogin.loginProcessingUrl(fSecurityProperties.getLoginUrl())
.successHandler(successHandler)
.permitAll());
// Specifies including the logout support and provide associated configuration parameters like logout url, session invalidation etc.
aHttp.logout(logout -> logout.invalidateHttpSession(true)
.logoutUrl(fSecurityProperties.getLogoutUrl())
.permitAll());
// Configures the CORS filter and associated parameters.
aHttp.cors(cors -> cors.configurationSource(getCorsConfigurationSource()));
// CSRF support is added by default. Needs to be explicitly disabled.
aHttp.csrf(AbstractHttpConfigurer::disable);
return aHttp.build();
}
private static UrlBasedCorsConfigurationSource getCorsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "PUT", "POST", "PATCH", "DELETE", "OPTIONS"));
configuration.addAllowedHeader("*");
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Make sure that the application can find the HTTP Security configuration
You must tell the Spring framework where to find the new HTTP Security configuration. You can use the fusion.config.additionalScanPackages
configuration property to specify extra packages for scanning by the Spring framework. See config/fusion.common.yml
configuration file for information about using this configuration property. As a result, you can run the LuciadFusion Platform
main class, TLfnFusionPlatformApplication
, with this extra property value. The LuciadFusion Platform framework also picks up all Spring components in the new package,
or the ones imported by a class in the new package.