一、问题描述
在项目中使用token取代session实现登录鉴权时,Vue中每次请求都会带有携带token的请求头Authorization
,此时前端会发送两次请求,在跨域请求中,浏览器会首先发送一个预检请求(Preflight Request)来检查服务器是否允许跨域请求。预检请求是一个 OPTIONS 请求,其中包含一个 Access-Control-Request-Headers
头部信息,用于列出实际请求中会包含的请求头。服务器需要正确响应预检请求,并在响应中包含 Access-Control-Allow-Headers
头部信息,以允许实际请求中包含列出的请求头。 如果在预检请求中使用拦截器检查请求头信息,但只有第一次预请求会被拦截,可能是因为您的服务器没有正确响应预检请求。在预检请求中,浏览器会发送一个 Access-Control-Request-Headers
头部信息来列出实际请求中会包含的请求头。如果服务器没有正确响应预检请求,即没有包含 Access-Control-Allow-Headers
头部信息,浏览器就会认为服务器不允许实际请求中包含列出的请求头,从而不会发送实际请求,也就不会触发拦截器。 因此,如果希望拦截器能够检查实际请求中包含的请求头信息,需要确保服务器正确响应预检请求,并在响应中包含 Access-Control-Allow-Headers
头部信息。您可以在 addCorsMappings
方法中使用 allowedHeaders
方法来指定允许跨域请求中包含的请求头。同时,还需要在拦截器中检查请求头信息,并根据需要进行处理。
二、解决
首先需要在配置类中进行跨域配置
/**
* 将拦截器配置入spring
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
/**
* 解决前端跨域问题
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowCredentials(true) // 允许携带cookie
.allowedHeaders("*") // 允许跨域请求中包含 token 头部信息
.allowedMethods("*")
.allowedOrigins("http://localhost:7070");// 跨域地址
}
}
然后现在我们需要去拦截器里处理预请求问题,以及真实请求到达服务器时的鉴权处理
package com.example.demo.interceptor;
import com.example.demo.component.RedisUtil;
import com.example.demo.component.TokenUtil;
import com.example.demo.model.User;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;
/**
* 对用户登录权限的校验进行统一拦截处理
*/
public class LoginInterceptor implements HandlerInterceptor {
/**
* 对用户的访问请求进行统一的登录校验
* @param request
* @param response
* @param handler
* @return false 拦截请求 true 请求继续
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (request.getMethod().equals("OPTIONS")) { // 如果是预检请求
String headers = request.getHeader("Access-Control-Request-Headers"); // 获取请求头信息
if (headers != null && headers.contains("token")) { // 检查请求头信息是否包含 Authorization
response.setHeader("Access-Control-Allow-Headers", "token"); // 在响应中包含 Access-Control-Allow-Headers 头部信息,允许实际请求中包含 Authorization 请求头
return true;
}
}
// 此处做鉴权处理
return true;
}
}