首页 \ 问答 \ Spring Boot + Security + Thymeleaf和CSRF令牌不会自动注入(Spring Boot + Security + Thymeleaf and CSRF token not injected automatically)

Spring Boot + Security + Thymeleaf和CSRF令牌不会自动注入(Spring Boot + Security + Thymeleaf and CSRF token not injected automatically)

免责声明 :我知道如何使用百万富翁手动注入令牌:

<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />`

这篇文章的目标是提高平台的知识,并更好地了解Spring Boot内部的情况

我没有尝试过Spring Boot,但最近我决定尝试一下,并且不得不承认它很棒,但是在Spring MVC上使用Thymeleaf和Security,我不需要在表单上注入CSRF令牌(POST),因为Thymeleaf会自动处理它,但是现在因为某些原因它没有。

Spring Boot Reference中 ,我找到了application.properties文件中使用的常用属性列表,与thymeleaf和security相关的属性是:

Thymeleaf Properties

spring.thymeleaf.check-template-location=true
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.excluded-view-names= # comma-separated list of view names that should be excluded from resolution
spring.thymeleaf.view-names= # comma-separated list of view names that can be resolved
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html # ;charset=<encoding> is added
spring.thymeleaf.cache=true # set to false for hot refresh

安全属性

security.user.name=user # login username
security.user.password= # login password
security.user.role=USER # role assigned to the user
security.require-ssl=false # advanced settings ...
security.enable-csrf=false
security.basic.enabled=true
security.basic.realm=Spring
security.basic.path= # /**
security.basic.authorize-mode= # ROLE, AUTHENTICATED, NONE
security.filter-order=0
security.headers.xss=false
security.headers.cache=false
security.headers.frame=false
security.headers.content-type=false
security.headers.hsts=all # none / domain / all
security.sessions=stateless # always / never / if_required / stateless
security.ignored= # Comma-separated list of paths to exclude from the     default secured paths

但如果让Thymeleaf再次注入令牌的解决方案就在那里,我就看不到了。

编辑 :添加我的配置

该项目是使用最新STS版本(我认为很棒)中提供的初始化程序创建的,其中包括Web,Thymeleaf,Security,JPA,MySQL,H2,Mail,Facebook,Twitter,LinkedIn和Actuator项目,以及添加了一些额外的东西

使用Java 7和Tomcat 7,因为我打算在不久的将来在Openshift上部署项目,接下来有我的配置文件:

的pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.2.3.RELEASE</version>
    <relativePath/>
</parent>
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <start-class>com.adrisasws.springmvc.WebApplication</start-class>
    <java.version>1.7</java.version>
    <tomcat.version>7.0.59</tomcat.version>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.spring.platform</groupId>
            <artifactId>platform-bom</artifactId>
            <version>1.1.2.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity3</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.social</groupId>
        <artifactId>spring-social-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-social-facebook</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-social-linkedin</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-social-twitter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.social</groupId>
        <artifactId>spring-social-google</artifactId>
        <version>1.0.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
<profiles>
    <profile>
        <id>openshift</id>
        <build>
            <finalName>webapp</finalName>
            <plugins>
                <plugin>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>2.1.1</version>
                    <configuration>
                        <outputDirectory>webapps</outputDirectory>
                        <warName>ROOT</warName>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

安全配置(我在非启动项目中使用的安全文件完全相同,其中CSRF令牌实际上是自动注入的)

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    //////////////////////////////////////////////////////////////////////////
    //                              DEPENDENCIES                            //
    //////////////////////////////////////////////////////////////////////////

    @Autowired private DataSource dataSource;
    @Autowired private UserRepository userRepository;


    //////////////////////////////////////////////////////////////////////////
    //                               PROPERTIES                             //
    //////////////////////////////////////////////////////////////////////////

    @Value("${custom.security.rememberme-secret}")  private String secret;
    @Value("${custom.security.rememberme-create-tables}") private String createTables;

    private final static String[] adminRequests = new String[] { ... some matchers here... };
    private final static String[] userRequests = new String[] { ... some matchers here... };
    private final static String[] publicRequests = new String[] { ...some matchers here... };


    //////////////////////////////////////////////////////////////////////////
    //                              AUTHORIZATION                           //
    //////////////////////////////////////////////////////////////////////////

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/css/**", "/images/**", "/js/**", "/error**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers(adminRequests).access("hasRole('"+Role.ADMIN.toString()+"')")
                .antMatchers(userRequests).access("hasRole('"+Role.USER.toString()+"')")
                .antMatchers(publicRequests).permitAll()
                .anyRequest().authenticated()
                .and()
            .requiresChannel()
                .anyRequest().requiresSecure()
                .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/", false)
                .permitAll()
                .and()
            .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login?logout")
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID")
                .permitAll()
                .and()
            .rememberMe()
                .rememberMeServices(rememberMeService())
                .and()
            .apply(new SpringSocialConfigurer());
    }


    //////////////////////////////////////////////////////////////////////////
    //                              AUTHENTICATION                          //
    //////////////////////////////////////////////////////////////////////////

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

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

    @Bean
    public UserDetailsService userDetailsService() {
        return new UserRepositoryUserDetailsService(userRepository);
    }

    @Bean
    public SocialUserDetailsService socialUserDetailsService() {
        return new UserRepositorySocialUserDetailsService(userDetailsService());
    }


    //////////////////////////////////////////////////////////////////////////
    //                               REMEMBER ME                            //
    //////////////////////////////////////////////////////////////////////////

    @Bean
    public JdbcTokenRepositoryImpl jdbcTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        jdbcTokenRepository.setCreateTableOnStartup(Boolean.valueOf(createTables));
        return jdbcTokenRepository; 
    }

    @Bean
    public RememberMeAuthenticationProvider rememberMeAuthenticationProvider() {
        return new RememberMeAuthenticationProvider(secret);
    }

    @Bean 
    public PersistentTokenBasedRememberMeServices rememberMeService() {
        PersistentTokenBasedRememberMeServices service = 
                new PersistentTokenBasedRememberMeServices(secret, userDetailsService(), jdbcTokenRepository());
        service.setUseSecureCookie(true);
        service.setParameter("rememberme");
        service.setTokenValiditySeconds(AbstractRememberMeServices.TWO_WEEKS_S);
        return service;
    }

    @Bean
    public RememberMeAuthenticationFilter authenticationFilter() throws Exception {
        return new RememberMeAuthenticationFilter(authenticationManager(), rememberMeService());
    }
}

在我的春季靴子配置目前与百里香叶有关,并用于发展目的

spring.thymeleaf.cache=false

和thymeleaf模板看起来像这样(我的登录页面,为了清晰起见,仅包含相关内容)

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
    xmlns:sec="http://www.thymeleaf.org/extras/spring-security/"
    xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
    layout:decorator="thymeleaf/layouts/default">
<head>
    ... css and meta tags ...
</head>
<body>
        ... some html ...
        <th:block sec:authorize="isAnonymous()">
        <!-- Bad Credentials -->
        <div th:if="${param.error}" class="alert alert-danger text-center">
            Invalid username and/or password.
        </div>
        <!-- Logout -->
        <div th:if="${param.logout}" class="alert alert-success text-center">
            You have been logged out.
        </div>

        <!-- Login Form -->
        <form id="f" th:action="@{/login}" method="post" role="form" autocomplete="off">
            <!-- Username -->       
            <input type="text" class="form-control text-center" id="username" name="username" th:placeholder="#{form.login.username}" />
            <!-- Password -->
            <input type="password" class="form-control text-center" id="password" name="password" th:placeholder="#{form.login.password}" />
            <!-- Remember me -->
            <input type="checkbox" id="rememberme" name="rememberme" />
            <!-- Submit -->
            <button type="submit" class="btn btn-primary" th:utext="#{form.login.submit}">Login</button>
            <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
        </form>
        ... more html and javascript ...
</body>
</html>

Edit2 - 在Faraj Farook指向的方向上做了一些调试之后,我发现,在我发布的配置的项目中,在Spring Boot版本中,在这个类org.thymeleaf.spring4.requestdata.RequestDataValueProcessor4Delegate ,以下函数返回一个空处理器

public Map<String, String> getExtraHiddenFields(
        final RequestContext requestContext, final HttpServletRequest request) {

    final RequestDataValueProcessor processor = requestContext.getRequestDataValueProcessor();
    if (processor == null) {
        return null;
    }

    return processor.getExtraHiddenFields(request);

}

而非Spring引导版本,它返回一个处理器,该处理器是org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor一个实例。


Disclaimer: I know how to inject the token in a form with thymeleaf manually with this:

<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />`

The goal of this post is to improve the knowledge of the platform and get a better understanding of what's going on inside Spring Boot

I haven't tried Spring Boot, but recently i just decided to give it a try, and have to admit its awesome, but with Thymeleaf and Security on Spring MVC, i didn't need to inject CSRF token on forms (POST), because Thymeleaf took care of it automatically, but now in Spring Boot for some reason it doesn't.

From the Spring Boot Reference, i found a list of the common properties used on application.properties file, and the ones related to thymeleaf and security are:

Thymeleaf Properties

spring.thymeleaf.check-template-location=true
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.excluded-view-names= # comma-separated list of view names that should be excluded from resolution
spring.thymeleaf.view-names= # comma-separated list of view names that can be resolved
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html # ;charset=<encoding> is added
spring.thymeleaf.cache=true # set to false for hot refresh

Security Properties

security.user.name=user # login username
security.user.password= # login password
security.user.role=USER # role assigned to the user
security.require-ssl=false # advanced settings ...
security.enable-csrf=false
security.basic.enabled=true
security.basic.realm=Spring
security.basic.path= # /**
security.basic.authorize-mode= # ROLE, AUTHENTICATED, NONE
security.filter-order=0
security.headers.xss=false
security.headers.cache=false
security.headers.frame=false
security.headers.content-type=false
security.headers.hsts=all # none / domain / all
security.sessions=stateless # always / never / if_required / stateless
security.ignored= # Comma-separated list of paths to exclude from the     default secured paths

But if the solution to make Thymeleaf inject the token again is there, i fail to see it.

Edit: adding my configuration

The project was created using the initializer that was shipped in the last STS version (which in my opinion is awesome), with Web, Thymeleaf, Security, JPA, MySQL, H2, Mail, Facebook, Twitter, LinkedIn and Actuator items checked, and added some extras aftwerwards

Using Java 7 and Tomcat 7 because i intend to deploy the project on Openshift in a near future, and next there are my config files:

pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.2.3.RELEASE</version>
    <relativePath/>
</parent>
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <start-class>com.adrisasws.springmvc.WebApplication</start-class>
    <java.version>1.7</java.version>
    <tomcat.version>7.0.59</tomcat.version>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.spring.platform</groupId>
            <artifactId>platform-bom</artifactId>
            <version>1.1.2.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity3</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.social</groupId>
        <artifactId>spring-social-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-social-facebook</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-social-linkedin</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-social-twitter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.social</groupId>
        <artifactId>spring-social-google</artifactId>
        <version>1.0.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
<profiles>
    <profile>
        <id>openshift</id>
        <build>
            <finalName>webapp</finalName>
            <plugins>
                <plugin>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>2.1.1</version>
                    <configuration>
                        <outputDirectory>webapps</outputDirectory>
                        <warName>ROOT</warName>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

Security config (exactly the same security file i'm using in a non-boot project in which the CSRF token actually gets injected automatically)

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    //////////////////////////////////////////////////////////////////////////
    //                              DEPENDENCIES                            //
    //////////////////////////////////////////////////////////////////////////

    @Autowired private DataSource dataSource;
    @Autowired private UserRepository userRepository;


    //////////////////////////////////////////////////////////////////////////
    //                               PROPERTIES                             //
    //////////////////////////////////////////////////////////////////////////

    @Value("${custom.security.rememberme-secret}")  private String secret;
    @Value("${custom.security.rememberme-create-tables}") private String createTables;

    private final static String[] adminRequests = new String[] { ... some matchers here... };
    private final static String[] userRequests = new String[] { ... some matchers here... };
    private final static String[] publicRequests = new String[] { ...some matchers here... };


    //////////////////////////////////////////////////////////////////////////
    //                              AUTHORIZATION                           //
    //////////////////////////////////////////////////////////////////////////

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/css/**", "/images/**", "/js/**", "/error**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers(adminRequests).access("hasRole('"+Role.ADMIN.toString()+"')")
                .antMatchers(userRequests).access("hasRole('"+Role.USER.toString()+"')")
                .antMatchers(publicRequests).permitAll()
                .anyRequest().authenticated()
                .and()
            .requiresChannel()
                .anyRequest().requiresSecure()
                .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/", false)
                .permitAll()
                .and()
            .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login?logout")
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID")
                .permitAll()
                .and()
            .rememberMe()
                .rememberMeServices(rememberMeService())
                .and()
            .apply(new SpringSocialConfigurer());
    }


    //////////////////////////////////////////////////////////////////////////
    //                              AUTHENTICATION                          //
    //////////////////////////////////////////////////////////////////////////

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

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

    @Bean
    public UserDetailsService userDetailsService() {
        return new UserRepositoryUserDetailsService(userRepository);
    }

    @Bean
    public SocialUserDetailsService socialUserDetailsService() {
        return new UserRepositorySocialUserDetailsService(userDetailsService());
    }


    //////////////////////////////////////////////////////////////////////////
    //                               REMEMBER ME                            //
    //////////////////////////////////////////////////////////////////////////

    @Bean
    public JdbcTokenRepositoryImpl jdbcTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        jdbcTokenRepository.setCreateTableOnStartup(Boolean.valueOf(createTables));
        return jdbcTokenRepository; 
    }

    @Bean
    public RememberMeAuthenticationProvider rememberMeAuthenticationProvider() {
        return new RememberMeAuthenticationProvider(secret);
    }

    @Bean 
    public PersistentTokenBasedRememberMeServices rememberMeService() {
        PersistentTokenBasedRememberMeServices service = 
                new PersistentTokenBasedRememberMeServices(secret, userDetailsService(), jdbcTokenRepository());
        service.setUseSecureCookie(true);
        service.setParameter("rememberme");
        service.setTokenValiditySeconds(AbstractRememberMeServices.TWO_WEEKS_S);
        return service;
    }

    @Bean
    public RememberMeAuthenticationFilter authenticationFilter() throws Exception {
        return new RememberMeAuthenticationFilter(authenticationManager(), rememberMeService());
    }
}

in my spring boot configt at the moment related to thymeleaf, and for development purposes

spring.thymeleaf.cache=false

and thymeleaf templates look like this (my login page at the moment, will include only the relevant content for the sake of clarity)

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
    xmlns:sec="http://www.thymeleaf.org/extras/spring-security/"
    xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
    layout:decorator="thymeleaf/layouts/default">
<head>
    ... css and meta tags ...
</head>
<body>
        ... some html ...
        <th:block sec:authorize="isAnonymous()">
        <!-- Bad Credentials -->
        <div th:if="${param.error}" class="alert alert-danger text-center">
            Invalid username and/or password.
        </div>
        <!-- Logout -->
        <div th:if="${param.logout}" class="alert alert-success text-center">
            You have been logged out.
        </div>

        <!-- Login Form -->
        <form id="f" th:action="@{/login}" method="post" role="form" autocomplete="off">
            <!-- Username -->       
            <input type="text" class="form-control text-center" id="username" name="username" th:placeholder="#{form.login.username}" />
            <!-- Password -->
            <input type="password" class="form-control text-center" id="password" name="password" th:placeholder="#{form.login.password}" />
            <!-- Remember me -->
            <input type="checkbox" id="rememberme" name="rememberme" />
            <!-- Submit -->
            <button type="submit" class="btn btn-primary" th:utext="#{form.login.submit}">Login</button>
            <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
        </form>
        ... more html and javascript ...
</body>
</html>

Edit2 - after doing some debugging in the direction Faraj Farook pointed, i found out that, in a project with the configuration i posted, in the Spring Boot version, in this class org.thymeleaf.spring4.requestdata.RequestDataValueProcessor4Delegate, the following function returns a null processor

public Map<String, String> getExtraHiddenFields(
        final RequestContext requestContext, final HttpServletRequest request) {

    final RequestDataValueProcessor processor = requestContext.getRequestDataValueProcessor();
    if (processor == null) {
        return null;
    }

    return processor.getExtraHiddenFields(request);

}

whereas the non Spring boot version, it returns a processor which is an instance of org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor.


原文:https://stackoverflow.com/questions/29509392
更新时间:2022-02-06 17:02

最满意答案

如果您不想更改项目以及希望避免复制,则auto const &是正确的选择:

for (auto const &x : vec)

谁建议你使用auto &错误。 别理他们。

这里是回顾:

  • 当您要使用副本时选择auto x
  • 选择auto &x当您想要处理原始项目,并可能会修改它们。
  • 当您想使用原始项目时选择auto const &x ,不会修改它们。

If you don't want to change the items as well as want to avoid making copies, then auto const & is the correct choice:

for (auto const &x : vec)

Whoever suggests you to use auto & is wrong. Ignore them.

Here is recap:

  • Choose auto x when you want to work with copies.
  • Choose auto &x when you want to work with original items and may modify them.
  • Choose auto const &x when you want to work with original items and will not modify them.

相关问答

更多
  • 标准容器都是从它们的迭代器中返回引用(但是,请注意,有些“容器不是真正的容器,例如, std::vector它返回一个代理)。其他迭代器可能会返回代理或值,尽管这不是'严格支持。 当然,这个标准对性能没有任何保证。 任何类型的性能相关特性(超出复杂性保证范围)都被认为是实现的质量。 也就是说,你可能想要考虑让编译器像以前一样为你做出选择: for (auto&& e: coll) { f(e); } 这里的主要问题是f()可能会收到非const引用。 如果需要,可以使用const一个const ...
  • template std::remove_reference_t const& as_const(T&&t){return t;} 可能有帮助。 返回右值的隐式共享对象可以隐式检测由于非常量迭代而导致的写入划分(和划分)。 这给你: for(auto&&item : as_const(foo())) { } 它可以让你以const方式迭代(并且很清楚)。 如果您需要参考寿命延长才能正常工作,请重复2次: template T const as_const(T&& ...
  • 如果您不想更改项目以及希望避免复制,则auto const &是正确的选择: for (auto const &x : vec) 谁建议你使用auto &错误。 别理他们。 这里是回顾: 当您要使用副本时选择auto x 。 选择auto &x当您想要处理原始项目,并可能会修改它们。 当您想使用原始项目时选择auto const &x ,不会修改它们。 If you don't want to change the items as well as want to avoid making copies, ...
  • 可能有办法,但我非常怀疑它会更加优雅。 你在第一个循环中已经是正确的方法,限制了循环变量的范围/生命周期。 我会简单地忽略未使用的变量警告(毕竟,这只是编译器的一个迹象, 可能是错误的),或者使用编译器工具(如果有的话)可以在这一点上关闭警告。 There may be a way to do it but I very much doubt it would be more elegant. What you have in that first loop is already the correct w ...
  • 是。 同样的原因,如果你只读过一个参数,你使参数const& 。 T // I'm copying this T& // I'm modifying this const T& // I'm reading this 那些是你的“默认值”。 当T是一个基本类型(内置)时,通常只要恢复为const T (无参考)才能读取,因为副本比别名便宜。 我正在开发一个正在开发的程序,因为效率是至关重要的 不要盲目地改变。 一个工作程序比一个快速但破碎的程序更好。 你如何迭代循环可能不会有太大 ...
  • 您的情况实际上是不能使用基于范围的迭代的典型情况。 只要您不关心容器中元素/迭代器的位置,就可以使用它。 相反,如果需要当前元素的索引(或指向它的迭代器),则需要经典循环。* 看你的例子: for(const auto& itr_m : myClassVec) 这里, itr_m不是迭代器,而是对MyClass const*的引用,这意味着您丢失了有关元素位置的所有信息。 *你当然可以在一个range-for循环中跟踪当前元素,但这会破坏它的目的并使代码更难阅读。 Your case actually i ...
  • 如果您实际想要执行的操作是您在示例中编写的简单操作,则实现您想要的操作的更好方法是: std::cout << std::string{boost::distance(input), '.'}; 否则,如果你只是想遍历一个范围并为每个元素执行一个操作,忽略元素值,那么for_each 就是你想要的。 使用boost: #include boost::for_each(input, [](auto&& /*ignored*/) { ...
  • N4606(C ++ 17 draft)3.3.3 basic.scope.block,第4节说 在init-statement,for-range-declaration以及if,while,for和switch语句的条件中声明的名称是if,while,for或switch语句(包括受控语句)的本地名称,以及不得在该陈述的后续条件下,也不得在受控陈述的最外层(或对于if陈述,任何最外面的区块)重新宣布; 见6.4 缩短: 在... for-range-declaration ...中声明的名称是... f ...
  • 问题是QJsonArray的迭代器返回一个临时的QJsonValueRef对象,而左值引用无法绑定到临时对象。 我们可以按价值暂时保留: // QJsonArray oldArray contains an array of which one element is "bar" QJsonArray newArray; for (auto v : oldArray) { QJsonObject element = v.toObject(); element["foo"] = element[ ...
  • 从你的语法看来, ticket是一个(聪明的)指针。 ticket->ClearCalled(); 这意味着ticket的类型可能类似于 const std::shared_ptr< Ticket >& ticket 你需要的是 const std::shared_ptr< const Ticket >& ticket; // ^^^^^ From your syntax it seems that ticket is a (smart?) pointer. t ...

相关文章

更多

最新问答

更多
  • h2元素推动其他h2和div。(h2 element pushing other h2 and div down. two divs, two headers, and they're wrapped within a parent div)
  • 创建一个功能(Create a function)
  • 我投了份简历,是电脑编程方面的学徒,面试时说要培训三个月,前面
  • PDO语句不显示获取的结果(PDOstatement not displaying fetched results)
  • Qt冻结循环的原因?(Qt freezing cause of the loop?)
  • TableView重复youtube-api结果(TableView Repeating youtube-api result)
  • 如何使用自由职业者帐户登录我的php网站?(How can I login into my php website using freelancer account? [closed])
  • SQL Server 2014版本支持的最大数据库数(Maximum number of databases supported by SQL Server 2014 editions)
  • 我如何获得DynamicJasper 3.1.2(或更高版本)的Maven仓库?(How do I get the maven repository for DynamicJasper 3.1.2 (or higher)?)
  • 以编程方式创建UITableView(Creating a UITableView Programmatically)
  • 如何打破按钮上的生命周期循环(How to break do-while loop on button)
  • C#使用EF访问MVC上的部分类的自定义属性(C# access custom attributes of a partial class on MVC with EF)
  • 如何获得facebook app的publish_stream权限?(How to get publish_stream permissions for facebook app?)
  • 如何防止调用冗余函数的postgres视图(how to prevent postgres views calling redundant functions)
  • Sql Server在欧洲获取当前日期时间(Sql Server get current date time in Europe)
  • 设置kotlin扩展名(Setting a kotlin extension)
  • 如何并排放置两个元件?(How to position two elements side by side?)
  • 如何在vim中启用python3?(How to enable python3 in vim?)
  • 在MySQL和/或多列中使用多个表用于Rails应用程序(Using multiple tables in MySQL and/or multiple columns for a Rails application)
  • 如何隐藏谷歌地图上的登录按钮?(How to hide the Sign in button from Google maps?)
  • Mysql左连接旋转90°表(Mysql Left join rotate 90° table)
  • dedecms如何安装?
  • 在哪儿学计算机最好?
  • 学php哪个的书 最好,本人菜鸟
  • 触摸时不要突出显示表格视图行(Do not highlight table view row when touched)
  • 如何覆盖错误堆栈getter(How to override Error stack getter)
  • 带有ImageMagick和许多图像的GIF动画(GIF animation with ImageMagick and many images)
  • USSD INTERFACE - > java web应用程序通信(USSD INTERFACE -> java web app communication)
  • 电脑高中毕业学习去哪里培训
  • 正则表达式验证SMTP响应(Regex to validate SMTP Responses)