Lorsque l'on doit gérer une application qui communique avec un base de données et plusieurs schémas.
Ce qu'il faut savoir : Hibernate permet de setter un schéma global dynamiquement ou un schéma par entité mais en dur...
Comment gérer le cas ou le nom des schémas change selon l'environnement ?
Je ne dis pas que le besoin est légitime (est-ce une bonne pratique ? probablement pas), en attentant, cette petite page va probablement sauver ma semaine.
Je rajoute aussi ce header, qui permet de garder la connexion vivante derrière un proxy :
Angular :
headers.append('Proxy-Connection', 'Keep-alive');
http.service.ts
import {Injectable} from '@angular/core';
import {Http, XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Headers} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
@Injectable()
export class HttpService extends Http {
constructor (backend: XHRBackend, options: RequestOptions) {
let token = localStorage.getItem('auth_token'); // your custom token getter function here
options.headers.set('Authorization', `Bearer ${token}`);
super(backend, options);
}
request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> {
let token = localStorage.getItem('auth_token');
if (typeof url === 'string') { // meaning we have to add the token to the options, not in url
if (!options) {
// let's make option object
options = {headers: new Headers()};
}
options.headers.set('Authorization', `Bearer ${token}`);
} else {
// we have to add the token to the url object
url.headers.set('Authorization', `Bearer ${token}`);
}
return super.request(url, options).catch(this.catchAuthError(this));
}
private catchAuthError (self: HttpService) {
// we have to pass HttpService's own instance here as `self`
return (res: Response) => {
console.log(res);
if (res.status === 401 || res.status === 403) {
// if not authenticated
console.log(res);
}
return Observable.throw(res);
};
}
}
À lire à tête reposée
À creuser...
Ouké ouké
<build>
<plugins>
<!-- java classes generation from WSDL -->
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.13.1</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<schemaLanguage>WSDL</schemaLanguage>
<generatePackage>com.my.target.package</generatePackage>
<schemas>
<schema>
<fileset>
<!-- Defaults to schemaDirectory. -->
<directory>${basedir}/src/main/schemas</directory>
<!-- Defaults to schemaIncludes. -->
<includes>
<include>*.wsdl</include>
</includes>
</fileset>
</schema>
</schemas>
</configuration>
</plugin>
</plugins>
</build>
</project>
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
/**
* The authentication entry point is called when the client makes a call to a resource without proper authentication. In
* other words, the client has not logged in.
* <p>
* If this class is not present in the code, then Spring security opens a default pop-up. We want a 401 error.
*/
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomAuthenticationEntryPoint.class);
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
LOGGER.info("intercepted request : 401 error code.");
response.getWriter().append("Access Denied : " + authException.getMessage());
response.setStatus(401);
}
}
Cette classe est incluse dans la couche sécurité via la configuration du http :
//Override the default login pop-up with a 401 response.
http.exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint);
Ne reste plus qu'à configurer la gestion des erreurs 401 avec Angular :
import { Injectable } from '@angular/core';
import { Request, XHRBackend, RequestOptions, Response, Http, RequestOptionsArgs, Headers } from '@angular/http';
import { Router, NavigationEnd, Event } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
/**
* This class handles generically the error on authentication.
*/
@Injectable()
export class AuthenticatedHttpService extends Http {
private router: Router;
constructor(backend: XHRBackend, defaultOptions: RequestOptions, router: Router) {
super(backend, defaultOptions);
this.router = router;
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
return super.request(url, options).catch((error: Response) => {
if ((error.status === 401 || error.status === 403) && (window.location.href.match(/\?/g) || []).length < 2) {
if(window.location.href != '/login') {
this.router.navigate(['/login']);
}
else {
return Observable.throw("authentication met an exception");
}
}
return Observable.throw("request authentication");
});
}
}
pratique
Dépendances nécessaires :
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>compile</scope>
</dependency>
<!-- Spring DATA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<scope>compile</scope>
</dependency>
<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
Définition de l'entité Hibernate :
import java.io.Serializable;
import javax.persistence.*;
import org.hibernate.annotations.GenericGenerator;
/**
* Represents a truc in the database.
*/
@Entity
@Table(name = "TRUCS")
public class Truc implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(generator = "increment")
@GenericGenerator(name = "increment", strategy = "increment")
@Column(name = "id", nullable = false, unique = true)
private Long id;
@Column(name = "name")
private String name;
/**
* Default constructor mandatory for hibernate instanciation.
*/
public Truc() {
super();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Définition du CRUD Manager :
import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
/**
* The technical manager able to make CRUD actions on the truc in the database.
*/
public interface ContactManager extends CrudRepository<Truc, Long> {
/**
* Get all the trucs in the database with the given name.
*
* @param name the name.
* @return the list of all trucs having the name.
*/
@Query("select c from Truc c where lower(c.name) = lower(:name)")
List<Truc> getByName(@Param("name") String name);
}
On utilise les managers via une injection autowired et un scan du package contenant les managers.
Les classes autogénérée du service SOAP sont présentes dans un jar.
Dépendances nécessaires :
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<scope>compile</scope>
</dependency>
Authentication avec un service SOAP :
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
/**
* Spring configuration to create authentication beans.
*/
@Configuration
public class AuthenticationConfig extends SpringConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationConfig.class.getCanonicalName());
/**
* Instantiates the Authentication configuration.
*
* @param environment the Spring environment.
*/
public AuthenticationConfig(Environment environment) {
super(environment);
}
/**
* return endpoint.
*
* @return MyAuthenticationServicesWSP service.
*/
@Bean
public MyAuthenticationServicesWSP myAuthenticationServicesWSP() {
JaxWsProxyFactoryBean jaxWsFactory = new JaxWsProxyFactoryBean();
jaxWsFactory.setServiceClass(MyAuthenticationServicesWSP.class);
String url = this.getContext().getBean("authenticationUrl", String.class) + getProperty("suffix.authentication");
jaxWsFactory.setAddress(url );
return (MyAuthenticationServicesWSP) jaxWsFactory.create();
}
/**
* The authentication service.
*
* @return the full service that can authenticate a user.
*/
@Bean
public AuthenticationService authenticationService() {
LOGGER.debug("Initializing authentication service");
return new AuthenticationService(
this.myAuthenticationServicesWSP(),
);
}
}
Configuration de la sécurité côté sécurité :
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.ws.WebServiceException;
/**
* The authentication service. Request the SAOP service with auto-generated classes.
*/
public class AuthenticationService {
private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationService.class.getCanonicalName());
private final MyAuthenticationServicesWSP myAuthenticationServicesWSP;
/**
* Instantiate the service.
*
* @param myAuthenticationServicesWSP the service to request sesame.
*/
public AuthenticationService(MyAuthenticationServicesWSP myAuthenticationServicesWSP) {
this.myAuthenticationServicesWSP = myAuthenticationServicesWSP;
}
/**
* Authenticate the user and retrieve a token to manage the user session.
*
* @param login the user's login.
* @param password the user's password.
* @return the created token.
* @throws MyException In case an exception is thrown.
*/
public String login(String login, String password) throws MyException {
if (login == null || login.length() == 0) {
throw new MyException("L'identifiant est vide.");
}
else if (password == null || password.length() == 0) {
throw new MyException("Le mot de passe est vide.");
}
String token;
try {
token = myAuthenticationServicesWSP.login(login, password);
}
catch (Exception e) {
throw new MyException("There was an error");
}
return token;
}
}
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.
Cettte technique géniale permet de distinguer deux applications autonomes au sein d'un seul et même repo angular2 (ou 4).
Super option.
Comment gérer le découpage de votre projet en sous-projets via la gestion des modules.
Ce gars a l'air top.
Plein de jeux agiles pour les équipes
Super jeu pour prendre conscience de l'importance du board agile