When Spring Security is used on Spring Boot, the CsrfRequestDataValueProcessor
(class for outputting the CSRF token value to the hidden of the form) provided by Spring Security is applied, and the project-specific RequestDataValueProcessor
cannot be applied.
This is ... Spring Boot Issue (gh-4676) seems to be discussing how to deal with it, but it's still a conclusion. Does not seem to appear.
In Issue ...
BeanDefinitionRegistryPostProcessor
@ AutoConfigureAfter
to override the Bean definition in SecurityAutoConfiguration.class
Is introduced as a workaround.
This time, I'll try to create my own AutoConfigure class and use @ AutoConfigureAfter
to override the Bean definition in SecurityAutoConfiguration.class
.
In addition, the project-specific RequestDataValueProcessor
outputs a common parameter (_s = system date and time epoch milliseconds) to avoid caching in the URL in the form
and ʻa` elements.
The class created this time does not consider versatility at all, and adopts an implementation method that applies the project-specific RequestDataValueProcessor
implementation to the application while retaining the functions provided by CsrfRequestDataValueProcessor
.
Note:
If you consider versatility ... It is better to create a Composite class of
RequestDataValueProcessor
that delegates processing to multipleRequestDataValueProcessor
s.
package com.example;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor;
import org.springframework.web.servlet.support.RequestDataValueProcessor;
import org.springframework.web.util.UriComponentsBuilder;
@Configuration
@AutoConfigureAfter(SecurityAutoConfiguration.class) //★★★ Here points[1]
public class MyRequestDataValueProcessorAutoConfiguration {
@Bean
RequestDataValueProcessor requestDataValueProcessor() { //★★★ Here points[2]
CsrfRequestDataValueProcessor csrfRequestDataValueProcessor = new CsrfRequestDataValueProcessor();
return new RequestDataValueProcessor() {
@Override
public String processAction(HttpServletRequest request, String action, String httpMethod) {
return addCachePreventQueryParameter(csrfRequestDataValueProcessor.processAction(request, action, httpMethod));
}
@Override
public String processFormFieldValue(HttpServletRequest request, String name, String value, String type) {
return csrfRequestDataValueProcessor.processFormFieldValue(request, name, value, type);
}
@Override
public Map<String, String> getExtraHiddenFields(HttpServletRequest request) {
return csrfRequestDataValueProcessor.getExtraHiddenFields(request);
}
@Override
public String processUrl(HttpServletRequest request, String url) {
return addCachePreventQueryParameter(csrfRequestDataValueProcessor.processUrl(request, url));
}
private String addCachePreventQueryParameter(String url) {
return UriComponentsBuilder.fromUriString(url).queryParam("_s", System.currentTimeMillis()).build().encode()
.toUriString();
}
};
}
}
[1]
By allowing your own AutoConfigure class to be called after SecurityAutoConfiguration
, you can overwrite the Bean definition provided by Spring Boot (Spring Security) with your own AutoConfigure class.
[2]
In order to override the bean definition, the bean name must be " requestDataValueProcessor "
.
Basically, it delegates the process to CsrfRequestDataValueProcessor
and implements it to add a cache avoidance parameter to the return values of processAction
and processUrl
.
In order to recognize the created configuration class as AutoConfigure class, create src / main / resources / META-INF / spring.factories
and set the property key value of ʻorg.springframework.boot.autoconfigure.EnableAutoConfiguration`. You need to specify the class you created in.
src/main/resources/META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyRequestDataValueProcessorAutoConfiguration
Let's create a simple View for operation verification.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Demo</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.css" type="text/css"/>
</head>
<body>
<div class="container">
<h1>Demo</h1>
<div id="demoForm">
<form action="index.html" method="post" class="form-horizontal"
th:action="@{/demo}" th:object="${demoForm}"> <!--★★★ Here points[3] -->
<div class="form-group">
<label for="title" class="col-sm-1 control-label">Title</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="title" th:field="*{title}"/>
<span class="text-error" th:errors="*{title}">error message</span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<button type="submit" class="btn btn-default">Create</button>
</div>
</div>
</form>
</div>
<a href="index.html" th:href="@{/demo}">Reload</a> <!--★★★ Here points[4] -->
</div>
</body>
</html>
[3]
The URL specified in th: action
is processed by the RequestDataValueProcessor # processAction
method.
[4]
The URL specified in th: href
is processed by the RequestDataValueProcessor # processUrl
method.
When the above HTML is executed on Spring Boot, the following HTML will be generated. The URL of the " form
element's ʻaction attribute" and the "ʻa
element's href
attribute "has a cache avoidance parameter, and the hidden
for linking the CSRF token value in the form
element. You can see that the element is output.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Demo</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.css" type="text/css" />
</head>
<body>
<div class="container">
<h1>Demo</h1>
<div id="demoForm">
<form action="/demo?_s=1488222896002" method="post" class="form-horizontal"> <!--★★★ Cache avoidance parameter is given-->
<div class="form-group">
<label for="title" class="col-sm-1 control-label">Title</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="title" name="title" value="" />
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<button type="submit" class="btn btn-default">Create</button>
</div>
</div>
<input type="hidden" name="_csrf" value="b952a1bf-222a-4a99-b889-6878935a5784" /></form> <!--★★★ Hidden of CSRF token value linkage is given-->
</div>
<a href="/demo?_s=1488222896002">Reload</a> <!--★★★ Cache avoidance parameter is given-->
</div>
</body>
</html>
RequestDataValueProcessor # processUrl
is linked with the redirect function of Spring MVC, so let's check the operation there as well.
package com.example;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping("/demo")
@Controller
public class DemoController {
@ModelAttribute
DemoForm setUpForm() {
return new DemoForm();
}
@GetMapping
String index() {
return "index";
}
@PostMapping
String submit(DemoForm form) {
return "redirect:/demo"; //★★★ Here points[5]
}
static class DemoForm {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
}
[5]
The URL specified when using the Spring MVC redirect function is processed by the RequestDataValueProcessor # processUrl
method.
If you check the redirect URL with your browser's developer tools, you can see that the cache avoidance parameter is properly assigned.
HTTP/1.1 302
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Location: http://localhost:8080/demo?_s=1488223437196 #★★★ Cache avoidance parameter is given
Content-Language: en-US
Content-Length: 0
Date: Mon, 27 Feb 2017 19:23:57 GMT
By creating my own AutoConfigure class, I was able to make CsrfRequestDataValueProcessor
and my own RequestDataValueProcessor
coexist.
Also, by using RequestDataValueProcessor
, it was confirmed that common parameters can be added to URLs handled under Spring MVC. However ... When handling URLs outside of Spring MVC management (for example, when handling the method of HttpServletResponse
directly), it is not under the jurisdiction of RequestDataValueProcessor
, so common parameters cannot be assigned.
If you want to add common parameters to URLs not managed by Spring MVC, you need to create HttpServletResponseWrapper
to add common parameters, wrap it in the class created by Servlet Filter, and call the subsequent processing. I will.
Recommended Posts