Printemps de Démarrage + Sécurité + Thymeleaf et jeton CSRF pas injecté automatiquement
Avertissement: je sais comment injecter le jeton dans un formulaire avec thymeleaf manuellement avec ceci:
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />`
Le but de ce post est d'améliorer la connaissance de la plate-forme et d'obtenir une meilleure compréhension de ce qui se passe à l'intérieur de Printemps de Démarrage
Je n'ai pas essayé de Printemps de Démarrage, mais récemment, j'ai décidé de lui donner un essai, et dois avouer son impressionnant, mais avec Thymeleaf et de la Sécurité sur Spring MVC, je n'ai pas besoin d'injecter de jeton CSRF sur les formes (POST), car Thymeleaf a pris soin d'elle automatiquement, mais, dès le Printemps de Démarrage pour une raison quelconque, il ne le fait pas.
De la Printemps De Démarrage De Référence, j'ai trouvé une liste de propriétés communes utilisé sur demande.fichier de propriétés, et ceux liés à la thymeleaf et de la sécurité sont:
Thymeleaf Propriétés
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
Propriétés De Sécurité
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
Mais si la solution pour faire Thymeleaf injecter le jeton est de nouveau là, je n'arrive pas à le voir.
Modifier: ajout de ma configuration
Le projet a été créé à l'aide de l'initialiseur, qui a été livré au la dernière version STS (qui à mon avis est génial), avec le Web, Thymeleaf, la Sécurité, la JPA, MySQL, H2, Mail, Facebook, Twitter, LinkedIn et de l'Actionneur éléments vérifiés, et a ajouté quelques extras aftwerwards
À l'aide de Java 7 et Tomcat 7, parce que j'ai l'intention de déployer le projet sur Openshift dans un proche avenir, et à côté il y a ma config fichiers:
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>
Sécurité config (exactement le même fichier de sécurité, je suis en utilisant un non-démarrage du projet dans lequel le jeton CSRF est injecté automatiquement)
@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());
}
}
dans mon printemps de démarrage configt au moment liées à thymeleaf, et à des fins de développement
spring.thymeleaf.cache=false
et thymeleaf modèles ressembler à ceci (ma page de connexion à l'heure actuelle, seules les contenus pertinents pour la clarté)
<!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 - après avoir fait un peu de débogage dans le sens Faraj Farook pointu, je trouve que, dans un projet avec la configuration que j'ai posté, au Printemps version de Démarrage, dans cette classe org.thymeleaf.spring4.requestdata.RequestDataValueProcessor4Delegate
, la fonction suivante renvoie une valeur null processeur
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);
}
tandis que le non de Printemps version de démarrage, il renvoie un processeur qui est une instance de org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor
.
Ajouté le pom, le Printemps de Sécurité config et un échantillon de Thymeleaf modèle, désolé pour le retard
OriginalL'auteur saljuama | 2015-04-08
Vous devez vous connecter pour publier un commentaire.
Selon la
Thymeleaf
développeurs,RequestDataValueProcessor
interface est utilisée par Thymeleaf de trouver le supplément de champs cachés qui est automatiquement ajouté au post de formulaire de retour.Le code ci-dessous dans
org/thymeleaf/spring3/processor/attr/SpringActionAttrProcessor.java
le montre.Pour trier la question, et de les ajouter automatiquement le Jeton CSRF; Dans votre demande de créer une demande personnalisée valeur de données du processeur et l'enregistrer avec le printemps. Pour ce faire, vous pouvez passer par le tutoriel ci-dessous.
Csrf de la Défense dans Spring-MVC
Je vous conseille aussi de vérifier votre précédente spring MVC code sans le ressort de démarrage, afin de confirmer que le projet de configuration XML a une coutume fait
RequestDataValueProcessor
ou pas.édité mon post initial, vous avez eu raison, merci pour les indications, été utile
bon à entendre 🙂
OriginalL'auteur Faraj Farook
J'ai eu un problème similaire. Après quelques recherches, j'ai trouvé que seuls les formulaires qui ont été à l'aide de la "th:action" attribut (pas simple "action") a le jeton csrf injecté.
Pour les formulaires de login, il semble que vous avez besoin pour injecter le csrf manuellement (lien).
Officiel printemps docs (lien) il y a une suggestion pour récupérer le jeton csrf juste avant la connexion de soumission de formulaire pour prévenir délais d'expiration de session. Dans ce scénario il n'y aurait pas de jeton csrf caché lors de la saisie sur le formulaire.
OriginalL'auteur lbd01
Que vous aurez à faire 2 choses. Déclarer un bean
Assurez-vous que le formulaire html dans votre themeleaf modèle utilise "th:l'action"
Ce insère automatiquement _csrf jeton comme ce
OriginalL'auteur mpprdev
À l'aide de Spring Boot + Thymeleaf + Ressort de la Sécurité, il a travaillé avec ceci:
Des Propriétés De L'Application
security.enable-csrf=true
Mise à jour 30/03/2017:
Un important chose est: utilisez th:action à l'intérieur de votre forme, ce sera de dire au Printemps de Sécurité pour injecter CSRF à l'intérieur de la forme sans la nécessité de l'insertion manuelle.
Pour l'insertion manuelle:
modèle html
Mise à jour 25/01/2017:
pom.xml
OriginalL'auteur mizerablebr