tutorial java spring angular spring-security ionic2

tutorial - Inicio de sesión de Spring Security con Java y Angular2



spring security tutorial (5)

Tengo problemas para integrar la seguridad de primavera (es decir, la parte de inicio de sesión) en mi aplicación web. Mi BackEnd se ejecuta en localhost: 8080 y FrontEnd (Ionic 2 con AngularJS 2) se está ejecutando en localhost: 8100 . Pude iniciar sesión (al menos eso creo) pero el resto de las solicitudes dejan de estar disponibles y aparece el siguiente error en la Consola de desarrollador de Chrome:

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

Cuando las pruebas con Postman parecen estar funcionando, inicio sesión y luego puedo realizar solicitudes POST en http: // localhost: 8080 / company / getAll y todo funciona según lo previsto.

Presumiblemente me falta algo (como un token) pero no puedo entender qué. Soy nuevo en Ionic y Spring Security, así que por favor, perdónenme si esto es algo trivial. Intenté buscar varios tutoriales en Google, pero no pude encontrar nada (la mayoría usaba JSP).

¿Cómo podría hacer que esto funcione? ¿Cómo deberían ser mis solicitudes de la interfaz?

Aquí está mi método de inicio de sesión desde el controlador BackEnd:

@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")); }

Este es mi 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()); } }

Y finalmente mi código FrontEnd responsable de iniciar sesión:

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 necesita detalles adicionales, por favor avíseme y se los proporcionaré.

¡Gracias por adelantado!

No recibo el error de Acceso-Origen-de-control pero traté de cambiarlo de todos modos de acuerdo con los comentarios y ahora recibo una "Solicitud de CORS inválida"

Encabezado de solicitud y respuesta para iniciar sesión del cartero:

EDITAR: Aquí están los registros de Spring Security. Sin embargo, Spring solo registra una solicitud OPTIONS y no POST aunque el FrontEnd llame a 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

ACTUALIZACIÓN: Entonces, después de agregar el CORSFilter sugerido en la respuesta aceptada, también tuve que modificar mis solicitudes en la interfaz para que la cookie con el JSESSIONID se envíe en cada solicitud. Bassically tuve que agregar lo siguiente a TODAS mis preguntas: let options = new RequestOptions({ headers: headers, withCredentials: true });


¿Intentó permitir explícitamente las solicitudes OPTIONS? Me gusta esto:

protected void configure(HttpSecurity http) throws Exception { ... http.authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll(); ... }


En primer lugar, este es definitivamente un problema de configuración de CORS porque está ejecutando backend y frontend en diferentes direcciones.

Puedes hacer back-end para soportar solicitudes CORS, pero esta es una solución sucia, especialmente porque probablemente no quieras CORS en la versión de producción de tu aplicación. Un enfoque mucho más limpio sería configurar frontend o backend al servidor como un proxy para el otro.

Prefiero configurar frontend a las solicitudes de proxy para back-end. De esta forma no tendrá ningún efecto en la versión de producción de la aplicación.

La siguiente parte asume que estás usando angular-cli (como deberías con la aplicación angular2).

Tienen una sección en sus documentos específicamente para la configuración del proxy: https://www.npmjs.com/package/angular-cli#proxy-to-backend .

Tengo todos mis servicios REST dentro / api context, por lo que mi proxy.conf.json se ve así:

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

Donde http: // localhost: 8083 es mi back-end. Luego puede ejecutar su aplicación con:

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

y use solo su dirección de frontend. No se necesita configuración de CORS.

PD: Si no usa angular-cli, estoy seguro de que puede usar este enfoque con cualquier herramienta que use para mostrarla. Antes de cambiar a angular-cli lo usé también con lite-server .


Esto parece ser un problema relacionado con la configuración de CORS. El navegador realiza la solicitud de verificación previa de OPCIÓN antes de su llamada GET real. No tengo mucho conocimiento en esta área, pero podría intentar agregar el filtro a continuación en la parte posterior del resorte.

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); } }

Y agregue la línea siguiente en el método de configuración de su WebSecurityConfig.

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


Estoy en el medio de escribir un artículo de integración de inicio de Angular 4 y Spring. Todavía no tengo el artículo terminado pero el código fuente está hecho. No estoy seguro de por qué está intentando servir su aplicación desde dos hosts diferentes, esto complica innecesariamente su aplicación al inyectar un requisito de cors y tener que administrar dos contextos de seguridad. Si esto no es un requisito absoluto, serviría todo desde el mismo servidor.

Eche un vistazo a este proyecto github para una integración completa de arranque angular y de resorte: https://github.com/tschiman/tutorials/tree/master/spring-cloud/spring-cloud-bootstrap/gateway . Puedes ignorar las cosas zuul allí si no las necesitas. No tiene impacto en el soporte para la ui angular.

Si desea habilitar csrf, y debería hacerlo, observe este compromiso: be4b206478c136d24ea1bf8b6f93da0f3d86a6c3

Si leer eso no tiene sentido, estos son los pasos que debes seguir:

  1. Mueva su código fuente angular en su aplicación de arranque de primavera
  2. Modifique su archivo angular-cli.json para tener un directorio de salida en sus recursos carpeta pública estática o de recursos: "outDir":"../../resources/static/home" Esto establece el directorio de salida para cuando ejecute ng build desde la línea de comando usando el cli angular.
  3. Modifique la url de éxito predeterminada de su inicio de sesión para redirigirla a .formLogin().defaultSuccessUrl("/home/index.html", true) Esto forzará un inicio de sesión exitoso para redirigir siempre a su aplicación angular.
  4. Si desea automatizar la construcción de su aplicación angular en Maven, agregue este complemento a su archivo 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 tiene preguntas adicionales, me complace responderle más si esto no fuera suficiente para que funcione.


La solución es muy simple. El nombre de usuario y la contraseña en POST deben enviarse como FormData y no como parte del cuerpo. Tuve el mismo problema con mi aplicación y después de una larga lucha, creé un formulario HTML e hice un POST, luego funcionó a las mil maravillas.

Ver a continuación el ejemplo en vivo. Debería poder registrarse con cualquier identificación e intentar iniciar sesión y debutarlo en su navegador. Utiliza Spring boot para el lado del servidor y Angular 4 en la interfaz de usuario.

Demostración en vivo: http://shop-venkatvp.rhcloud.com/#/

HTML de inicio de sesión: https://github.com/reflexdemon/shop/blob/master/src/app/login/login.component.html#L7-L31

Spring Security Configuration: https://github.com/reflexdemon/shop/blob/master/src/main/java/org/shop/WebSecurityConfig.java#L20-L34

Controlador de inicio de sesión: https://github.com/reflexdemon/shop/blob/master/src/main/java/org/shop/page/controller/LoginController.java

Espero que esto sea útil.