2014-09-05 15 views
15

hallo Ich versuche, ein einfaches Beispiel zu folgen über eine einfache Login-Formular Seite zu tun, dass ich in dieser Seite ist http://docs.spring.io/autorepo/docs/spring-security/4.0.x/guides/form.htmlEinfaches Beispiel von Spring Security mit Thymeleaf

das Problem festgestellt, dass du Sein diese Fehler jedes Mal bekommen, dass ich versuche, um sich einzuloggen ich diese Fehlermeldung erhalten: Expected CSRF token not found. Has your session expired?

wenn ich diesen Fehler i drücken Sie die zurück-Taste in meinem Explorer und versuchen, ein zweites Mal anmelden und wenn ich das tue, dass ich diesen Fehler: HTTP 403 - Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'

In der Tutorial-Seite ist diese Nachricht: We use Thymeleaf to automatically add the CSRF token to our form. If we were not using Thymleaf or Spring MVCs taglib we could also manually add the CSRF token using <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>

„so, weil ich auch bin mit thymeleaf ich habe nicht den Tag auf meine Seite hinzufügen“

ich eine andere Lösung gefunden und es funktioniert, und diese Lösung ist, diese zu meiner Sicherheitskonfigurations Klasse .csrf().disable() diese Lösung funktioniert, aber ich nehme an was das tun ist csrf Schutz in meiner Seite zu deaktivieren, und ich möchte diese Art von Schutz nicht deaktivieren.

das ist meine Sicherheits-config-Klasse:

@Configuration 
@EnableWebSecurity 
public class ConfigSecurity extends WebSecurityConfigurerAdapter { 

    @Autowired 
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 
     auth 
      .inMemoryAuthentication() 
       .withUser("user").password("password").roles("USER"); 
    } 


    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http 

     //.csrf().disable() is commented because i dont want disable this kind of protection 
     .authorizeRequests() 
       .anyRequest().authenticated() 
       .and() 
      .formLogin() 
       .loginPage("/login") 
       .permitAll() 
       .and() 
      .logout()          
       .permitAll(); 
    } 
} 

meine Sicherheit initialzer:

public class InitSecurity extends AbstractSecurityWebApplicationInitializer { 

    public InicializarSecurity() { 
     super(ConfigSecurity .class); 

    } 
} 

meine app-config-Klasse, wo ich meine thymeleaf Konfiguration

@EnableWebMvc 
@ComponentScan(basePackages = {"com.myApp.R10"}) 
@Configuration 
public class ConfigApp extends WebMvcConfigurerAdapter{ 

    @Override 
    public void addResourceHandlers(ResourceHandlerRegistry registry) { 
     registry.addResourceHandler("/css/**").addResourceLocations("/css/**"); 
     registry.addResourceHandler("/img/**").addResourceLocations("/img/**"); 
     registry.addResourceHandler("/js/**").addResourceLocations("/js/**"); 
     registry.addResourceHandler("/sound/**").addResourceLocations("/sound/**"); 
     registry.addResourceHandler("/fonts/**").addResourceLocations("/fonts/**"); 
    } 

    @Override 
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { 
     configurer.enable(); 
    } 

    @Bean 
     public MessageSource messageSource() { 
     ReloadableResourceBundleMessageSource messageSource = new  ReloadableResourceBundleMessageSource(); 
     messageSource.setBasenames("classpath:messages/messages"); 
     messageSource.setUseCodeAsDefaultMessage(true); 
     messageSource.setDefaultEncoding("UTF-8"); 
     messageSource.setCacheSeconds(0);// # -1 : never reload, 0 always reload 
     return messageSource; 
    } 
// THYMELEAF 

     @Bean 
     public ServletContextTemplateResolver templateResolver() { 
      ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(); 
      resolver.setPrefix("/WEB-INF/views/pagLogin/"); 
      resolver.setSuffix(".html"); 
      resolver.setTemplateMode("HTML5"); 
      resolver.setOrder(0); 
      resolver.setCacheable(false); 
      return resolver; 
     } 

     @Bean 
     public SpringTemplateEngine templateEngine() { 
      SpringTemplateEngine engine = new SpringTemplateEngine(); 
      engine.setTemplateResolver(templateResolver()); 
      engine.setMessageSource(messageSource()); 



      return engine; 
     } 

     @Bean 
     public ThymeleafViewResolver thymeleafViewResolver() { 
      ThymeleafViewResolver resolver = new ThymeleafViewResolver(); 

      resolver.setTemplateEngine(templateEngine()); 
      resolver.setOrder(1); 

      resolver.setCache(false); 
      return resolver; 
     } 

     @Bean 
     public SpringResourceTemplateResolver thymeleafSpringResource() { 
      SpringResourceTemplateResolver vista = new SpringResourceTemplateResolver(); 
      vista.setTemplateMode("HTML5"); 
      return vista; 
     } 
} 

meine app -config Initialisierung

public class InicializarApp extends AbstractAnnotationConfigDispatcherServletInitializer { 

    @Override 
    protected Class<?>[] getRootConfigClasses() { 
     return null; 
    } 
@Override 
    protected Class<?>[] getServletConfigClasses() { 
     return new Class[] { ConfigApp .class }; 
    } 

    @Override 
    protected String[] getServletMappings() { 
     return new String[]{"/"}; 
    } 

    @Override 
    protected Filter[] getServletFilters() { 
     return new Filter[] { new HiddenHttpMethodFilter() }; 
    } 
} 

Anmeldungs ​​Controller-Klasse

@Controller 
public class ControllerLogin { 



    @RequestMapping(value = "/login", method = RequestMethod.GET) 
    public String pageLogin(Model model) { 



     return "login"; 
    } 

my home Controller-Klasse

@Controller 
public class HomeController { 

     @RequestMapping(value = "/", method = RequestMethod.GET) 
     public String home(Model model) { 


     return "home"; 
     } 


} 

mein login.html

<html xmlns:th="http://www.thymeleaf.org" xmlns:tiles="http://www.thymeleaf.org"> 
    <head> 
    <title tiles:fragment="title">Messages : Create</title> 
    </head> 
    <body> 
    <div tiles:fragment="content"> 
     <form name="f" th:action="@{/login}" method="post">    
      <fieldset> 
       <legend>Please Login</legend> 
       <div th:if="${param.error}" class="alert alert-error">  
        Invalid username and password. 
       </div> 
       <div th:if="${param.logout}" class="alert alert-success"> 
        You have been logged out. 
       </div> 

       <label for="username">Username</label> 
        <input type="text" id="username" name="username"/>   
       <label for="password">Password</label> 
        <input type="password" id="password" name="password"/>  

       <div class="form-actions"> 
        <button type="submit" class="btn">Log in</button> 
       </div> 

       <!-- THIS IS COMMENTED it dont work beacuse i am already using thymeleaf <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> --> 


      </fieldset> 
     </form> 
    </div> 


    </body> 
</html> 

meine home.html Seite zeigt nur, nachdem ich dich anmelden und Die einzige Möglichkeit, wie ich mich anmelden kann, ist eine .csrf(). disable() in meiner Sicherheits-Config-Klasse, aber ich möchte nicht deaktivieren dieser Schutz, wenn ich das nicht in meine Sicherheitskonfiguration Klasse bringe, bekomme ich die Fehler, die ich am Anfang dieser Frage erwähne.

Antwort

37

Vom Spring Security documentation

CSRF protection is enabled by default with Java configuration. If you would like to disable CSRF, the corresponding Java configuration can be seen below. Refer to the Javadoc of csrf() for additional customizations in how CSRF protection is configured.

Und wenn CSRF Schutz

aktiviert ist

The last step is to ensure that you include the CSRF token in all PATCH, POST, PUT, and DELETE methods.

In Ihrem Fall:

  • Sie haben CSRF-Schutz standardmäßig aktiviert (weil Sie verwenden Java-Konfiguration),
  • Sie sind su Wenn Sie das Anmeldeformular mit einem HTTP-POST abbestellen und
  • enthalten, wird das CSRF-Token nicht in das Anmeldeformular eingeschlossen.Aus diesem Grund wird Ihre Anmeldeanforderung bei der Übermittlung abgelehnt, da der CSRF-Schutzfilter das CSRF-Token in der eingehenden Anfrage nicht finden kann.

Sie haben bereits die möglichen Lösungen bestimmt:

  1. Disable CSRF-Schutz als http.csrf().disable(); oder
  2. Fügen Sie das CSRF-Token als versteckten Parameter in das Anmeldeformular ein.

    <form name="f" th:action="@{/login}" method="post">    
        <fieldset> 
    
        <input type="hidden" 
          th:name="${_csrf.parameterName}" 
          th:value="${_csrf.token}" /> 
    
        ... 
        </fieldset> 
    </form> 
    

    Beachten Sie, dass th:action verwenden müssen und HTML nicht action:

Da Sie Thymeleaf verwenden, werden Sie so etwas wie die folgenden in Ihrem HTML-Template für die Login-Seite zu tun haben als der Thymeleaf CSRF-Prozessor wird nur mit dem ehemaligen Kick-in.

Sie können die Formularübermittlungsmethode auf GET ändern, um das Problem zu beheben. Dies wird jedoch nicht empfohlen, da die Benutzer vertrauliche Informationen im Formular übermitteln.

Ich erstelle normalerweise ein Thymeleaf-Fragment, das dann auf allen Seiten mit Formularen verwendet wird, um das Markup für die Formulare mit dem CSRF-Token zu generieren. Dies reduziert den Standardcode in der App.


Mit @EnableWebMvcSecurity statt @EnableWebSecurity automatische Injektion von CSRF-Token mit Thymeleaf Tags zu ermöglichen. Verwenden Sie auch anstelle von <form action> mit Spring 3.2+ und Thymeleaf 2.1+, um Thymeleaf dazu zu zwingen, das CSRF-Token automatisch als verstecktes Feld einzubeziehen (Quelle Spring JIRA).

+0

danke Mann, es hat funktioniert, ich weiß nicht, warum das Tutorial sagt: "" Wir verwenden Thymeleaf, um den CSRF-Token automatisch zu unserem Formular hinzuzufügen. Wenn wir Thymleaf oder Spring MVCs taglib nicht verwenden, können wir das CSRF-Token auch manuell hinzufügen, indem wir "" aber du versuchst was du sagst und es hat funktioniert – stackUser2000

+0

Welche Spring, Spring Security und Thymeleaf Versionen verwendest du für dein Projekt? – manish

+0

Frühling Version: 4.0.6.RELEASE ----- thymeleaf: 2.1.3.RELEASE ----- thymeleaf-Spring4: 2.1.3.RELEASE ----- Frühling secuirty web: 3.2.5.RELEASE ----- feder-security-config: 3.2.4.RELEASE – stackUser2000

1

Vielleicht hilft dieser kleine Informationsfrieden jedem: Es ist auch obligatorisch, das Formular mit th:action zu versehen. Wenn Sie nur HTML-Code action zuweisen, wird das versteckte CSRF-Eingabefeld nicht automatisch hinzugefügt.

Konnte nicht finden, dass Frieden der Informationen irgendwo dokumentiert und verbrachte 2h Forschung darüber. Ich hatte das Formular mit action="#" zugeordnet und den entsprechenden Wert mit Java Script gesetzt. Das CSRF-Token-Eingabefeld wurde nicht automatisch hinzugefügt, bis das Formular th:action="@{#}" hinzugefügt wurde. Funktioniert jetzt als Charme.

2

Sie müssen Thymleaf Spring Security Dialekt hinzufügen.

1.) Fügen Sie das Spring Security Dialekt-Modul zu Ihrem Klassenpfad hinzu.

Maven Beispiel:

<dependency> 
    <groupId>org.thymeleaf.extras</groupId> 
    <artifactId>thymeleaf-extras-springsecurity3</artifactId> 
    <version>2.1.2.RELEASE</version> 
</dependency> 

2.)

import org.thymeleaf.extras.springsecurity3.dialect.SpringSecurityDialect; 
templateEngine.addDialect(new SpringSecurityDialect()); //add this line in your config 

Quelle der SpringSecurityDialect Objekt in den SpringTemplateEngine hinzufügen: Spring in Action 4th Edition

12

Hier ist die Lösung, die es genau so, wie OP implementiert wollte:

  1. mit @EnableWebMvcSecurity ersetzen @EnableWebSecurity (das ist, was OP fehlt)
  2. Verwenden Sie th:action auf <form> Tag

Wenn Sie @EnableWebMvcSecurity Spring Security verwenden registriert die CsrfRequestDataValueProcessor, und wenn Sie th:action thymeleaf verwenden verwendet es getExtraHiddenFields Methode ist, na ja, extra versteckte Felder zum Formular hinzuzufügen. Und das CSRF ist das zusätzliche versteckte Feld.

Since Spring Security 4.0, @EnableWebMvcSecurity wurde eingestellt und nur @EnableWebSecurity ist erforderlich. The _csrf protection continues to apply automatically.

+0

Das ist Gegenteil zu dem, was ich in der aktuellen lesen Spring-Dokumente: https: //docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html, die besagt: Wenn Sie Spring MVC -Tag oder Thymeleaf 2.1 + verwenden und @EnableWebSecurity verwenden Das CsrfToken wird automatisch für Sie einbezogen (mithilfe des CsrfRequestDataValueProcessor). – user216661

+0

Danke Mann! das funktioniert wie ein Zauber – fuzzy28

+0

Und es sollte eine akzeptierte Antwort sein, ich habe 2 Stunden verloren, um zu finden, was mit meiner Form nicht stimmt (Mangel an 'th: action') –