it-swarm.com.de

Ein einfaches Beispiel für Spring Security mit Thymeleaf

hallo, ich versuche, einem einfachen Beispiel zu folgen, in dem Sie eine einfache Anmeldeformularseite ausführen, die ich auf dieser Seite gefunden habe. http://docs.spring.io/autorepo/docs/spring-security/4.0.x/guides/form.html 

das Problem ist, dass ich diese Fehlermeldung jedes Mal bekomme, wenn ich versuche, mich anzumelden. Ich erhalte diese Fehlermeldung: Expected CSRF token not found. Has your session expired? 

Wenn ich diese Fehlermeldung bekomme, drücke ich auf den Zurück-Button in meinem Explorer und versuche mich ein zweites Mal anzumelden. Wenn ich das tue, bekomme ich diese Fehlermeldung: 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}"/> 

"Da ich auch Thymeleaf verwende, habe ich diesen Tag nicht zu meiner Seite hinzugefügt."

ich habe eine andere Lösung gefunden, die funktioniert. Diese Lösung fügt dies meiner Sicherheitskonfigurationsklasse hinzu. .csrf().disable() Diese Lösung funktioniert, aber ich nehme an, dass dies den csrf-Schutz auf meiner Seite deaktiviert, und ich möchte diese Art von Schutz nicht deaktivieren. 

das ist meine Sicherheitskonfigurationsklasse:

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

mein sicherheitsinitialisierer:

public class InitSecurity extends AbstractSecurityWebApplicationInitializer {

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

    }
}

meine app-config Klasse, wo ich meine Thymeleaf Konfiguration habe

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

mein App-Konfigurationsinitialisierer

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

meine Login-Controller-Klasse

@Controller
public class ControllerLogin {



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



         return "login";
    }

meine Heimsteuerungsklasse

@Controller
public class HomeController {

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


        return "home";
        }


}

meine 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 wird nur angezeigt, nachdem ich mich angemeldet habe. Die einzige Möglichkeit, mich einzuloggen, wenn ein .csrf (). disable () in meiner Security-Konfigurationsklasse ist, aber ich möchte diesen Schutz nicht deaktivieren, wenn ich das nicht setze In meiner Sicherheitskonfigurationsklasse bekomme ich die Fehler, die ich zu Beginn dieser Frage erwähne.

15
stackUser2000

Aus der Spring Security Dokumentation

Der CSRF-Schutz ist bei der Java-Konfiguration standardmäßig aktiviert. Wenn du möchte CSRF deaktivieren, die entsprechende Java-Konfiguration kann siehe unten Weitere Informationen finden Sie im Javadoc von csrf () Anpassungen bei der Konfiguration des CSRF-Schutzes.

Und wenn der CSRF-Schutz aktiviert ist

Der letzte Schritt besteht darin sicherzustellen, dass Sie das CSRF-Token in alle .__ einschließen. PATCH-, POST-, PUT- und DELETE-Methoden.

In Ihrem Fall: 

  • sie haben standardmäßig den CSRF-Schutz aktiviert (weil Sie die Java-Konfiguration verwenden). 
  • sie senden das Anmeldeformular mit einem HTTP POST und 
  • enthalten das CSRF-Token nicht im Anmeldeformular. Aus diesem Grund wird Ihre Anmeldeanforderung bei der Übermittlung abgelehnt, da der CSRF-Schutzfilter das CSRF-Token in der eingehenden Anforderung nicht finden kann.

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

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

Da Sie Thymeleaf verwenden, müssen Sie in Ihrer HTML-Vorlage für die Anmeldeseite Folgendes tun:

<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 Sie th:action und nicht HTML action verwenden müssen, da der Thymeleaf-CSRF-Prozessor nur mit der vorherigen Version startet.

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

Normalerweise erstelle ich ein Thymeleaf-Fragment, das dann in allen Seiten mit Formularen verwendet wird, um das Markup für die Formulare mit dem enthaltenen CSRF-Token zu generieren. Dies reduziert den Boilerplate-Code in der App.


Verwenden Sie @EnableWebMvcSecurity anstelle von @EnableWebSecurity, um die automatische Injektion von CSRF-Token mit Thymeleaf-Tags zu aktivieren. Verwenden Sie auch <form th:action> anstelle von <form action> mit Spring 3.2+ und Thymeleaf 2.1+, um Thymeleaf zu zwingen, das CSRF-Token automatisch als verstecktes Feld einzuschließen (source Spring JIRA ).

39
manish

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

  1. Ersetzen Sie @EnableWebSecurity durch @EnableWebMvcSecurity (das ist, was OP fehlt)
  2. Verwenden Sie das th:action am <form>-Tag

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

Seit Spring Security 4.0 ist @EnableWebMvcSecurity veraltet und nur @EnableWebSecurity ist erforderlich. Der _csrf-Schutz gilt weiterhin automatisch .

13
name_no

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

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

Maven Beispiel:

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

2.) Fügen Sie das SpringSecurityDialect-Objekt zu Ihrer SpringTemplateEngine hinzu 

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

Quelle: Frühling in Aktion 4. Auflage

3

Vielleicht hilft diese kleine Ruhe der Informationen jedem, der das Formular mit th:action versehen hat. Nur einfaches HTML-Attribut action zuzuweisen, reicht nicht aus und das versteckte CSRF-Eingabefeld wird nicht automatisch hinzugefügt. 

Konnte diesen Informationsfrieden nirgends finden und für 2 Stunden recherchieren. Ich hatte das Formular mit action="#" zugeordnet und den entsprechenden Wert per Java-Skript festgelegt. Das Eingabefeld für das CSRF-Token wurde erst automatisch hinzugefügt, wenn dem Formular th:action="@{#}" hinzugefügt wurde. Funktioniert jetzt als Zauber.

1
Mario Eis

Arbeitete für mich nur, nachdem Folgendes hinzugefügt wurde:

protected void configure(HttpSecurity http) throws Exception {
    ...

    http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    ...
}
0
Alex Rewa