Bonjour à tous,
Voilà plusieurs jours que j'essaye en vain différentes solutions à mon problème. J'ai ajouté à mon application le framework Spring-Security et je n'arrive pas à le rendre actif avec une page HTML static ET j'ai un problème de logique dans mon code quand à l'utilisation du CSRF via requête ajax.
J'ai d'abord essayer avec la page de login par défaut de Spring (le pop-up) qui marche bien mais quand je fais un logout sur ma page principale, le pop-up ne réapparaît pas. Quand je logout, je ne peux plus effectuer de requête (ce qui est bon) mais quand je refresh ma page, l'user peut à nouveau faire des requêtes sans avoir du se reconnecter.
J'ai un Front-End côté client en Html/Js qui communique via des requêtes ajax à mon Back-End.
Mes 2 problèmes principaux :
- Comment load mon login.html sur la route /login
- Comment envoyer les infos du token à l'user pour faire les requêtes, je le fais déjà mais problème de logique pour le logout
- Pourquoi après le logout, l'user en rechargeant la page peut toujours effectuer les actions ?
Architecture ressources :
login.html pour la page avec un simple formulaire de login, formulaire avec username, password et fichier JS qui s'occupe de faire la requete Ajax en POST, url : login
index.html qui est un One-Page de toute l'application.
WebSecurity
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 @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .httpBasic() .and() .authorizeRequests() .antMatchers("/static/**").permitAll() .antMatchers("/css/**", "/img/**", "/js/**").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login").permitAll() .defaultSuccessUrl("/", true) .successHandler(loginSuccessHandler()) .and() // .logout() // .invalidateHttpSession(true) // .logoutUrl("/logout") // .permitAll() .logout() //.logoutRequestMatcher(new AntPathRequestMatcher("/logout")) .logoutSuccessUrl("/login") //.clearAuthentication(true) //.deleteCookies("JSESSIONID") //.invalidateHttpSession(true) .logoutSuccessHandler(logoutSuccessHandler()); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("test").password("test").roles("TEST"); } @Bean public AuthenticationSuccessHandler loginSuccessHandler() { return new CustomAuthentificationSuccessHandler(); } @Bean public LogoutSuccessHandler logoutSuccessHandler() { return new CustomLogoutSuccessHandler(); }
Pour le logout, j'ai essayé plusieurs solutions, aussi avec un LougoutHandler ou je désactive la session + cookie.
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 public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler { public CustomLogoutSuccessHandler() { super(); } // API @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { System.out.println("---------------- LOGOUT OK -------------------"); CsrfToken token = (CsrfToken) request.getAttribute("_csrf"); System.out.println("Ancien token -> " + token.getToken()); System.out.println("Ancien header -> " + token.getHeaderName()); HttpSession session = request.getSession(false); SecurityContextHolder.clearContext(); session = request.getSession(false); if (session != null) { session.invalidate(); } for (Cookie c : request.getCookies()) { c.setMaxAge(0); } super.onLogoutSuccess(request, response, authentication); } }
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 @Configuration @EnableWebMvc public class MvcConfigView extends WebMvcConfigurerAdapter { public MvcConfigView() { super(); } @Override public void addViewControllers(ViewControllerRegistry registry) { //forward:index.html // /static/login.html registry.addViewController("/login").setViewName("login.html"); registry.setOrder(Ordered.HIGHEST_PRECEDENCE); //registry.addViewController("/logout").setViewName("login.html"); } /* @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**").addResourceLocations("/static/"); } */ }
Donc problème ici d'abord, comment load avec le bon path ma page login.html ?
Ensuite, comme j'utilise des requêtes Ajax, je dois aller chercher les infos de la session, notamment pour le token csrf pour les passer dans mes requêtes ajax, ce que je fais via un GET dès le chargement de la page index.html
Au niveau du controller, ça renvoie les informations au client :
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 function getCsrfTokenAjax() { $.ajax({ url: "getCsrfToken", method: 'GET' }) .done(function(data, textStatus, jqXHR) { getCsrfToken(jqXHR); }); } function getCsrfToken(jqXHR) { $('meta[name="X-CSRF-TOKEN"]').attr('content', jqXHR.getResponseHeader('X-CSRF-TOKEN')); $('meta[name="X-CSRF-HEADER"]').attr('content', jqXHR.getResponseHeader('X-CSRF-HEADER')); } function setHeaderWithCsrfToken(request) { var token = $('meta[name="X-CSRF-TOKEN"]').attr('content'); var header = $('meta[name="X-CSRF-HEADER"]').attr('content'); var param = $('meta[name="X-CSRF-PARAM"]').attr('content'); request.setRequestHeader(header, token); }
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 @GetMapping("/getCsrfToken") public void getCsrfToken(HttpServletRequest request, HttpServletResponse response) { logger.info("ROUTE --> /getCsrfToken"); //CsrfToken token = (CsrfToken) request.getSession().getAttribute(DEFAULT_CSRF_TOKEN_ATTR_NAME); CsrfToken token = (CsrfToken) request.getAttribute("_csrf"); // Spring Security will allow the Token to be included in this header name response.setHeader("X-CSRF-HEADER", token.getHeaderName()); System.out.println("New Header -> " + token.getHeaderName()); // Spring Security will allow the token to be included in this parameter name response.setHeader("X-CSRF-PARAM", token.getParameterName()); // this is the value of the token to be included as either a header or an HTTP parameter response.setHeader("X-CSRF-TOKEN", token.getToken()); System.out.println("New Token -> " + token.getToken()); }
Et donc pour chaque requete j'ajoute ces informations dans le header
Mon problème ici, pourquoi après un logout + un refresh de la page l'user peut toujours effectuer des requêtes, je suppose que c'est un problème de logique avec le requête GET que je fais au début du chargement de ma page index.html..
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 beforeSend: function (request) { setHeaderWithCsrfToken(request); }
Je vois bien qu'après un logout, le token change mais qu'elle est l'intérêt si je peux aller GET ce nouveau token alors que l'user n'est plus censé pouvoir y accéder (dans le cas ou il modifierait le JS).
J'espère que quelqu'un sera m'aider parce qu'après 4 jours de recherche, j'en ai perdu la patience
Partager