Connexion de sécurité de printemps avec Java et Angular2

j'ai quelques difficultés à intégrer la sécurité du ressort (à savoir la partie login) dans mon application web. Mon BackEnd est en cours d'exécution sur localhost: 8080 et le FrontEnd (Ionique 2 avec AngularJS 2) est en cours d'exécution sur localhost: 8100. J'ai réussi à me connecter (du moins je le pense) mais le reste des requêtes est indisponible et j'obtiens l'erreur suivante dans la console de développement Chrome:

XMLHttpRequest cannot load http://localhost:8080/company/getAll. Response for preflight is invalid (redirect)

lors des tests avec Postman il semble que cela fonctionne, je me connecte et puis je suis en mesure d'effectuer des requêtes POST sur http://localhost:8080/company/getAll et tout fonctionne comme prévu.

probablement je manque quelque chose (comme un jeton) mais je ne peux pas comprendre quoi. Je suis nouveau à la fois ionique et la sécurité de printemps donc s'il vous plaît pardonnez-moi si c'est quelque chose de trivial. J'ai essayé de googler différents tutoriels mais j'ai été incapable de trouver quoi que ce soit (la plupart d'entre eux utilisaient JSP).

Comment faire pour que ça marche? Comment doit-mes demandes de le fronton ressemble à?

voici ma méthode de connexion à partir du contrôleur D'arrière-plan:

@RequestMapping(value = "/login", method = RequestMethod.GET)
    @CrossOrigin(origins = "*")
    public ResponseEntity<GeneralResponse> login(Model model, String error, String logout) {
        System.out.println("LOGIN"+model.toString());

        if (error != null) {
            System.out.println("1.IF");
            model.addAttribute("error", "Your username and password is invalid.");
        }

        if (logout != null) {
            System.out.println("2.IF");
            model.addAttribute("message", "You have been logged out successfully.");
        }

        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new GeneralResponse(true, "FAILURE: Error"));
    }

C'est mon WebSecurityConfig:

@Configuration
@EnableWebSecurity
@ComponentScan("com.SAB.service")
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .antMatchers("/resources/**", "/registration").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .loginPage("/user/login")
            .permitAll()
            .and()
            .logout()
            .permitAll();
        http
            .csrf().disable();
        http.formLogin().defaultSuccessUrl("http://localhost:8100/", true);
        //.and().exceptionHandling().accessDeniedPage("/403")
        //.and().sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true);
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }
}

Et enfin mon FrontEnd code responsable de la session:

  login(){
    var body = 'username='+this.loginForm.controls['username'].value+'&password='+this.loginForm.controls['password'].value;
    var headers = new Headers();
    headers.append('Content-Type', 'application/x-www-form-urlencoded');
    //headers.append("Access-Control-Allow-Origin", "*");

    this.http
      .post('http://localhost:8080/user/login',
        body, {
          headers: headers
        })
      .subscribe(data => {
        console.log('ok');
        console.log(data)
      }, error => {
        console.log(JSON.stringify(error.json()));
      });
  }

si vous avez besoin de plus amples détails, n'hésitez pas à me le faire savoir et je vous les fournirai.

Merci d'avance!

Je n'obtiens pas L'erreur Access-COntroll-Origin mais j'ai essayé de la changer en fonction des commentaires quoi qu'il en soit et maintenant je reçois une "requête CORS non valide"

en-tête de requête et de réponse pour la connexion depuis postman: enter image description here

EDIT: Voici les logs de sécurité du printemps. Cependant Spring n'enregistre qu'une requête D'OPTIONS et aucun POST même si le FrontEnd appelle POST.

************************************************************

Request received for OPTIONS '/login':

org.apache.catalina.connector.RequestFacade@18859793

servletPath:/login
pathInfo:null
headers: 
host: localhost:8080
connection: keep-alive
access-control-request-method: POST
origin: http://localhost:8100
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.96 Safari/537.36
access-control-request-headers: access-control-allow-origin
accept: */*
referer: http://localhost:8100/
accept-encoding: gzip, deflate, sdch, br
accept-language: en-US,en;q=0.8


Security filter chain: [
  WebAsyncManagerIntegrationFilter
  SecurityContextPersistenceFilter
  HeaderWriterFilter
  CorsFilter
  LogoutFilter
  UsernamePasswordAuthenticationFilter
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  FilterSecurityInterceptor
]


************************************************************
2017-05-12 19:18:28.527 DEBUG 16500 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /login at position 1 of 12 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2017-05-12 19:18:28.527 DEBUG 16500 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /login at position 2 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2017-05-12 19:18:28.527 DEBUG 16500 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2017-05-12 19:18:28.527 DEBUG 16500 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
2017-05-12 19:18:28.529 DEBUG 16500 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /login at position 3 of 12 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2017-05-12 19:18:28.529 DEBUG 16500 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /login at position 4 of 12 in additional filter chain; firing Filter: 'CorsFilter'
2017-05-12 19:18:28.541 DEBUG 16500 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@5baaaa2b
2017-05-12 19:18:28.541 DEBUG 16500 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2017-05-12 19:18:28.541 DEBUG 16500 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed

mise à jour: Ainsi, après avoir ajouté le CORSFilter suggéré dans la réponse acceptée, j'ai également dû modifier mes requêtes dans la frontend en commande pour le cookie avec le JSESSIONID à envoyer sur chaque demande. Bassically j'ai dû ajouter ce qui suit à toutes mes requêtes: let options = new RequestOptions({ headers: headers, withCredentials: true });

20
demandé sur Irina Avram 2017-05-09 19:22:54

5 réponses

cela semble être un problème lié à la configuration de CORS. Le navigateur ne fait L'OPTION Demande de pré-vol avant votre appel obtenir réelle. Je n'ai pas beaucoup de connaissances dans ce domaine, mais vous pourriez essayer d'ajouter le filtre ci-dessous pour le printemps.

public class CORSFilter extends GenericFilterBean {

    public void doFilter(ServletRequest request, ServletResponse res, FilterChain chain)
        throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse response = (HttpServletResponse) res;
        response.addHeader("Access-Control-Allow-Origin", "*");
        response.addHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
        response.addHeader("Access-Control-Max-Age", "3600");
        response.addHeader("Access-Control-Allow-Headers", "X-Requested-With, X-Auth-Token");
        response.addHeader("Access-Control-Allow-Credentials", "true");

        if(req.getMethod().equalsIgnoreCase("options")){
             return;
        }
        chain.doFilter(request, response);
    }
}

et ajouter la ligne ci-dessous dans la méthode configure de votre Config WebSecurityConfig.

.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class);

9
répondu Vipul Goyal 2017-05-15 15:46:21

La solution est très simple. Le nom d'utilisateur et le mot de passe sur le POST doivent être présentés comme FormData et non pas partie du corps. J'ai eu le même problème avec mon application et après une longue lutte, j'ai créé un formulaire HTML et j'ai fait un POST, puis ça a fonctionné comme un charme.

Voir ci-dessous exemple vivant. Vous devriez être en mesure de vous inscrire avec n'importe quel identifiant et essayez de vous connecter et débuts dans votre navigateur. Il utilise le démarrage à ressort pour Côté Serveur et angulaire 4 sur L'interface utilisateur.

Démonstration En Direct: http://shop-venkatvp.rhcloud.com/#/

Login HTML: https://github.com/reflexdemon/shop/blob/master/src/app/login/login.component.html#L7-L31

Configuration De Sécurité Du Ressort: https://github.com/reflexdemon/shop/blob/master/src/main/java/org/shop/WebSecurityConfig.java#L20-L34

Connexion du contrôleur: https://github.com/reflexdemon/shop/blob/master/src/main/java/org/shop/page/controller/LoginController.java

J'espère que c'est utile.

2
répondu reflexdemon 2017-05-14 01:47:51

je suis en train d'écrire un article sur L'intégration des bottes angulaires 4 et spring. Je n'ai pas encore fini l'article, mais le code source est terminé. Je ne suis pas sûr pourquoi vous essayez de servir votre application à partir de deux hôtes différents, cela complique inutilement votre application en injectant une condition cors et en ayant à gérer deux contextes de sécurité. Si ce n'est pas une exigence absolue, je servirais tout depuis le même serveur.

jetez un oeil à cette github projet d'intégration complète de la botte angulaire et de la botte à ressort: https://github.com/tschiman/tutorials/tree/master/spring-cloud/spring-cloud-bootstrap/gateway. Tu peux ignorer les trucs zuul là-dedans si tu n'as pas besoin de ça. Cela n'a aucune incidence sur le soutien de l'assurance-chômage angulaire.

si vous voulez activer csrf, et vous devriez, regardez cette propagation: be4b206478c136d24ea1bf8b6f93da0f3d86a6c3

si la lecture n'a pas de sens, voici les étapes à suivre: suivre:

  1. Déplacement angulaire de votre code source dans votre ressort de démarrage app
  2. Modifier vous angulaires-cli.fichier json pour avoir un répertoire de sortie dans vos ressources statiques ou des ressources publiques dossier: "outDir":"../../resources/static/home" définit le répertoire de sortie lorsque vous lancez ng build à partir de la ligne de commande en utilisant le cli angulaire.
  3. Modifiez l'url de succès par défaut de votre login pour rediriger vers /home / index.html .formLogin().defaultSuccessUrl("/home/index.html", true) cela va forcer une connexion réussie à toujours rediriger vers votre angulaire de l'app.
  4. si vous voulez automatiser la construction de votre application angulaire dans maven puis ajouter ce plugin à votre fichier POM:

        <plugin>
            <artifactId>maven-antrun-plugin</artifactId>
            <executions>
                <execution>
                    <phase>generate-resources</phase>
                    <configuration>
                        <tasks>
                            <exec executable="cmd" osfamily="windows" dir="${project.basedir}/src/main/angular/ui">
                                <arg value="/c"/>
                                <arg value="ng"/>
                                <arg value="build"/>
                            </exec>
                            <exec executable="/bin/sh" osfamily="mac" dir="${project.basedir}/src/main/angular/ui">
                                <arg value="-c"/>
                                <arg value="ng build"/>
                            </exec>
                        </tasks>
                    </configuration>
                    <goals>
                        <goal>run</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    

si vous avez d'autres questions, je me ferai un plaisir d'y répondre si cela ne vous suffit pas.

2
répondu Ceekay 2017-05-16 20:03:09

avez-vous essayé d'autoriser explicitement les requêtes D'OPTIONS? Comme ceci:

protected void configure(HttpSecurity http) throws Exception {
   ...
   http.authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll();
   ...
}
1
répondu Nikolay Shevchenko 2017-05-16 19:41:10

tout d'abord - il s'agit certainement d'un problème de configuration de CORS parce que vous exécutez le backend et le frontend sur différentes adresses.

vous pouvez twick backend pour prendre en charge les requêtes CORS mais c'est une solution sale, surtout depuis que vous avez probablement Je ne veux pas de CORS en production la version de votre application. Une approche beaucoup plus propre serait de configurer soit frontend soit backend à server comme un proxy à l'autre.

je préfère configurer frontend pour proxy demandes de rétrocession. De cette façon, il n'aura aucun effet sur la version de production de l'application.

partie Suivante suppose que vous utilisez angulaires-cli (comme vous devriez avec angular2 app).

ils ont une section dans leur docs spécifiquement pour la configuration proxy: https://www.npmjs.com/package/angular-cli#proxy-to-backend.

j'ai tous mes services de repos à l'intérieur /api contexte, so my proxy.conf.json ressemble ceci:

{
  "/api": {
    "target": "http://localhost:8083",
    "secure": false
  },
  "/login": {
    "target": "http://localhost:8083",
    "secure": false
  },
  "/logout": {
    "target": "http://localhost:8083",
    "secure": false
  }
}

http://localhost:8083 est mon arrière-plan. Vous pouvez alors lancer votre application avec:

ng serve --proxy-config proxy.conf.json
Lite-server.

1
répondu chimmi 2017-05-19 08:34:58