IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Spring Web Java Discussion :

Spring security - login en ajax (api rest) -> erreur 401


Sujet :

Spring Web Java

  1. #1
    Membre habitué Avatar de racine carrée
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2018
    Messages
    156
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 23
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2018
    Messages : 156
    Points : 137
    Points
    137
    Par défaut Spring security - login en ajax (api rest) -> erreur 401
    Bonjour à tous,
    je suis en train d'implémenter un processus d'authentification pour une appli web (backend: java spring boot, front-end: angular) et je suis confronté à un problème depuis plus d'une semaine.
    Jusqu'à présent j'ai réussi à faire un code qui gère l'authentification en me redirigeant vers une page de login auto-générée par spring, mais je voudrais éviter ce comportement car mon backend fournit une api (et la page de login est gérée par angular). J'ai donc configuré spring security ainsi :
    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
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
     
    	@Autowired
    	@Qualifier("authUserDetailsService")
    	private UserDetailsService authUserDetailsService;
    	
    	@Override
    	protected void configure(HttpSecurity http) throws Exception {
                http.authorizeRequests()
                // permet l'accès au endpoint "/login" pour les utilisateurs non authentifiés et leur bloque les autres endpoints
                    .antMatchers("/login").permitAll()  
                    .anyRequest().authenticated()
                // 401-UNAUTHORIZED quand on tente d'accéder à une url sans authentification (au lieu de rediriger vers une page de login)
                .and()
                    .exceptionHandling()
                    .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
                // permet de s'authentifier avec la méthode Post (en passant le username et le password) et renvoie un 204-NO_CONTENT quand l'authentification réussi (pas de redirection) et 401-UNAUTHORIZED sinon
                .and()
                    .formLogin()
                    .successHandler((req, res, auth) -> res.setStatus(HttpStatus.NO_CONTENT.value()))
                    .failureHandler(new SimpleUrlAuthenticationFailureHandler())
    
                // endpoint pour le logout qui renvoie 204-NO_CONTENT (pas de redirection)
                .and()
                    .logout()
    				.deleteCookies("JSESSIONID")
                    .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.NO_CONTENT))
    
                // protection CSRF
                .and()
                     .csrf()
                    .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    	}
    	
            // utilise mon service authUserDetailsService pour gérer l'authenfication (utilisateurs stockés en base de donnée)
    	@Override
    	protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
    		authManagerBuilder.userDetailsService(authUserDetailsService).passwordEncoder(passwordEncoder());
    	}
    
            // pour le moment je n'utilise pas d'encodage de password pour simplifier les tests
    	@Bean
            public PasswordEncoder passwordEncoder() {
                return NoOpPasswordEncoder.getInstance(); //TODO: replace by new BCryptPasswordEncoder()
            }
    	
    }
    J'ai aussi créé ce service côté client pour permettre une authentification avec un appel à l'api, grâce à une requête ajax :
    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
    @Injectable({
        providedIn: 'root'
    })
    export class AuthenticationService {
    
        private loginUrl = environment.apiUrl + "login"
    
        constructor(private http: HttpClient) {}
    
        // envoie une requête Post avec le username et le password
        public authenticate(username, password): Promise<boolean> {
            return new Promise( resolve =>
                this.http.post(this.loginUrl, {"username": username, "password": password}).subscribe(
                    reponse => resolve(true),
                    error => resolve(false)
                )
            )
        }
    
    }
    Mon soucis est que cet appel d'api génère une erreur 401, ce qui signifie donc qu'il y a une une erreur d'authentification, mais je suis incapable de savoir quelle est cette erreur (aucune info dans les logs) :

    Nom : erreur 401.png
Affichages : 1024
Taille : 42,1 Ko

    Est-ce que quelqu'un aurait une idée de ce qui ne va pas ?
    Merci beaucoup d'avance !

  2. #2
    Membre habitué Avatar de racine carrée
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2018
    Messages
    156
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 23
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2018
    Messages : 156
    Points : 137
    Points
    137
    Par défaut
    Bonsoir,
    personne n'a ne serait-ce qu'une piste ?

  3. #3
    Membre éclairé

    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    462
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 462
    Points : 896
    Points
    896
    Billets dans le blog
    5
    Par défaut
    Bonjour,

    Je me permets de ré-ouvrir la discussion car j'ai un problème semblable.
    Et ça fait une semaine que je suis dessus.

    A terme, mon but est d'avoir un client Angular, mais pour le moment, il est Javascript (quel horreur!).

    Mais il doit pouvoir être Client Lourd ou Android.

    Mes Besoins
    J'ai deux applications, une Serveur (Spring,Spring Boot et Spring Security) et une cliente (ici, en Javascript et une utilisation de node.js).
    Les deux, bien que ça communique, sont totalement indépendantes.

    Le client va chercher des données sur le serveur et le serveur lui renvoi du JSON.

    Seulement, le serveur est sécurisé. Il faut se connecter.

    J'aimerai que le client se connecte une seule fois, et après consomme les service REST qu'il peut consommer.

    Le Serveur
    Pour commencer, j'ai fait un AuthenticationProvider soit:
    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
    package com.alten.assistantnc.security;
     
    import org.springframework.security.authentication.AuthenticationProvider;
    import org.springframework.security.authentication.BadCredentialsException;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
     
    public class AuthenticationProviderImpl implements AuthenticationProvider{
     
    	private final UserDetailsService userDetailsService;
     
    	public AuthenticationProviderImpl(UserDetailsService userDetailsService) {
    		this.userDetailsService = userDetailsService;
    	}
     
    	@Override
    	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    		UserDetails user = userDetailsService.loadUserByUsername(authentication.getName());
    		if(user == null) {
    			throw new UsernameNotFoundException("User "+authentication.getName()+" was not found");
    		}
    		if(user.getPassword().equals(authentication.getCredentials())) {
    			throw new BadCredentialsException("The password was wrong for user "+authentication.getName());
    		}		
    		return new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword(),user.getAuthorities());
    	}
     
    	@Override
    	public boolean supports(Class<?> authentication) {
    		return authentication.equals(UsernamePasswordAuthenticationToken.class);
    	}
     
    }
    Il utilise le service suivant:
    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
    package com.alten.assistantnc.service;
     
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Optional;
     
    import javax.annotation.PostConstruct;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.stereotype.Service;
     
    @Service
    public class UserService implements UserDetailsService{
     
    	private final List<UserDetails> users;
     
    	@Autowired
    	private PasswordEncoder passwordEncoder;
     
    	public UserService() {
    		users = new ArrayList<>();
    	}
     
    	@PostConstruct
    	private void remplirDadabase() {
    		users.add(new User("admin",passwordEncoder.encode("admin"), Arrays.asList(getAdmin(),getUser())));
    		users.add(new User("user1",passwordEncoder.encode("user1"), Arrays.asList(getUser())));
    		users.add(new User("user2",passwordEncoder.encode("user2"), Arrays.asList(getUser())));
    	}
     
    	@Override
    	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    		Optional<UserDetails> o = users.stream().filter(u -> u.getUsername().equals(username)).findFirst();
    		if(o.isEmpty()) {
    			throw new UsernameNotFoundException(username + " was not found");
    		}
    		return o.get();
    	}
     
    	private GrantedAuthority getAdmin() {
    		return () -> "ROLE_ADMIN";
    	}
     
    	private GrantedAuthority getUser() {
    		return () -> "ROLE_USER";
    	}
    }
    Notez que pour les rôles dans Spring, il y a une convention.

    Pour le test, j'ai le service suivant:
    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
    package com.alten.assistantnc.rest;
     
    import org.springframework.security.access.prepost.PreAuthorize;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
     
    @RestController
    @RequestMapping("/HelloWord")
    public class HelloWordController {
     
    	@GetMapping(path = "/basic")
    	public String helloWord() {
    		return "bonjour";
    	}
     
    	@PreAuthorize("hasRole('USER')")
    	@GetMapping(path = "/user")
    	public String helloWordUser() {
    		return "bonjour utilisateur";
    	}
     
    	@PreAuthorize("hasRole('ADMIN')")
    	@GetMapping(path = "/admin")
    	public String helloWordAdmin() {
    		return "bonjour admin";
    	}
    }
    Et mon service de login suivant:
    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
    package com.alten.assistantnc.rest;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import org.springframework.security.authentication.AuthenticationProvider;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.web.bind.annotation.CrossOrigin;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
     
    import com.alten.assistantnc.dto.LoginDTO;
     
    @RestController
    @RequestMapping("/login")
    @CrossOrigin
    public class LoginController {
     
    	@Autowired
    	private AuthenticationProvider authenticationProvide;
     
    	@Autowired
    	private PasswordEncoder passwordEncoder;
     
    	@PostMapping(value = "/log", consumes = MediaType.APPLICATION_JSON_VALUE,produces = MediaType.APPLICATION_JSON_VALUE)
    	public ResponseEntity<Authentication> login(@RequestBody LoginDTO login){
    		Authentication aut = authenticationProvide.authenticate(new UsernamePasswordAuthenticationToken(login.getUser(), passwordEncoder.encode(login.getPassword())));
    		SecurityContextHolder.getContext().setAuthentication(aut);
    		return ResponseEntity.ok(aut);
    	}
    }
    La DTO n'a pas vraiment d'intérêt ici:
    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
    package com.alten.assistantnc.dto;
     
    public class LoginDTO {
     
    	private String user;
     
    	private String password;
     
    	public String getUser() {
    		return user;
    	}
     
    	public void setUser(String user) {
    		this.user = user;
    	}
     
    	public String getPassword() {
    		return password;
    	}
     
    	public void setPassword(String password) {
    		this.password = password;
    	}
    }
    J'ai une configuration pour les services:
    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
    package com.alten.assistantnc.configuration.service;
     
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
     
    @ComponentScan("com.alten.assistantnc.service")
    public class ConfigurationAssistantNCConfigurationService {
     
     
    	@Bean
    	public PasswordEncoder passwordEncoder() {
    		return new BCryptPasswordEncoder();
    	}
    }
    Et une pour les services REST:
    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
    99
    100
    101
    102
    package com.alten.assistantnc.configuration.rest;
     
    import java.util.Arrays;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Import;
    import org.springframework.security.authentication.AuthenticationProvider;
    import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.SecurityFilterChain;
    import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
    import org.springframework.security.web.csrf.CsrfTokenRepository;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.CorsConfigurationSource;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
     
    import com.alten.assistantnc.configuration.service.ConfigurationAssistantNCConfigurationService;
    import com.alten.assistantnc.security.AuthenticationProviderImpl;
    import com.alten.assistantnc.security.LoginHandler;
    import com.alten.assistantnc.security.LoginHandlerImpl;
     
    @ComponentScan("com.alten.assistantnc.rest")
    @Import(ConfigurationAssistantNCConfigurationService.class)
    @EnableGlobalMethodSecurity(
    		  prePostEnabled = true, 
    		  securedEnabled = true, 
    		  jsr250Enabled = true)
    public class ConfigurationAssistantNCRest implements WebMvcConfigurer{
     
    	@Autowired
    	private UserDetailsService userService;
     
    	@Bean
    	public AuthenticationProvider authenticationProvider() {
    		return new AuthenticationProviderImpl(userService);
    	}
     
    	@Bean
    	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    			return http
    				.authenticationProvider(authenticationProvider())
    				.cors().configurationSource(corsConfigurationSource())
    				.and()
    //				.cors().disable()
    				.authorizeHttpRequests()
    					.antMatchers("/login/log").permitAll()
    					.antMatchers("/login/logout").permitAll()
    				.and()
    //					.csrf()
    //					.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
    				.csrf().disable()	
    //				.and()
    //					.httpBasic()
    //					.formLogin()
    //					.loginProcessingUrl("http://localhost:3000")
    //					.usernameParameter("user")
    //					.passwordParameter("password")
    //				.and()
    //					.cors().disable()
    //					.cors().configurationSource(corsConfigurationSource())
    //					.successHandler(loginHandler())
    //					.failureHandler(loginHandler())
    //				.and()	
    //					.logout()
    //					.logoutUrl("login/logout")
    //					.addLogoutHandler(loginHandler())
    //				.and()	
    //					.httpBasic()
    //				.formLogin()
    //				.and()	
    //				.and()
    //					.httpBasic()
    //				.and()	
    //					.csrf().disable()
    //					.rememberMe().userDetailsService(userService)
    //					.key("uniqueAndSecret").tokenValiditySeconds(86400)
    //				.and()				
    				.build();
    	}
     
    	@Bean
    	public CorsConfigurationSource corsConfigurationSource() {
    		CorsConfiguration configuration = new CorsConfiguration();
    		configuration.setAllowedMethods(Arrays.asList("GET","POST"));
    		configuration.applyPermitDefaultValues();
    		configuration.addAllowedOrigin("http://localhost:3000");
    		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    		source.registerCorsConfiguration("/**", configuration);
     
    		return source;
    	}	
    //	
    //	@Bean
    //	public LoginHandler loginHandler() {
    //		return new LoginHandlerImpl();
    //	}
    }
    Notez que l'on voit bien que je galère sur Spring Security...

    Le Client
    Le code JS est le suivant:
    Code JavaScript : 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
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    //var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
     
    var mUsername = null;
    var mPassword = null;
    var mCookies = null;
     
    function login(){
     
     
    	var user = document.getElementById('idUser').value;
     
    	var password = document.getElementById('idPassword').value;
     
    	var reponse = null;
     
    	var url = 'http://localhost:8080/login/log';
     
    	var data = '{"user":"'+user+'","password":"'+password+'"}';	
     
    	var xhttp = new XMLHttpRequest();
     
    	xhttp.onreadystatechange = function() {
    		console.log(xhttp.responseText);
    		if (xhttp.readyState == 4 && xhttp.status == 200) {
    		  reponse =   xhttp.responseText;
     
    		  var aResponse =  xhttp.response;
    		  var aJsonResponse = JSON.parse(aResponse);
    		  var aUsername = aJsonResponse.principal;
    		  var aPassword = aJsonResponse.credentials;
    		  mUsername = aUsername;
    		  mPassword = aPassword;	
    		}
    	  };
     
    	xhttp.open("POST", url,true);
     
    	//xhttp.setRequestHeader('Origin', 'http://localhost:8080');
    	xhttp.setRequestHeader('Access-Control-Allow-Origin', 'http://localhost:8080');
    	//xhttp.setRequestHeader('Access-Control-Allow-Headers', 'X-Requested-With');
    	xhttp.setRequestHeader('Content-Type', "application/json;charset=UTF-8");
    	//xhttp.withCredentials = true;
     
    	xhttp.send(data);
     
    	console.log(reponse);
    }
     
    function testBasic(){
     
    	var xhttp = null;
    	var url = 'http://localhost:8080/HelloWord/basic';
     
    	var reponse = null;
     
    	xhttp = new XMLHttpRequest();
     
    	xhttp.onreadystatechange = function() {
    		if (xhttp.readyState == 4 && xhttp.status == 200) {
    		  reponse =   xhttp.responseText;
    		}
    	  };
     
    	xhttp.open("GET", url, true);
    	xhttp.send();
     
     
    	console.log(reponse);
    }
     
    function testAdmin(){
     
    	var xhttp = null;
    	var url = 'http://localhost:8080/HelloWord/admin';
     
    	var reponse = null;
     
    	xhttp = new XMLHttpRequest();
     
    	xhttp.onreadystatechange = function() {
    		if (xhttp.readyState == 4 && xhttp.status == 200) {
    		  reponse =   xhttp.responseText;
    		}
    	  };
    	xhttp.open("GET", url, true);
    	xhttp.send();
     
     
    	console.log(reponse);
    }
     
    function testUser(){
     
    	var xhttp = null;
    	var url = 'http://localhost:8080/HelloWord/user';
     
    	var reponse = null;
     
    	xhttp = new XMLHttpRequest();
     
    	xhttp.onreadystatechange = function() {
    		if (xhttp.readyState == 4 && xhttp.status == 200) {
    		  reponse =   xhttp.responseText;
    		}
    	  };
     
    	xhttp.open("GET", url, true);
    	xhttp.send();
     
     
    	console.log(reponse);
    }
     
    function logout(){
    	var reponse = null;
     
    	var url = 'http://localhost:8080/login/logout';
     
    	var xhttp = new XMLHttpRequest();
     
    	xhttp.onreadystatechange = function() {
    		console.log(xhttp.responseText);
    		if (xhttp.readyState == 4 && xhttp.status == 200) {
    		  reponse =   xhttp.responseText;
    		}
    	  };
     
    	xhttp.open("POST", url,true);
     
     
    	xhttp.send(null);
     
    	console.log(reponse);
    }

    La page HTML, permetant de tester tout:
    Code HTML : 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
    <!DOCTYPE html>
    <html>
     <head>
      <meta charset="utf-8">
     
      <title>
       Login
      </title>
      <script type="text/javascript" src="/test.js"></script>
     </head>
     <body>
      <table>
    	<tr>
    		<td>Login</td>
    		<td><input type="text" id="idUser"/></td>
    	</tr>
    	<tr>
    		<td>Password</td>
    		<td><input type="password" id="idPassword"></td>
    	</tr>
      <table>
      <input type="submit" value="login" id="idBoutonLogin" onclick="login()"/>
      <br/>
      <input type="submit" value="basic" id="idBoutonBasic" onclick="testBasic()"/>
      <br/>
      <input type="submit" value="admin" id="idBoutonAdmin" onclick="testAdmin()"/>
      <br/>
      <input type="submit" value="user" id="idBoutonUser" onclick="testUser()"/>
      <br/>
      <input type="submit" value="logout" id="idBoutonLogout" onclick="logout()"/>
     </body>
    </html>

    Et le javascript lancé par node.js:
    Code JavaScript : 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
    const http = require('http');
    const fs = require('fs');
     
    const server = http.createServer((req,res)=>{
    	var url = req.url;
    	if(url == '/test.js'){
    		fs.readFile('./test.js',null,(error,data)=>{
            if(error){
                res.end('fill have error')
            }else{
                res.end(data)
            }
        })
    	}
     
        fs.readFile('./login.html',null,(error,data)=>{
            if(error){
                res.end('fill have error')
            }else{
                res.end(data)
            }
        })
    });
    server.listen(3000,'127.0.0.1',()=>{
        console.log('Server running')
    })

    Sans Client
    En ajoutant:
    J'ai un fonctionnement intéressant.

    J'ai un formulaire de login lorsque je tente de consommer user:
    Nom : 2022_09_20_spring_security_1.jpg
Affichages : 821
Taille : 88,7 Ko

    Qui fonctionne quand je me connecte avec user1:
    Nom : 2022_09_20_spring_security_2.jpg
Affichages : 825
Taille : 80,5 Ko

    Et qui m'envoie bouler quand je me connecte en tant que admin:
    Nom : 2022_09_20_spring_security_3.jpg
Affichages : 814
Taille : 104,8 Ko

    Bref, un fonctionnement normal...

    Avec le client
    Là, les ennuis commencent.
    Je me connecte avec l'admin, j'ai bien la réponse attendu.
    Nom : 2022_09_20_spring_security_4.jpg
Affichages : 812
Taille : 224,0 Ko

    Si je vais sur Basic, pas de problème:
    Nom : 2022_09_20_spring_security_5.jpg
Affichages : 815
Taille : 190,8 Ko

    Mais sur user (il a les droits...), là, j'ai:
    Nom : 2022_09_20_spring_security_6.jpg
Affichages : 808
Taille : 199,0 Ko

    Conclusion
    Il me manque quelque chose, mais je n'arriva pas à mettre la main dessus. Est-ce qu'il faut configurer un jettons, où est-ce quelque chose qu'il faut récupérer sur Javascript et donner pour ce connecter?

    J'attends vos avis et connaissances sur le sujet.

    Cordialement.

  4. #4
    Membre éclairé

    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    462
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 462
    Points : 896
    Points
    896
    Billets dans le blog
    5
    Par défaut
    Après un long périple, j'ai réussi à répondre à mes besoins.

    Je me suis aidé de cette vidéo:


    Pour commencer, on va utiliser JWT. En résumé, à la connexion, on envoie un token, et à chaque connexion demandant un mot de passe, on va lire un token que l'on a préalablement mis d'en l'entête de la requête.

    Serveur

    Pour commencer, il faut gérer JWT.
    Pour cela, il me faut une classe de constante:
    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
    package com.alten.assistantnc.security;
     
    public class SecurityConstant {
     
    	public static final String SECRET = "ASSISTANT_NC_SECRET";
     
    	public static final long EXPIRATION_TIME_DAYS = 10;//10 jours
     
    	public static final String TOKEN_PREFIX = "ASSISTANT_NC_TOKEN ";
     
    	public static final String HEADER_STRING = "Authorization";
     
    	public static final String ROLES = "roles";
     
    	private SecurityConstant() {
     
    	}
    }
    On va définir une interface (le contrat) pour avoir le JWT à envoyer pour un login.

    L'interface:
    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
    package com.alten.assistantnc.security;
     
    import org.springframework.security.core.Authentication;
     
    /**
     * Management des jetons JWT
     * @author phili
     *
     */
    public interface JWTManagement {
     
    	/**
             * Make a JWT and set it in response
             * @param request
             * @param response
             * @param authResult
             */
    	String getJwt(Authentication authResult);
    }
    Et l'implémentation:
    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
    package com.alten.assistantnc.security;
     
    import java.time.Instant;
    import java.time.temporal.ChronoUnit;
    import java.util.Date;
     
    import org.springframework.security.core.Authentication;
     
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
     
    public class JWTManagementImpl implements JWTManagement{
     
    	@Override
    	public String getJwt(Authentication authResult) {
    		Instant instant = Instant.now().plus(SecurityConstant.EXPIRATION_TIME_DAYS, ChronoUnit.DAYS);
    		return  SecurityConstant.TOKEN_PREFIX + Jwts.builder()
    				.setSubject(authResult.getPrincipal().toString())
    				.setExpiration(Date.from(instant))
    				.signWith(SignatureAlgorithm.HS512, SecurityConstant.SECRET)
    				.claim(SecurityConstant.ROLES, authResult.getAuthorities())
    				.compact();
    	}
    }
    Pour filtrer les requêtes hors login, il faut un filtre Spring qui va récupérer les JWT et les interpréter.
    En l'occurrence:
    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
    package com.alten.assistantnc.security;
     
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
     
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
     
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.web.filter.OncePerRequestFilter;
     
    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jwts;
     
    public class JWTAuthorizationFilter extends OncePerRequestFilter{
     
    	@Override
    	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
    			throws ServletException, IOException {
    		String jwt = request.getHeader(SecurityConstant.HEADER_STRING);
    		if(jwt == null || !jwt.startsWith(SecurityConstant.TOKEN_PREFIX)) {
    			continu(request, response, filterChain);
    		}else {
    			treatJwt(request, response, filterChain, jwt);
    		}
     
    	}
     
    	private void continu(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
    		filterChain.doFilter(request, response);
    	}
     
    	@SuppressWarnings("unchecked")
    	private void treatJwt(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain,String jwt) throws IOException, ServletException {
    		Claims claims = Jwts.parser()
    				.setSigningKey(SecurityConstant.SECRET)
    				.parseClaimsJws(jwt.replace(SecurityConstant.TOKEN_PREFIX, ""))
    				.getBody();
    		String userName = claims.getSubject();
    		List<Map<String, String>> roles = (List<Map<String, String>>)claims.get(SecurityConstant.ROLES);
    		List<GrantedAuthority> authorithies = new ArrayList<>();
    		roles.stream().forEach(r -> authorithies.add(new SimpleGrantedAuthority(r.get("authority"))));
    		UsernamePasswordAuthenticationToken authenticatioonToken = new UsernamePasswordAuthenticationToken(userName, null, authorithies);
    		SecurityContextHolder.getContext().setAuthentication(authenticatioonToken);
    		continu(request, response, filterChain);
    	}
    }
    Pour avoir la main sur l'authentification, je définis mon service user (sans BDD pour l'instant):
    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
    package com.alten.assistantnc.service;
     
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Optional;
     
    import javax.annotation.PostConstruct;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.stereotype.Service;
     
    @Service
    public class UserService implements UserDetailsService{
     
    	private final List<UserDetails> users;
     
    	@Autowired
    	private PasswordEncoder passwordEncoder;
     
    	public UserService() {
    		users = new ArrayList<>();
    	}
     
    	@PostConstruct
    	private void remplirDadabase() {
    		users.add(new User("admin",passwordEncoder.encode("admin"), Arrays.asList(getAdmin(),getUser())));
    		users.add(new User("user1",passwordEncoder.encode("user1"), Arrays.asList(getUser())));
    		users.add(new User("user2",passwordEncoder.encode("user2"), Arrays.asList(getUser())));
    	}
     
    	@Override
    	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    		Optional<UserDetails> o = users.stream().filter(u -> u.getUsername().equals(username)).findFirst();
    		if(o.isEmpty()) {
    			throw new UsernameNotFoundException(username + " was not found");
    		}
    		return o.get();
    	}
     
    	private GrantedAuthority getAdmin() {
    		return () -> "ROLE_ADMIN";
    	}
     
    	private GrantedAuthority getUser() {
    		return () -> "ROLE_USER";
    	}
    }
    Je redéfinis mon AuthenticationProvider pour qu'il utilise mon service (qui à terme tapera dans une vraie base de données):
    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
    package com.alten.assistantnc.security;
     
    import org.springframework.security.authentication.AuthenticationProvider;
    import org.springframework.security.authentication.BadCredentialsException;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
     
    public class AuthenticationProviderImpl implements AuthenticationProvider{
     
    	private final UserDetailsService userDetailsService;
     
    	public AuthenticationProviderImpl(UserDetailsService userDetailsService) {
    		this.userDetailsService = userDetailsService;
    	}
     
    	@Override
    	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    		UserDetails user = userDetailsService.loadUserByUsername(authentication.getName());
    		if(user == null) {
    			throw new UsernameNotFoundException("User "+authentication.getName()+" was not found");
    		}
    		if(user.getPassword().equals(authentication.getCredentials())) {
    			throw new BadCredentialsException("The password was wrong for user "+authentication.getName());
    		}		
    		return new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword(),user.getAuthorities());
    	}
     
    	@Override
    	public boolean supports(Class<?> authentication) {
    		return authentication.equals(UsernamePasswordAuthenticationToken.class);
    	}
     
    }
    Côté Rest, j'ai le service de login qui envoie au client (si connexion) le JWT pour mon serveur. Soit:
    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
    package com.alten.assistantnc.rest;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import org.springframework.security.authentication.AuthenticationProvider;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.web.bind.annotation.CrossOrigin;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
     
    import com.alten.assistantnc.dto.LoginDTO;
    import com.alten.assistantnc.security.JWTManagement;
     
    @RestController
    @RequestMapping("/login")
    @CrossOrigin
    public class LoginController {
     
    	@Autowired
    	private AuthenticationProvider authenticationProvide;
     
    	@Autowired
    	private PasswordEncoder passwordEncoder;
     
    	@Autowired
    	private JWTManagement jwtManagement;
     
    	@PostMapping(value = "/log", consumes = MediaType.APPLICATION_JSON_VALUE,produces = MediaType.APPLICATION_JSON_VALUE)
    	public ResponseEntity<String> login(@RequestBody LoginDTO login){
    		Authentication aut = authenticationProvide.authenticate(new UsernamePasswordAuthenticationToken(login.getUser(), passwordEncoder.encode(login.getPassword())));
    		return ResponseEntity.ok(jwtManagement.getJwt(aut));
    	}
    }
    Et des services REST bidons:
    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
    package com.alten.assistantnc.rest;
     
    import org.springframework.security.access.prepost.PreAuthorize;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
     
    @RestController
    @RequestMapping("/HelloWord")
    public class HelloWordController {
     
    	@GetMapping(path = "/basic")
    	public String helloWord() {
    		return "bonjour";
    	}
     
    	@PreAuthorize("hasRole('USER')")
    	@GetMapping(path = "/user")
    	public String helloWordUser() {
    		return "bonjour utilisateur";
    	}
     
    	@PreAuthorize("hasRole('ADMIN')")
    	@GetMapping(path = "/admin")
    	public String helloWordAdmin() {
    		return "bonjour admin";
    	}
    }
    La DTO pour se logger:
    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
    package com.alten.assistantnc.dto;
     
    public class LoginDTO {
     
    	private String user;
     
    	private String password;
     
    	public String getUser() {
    		return user;
    	}
     
    	public void setUser(String user) {
    		this.user = user;
    	}
     
    	public String getPassword() {
    		return password;
    	}
     
    	public void setPassword(String password) {
    		this.password = password;
    	}
    }
    J'ai séparé les configurations.
    Celle des services:
    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
    package com.alten.assistantnc.configuration.service;
     
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
     
    @ComponentScan("com.alten.assistantnc.service")
    public class ConfigurationAssistantNCConfigurationService {
     
     
    	@Bean
    	public PasswordEncoder passwordEncoder() {
    		return new BCryptPasswordEncoder();
    	}
    }
    Et celle du REST, contenant Spring security:
    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
    package com.alten.assistantnc.configuration.rest;
     
    import java.util.Arrays;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Import;
    import org.springframework.security.authentication.AuthenticationProvider;
    import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.web.SecurityFilterChain;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.CorsConfigurationSource;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
     
    import com.alten.assistantnc.configuration.service.ConfigurationAssistantNCConfigurationService;
    import com.alten.assistantnc.security.AuthenticationProviderImpl;
    import com.alten.assistantnc.security.JWTAuthorizationFilter;
    import com.alten.assistantnc.security.JWTManagement;
    import com.alten.assistantnc.security.JWTManagementImpl;
    import com.alten.assistantnc.security.SecurityConstant;
     
    @ComponentScan("com.alten.assistantnc.rest")
    @Import(ConfigurationAssistantNCConfigurationService.class)
    @EnableGlobalMethodSecurity(
    		  prePostEnabled = true, 
    		  securedEnabled = true, 
    		  jsr250Enabled = true)
    public class ConfigurationAssistantNCRest implements WebMvcConfigurer{
     
    	@Value("${client}")
    	private String client;
     
    	@Autowired
    	private UserDetailsService userService;
     
    	@Bean
    	public AuthenticationProvider authenticationProvider() {
    		return new AuthenticationProviderImpl(userService);
    	}
     
    	@Bean
    	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    			return http
    				.authenticationProvider(authenticationProvider())
    				.cors().configurationSource(corsConfigurationSource())
    				.and()
    				.authorizeHttpRequests()
    					.antMatchers("/login/log").permitAll()
    					.antMatchers("/login/logout").permitAll()
    				.and()
    				.authorizeHttpRequests().anyRequest().authenticated()
    				.and()
    				.csrf().disable()	
    				.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    				.and()
    				.addFilterBefore(new JWTAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class)
    				.build();
    	}
     
    	@Bean
    	public CorsConfigurationSource corsConfigurationSource() {
    		CorsConfiguration configuration = new CorsConfiguration();
    		configuration.setAllowedMethods(Arrays.asList("GET","POST"));
    		configuration.applyPermitDefaultValues();
    		configuration.addAllowedOrigin(client);
    		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    		source.registerCorsConfiguration("/**", configuration);
     
    		return source;
    	}	
     
    	@Bean
    	public JWTManagement jwtManagement() {
    		return new JWTManagementImpl();
    	}
    }
    Notez qu'en plus, on a une configuration CORS, qui n'accepte que le client spécifié. Le client est passé en paramètre.

    Serveur

    Le Javascript (bah, quel horreur!) de test est:
    Code JavaScript : 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
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    //var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
     
    var jwt = null;
     
    function login(){
     
     
    	var user = document.getElementById('idUser').value;
     
    	var password = document.getElementById('idPassword').value;
     
    	var reponse = null;
     
    	var url = 'http://localhost:8080/login/log';
     
    	var data = '{"user":"'+user+'","password":"'+password+'"}';	
     
    	var xhttp = new XMLHttpRequest();
     
    	xhttp.onreadystatechange = function() {
    		console.log(xhttp.responseText);
    		if (xhttp.readyState == 4 && xhttp.status == 200) {
    			reponse =   xhttp.responseText;
    			jwt = reponse;
    		}
    	  };
     
    	xhttp.open("POST", url,true);
     
    	xhttp.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
    	xhttp.setRequestHeader('Authorization', null);
     
    	xhttp.send(data);
     
    	console.log(reponse);
    }
     
    function testBasic(){
     
    	var xhttp = null;
    	var url = 'http://localhost:8080/HelloWord/basic';
     
    	var reponse = null;
     
    	xhttp = new XMLHttpRequest();
     
    	xhttp.onreadystatechange = function() {
    		if (xhttp.readyState == 4 && xhttp.status == 200) {
    		  reponse =   xhttp.responseText;
    		}
    	  };
     
    	xhttp.open("GET", url, true);
     
    	xhttp.setRequestHeader('Authorization', jwt);
    	xhttp.send();
     
     
    	console.log(reponse);
    }
     
    function testAdmin(){
     
    	var xhttp = null;
    	var url = 'http://localhost:8080/HelloWord/admin';
     
    	var reponse = null;
     
    	xhttp = new XMLHttpRequest();
     
    	xhttp.onreadystatechange = function() {
    		if (xhttp.readyState == 4 && xhttp.status == 200) {
    		  reponse =   xhttp.responseText;
    		}
    	  };
    	xhttp.open("GET", url, true);
    	xhttp.setRequestHeader('Authorization', jwt);
    	xhttp.send();
     
     
    	console.log(reponse);
    }
     
    function testUser(){
     
    	var xhttp = null;
    	var url = 'http://localhost:8080/HelloWord/user';
     
    	var reponse = null;
     
    	xhttp = new XMLHttpRequest();
     
    	xhttp.onreadystatechange = function() {
    		if (xhttp.readyState == 4 && xhttp.status == 200) {
    		  reponse =   xhttp.responseText;
    		}
    	  };
     
    	xhttp.open("GET", url, true);
    	xhttp.setRequestHeader('Authorization', jwt);
    	xhttp.send();
     
     
    	console.log(reponse);
    }
     
    function logout(){
    	jwt = null;
    }

    Notez qu'à chaque fois, on met l'entête "Authorization". Au login, on récupère le jwt que l'on ajoute aux requêtes GET (de HelloWord). logout devient donc simple.

    On a la page HTML de test:
    Code HTML : 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
    <!DOCTYPE html>
    <html>
     <head>
      <meta charset="utf-8">
     
      <title>
       Login
      </title>
      <script type="text/javascript" src="/test.js"></script>
     </head>
     <body>
      <table>
    	<tr>
    		<td>Login</td>
    		<td><input type="text" id="idUser"/></td>
    	</tr>
    	<tr>
    		<td>Password</td>
    		<td><input type="password" id="idPassword"></td>
    	</tr>
      <table>
      <input type="submit" value="login" id="idBoutonLogin" onclick="login()"/>
      <br/>
      <input type="submit" value="basic" id="idBoutonBasic" onclick="testBasic()"/>
      <br/>
      <input type="submit" value="admin" id="idBoutonAdmin" onclick="testAdmin()"/>
      <br/>
      <input type="submit" value="user" id="idBoutonUser" onclick="testUser()"/>
      <br/>
      <input type="submit" value="logout" id="idBoutonLogout" onclick="logout()"/>
     </body>
    </html>

    Et le tout pour lancer par node.js:
    Code JavaScript : 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
    const http = require('http');
    const fs = require('fs');
     
    const server = http.createServer((req,res)=>{
    	var url = req.url;
    	if(url == '/test.js'){
    		fs.readFile('./test.js',null,(error,data)=>{
            if(error){
                res.end('fill have error')
            }else{
                res.end(data)
            }
        })
    	}
     
        fs.readFile('./login.html',null,(error,data)=>{
            if(error){
                res.end('fill have error')
            }else{
                res.end(data)
            }
        })
    });
    server.listen(3000,'127.0.0.1',()=>{
        console.log('Server running')
    })

    Test écran
    On est KO avec un user n'existant pas:
    Nom : 2022_09_20_soluce_1b.jpg
Affichages : 806
Taille : 184,3 Ko

    On se connecte avec user1:
    Nom : 2022_09_20_soluce_2.jpg
Affichages : 818
Taille : 191,8 Ko

    Notez que l'on récupère un JWT.

    Qui peut aller sur le HelloWord User (il a le droit):
    Nom : 2022_09_20_soluce_3.jpg
Affichages : 797
Taille : 199,9 Ko

    Mais pas sur admin (il n'a pas le droit):
    Nom : 2022_09_20_soluce_4.jpg
Affichages : 793
Taille : 175,0 Ko

    Et si on se déconnexte et que l'on va sur user...
    Nom : 2022_09_20_soluce_5.jpg
Affichages : 806
Taille : 232,3 Ko

    On a une erreur 403 (Accès à la ressource interdite).
    Images attachées Images attachées  

Discussions similaires

  1. API Rest et l'authentification Spring Security
    Par kolbek dans le forum Développement Web en Java
    Réponses: 1
    Dernier message: 21/03/2019, 13h24
  2. [Security] Spring security login.htm?
    Par Logic_613 dans le forum Spring
    Réponses: 1
    Dernier message: 28/11/2012, 17h05
  3. [JSF] spring security login.xhtml appeler j_spring_security_check
    Par Bubu017 dans le forum Spring Web
    Réponses: 3
    Dernier message: 08/04/2011, 14h38
  4. [Security] Spring security - Login after logout
    Par duff01 dans le forum Spring
    Réponses: 1
    Dernier message: 30/07/2010, 13h29
  5. Spring Security Login Role
    Par g25452 dans le forum Spring
    Réponses: 0
    Dernier message: 12/05/2009, 14h38

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo