Bonjour à tous,

J'ai créé un template de projet SpringBoot pour exposer une API REST pour une appli angular.
Pour les tests et afin de partager l'API avec d'autres développeurs j'ai mis en place Swagger-ui
Pour sécuriser : j'utilise Spring Security et JWT: tout fonctionne comme je veux, mon seul soucis c'est d'arriver à proposer dans Swagger-ui le service permettant de générer le Token

Voici ma conf :
Swagger

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Configuration
public class Swagger2Config {
 
	private ApiInfo apiEndPointsInfo() {
 
		return new ApiInfoBuilder().title("Application - REST API")
				.description("API REST - Exposition des services REST de l'application")
				.contact(new Contact("Toto", "https://www.toto.fr", "toto@toto.fr"))
				.version(MavenUtils.getVersion()).build();
	}
 
	@Bean
	public Docket productApi() {
		return new Docket(DocumentationType.OAS_30).select()
				.apis(RequestHandlerSelectors.basePackage("webservices"))
				.paths(PathSelectors.any()).build().securityContexts(Arrays.asList(this.securityContext()))
				.securitySchemes(Arrays.asList(this.apiKey())).apiInfo(this.apiEndPointsInfo());
	}
 
	private ApiKey apiKey() {
		return new ApiKey(SecurityConstants.HEADER_STRING, "Authorization", "header");
	}
 
	private SecurityContext securityContext() {
		return SecurityContext.builder().securityReferences(this.defaultAuth()).build();
	}
 
	private List<SecurityReference> defaultAuth() {
		final AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
		final AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
		authorizationScopes[0] = authorizationScope;
		return Arrays.asList(new SecurityReference(SecurityConstants.HEADER_STRING, authorizationScopes));
	}
}
AuthorizationFilter
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
 
	Logger logger = LoggerFactory.getLogger(JWTAuthorizationFilter.class);
 
	public JWTAuthorizationFilter(final AuthenticationManager authManager) {
		super(authManager);
	}
 
	@Override
	protected void doFilterInternal(final HttpServletRequest req, final HttpServletResponse res,
			final FilterChain chain) throws IOException, ServletException {
		final String header = req.getHeader(SecurityConstants.HEADER_STRING);
 
		if (header == null || !header.startsWith(SecurityConstants.TOKEN_PREFIX)) {
			chain.doFilter(req, res);
			return;
		}
 
		final UsernamePasswordAuthenticationToken authentication = this.getAuthentication(req);
 
		SecurityContextHolder.getContext().setAuthentication(authentication);
		chain.doFilter(req, res);
	}
 
	// Reads the JWT from the Authorization header, and then uses JWT to
	// validate the token
	private UsernamePasswordAuthenticationToken getAuthentication(final HttpServletRequest request) {
		final String token = request.getHeader(SecurityConstants.HEADER_STRING);
 
		if (token != null) {
			// parse the token.
			final String user = JWT.require(Algorithm.HMAC512(SecurityConstants.SECRET.getBytes())).build()
					.verify(token.replace(SecurityConstants.TOKEN_PREFIX, "")).getSubject();
 
			if (user != null)
				return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
 
			return null;
		}
 
		return null;
	}
 
}
AuthenticationFilter

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
 
	Logger logger = LoggerFactory.getLogger(JWTAuthenticationFilter.class);
 
	private final AuthenticationManager authenticationManager;
 
	public JWTAuthenticationFilter(final AuthenticationManager authenticationManager) {
		this.authenticationManager = authenticationManager;
 
		this.setFilterProcessesUrl(SecurityConstants.SIGN_IN_URL);
	}
 
	@Override
	public Authentication attemptAuthentication(final HttpServletRequest req, final HttpServletResponse res)
			throws AuthenticationException {
		try {
			final ObjectNode node = new ObjectMapper().readValue(req.getInputStream(), ObjectNode.class);
			final User creds = new User(node.get("username").asText(), node.get("password").asText(),
					Lists.newArrayList());
 
			return this.authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(creds.getUsername(),
					creds.getPassword(), new ArrayList<>()));
		} catch (final IOException e) {
			throw new RuntimeException(e);
		}
	}
 
	@Override
	protected void successfulAuthentication(final HttpServletRequest req, final HttpServletResponse res,
			final FilterChain chain, final Authentication auth) throws IOException {
		final String token = JWT.create().withSubject(((User) auth.getPrincipal()).getUsername())
				.withExpiresAt(new Date(System.currentTimeMillis() + SecurityConstants.EXPIRATION_TIME))
				.sign(Algorithm.HMAC512(SecurityConstants.SECRET.getBytes()));
 
		final String body = ((User) auth.getPrincipal()).getUsername() + " " + token;
 
		res.getWriter().write(body);
		res.getWriter().flush();
	}
}
Mes constantes
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
public class SecurityConstants {
 
	public static final String SECRET = "SECRET_KEY";
	public static final long EXPIRATION_TIME = 900_000; // 15 mins
	public static final String TOKEN_PREFIX = "Bearer ";
	public static final String HEADER_STRING = "Authorization";
	public static final String SIGN_IN_URL = "/services/login";
	public static final String SIGN_UP_URL = "/services/users";
 
}
Et mon globalSecurity
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
@Configuration
public class GlobalSecurity extends WebSecurityConfigurerAdapter {
 
	Logger logger = LoggerFactory.getLogger(GlobalSecurity.class);
 
	private final UserDetailsService userDetailsService;
 
	public GlobalSecurity(final UserDetailsService userService) {
		this.userDetailsService = userService;
	}
 
	private static final String[] AUTH_WHITELIST = {
			// -- Swagger UI v2
			"/v2/api-docs", "/swagger-resources", "/swagger-resources/**", "/configuration/ui",
			"/configuration/security", "/swagger-ui.html", "/webjars/**",
			// -- Swagger UI v3 (OpenAPI)
			"/v3/api-docs/**", "/swagger-ui/**",
			// On autorise l'url qui génére le token
			SecurityConstants.SIGN_IN_URL };
 
	@Override
	protected void configure(final HttpSecurity http) throws Exception {
		http.
		// whitelist Swagger UI resources
				authorizeRequests().antMatchers(AUTH_WHITELIST).permitAll()
				// Ajout de la partie JWT
				.and()
				// On autorise les requetes pour créer un user
				.authorizeRequests().antMatchers(HttpMethod.POST, SecurityConstants.SIGN_UP_URL).permitAll()
				// Toutes les autres doivent être authentifiées
				.anyRequest().authenticated().and()
				// On ajoute les filtres JWT
				.addFilter(new JWTAuthenticationFilter(this.authenticationManager()))
				.addFilter(new JWTAuthorizationFilter(this.authenticationManager()))
				// this disables session creation on Spring Security
				.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
		;
	}
 
	@Override
	public void configure(final AuthenticationManagerBuilder auth) throws Exception {
		this.logger.debug("Message d elog");
		auth.userDetailsService(this.userDetailsService);
	}
 
	@Bean
	CorsConfigurationSource corsConfigurationSource() {
		final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
 
		final CorsConfiguration corsConfiguration = new CorsConfiguration().applyPermitDefaultValues();
		source.registerCorsConfiguration("/**", corsConfiguration);
 
		return source;
	}
 
}
L'idée c'est donc d'avoir mon service /services/login dispo dans mon Swagger-ui pour me générer le token.
PS : je précise que j'utilise un utilisateur défini dans les properties de Spring pour générer le Token (pas de BDD derrière)

Merci bcp pour vos retours !