Bonjour,
Confinement oblige, je retravaille sur un projet maison qui me sert de base pour approfondir mes connaissances autour d'Angular et de Spring.
J'avais un ancien projet avec une configuration xml.
La configuration xml pour spring security était la suivante:
Code XML : 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 <!-- Activation des annotations @PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize sur chaque bean de Spring --> <security:global-method-security pre-post-annotations="enabled" /> <!-- On indique que pour "/rest/authentication", il n'y a pas de sécurité (tout le monde peut appeler pour s'authentifier). --> <security:http pattern="/rest/authentication" security="none"/> <!-- On indique le point d'entrée lors d'un appel à tous les services REST (sauf celui déclaré juste avant sur lequel on ne veut pas de sécurité). "stateless" indique qu'on ne veut pas de session créée pour la sécurité vu que l'on utilise des token pour chaque requête. --> <security:http pattern="/rest/**" entry-point-ref="restAuthenticationEntryPoint" create-session="stateless"> <!-- Nous n'avons pas besoin de protection csrf car le token est immunisé à ca. --> <security:csrf disabled="true"/> <!-- On indique que l'on utilise un filtre customisé référencé par "jwtAuthenticationFilter" (défini plus bas). Il sera placé avant le filtre pour le formulaire de login dans la chaine des filtres prédéfinis dans Spring. --> <security:custom-filter before="FORM_LOGIN_FILTER" ref="jwtAuthenticationFilter"/> </security:http> <!-- On indique où se trouve notre filtre qui sera géré comme un bean par Spring. --> <bean id="jwtAuthenticationFilter" class="com.testAngularRestToken.filter.JwtAuthenticationFilter"> <property name="authenticationManager" ref="authenticationManager" /> <property name="authenticationSuccessHandler" ref="jwtAuthenticationSuccessHandler" /> </bean> <!-- On défini le "authenticationManager" qui va s'appuyer sur notre classe "JwtAuthenticationProvider". --> <security:authentication-manager alias="authenticationManager"> <security:authentication-provider ref="jwtAuthenticationProvider" /> </security:authentication-manager>
J'ai commencé la conversion en classe de configuration java qui n'est pas finie mais je ne sais pas si c'est correct:
En gros, voici la sécurité que je souhaite avoir:
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98 @Configuration @EnableWebSecurity class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private JwtAuthenticationProvider jwtAuthProvider; // Configure @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity // "stateless" indique qu'on ne veut pas de session créée pour la sécurité vu que l'on utilise // des jetons JWT pour les requête. .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() // Nous n'avons pas besoin de protection csrf car le token qu'on utilise est immunisé à ca .csrf().disable() // Filtre personnalisé pour la vérification du token JWT .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) // Configuration des accès selon la requête .authorizeRequests() // On autorise tout le monde pour des requêtes sur ces URLs .antMatchers("/rest/authentication").permitAll() // Pour toutes les autres requêtes sur les autres URLs on doit être authentifié .anyRequest() .authenticated() .and() // Permet d'activer l'authentification par HTTP basic. Utile dans mon cas? // Whenever an HTTP request is sent to the application Spring Security now checks if the header // contains Authorization: Basic <credentials>. .httpBasic(); } // Configure un authentication provider spécifique implémenté pour notre jeton JWT pour le // authentication manager. @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(jwtAuthProvider); } // On définit le bean géré par Spring pour le filtrage des requêtes (vérification du jeton JWT). @Bean public JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception { JwtAuthenticationFilter lJwtAuthenticationFilter = new JwtAuthenticationFilter(); lJwtAuthenticationFilter.setAuthenticationManager(authenticationManager()); lJwtAuthenticationFilter.setAuthenticationSuccessHandler(new JwtAuthenticationSuccessHandler()); return lJwtAuthenticationFilter; } // On défini le authenticationManager qui va s'appuyer sur notre classe "JwtAuthenticationProvider" // pour la vérification du jeton JWT et la récupération des UserDetails (cf. méthode // configure(AuthenticationManagerBuilder auth) surchargée plus haut). @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } // On définit le password encoder à utiliser pour les mots de passe. // Cela va donc activer l'encodage et la vérification des mots de passe automatiquement par Spring // Security en utilisant cet encoder. @Bean public PasswordEncoder passwordEncoder() { // Ici utilisation de BCrypt avec le facteur de travail 10 (valeur par défaut de Spring Security). // Utilisation d'un SecureRandom comme générateur de sel (donne un nombre aléatoire // cryptographiquement fort). int lStrength = 10; return new BCryptPasswordEncoder(lStrength, new SecureRandom()); // INFO: On peut aussi utiliser d'autres encoder // String pepper = "pepper"; // secret key used by password encoding // int iterations = 200000; // number of hash iteration // int hashWidth = 256; // hash width in bits // return new Pbkdf2PasswordEncoder(pepper, iterations, hashWidth); // int cpuCost = (int) Math.pow(2, 14); // factor to increase CPU costs // int memoryCost = 8; // increases memory usage // int parallelization = 1; // currently not supported by Spring Security // int keyLength = 32; // key length in bytes // int saltLength = 64; // salt length in bytes // // return new SCryptPasswordEncoder(cpuCost, memoryCost, parallelization, keyLength, saltLength); } @Bean public AuthenticationProvider daoAuthenticationProvider() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); // Ajoute le password encoder définit au dessus au DaoAuthenticationProvider de Spring Security. provider.setPasswordEncoder(passwordEncoder()); // Définit le service servant au changement de mot de passe. provider.setUserDetailsPasswordService(this.databaseUserDetailPasswordService); // Définit le service de récupération des données utilisateur pour l'authentification. provider.setUserDetailsService(this.databaseUserDetailsService); return provider; } }
- A la création d'un compte, le mot de passe utilisateur est hashé avec BCrypt (gestion du sel automatique de ce que j'ai compris)
- Quand l'utilisateur s'est authentifié, il y a un jeton JWT qui sera mis dans les header des requêtes et donc vérifié par le filtre côté back
- JwtAuthenticationFilter se charge d'extraire le token de la requête puis d'appeler la méthode "authenticate()" du AuthenticationManager
- Cela provoquera un appel à la méthode "retrieveUser()" de JwtAuthenticationProvider dans laquelle je vérifie le token (validité), récupère le username qu'il contient, récupère les authorisations en BD associées puis créé l'AuthenticatedUser
Plus tard je passerai sur de l'OAuth2.
Mes questions:
- Dans jwtAuthenticationFilter(), pourquoi définir un bean pour l'authenticationManager? Pourquoi pas un @Autowired ou un new? Egalement, pourquoi un new pour le success handler (JwtAuthenticationSuccessHandler) et pas un @Autowired vu qu'il déclaré avec @Component (je me suis basé sur un exemple trouvé)?
- Dans un exemple, j'ai vu que le bean daoAuthenticationProvider était déclaré mais je ne comprend pas son utilité car je ne l'avais pas avant (je souhaite hasher le mot de passe avec BCrypt ce que je n'avais pas dans mon précédent exemple donc c'est peut être pour ca).
- Il y a des "conversions" dont je ne suis pas sûr comme pour pattern="/rest/**" entry-point-ref="restAuthenticationEntryPoint" create-session="stateless" ou before="FORM_LOGIN_FILTER" ou bien l'authenticationManager ou bien encore <security:global-method-security pre-post-annotations="enabled" />.
- Je ne comprend si .httpBasic() est utile dans mon cas car je vérifie la présence du token et sa validité dans le header de la requête avec mon filtre personnalisé.
- Lors de l'authentification (appel au service correspondant), comment se passe la vérification du mot de passe avec BCrypt?
Pouvez-vous m'aider svp?
Merci d'avance.
Partager