CSRF全称Cross Site Request Forgery,中文是跨站请求伪造。
Spring Security结合Thymeleaf templates,会在form中加入隐藏字段(token),从而减缓csrf威胁。
依赖配置
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies
定制后的表单
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<body>
<form method="post" th:action="@{/jwt-csrf-form}">
<input type="submit" class="btn btn-primary" value="Click Me!"/>
</form>
</body>
</html>
xmlns:th
和th:action
可以让Spring Security触发,从而注入token
JWT
通过JWT改进token
首先,配置依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
生成jwt token
@Override
public CsrfToken generateToken(HttpServletRequest request) {
String id = UUID.randomUUID().toString().replace("-", "");
Date now = new Date();
Date exp = new Date(now.getTime() + (1000*30)); // 30 seconds
String token = Jwts.builder()
.setId(id)
.setIssuedAt(now)
.setNotBefore(now)
.setExpiration(exp)
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
return new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", token);
}
csrf token验证器
// CsrfFilter already made sure the token matched. Here, we'll make sure it's not expired
try {
Jwts.parser()
.setSigningKeyResolver(secretService.getSigningKeyResolver())
.parseClaimsJws(token.getToken());
} catch (JwtException e) {
// most likely an ExpiredJwtException, but this will handle any
request.setAttribute("exception", e);
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
RequestDispatcher dispatcher = request.getRequestDispatcher("expired-jwt");
dispatcher.forward(request, response);
}
配置spring security
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterAfter(new JwtCsrfValidatorFilter(), CsrfFilter.class)
.csrf()
.csrfTokenRepository(jwtCsrfTokenRepository)
.ignoringAntMatchers(ignoreCsrfAntMatchers)
.and()
.authorizeRequests()
.antMatchers("/**")
.permitAll();
}