我使用ssm框架开发了一个演示网站,并使用spring.security进行身份验证。我能够使用post请求登录到站点,并使用get请求获取数据。但是,我无法使用post请求添加数据。它总是被禁止的。如果csrf被禁用,则正常。我尝试过以下方法,但没有一种有效。
在标题中添加“x-csrf-token:csrf值”。
在标题中添加“\u csrf:csrf值”。
将_csrf添加到post请求正文。
那么,在启用csrf的情况下,如何使post请求工作?此外,我不确定是否需要使用不同的帖子重新生成csrf令牌。奇怪的是,登录post请求是有效的。
应用程序属性
spring.datasource.url=jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
mybatis.mapper-locations=classpath:mybatis/*.xml
spring.security.user.name=root
spring.security.user.password=123456
debug=true
证券配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
}
用户控制器
@RestController
@RequestMapping("/rest/users")
public class UserController {
@Autowired
private UserMapper userMapper;
@PostMapping
public void add(@RequestBody User user){
userMapper.add(user);
}
@GetMapping
public List<User> getAll(){
return userMapper.findAll();
}
}
curl 脚本
# !/usr/bin/env bash
host=192.168.44.109:8080
remote=http://${host}
login=${remote}/login
users=${remote}/rest/users/
csrf=$( \
curl --url ${login} -L -c cookie.txt 2>&1 \
|grep _csrf \
|sed 's/^.*value="\(.*\)".*$/\1/' \
)
echo "before login, csrf=${csrf}"
curl --url ${login} -L -b cookie.txt -c cookie.txt -i \
-d "username=root&password=123456&_csrf=${csrf}"
curl --url ${users} -L -b cookie.txt -v \
-H "Content-Type: application/json" \
-H "X-CSRF-Token: ${csrf}" \
-H "x-csrf-token: ${csrf}" \
-H "_csrf: ${csrf}" \
-d "{\"name\": \"name2\"}"
rm -f cookie.txt
脚本输出
before login, csrf=6d7b2d7b-f9aa-4463-ad9b-468082df4d74
HTTP/1.1 302
Set-Cookie: JSESSIONID=D728694163DEEC78DDBC8869DC54C870; Path=/; HttpOnly
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://192.168.44.109:8080/
Content-Length: 0
Date: Sun, 20 Jun 2021 11:05:52 GMT
HTTP/1.1 200
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
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
Content-Type: application/hal+json
Transfer-Encoding: chunked
Date: Sun, 20 Jun 2021 11:05:52 GMT
{
"_links" : {
"profile" : {
"href" : "http://192.168.44.109:8080/profile"
}
}
}* Trying 192.168.44.109...
* TCP_NODELAY set
* Connected to 192.168.44.109 (192.168.44.109) port 8080 (#0)
> POST /rest/users/ HTTP/1.1
> Host: 192.168.44.109:8080
> User-Agent: curl/7.64.1
> Accept: */*
> Cookie: JSESSIONID=D728694163DEEC78DDBC8869DC54C870
> Content-Type: application/json
> X-CSRF-Token: 6d7b2d7b-f9aa-4463-ad9b-468082df4d74
> x-csrf-token: 6d7b2d7b-f9aa-4463-ad9b-468082df4d74
> _csrf: 6d7b2d7b-f9aa-4463-ad9b-468082df4d74
> Content-Length: 17
>
* upload completely sent off: 17 out of 17 bytes
< HTTP/1.1 403
< 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
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Sun, 20 Jun 2021 11:05:52 GMT
<
* Connection #0 to host 192.168.44.109 left intact
{"timestamp":"2021-06-20T11:05:52.540+00:00","status":403,"error":"Forbidden","message":"","path":"/rest/users/"}* Closing connection 0
1条答案
按热度按时间rqmkfv5c1#
是否需要使用不同的POST重新生成csrf令牌
不,没有必要。
奇怪的是,登录post请求是有效的
登录后,spring.security将重新生成令牌。
那么,在启用csrf的情况下,如何使post请求工作?
关键部分是如何获取登录后生成的新csrf令牌。一种可能的解决方案是将csrf令牌保存在cookie中。和securityconfig应更改为:
然后,可以通过以下方式从cookie中获取csrf令牌:
awk '/XSRF-TOKEN/{print $7}' cookie
并使用x-xsrf-token请求头发送到服务器。整个脚本更改为: