我们正在使用SpringBoot 2.7构建一个简单的应用程序,并且遇到了CSRF令牌设置的问题。用户操作(POST请求)返回403 forbidden。这包含在AWS ecs集群中,并运行SpringBoot 2.7和SpringBootSecurity 5.8。csrf令牌阻止了post操作,我不确定正确的设置是什么样子。我们只能在禁用CSRF的情况下执行操作。
编辑:进一步排查后,我们可以看到文件上传的post请求中没有xsrf令牌响应头。
这是当前的设置。
SpringBoot依赖关系:
implementation("org.springframework.boot:spring-boot-starter-web:2.7.12")
implementation("org.springframework.boot:spring-boot-starter-security:2.7.12")
implementation("io.jsonwebtoken:jjwt:0.9.1")
implementation("com.jcraft:jsch:0.1.54")
implementation("software.amazon.awssdk:secretsmanager:2.20.79")
implementation("org.springframework.security:spring-security-web:5.8.0")
implementation("org.springframework.security:spring-security-config:5.8.0")
implementation("org.springframework.security:spring-security-core:5.8.0")
implementation("com.sun.xml.bind:jaxb-core:2.3.0.1")
implementation("javax.xml.bind:jaxb-api:2.3.1")
implementation("com.sun.xml.bind:jaxb-impl:2.3.1")
JWT过滤器:
package com.place.sftp.portal.security;
import com.place.sftp.portal.constant.Path;
import com.place.sftp.portal.model.SecretHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class JwtFilter extends OncePerRequestFilter
{
private static final String AUTHORIZATION = "Authorization";
private static final String BEARER = "Bearer ";
private static final String OPTIONS = "OPTIONS";
@Autowired
private SecretHolder secretHolder;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException
{
if(
Path.AUTHENTICATE_FULL.equals(request.getRequestURI()) ||
Path.IS_CONNECTED_FULL.equals(request.getRequestURI()) ||
OPTIONS.equals(request.getMethod()))
{
logger.info("start: doFilterInternal");
CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
response.setHeader(csrfToken.getHeaderName(), csrfToken.getToken());
filterChain.doFilter(request, response);
}
else
{
logger.info("start: processJWT");
processJWT(request, response, filterChain);
}
}
private void processJWT(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException
{
logger.info("Processing JWT");
String authorizationHeader = request.getHeader(AUTHORIZATION);
if(validAuthHeader(authorizationHeader))
{
String token = authorizationHeader.substring(7);
try
{
validateJWT(token, filterChain, request, response);
logger.info("JWT validated successfully");
}
catch (Exception e)
{
logger.error("error in if ",e);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
else
{
logger.info("Error in else");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
private boolean validAuthHeader(String authorizationHeader)
{
return authorizationHeader != null && authorizationHeader.startsWith(BEARER);
}
private void validateJWT(String token, FilterChain filterChain,
HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
logger.info("starting: JWT Validation");
logger.info("token is");
logger.info(token);
logger.info("secret is");
//logger.info(secretHolder.getSecret());
UsernamePasswordAuthenticationToken auth = SECRET_VALUE_HERE
logger.info("got auth");
SecurityContextHolder.getContext().setAuthentication(auth);
logger.info("set security context successfully");
CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
logger.info("ending: JWT Validation");
response.setHeader(csrfToken.getHeaderName(), csrfToken.getToken());
filterChain.doFilter(request, response);
}
}
安全配置:
package com.place.sftp.portal.security;
import com.place.sftp.portal.constant.Path;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SimpleSavedRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Configuration
@EnableWebSecurity
public class SecurityConfig
{
private final Logger logger = LogManager.getLogger(SecurityConfig.class);
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception
{
logger.info("Configure CSRF Token.");
http
.authorizeHttpRequests((authz) -> authz.requestMatchers("/", "/index.html", "/static/**",
"/*.ico", "/*.json", "/*.png", "/api/isconnected", "/api/listchildnodes","/api/authenticate","/api/upload").permitAll()
.anyRequest().authenticated()
)
.csrf((csrf) -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
// https://stackoverflow.com/a/74521360/65681
.csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())
)
.addFilterAfter(new JwtFilter(), BasicAuthenticationFilter.class);
logger.info("Authorize CSRF Token.");
return http.build();
}
@Bean
public RequestCache refererRequestCache() {
return new HttpSessionRequestCache() {
@Override
public void saveRequest(HttpServletRequest request, HttpServletResponse response) {
String referrer = request.getHeader("referer");
if (referrer == null) {
referrer = request.getRequestURL().toString();
}
request.getSession().setAttribute("SPRING_SECURITY_SAVED_REQUEST",
new SimpleSavedRequest(referrer));
}
};
}
}
前台岗位:
const UploadModal = ({ open, handleClose }: UploadModalProps) => {
const [fileData, setFileData] = useState<File | null>(null);
const folderPath = "";
const [cookies] = useCookies(["XSRF-TOKEN"]);
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.files) {
setFileData(event.target.files[0]);
}
};
const handleFileUpload = async () => {
if (fileData) {
const formData: FormData = new FormData();
formData.append("file", fileData);
formData.append("folderPath", folderPath);
try {
const response = await axios.post(
`${process.env.API_URL}/api/upload`,
formData,
{
withCredentials: true,
headers: { "X-XSRF-TOKEN": cookies["XSRF-TOKEN"] }
}
);
if (response) {
toast.success(
"File Upload Successfully",
successToastOptions
);
setFileData(null);
} else {
toast.error("File Upload Failed", errorToastOptions);
}
} catch (error) {
toast.error("File Upload Failed", errorToastOptions);
}
}
};
1条答案
按热度按时间nmpmafwu1#
你试过用Stack Overflow answer吗?它建议将这一行添加到安全配置的过滤器链中:
CsrfTokenResponseHeaderBindingFilter.java定义为: