首先配置允許跨域
@Configuration
public class CorsConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT")
.maxAge(3600);
}
}
因項目需對用戶校驗,因此配置了過濾器,
然後出現問題:
**1.過濾器攔截後,頁面提示沒有跨域
2.在過濾器配置跨域後,返回responsebody是空的**
代碼如下:
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@Component
@WebFilter(filterName = "sessionFilter", urlPatterns = {"/*"})
public class SessionFilter implements Filter {
@Value("${manage.session.timeout}")
private long timeout;
private final ObjectMapper mapper = new ObjectMapper();
//不需要登錄就可以訪問的路徑(比如:註冊登錄等)
String[] includeUrls = new String[]{
"/web-manage/su/submitLogin", "/su/submitLogin"};
/**
* 登錄校驗
*
* @param servletRequest
* @param servletResponse
* @param filterChain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
log.info("Headers:{}", response.getHeader("Access-Control-Request-Headers"));
//String origin = request.getHeader("Origin");
//if (origin == null) {
// origin = request.getHeader("Referer");
//}
//response.setHeader("Access-Control-Allow-Origin", origin);
//使用通配符responseBody是空的!放開上段代碼後項目正常
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "*");
response.setHeader("Access-Control-Allow-Headers", "Content-Type,Access-Token");
response.setHeader("Access-Control-Expose-Headers", "*");
String uri = request.getRequestURI();
String jsessionId = "";
log.info("uri :{}, request.getContentType(): {}", uri, request.getContentType());
//是否需要過濾
boolean needFilter = isNeedFilter(uri, includeUrls);
if (!needFilter) {
//不需要過濾直接傳給下一個過濾器
filterChain.doFilter(servletRequest, servletResponse);
} else {
Cookie[] cookies = request.getCookies();
if (cookies == null || cookies.length == 0) {
cookieNoJessionid(response);
} else {
for (Cookie cookie : cookies) {
if (CommonConstant.JSESSIONID.equals(cookie.getName())) {
jsessionId = cookie.getValue();
break;
}
}
log.info("ContentType:" + request.getContentType());
if (request.getMethod().equals("OPTIONS")) {
response.setStatus(HttpStatus.OK.value());
return;
}
RequestWrapper requestWrapper = null;
if (request instanceof HttpServletRequest) {
if ("POST".equals(request.getMethod().toUpperCase())
&& request.getContentType() != null
&& request.getContentType().contains("application/json")) {
requestWrapper = new RequestWrapper(request);
}
}
judgeLogin(jsessionId, response, request, requestWrapper, filterChain);
}
}
}
/**
* 判斷是否已登錄
*
* @param jsessionId
* @param response
* @param request
* @param requestWrapper
* @param filterChain
* @throws IOException
* @throws ServletException
*/
private void judgeLogin(String jsessionId, HttpServletResponse response, HttpServletRequest request,
RequestWrapper requestWrapper, FilterChain filterChain) throws IOException, ServletException {
// 判斷 jsessionId 是否爲空
if (StringUtils.isBlank(jsessionId)) {
cookieNoJessionid(response);
} else {
// 獲取ValueOperations bean
ServletContext context = request.getServletContext();
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(context);
ValueOperations valueOperations = (ValueOperations) ctx.getBean("valueOperations");
// 獲取用戶信息
String userBoStr = (String) valueOperations.get(RedisConstant.REDIS_LOGIN + jsessionId);
SysUserBo sysUserBo = mapper.readValue(userBoStr, SysUserBo.class);
if (sysUserBo != null) {
filterChain.doFilter(request == null ? requestWrapper : request, response);
} else {
// 返回登錄超時提醒
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter().write(this.mapper.writeValueAsString(ResultUtil.errorResult(
ExceptionEnum.ERROR_PARAMETERS.getCode(), "jsessionid 登錄超時")));
}
}
}
/**
* cookie 無jsessionid
* @param response
* @throws IOException
*/
private void cookieNoJessionid(HttpServletResponse response) throws IOException {
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter().write(this.mapper.writeValueAsString(ResultUtil.errorResult(
ExceptionEnum.ERROR_PARAMETERS.getCode(),
"jsessionid 爲空!")));
}
/**
* @param uri
* @Description: 是否需要過濾
*/
public boolean isNeedFilter(String uri, String[] includeUrls) {
for (String includeUrl : includeUrls) {
if (includeUrl.equals(uri)) {
return false;
}
}
return true;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
百度一波,給出的解釋是:
出自:https://www.cnblogs.com/nopnog/articles/9133115.html
Access-Control-Allow-Origin 的正確玩法
前端發起的跨域請求需要適當地響應 Access-Control-Allow-Origin 頭。但是這個頭目前並不支持部分通配符,無法匹配某個域名下的子域。直接使用星號又可能帶來各種問題。更坑爹的是它還不支持設置多個值。那麼如果一個後端程序用於多個域名的訪問該怎麼辦呢?
很多時候一套後端的程序都對應了多個調用方,它們使用的的域名不同,scheme 可能也不同。所以 Access-Control-Allow-Origin 的值沒法寫死一個域名。如果直接暴力地將其設置爲「」,可能導致一些未被授權的域名也能請求到資源。而且當 Access-Control-Allow-Credentials 的值爲 true 時會導致Access-Control-Allow-Origin 無法被設置爲「」。
正確的玩法應該是在後端程序中獲取從 HTTP 請求頭傳過來的 Origin 字段,然後在程序中驗證它的值是否合法,並且做出適當的響應。也就是說,可以不依賴前端的跨域限制,後端如果認爲一個請求的 Origin 來自不正確的地方就直接毫不留情地 403 掉。其它情況如果沒有 Origin 或者 Origin 正確則將 Origin 的值原原本本地放入 Access-Control-Allow-Origin 中響應回去。
其實雖然這個問題可以完全通過後端解決,但我還是很費解規範中無法使用局部通配符,和無法配置多個值的設定。也許這麼設計的目的就是希望後端直接 403,而不是讓瀏覽器來攔截吧(反正讓瀏覽器來攔截還需要額外的傳輸成本,有點浪費)