Java 带有 Thymeleaf 的 Spring Security 的简单示例
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25692735/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Simple example of Spring Security with Thymeleaf
提问by stackUser2000
hi I'm trying to follow a simple example about doing a simple login form page that i found in this page http://docs.spring.io/autorepo/docs/spring-security/4.0.x/guides/form.html
嗨,我正在尝试按照一个简单的例子来做一个我在这个页面中找到的简单登录表单页面 http://docs.spring.io/autorepo/docs/spring-security/4.0.x/guides/form.html
the problem is that i′m getting this error everytime that i try to login i get this error: Expected CSRF token not found. Has your session expired?
问题是我每次尝试登录时都会收到此错误我收到此错误: Expected CSRF token not found. Has your session expired?
When i get this error i press the back button in my explorer and try a second time to log in and when i do that i get this error: HTTP 403 - Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'
当我收到此错误时,我按资源管理器中的后退按钮并尝试第二次登录,当我这样做时,我收到此错误: HTTP 403 - Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'
in the tutorial page is this message: 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}"/>
在教程页面是这条消息: 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 because i am using thymeleaf too i didnt add that tag to my page"
“所以因为我也在使用百里香叶,所以我没有将该标签添加到我的页面中”
i found another solution and it works and this solution is adding this to my security config class .csrf().disable()
this solution works but i suppose that what this do is to disable csrf protection in my page and i dont want to disable this type of protection.
我找到了另一个解决方案并且它有效并且这个解决方案将它添加到我的安全配置类.csrf().disable()
这个解决方案有效但我想这样做是为了在我的页面中禁用 csrf 保护,我不想禁用这种类型的保护。
this is my security-config class :
这是我的安全配置类:
@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();
}
}
my security initialzer :
我的安全初始值设定项:
public class InitSecurity extends AbstractSecurityWebApplicationInitializer {
public InicializarSecurity() {
super(ConfigSecurity .class);
}
}
my app-config class where i have my thymeleaf configuration
我的 app-config 类在那里我有我的百里香配置
@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;
}
}
my app-config initializer
我的应用程序配置初始化程序
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() };
}
}
my login controller class
我的登录控制器类
@Controller
public class ControllerLogin {
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String pageLogin(Model model) {
return "login";
}
my home controller class
我的家庭控制器类
@Controller
public class HomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Model model) {
return "home";
}
}
my login.html
我的登录名.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>
my home.html page only shows after i log in and the only way i can log in if is a put .csrf().disable() in my Security config class but i dont want to disable that protection , if i dont put that in my security config class i get the errors that i mention at the start of this question.
我的 home.html 页面仅在我登录后显示,并且我可以登录的唯一方法是在我的安全配置类中放置 .csrf().disable() 但我不想禁用该保护,如果我不放置它在我的安全配置类中,我收到了我在这个问题开始时提到的错误。
采纳答案by manish
From the 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.
使用 Java 配置默认启用 CSRF 保护。如果您想禁用 CSRF,可以在下面看到相应的 Java 配置。有关如何配置 CSRF 保护的其他自定义,请参阅 csrf() 的 Javadoc。
And, when CSRF protection is enabled
并且,当启用 CSRF 保护时
The last step is to ensure that you include the CSRF token in all PATCH, POST, PUT, and DELETE methods.
最后一步是确保在所有 PATCH、POST、PUT 和 DELETE 方法中包含 CSRF 令牌。
In your case:
在你的情况下:
- you have CSRF protection enabled by default (because you are using Java configuration),
- you are submitting the login form using an HTTP POST and
- are not including the CSRF token in the login form. For this reason, your login request is denied upon submission because the CSRF protection filter cannot find the CSRF token in the incoming request.
- 您默认启用了 CSRF 保护(因为您使用的是 Java 配置),
- 您正在使用 HTTP POST 提交登录表单,并且
- 不包括登录表单中的 CSRF 令牌。因此,您的登录请求在提交时被拒绝,因为 CSRF 保护过滤器无法在传入请求中找到 CSRF 令牌。
You have already determined the possible solutions:
您已经确定了可能的解决方案:
- Disable CSRF protection as
http.csrf().disable()
; or - Include the CSRF token in the login form as a hidden parameter.
- 禁用 CSRF 保护为
http.csrf().disable()
;或者 - 在登录表单中包含 CSRF 令牌作为隐藏参数。
Since you are using Thymeleaf, you will have to do something like the following in your HTML template for the login page:
由于您使用的是 Thymeleaf,因此您必须在登录页面的 HTML 模板中执行以下操作:
<form name="f" th:action="@{/login}" method="post">
<fieldset>
<input type="hidden"
th:name="${_csrf.parameterName}"
th:value="${_csrf.token}" />
...
</fieldset>
</form>
Note that you must use th:action
and not HTML action
as the Thymeleaf CSRF processor will kick-in only with the former.
请注意,您必须使用th:action
而不是 HTML,action
因为 Thymeleaf CSRF 处理器只会与前者一起使用。
You could change the form submission method to GET
just to get over the problem but that isn't recommended since the users are going to submit sensitive information in the form.
您可以将表单提交方法更改为GET
只是为了解决问题,但不建议这样做,因为用户将在表单中提交敏感信息。
I typically create a Thymeleaf fragment that is then used in all pages with forms to generate the markup for the forms with the CSRF token included. This reduces boilerplate code across the app.
我通常会创建一个 Thymeleaf 片段,然后在所有带有表单的页面中使用该片段为包含 CSRF 令牌的表单生成标记。这减少了整个应用程序的样板代码。
Using @EnableWebMvcSecurity
instead of @EnableWebSecurity
to enable automatic injection of CSRF token with Thymeleaf tags. Also use <form th:action>
instead of <form action>
with Spring 3.2+ and Thymeleaf 2.1+ to force Thymeleaf to include the CSRF token as a hidden field automatically (source Spring JIRA).
使用@EnableWebMvcSecurity
代替@EnableWebSecurity
启用带有 Thymeleaf 标签的 CSRF 令牌的自动注入。也可以<form th:action>
代替<form action>
Spring 3.2+ 和 Thymeleaf 2.1+ 来强制 Thymeleaf 自动将 CSRF 令牌包含为隐藏字段(来源Spring JIRA)。
回答by Mario Eis
Maybe that small piece of information helps anybody out: It is also mandatory to have the form attributed with th:action
. Just attributing plain HTML action
won't do and the hidden CSRF input filed won't be added automatically.
也许那一小段信息可以帮助任何人:将表单归因于th:action
. 仅仅归因于纯 HTML 是action
行不通的,并且不会自动添加隐藏的 CSRF 输入文件。
Couldn't find that piece of information documented anywhere and spent 2h research on that. I had attributed the form with action="#"
and set the corresponding value by javascript. The CSRF token input field wasn't added automatically until added th:action="@{#}"
to the form. Works like a charm now.
找不到在任何地方记录的那条信息,并花了 2 小时研究它。我已经将表单归因于action="#"
并通过 javascript 设置了相应的值。CSRF 令牌输入字段在添加th:action="@{#}"
到表单之前不会自动添加。现在就像一个魅力。
回答by Hack of All Codes
You need to add Thymleaf's Spring Security Dialect.
您需要添加 Thymleaf 的 Spring Security 方言。
1.) Add the Spring Security Dialect module to your classpath.
1.) 将 Spring Security Dialect 模块添加到您的类路径中。
Maven Example:
Maven 示例:
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity3</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
2.) Add the SpringSecurityDialect object to your SpringTemplateEngine
2.) 将 SpringSecurityDialect 对象添加到您的 SpringTemplateEngine
import org.thymeleaf.extras.springsecurity3.dialect.SpringSecurityDialect;
templateEngine.addDialect(new SpringSecurityDialect()); //add this line in your config
Source: Spring in Action 4th Edition
回答by name_no
Here is the solution that implements it exactly the way OP wanted:
这是完全按照OP想要的方式实现它的解决方案:
- Replace
@EnableWebSecurity
with@EnableWebMvcSecurity
(that's what OP is missing) - Use
th:action
on<form>
tag
- 替换
@EnableWebSecurity
为@EnableWebMvcSecurity
(这就是 OP 所缺少的) th:action
在<form>
标签上使用
When you use @EnableWebMvcSecurity
Spring Security registers the CsrfRequestDataValueProcessor
, and when you use th:action
thymeleaf uses it's getExtraHiddenFields
method to add, well, extra hidden fields to the form. And the csrf is the extra hidden field.
当您使用@EnableWebMvcSecurity
Spring Security 时注册CsrfRequestDataValueProcessor
,当您使用th:action
thymeleaf 时使用它的getExtraHiddenFields
方法向表单添加额外的隐藏字段。而 csrf 是额外的隐藏字段。
Since Spring Security 4.0, @EnableWebMvcSecurity has been deprecated and only @EnableWebSecurity is necessary. The _csrf protection continues to apply automatically.
从 Spring Security 4.0 开始,@EnableWebMvcSecurity 已被弃用,只需要 @EnableWebSecurity。_csrf 保护继续自动应用。
回答by Alex Rewa
Worked for me only after added the following:
仅在添加以下内容后才为我工作:
protected void configure(HttpSecurity http) throws Exception {
...
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
...
}