Moi ce que je voudrais, c'est pouvoir définir MES valeurs par défaut pour mon application Spring-Boot, que je pourrais surcharger avec un fichier properties.
Donc, je veux surcharger programmatiquement les valeurs par défault de Spring-Boot, et quand même permettre la surcharge par fichier. Dans l'idée, ça marche :
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertiesPropertySource;
import java.util.Properties;
@Configuration
@PropertySource(value = "classpath:application.properties", ignoreResourceNotFound = true)
public class DatabaseConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseConfiguration.class.getCanonicalName());
public static final String DATABASE_URL = "jdbc:h2:./db/dbfile;MODE=MySQL;";
public static final String DATABASE_USER = "sa";
public static final String DATABASE_DRIVER = "org.h2.Driver";
public static final String DATABASE_SCHEMA = "PUBLIC";
@Autowired
private Environment env;
@Value("${spring.datasource.url:}") // Defaulted with empty value
private String databaseUrl;
@Value("${spring.datasource.username:}") // Defaulted with empty value
private String databaseUser;
@Value("${spring.datasource.password:}") // Defaulted with empty value
private String databasePassword;
@Value("${spring.datasource.driver-class-name:}") // Defaulted with empty value
private String databaseDriver;
@Value("${spring.datasource.schema:}") // Defaulted with empty value
private String databaseSchema;
private void overrideProperty(String name, String currentValue, String defaultValue) {
if (currentValue == null || currentValue.isEmpty()) {
currentValue = defaultValue;
ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) this.env;
PropertiesPropertySource propertySource = (PropertiesPropertySource) configurableEnvironment.getPropertySources().get("myProps");
Properties myProperties = new Properties();
if (propertySource != null) {
for (String propertyName : propertySource.getPropertyNames()) {
myProperties.put(propertyName, propertySource.getProperty(propertyName));
}
}
myProperties.put(name, currentValue);
configurableEnvironment.getPropertySources().addFirst(new PropertiesPropertySource("myProps", myProperties));
System.out.println("Property " + name + " not found and defaulted to => " + currentValue);
} else {
System.out.println("Property unchanged " + name + " => " + currentValue);
}
}
/**
* I swear to you, I tried to override configuration !!!
*/
@Bean
public void defaultConfiguration() {
this.overrideProperty("spring.datasource.url", this.databaseUrl, DATABASE_URL);
this.overrideProperty("spring.datasource.jdbc-url", this.databaseUrl, DATABASE_URL);
this.overrideProperty("spring.datasource.user", this.databaseUser, DATABASE_USER);
this.overrideProperty("spring.datasource.driver-class-name", this.databaseDriver, DATABASE_DRIVER);
this.overrideProperty("spring.datasource.schema", this.databaseSchema, DATABASE_SCHEMA);
}
}
Mais mon script s'exécute après la création de la DataSource et pas moyen de l'exécuter avant tout le reste T__T
Faut je creuse encore...
Dépendances nécessaire dans Maven :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<scope>compile</scope>
</dependency>
<!-- Pas sure que celle-ci soit nécessaire : -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<scope>compile</scope>
</dependency>
Configuration de la sécurité côté sécurité :
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
@ComponentScan("com.pacakge.security")
public class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(MyWebSecurityConfigurerAdapter.class);
@Autowired
@Qualifier("authenticationProvider")
private AuthenticationProvider authenticationProvider;
@Autowired
@Qualifier("isSecurityDisabled")
private Boolean isSecurityDisabled;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationProvider) throws Exception {
authenticationProvider.authenticationProvider(this.authenticationProvider);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
LOGGER.debug("configure HttpSecurity");
LOGGER.info("Security is disabled : {}", isSecurityDisabled);
if (isSecurityDisabled) {
http.httpBasic().and()
.authorizeRequests().antMatchers("/index.html", "/", "/logout.html").permitAll().and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/user").permitAll()
.anyRequest().authenticated();
}
else {
http.httpBasic().and()
.authorizeRequests().antMatchers("/index.html", "/", "/logout.html").permitAll().and()
// configure the csrfTokenRepository
.csrf().csrfTokenRepository(csrfTokenRepository()).and()
.authorizeRequests()
.antMatchers("/user").permitAll()
.antMatchers("/contact/**").access("hasRole('ADMIN') and hasRole('DBA')")
.anyRequest().authenticated();
// Add the csrf filter
http.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);
}
http.exceptionHandling().accessDeniedPage("/403");
}
/**
* Csrf Token Repository that contains the header named "X-XSRF-TOKEN".
* Angular adds CRSF token headers to keep sure server and client are not talking to someone else.
*
* @return CsrfTokenRepository
*/
private static CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
@Override
public void configure(WebSecurity web) throws Exception {
LOGGER.info("Security is disabled : {}", isSecurityDisabled);
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
// Spring Security should completely ignore URLs starting with
// /static/assets/
web.ignoring().antMatchers("/static/assets/**").and();
web.ignoring().antMatchers("/static/app/**").and();
// Spring Security should completely ignore URLs for the js / images /
// font
web.ignoring().antMatchers("/static/*.ico").and();
web.ignoring().antMatchers("/static/*.eot").and();
web.ignoring().antMatchers("/static/*.svg").and();
web.ignoring().antMatchers("/static/*.ttf").and();
web.ignoring().antMatchers("/static/*.woff").and();
web.ignoring().antMatchers("/static/*.woff2").and();
web.ignoring().antMatchers("/static/*.js").and();
web.ignoring().antMatchers("/static/*.css").and();
web.ignoring().antMatchers("/static/*.map").and();
web.ignoring().antMatchers("/static/*.png").and();
web.ignoring().antMatchers("/static/*.jpg").and();
}
}
Exemple d'Authentication provider :
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
/**
* A mock for authentication provider.
*/
@Component
public class MockAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
private static final Logger CLASS_LOGGER = LoggerFactory.getLogger(MockAuthenticationProvider.class);
//Your own service to manage authentication.
private final AuthentiService authentiService;
private final LDAPUserToSecurityUserConverter lDAPUserToSecurityUserConverter;
/**
* Initialize the Mock authentication provider.
*
* @param authentiService the authent service.
* @param lDAPUserToSecurityUserConverter the converter from LDAP user to security user.
*/
public MockAuthenticationProvider(LDAPUserToSecurityUserConverter lDAPUserToSecurityUserConverter, AuthentiService authentiService) {
CLASS_LOGGER.trace("Set up of mocked authentication provider.");
this.lDAPUserToSecurityUserConverter = lDAPUserToSecurityUserConverter;
this.authentiService = authentiService;
}
@Override
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
@Override
public Authentication authenticate(Authentication authentication) {
CLASS_LOGGER.info("requesting authentication");
String name = authentication.getName();
String password = authentication.getCredentials().toString();
StopWatch stopWatch = new StopWatch();
stopWatch.start();
SecurityUser authenticatedUser = (SecurityUser) this.retrieveUser(name, (UsernamePasswordAuthenticationToken) authentication);
stopWatch.stop();
authenticatedUser.setTimeToSpentOnAuthentication(stopWatch.getTotalTimeMillis());
return new UsernamePasswordAuthenticationToken(authenticatedUser, password, authenticatedUser.getAuthorities());
}
@Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) {
String password = (String) authentication.getCredentials();
try {
// Login User
String token = this.authentService.login(username, password);
CLASS_LOGGER.debug("Username : {} got token : {}", username, token);
// Retrieve User Identity + roles + permissions
LDAPUser user = this.authentService.getUserInformation(token);
// Build the AuthenticatedUser object based on all the data
return this.lDAPUserToSecurityUserConverter.convert(user, password, token);
}
catch (Exception e) {
CLASS_LOGGER.debug(e.getMessage(), e);
throw new AuthenticationServiceException("Authentication error", e);
}
}
@Override
protected void additionalAuthenticationChecks(UserDetails arg0, UsernamePasswordAuthenticationToken arg1) throws AuthenticationException {
// Nothing to do.
}
}
Controller d'authentication :
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AuthenticationController {
private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationController.class);
@RequestMapping(value = "/user", method = RequestMethod.GET)
public SecurityUser user() {
LOGGER.debug("user controller");
SecurityUser authenticationUser = null;
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
LOGGER.info("Authentication provider is {}", auth.getClass().getSimpleName());
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = (UsernamePasswordAuthenticationToken) auth;
authenticationUser = (SecurityUser) usernamePasswordAuthenticationToken.getPrincipal();
}
else {
LOGGER.warn("No Authentication found");
}
return authenticationUser;
}
}
Service Angular d'authentication :
import { Injectable } from '@angular/core';
import { Http, Response, Headers, RequestOptions, URLSearchParams } from '@angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { Observable } from 'rxjs/Observable';
/**
* This class can request the server to get contact information.
*/
@Injectable()
export class AuthenticationService {
constructor(private http: Http) {
}
authenticate(username: string, password: string): Observable<any> {
//Authentication goes by using headers
const headers = new Headers();
if (username != null && password != null) {
const authorizationValue: string = 'Basic ' + btoa(username + ':' + password);
headers.append('authorization', authorizationValue);
}
return this.http.get('user', {headers: headers})
.map((res: Response) => res.json())
.catch( error => this.handleError(error) );
}
}
Au sein du projet Java, rajouter les classes nécessaires :
Le starter :
@SpringBootApplication
public class SpringBootWebApplication extends SpringBootServletInitializer {
/**
* Configure the server.
*
* @param application the application.
* @return the builder.
*/
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringBootWebApplication.class);
}
/**
* Start the server.
*
* @param args the arguments.
*/
public static void main(String[] args) {
SpringApplication.run(SpringBootWebApplication.class, args);
}
}
La configuration Web :
@Configuration
@EnableWebMvc
public class ApplicationWebConfiguration extends WebMvcConfigurerAdapter {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
private static final int CACHE_PERIOD_ONE_WEEK = 604800;
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS)
.setCachePeriod(CACHE_PERIOD_ONE_WEEK);
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addRedirectViewController("/", "/index.html");
}
// ou
//@Override
//public void addViewControllers(ViewControllerRegistry registry) {
// registry.addViewController("/").setViewName("forward:/index.html");
//}
}
Dans le pom, rajouter le format de packaging et le re-build du projet dans les plugins (spring-boot-maven-plugin).
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itametis</groupId>
<artifactId>itartefactId</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<!-- Spring jar version -->
<spring.version>5.0.4.RELEASE</spring.version>
<spring-boot.version>1.5.10.RELEASE</spring-boot.version>
<!-- Jackson version for Spring -->
<jackson.version>2.9.3</jackson.version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
<scope>compile</scope>
</dependency>
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- War maker -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifest>
<mainClass>${project.groupId}.webserver.SpringBootWebApplication</mainClass>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
</archive>
</configuration>
</plugin>
<!-- Spring boot : re-build the war to carry the tomcat server -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.5.3.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>${project.groupId}.webserver.SpringBootWebApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
Le war généré par le maven-war-plugin sera re-buildé pour embarquer le tomcat et les outils Spring.