2015-04-08 2 views
3

Отказ от ответственности: Я знаю, как придать маркер в форме с thymeleaf вручную с этим:Spring Загрузочный + Безопасность + Thymeleaf и CSRF токен не вводится автоматически

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

Цель этого поста заключается в повышении знание платформы и лучшее понимание того, что происходит внутри Spring Boot

Я не пробовал Spring Boot, но в последнее время я просто решил попробовать, и должен признать, что это потрясающе, но с Thymeleaf и Security on Spring MVC, мне не нужно вводить токен CSRF в формах (POST), потому что Thymelea f позаботился об этом автоматически, но теперь в Spring Boot почему-то это не так.

Из Spring Boot Reference, я нашел список общих свойств, используемых на application.properties файле, и те, связанные с thymeleaf и безопасность:

Thymeleaf Свойства

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 вводить токен снова, я не вижу его.

Edit: добавление моя конфигурация

Проект был создан с использованием инициализатора, который был отправлен в последней версии STS (на мой взгляд, является удивительным), с Web, Thymeleaf, безопасности, 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()); 
    } 
} 

в моей весенней загрузке configt в данный момент связанные с thymeleaf, а также в целях развития

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 загрузки, в этом классе 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); 

} 

в то время как, не версии Спринг загрузки, он возвращает процессор, который является экземпляром org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor.

+0

Что делает ваш взгляд конфигурации Spring Security как? Как выглядит ваш pom? Как выглядит ваш файл Thymeleaf? –

+0

Добавил pom, конфигурацию Spring Security и образец шаблона Thymeleaf, извините за задержку – saljuama

ответ

2

В соответствии с разработчиками Thymeleaf интерфейс пользователя RequestDataValueProcessor используется Thymeleaf для поиска дополнительных скрытых полей, которые автоматически добавляются к форме post back.

Приведенный ниже код в org/thymeleaf/spring3/processor/attr/SpringActionAttrProcessor.java показывает это.

final Map<String,String> extraHiddenFields = 
        RequestDataValueProcessorUtils.getExtraHiddenFields(arguments.getConfiguration(), arguments); 

Чтобы устранить проблему и автоматически добавить токен CSRF; В вашем приложении создайте обработчик данных пользовательских запросов и зарегистрируйте его с помощью пружины. Для этого вы можете ознакомиться с учебным пособием ниже.

Csrf Defense in Spring-MVC

Я также предлагаю вам проверить свой предыдущий пружинный MVC код без загрузки яровой, чтобы подтвердить конфигурацию XML этого проекта имеет выполненное на заказ RequestDataValueProcessor или нет.

+0

, я посмотрю, как только у меня будет время для этого позже сегодня вечером, и спасибо за ответ – saljuama

+0

отредактировал мой первоначальный пост, вы были правы , спасибо за показания, было полезно – saljuama

+0

приятно слышать :) –

-1

Использование Spring Загрузочный + Thymeleaf + Spring Security он работал с этим:

Свойства Применение

security.enable-csrf=true

Update 30/03/2017:

Один важный дело в том, что: используйте th: действие внутри вашей формы, это сообщит Spring Security о введении CSRF insi формы без необходимости ручной установки.

Для ручного ввода:

HTML шаблон

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

Update 25/01/2017:

pom.xml

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