跨域问题 后端处理 与 白名单设置

 

 检查nginx跨域配置:add_header X-Permitted-Cross-Domain-Policies all;

1.为什么会跨域
出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)


2.什么是跨域
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

3.前端跨域的表现

 

 

情况1:配置了多次跨域,如在前端vue配置了一次跨域,后端spring zuul配置了一次跨域或nginx配置了一次跨越。此时只需要关闭某几种,留下一种即可。

情况2:单纯是的只配置了spring zuul一次跨域,还出现重复跨域,此时只需要在网关配置文件中加入:

zuul:
ignored-headers: Access-Control-Allow-Credentials, Access-Control-Allow-Origin
情况2的原因:
zuul网关为了解决跨域问题,设置了response的Access-Control-Allow-Origin为客户端orgin,Access-Control-Allow-Origin:http://localhost:8080,然后服务网关访问微服务将response中的Access-Control-Allow-Origin:http://localhost:8080带了过去,微服务为了解决跨域,又在Access-Control-Allow-Origin中加了客户端orgin,Access-Control-Allow-Origin:http://localhost:8080,http://localhost:8080

 

1.nginx配置跨域

server {
listen 8081 ssl;
server_name xxx;

ssl_certificate cert/xxx
ssl_certificate_key cert/xxx;
ssl_session_timeout 30m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;

#允许跨域请求的域,* 代表所有
add_header Access-Control-Allow-Origin *;
#允许请求的header
add_header Access-Control-Allow-Headers X-Requested-With;

#允许请求的方法,比如 GET/POST/PUT/DELETE
add_header Access-Control-Allow-Methods *;


error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}

}

或者
server {
listen 80; # 监听的端⼝
server_name localhost; # 域名或ip
location / { # 访问路径配置
#允许跨域请求的域,* 代表所有
add_header 'Access-Control-Allow-Origin' *;
#允许带上cookie请求
add_header 'Access-Control-Allow-Credentials' 'true';
#允许请求的方法,比如 GET/POST/PUT/DELETE
add_header 'Access-Control-Allow-Methods' *;
#允许请求的header
add_header 'Access-Control-Allow-Headers' *;
root /usr/share/nginx/html;# 根⽬录
index index.html index.htm; # 默认⾸⻚
}
error_page 500 502 503 504 /50x.html; # 错误⻚⾯
location = /50x.html {
root html;
}

PS:如果浏览器中报2个请求头错误,则删除nginx中的Access-Control-Allow-Origin跨域请求头

2.springBoot中配置跨域

1.springboot跨域解决
添加配置以下配置即可解决
@Configuration
public class CorsConfig implements WebMvcConfigurer {

@Bean
public WebMvcConfigurer corsConfigurer()
{
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*") //允许跨域的域名,可以用*表示允许任何域名使用
.allowedMethods("*") //允许任何方法(post、get等)
.allowedHeaders("*") //允许任何请求头
.allowCredentials(true) //带上cookie信息
.exposedHeaders(HttpHeaders.SET_COOKIE)
.maxAge(3600L); //maxAge(3600)表明在3600秒内,不需要再发送预检验请求,可以缓存该结果
}
};
}
}

2 shiro拦截接口实现跨域则需要在Filter中配置如下
在实现BasicHttpAuthenticationFilter类中添加
/**
* 对跨域提供支持
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); //带上cookie信息
// // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
return super.preHandle(request, response);
}

注意 Access-Control-Allow-Credentials 设置为true,本人就是这里没设置,一直被shiro拦截的不能跨域访问,折腾很久

 

 

第一步:

由于shiro是基于过滤器的,所以我们这里继承Filter ,进行跨域处理

package com.guangjutx.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* @Title:CorsFilter
* @Author 丁文浩
* @Date 2020/3/21 13:03
*/
@Component
@Slf4j
public class CORSFilter implements Filter {

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain){
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
//放行所有,类似*,这里*无效
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
//允许请求方式
response.setHeader("Access-Control-Allow-Methods", "POST,PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
//需要放行header头部字段 如需鉴权字段,自行添加,如Authorization
response.setHeader("Access-Control-Allow-Headers", "content-type,x-requested-with,token,Authorization,authorization");
try {
chain.doFilter(request, response);
} catch (Exception e) {
log.error("CORS过滤器放行异常:",e);
}
}

public void init(FilterConfig filterConfig) {
}

public void destroy() {
}
}
第二步:

在shiro拦截时,若未登录等,将会自动重定向到登录或无权限,会出现跨域失效问题,继承BasicHttpAuthenticationFilter 重写preHandle方法,处理跨域

package com.guangjutx.config;

import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* @ClassName AuthenticationFilter
* @Author dwhhome@163.com
* @Date 2020-03-21 16:23
* @Version 1.0
**/
public class AuthenticationFilter extends BasicHttpAuthenticationFilter {

@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpServletRequest httpRequest = (HttpServletRequest) request;
//无条件放行OPTIONS
if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
setHeader(httpRequest, httpResponse);
return true;
}
return super.preHandle(request, response);
}

/**
* 为response设置header,实现跨域
*/
private void setHeader(HttpServletRequest request, HttpServletResponse response) {
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods","POST,PUT,GET,OPTIONS,DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "content-type,x-requested-with,token,Authorization,authorization");
response.setHeader("Content-Type", "application/json;charset=UTF-8");
response.setStatus(HttpStatus.OK.value());
}
}
第三步:

将第二步自定义的AuthenticationFilter注册到shiro中,这一步在shiro配置工厂的地方配置即可

Map<String, Filter> filters = filterFactory.getFilters();
filters.put("authc", new AuthenticationFilter());


 

 

1.跨域的问题是浏览器的原因,允不允许跨域是服务端来决定的。

2.整个跨域请求,包括两个步骤,首先是浏览器发起跨域请求,即option,看看服务端的意思,如果不允许,那就算了,直接报跨域错误,如果允许,那就不客气了,第二步就发起真正的请求。

2.1完成第一步有三种方式

//1.局部允许 在Controller 的方法中使用 @CrossOrigin
@CrossOrigin //允许跨域
@RequestMapping(value ="/getLabelContents.do", produces="application/json;charset=UTF-8")
@ResponseBody
public String getLabelContents(HttpServletRequest request){
return service.getLabelContents(request);
}
//2.全局允许 在 WebMvcConfigurer 中重写 addCorsMappings方法

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
.allowCredentials(true)
.maxAge(3600)
.allowedHeaders("*");
}
//3.通过serlet过滤器

@Component
public class CORSFilter implements Filter {


public void init(FilterConfig filterConfig) {

}

@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
//放行所有,类似*,这里*无效
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
//允许请求方式
response.setHeader("Access-Control-Allow-Methods", "POST,PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
//需要放行header头部字段 如需鉴权字段,自行添加,如Authorization
response.setHeader("Access-Control-Allow-Headers", "content-type,x-requested-with,token,Authorization,authorization");
try {
chain.doFilter(request, response);
} catch (Exception e) {
//log.error("CORS过滤器放行异常:",e);
}
}

public void destroy() {
}


}
【注】1.浏览器只在乎后端有没有允许,谁回复他允许,无所谓。

2.目前使用网上的方法配置shiro过滤器,无法达到允许跨域请求。

2.2 光完成第一步,第二步会被shiro拦截。

// 在 ShiroConfig 中的 shirFilter方法中配置下面的设置,一点用也没有
Map<String, Filter> filtersMap = shiroFilterFactoryBean.getFilters();
filtersMap.put("authc", new TokenFilter()); //财哥 2021/9/4 14:04 加入自定义过滤器
//最直接粗暴的就是,允许匿名访问
filterChainDefinitionMap.put(managerPath + "/xjc/label/getLabelContents.do", "anon");

Springboot配置跨域问题

Access to XMLHttpRequest at 'http://xxxxxx' from origin 'http://localhost:8081' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

跨域问题,后台SpringBoot+Shiro,跨域时GET会发起Options请求,若对Options进行session检查拦截会有问题,因为不会携带token等参数,需要对其过滤不进行处理,需要改动两处:
1.SpringMVC增加response设置


@Component
public class WebInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //允许api跨域
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE,OPTIONS");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "*");

        String method= request.getMethod();

        if (method.equals("OPTIONS")){
            response.setStatus(200);
            return false;
        }
        return true;
    }
}

2.Shiro增加配置,不对OPTIONS进行拦截,同时对于无效会话直接输出json参数,不能通过redirect跳转到别的url输出,否则在浏览器调试时也不支持

public class MyAuthenticationFilter extends FormAuthenticationFilter {
    @Override
    protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
	    String method= httpServletRequest.getMethod();
	    //跨域时OPTIONS请求,此时不会带着token,若跳转就不对了。
	    if (method.equals("OPTIONS")){
		return ;
	    }

	    //WebUtils.issueRedirect(request, response, "/api/nologin");
	    //此处不不能redirect,跨域不能用
	    //API过期,返回过期json
	    JSONObject json = new JSONObject();
	    json.put("code", 2);
	    json.put("msg", "登录会话失效,请重新登录");

	    render((HttpServletResponse) response, "text/plain;charset=UTF-8", json.toString());

        
    }

    public void render(HttpServletResponse response, String contentType,
                       String text) {

        response.setContentType(contentType);
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);

        try {
            response.getWriter().write(text);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            return true;
        }

        return super.preHandle(request,response);
    }
}
JAVA 复制 全屏

MyAuthenticationFilter 需要配置到Shiro配置文件中
map.put("/api/**","user");

    Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
    filters.put("user", new MyAuthenticationFilter());
    shiroFilterFactoryBean.setFilters(filters);

 

 

因为项目中是用springBoot+shiro

shiro发现用户没有登录时,会重定向到/login页面。但是,因为前后端是分离的。所以得给前端返一个未登录

的json消息。

跨域重定向不好处理,貌似jsonp可以解决。这个重定向,返回的header里面有一个

Location... 后面跟的是重定向的地址,浏览器是不会去处理的。

所以。需要重写一些东西。我只重写了量个过滤器:

 shiro配置


 除了以上,在shiroFilter中还需要配置at.pollux.thymeleaf.shiro.dialect.ShiroDialect,为了在thymeleaf里使用shiro的标签的bean

 

SpringBoot跨域请求处理方式
方法一、直接采用SpringBoot的注解@CrossOrigin(也支持SpringMVC)
简单粗暴的方式,Controller层在需要跨域的类或者方法上加上该注解即可

@RestController
@CrossOrigin
@RequestMapping("/user")
public class UserController {

@Autowired
private SituationService situationService;
// log日志信息
private static Logger LOGGER = Logger.getLogger(SituationController.class);
}

方法二、处理跨域请求的Configuration
增加一个配置类,CorsConfig.java。继承WebMvcConfigurerAdapter或者实现WebMvcConfigurer接口,其他都不用管,项目启动时,会自动读取配置。

@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").
allowedOriginPatterns("*"). //允许跨域的域名,可以用*表示允许任何域名使用
// allowedOrigins("*"). //在Springboot2.4对应Spring5.3后在设置allowCredentials(true)的基础上不能直接使用通配符设置allowedOrigins,而是需要指定特定的URL。如果需要设置通配符,需要通过allowedOriginPatterns指定
allowedMethods("GET", "POST", "DELETE", "PUT") . //允许任何方法(post、get等)
allowedHeaders("*"). //允许任何请求头
allowCredentials(true). //带上cookie信息
exposedHeaders(HttpHeaders.SET_COOKIE).maxAge(3600L); //maxAge(3600)表明在3600秒内,不需要再发送预检验请求,可以缓存该结果


}


}

方法三、采用过滤器(filter)的方式
同方法二加配置类,增加一个CORSFilter 类,并实现Filter接口即可,其他都不用管,接口调用时,会过滤跨域的拦截。

package com.shiyun.filter;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class CoresFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void destroy() {

}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, HEAD");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "access-control-allow-origin, authority, content-type, version-info, X-Requested-With");
filterChain.doFilter(servletRequest, servletResponse);
}
}

方法四:使用CorsConfiguration和UrlBasedCorsConfigurationSource
@Configuration
public class CorsConfig1 {
/**
* @Description :
* @Date 11:18 2021/2/21 0021
* @Param * @param :
* @return org.springframework.web.cors.CorsConfiguration
**/
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true);
//允许跨域的域名,可以用*表示允许任何域名使用, 在Springboot2.4对应Spring5.3后在设置allowCredentials(true)的基础上不能直接使用通配符设置allowedOrigins,而是需要指定特定的URL。如果需要设置通配符,需要通过allowedOriginPatterns指定
// corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedOriginPattern("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
return corsConfiguration;
}

/**
* @Description :
* @Date 11:19 2021/2/21 0021
* @Param * @param :
* @return org.springframework.web.filter.CorsFilter
**/
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}

}
方法五:使用FilterRegistrationBean并且设置过滤器设置执行顺序

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

/**
* Classname:CorsFilterConfig
*
* @description:解决跨域请求问题
* @author: 陌意随影
* @Date: 2021-05-30 01:54
* @Version: 1.0
**/
@Configuration
public class CorsFilterConfig {
/**
* @Description :跨域访问过滤器,设置执行顺序
* @Date 19:55 2021/6/15 0015
* @return org.springframework.boot.web.servlet.FilterRegistrationBean<org.springframework.web.filter.CorsFilter>
**/
@Bean
public FilterRegistrationBean<CorsFilter> corsFilterRegistrationBean(){
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOriginPattern("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.setAllowCredentials(true);
source.registerCorsConfiguration("/**", config); // CORS 配置对所有接口都有效
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
//设置执行顺序,数字越小越先执行
bean.setOrder(0);
return bean;
}
}

1、通过实现WebMvcConfigurer接口然后重写addCorsMappings方法解决跨域问题。

@Configuration
public class CorsConfig implements WebMvcConfigurer {

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")// 指定需要跨域处理的URL
.allowedOrigins("*")// 指定允许跨越请求的host
.allowCredentials(true)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")// 指定允许跨越处理的请求方法
.maxAge(3600);
}
}

2、通过配置CorsFilter Bean

@Configuration
public class CorsConfig {

@Bean
public CorsFilter corsFilter() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(urlBasedCorsConfigurationSource);
}

}

3、可通过过滤器实现CorsFilter

@Component
public class MyFilter implements Filter {

private final static Logger logger = LoggerFactory.getLogger(MyFilter.class);

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest reqs = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin",reqs.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
// 根据情况可以去掉delete
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}

4、使用拦截器实现

public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "*");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept");
return true;

}
}

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
public class InterceptorRegister implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
WebMvcConfigurer.super.addInterceptors(registry);
}
}

5、整合ApacheShiro实现跨域

登录后复制

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

 

public class JWTFilter extends BasicHttpAuthenticationFilter {
/**
* 判断用户是否想要登入。 检测header里面是否包含Authentication-Info字段即可
*/
@Override
protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
HttpServletRequest req = (HttpServletRequest) request;
String authorization = req.getHeader("Authentication-Info");
return authorization != null;
}


@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String authorization = httpServletRequest.getHeader("Authentication-Info");

JWTToken token = new JWTToken(authorization);
// 提交给realm进行登入,如果错误他会抛出异常并被捕获
getSubject(request, response).login(token);
// 如果没有抛出异常则代表登入成功,返回true
return true;
}

@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (isLoginAttempt(request, response)) {
try {
executeLogin(request, response);
} catch (Exception e) {
e.printStackTrace();
}
}
return true;
}

/**
* 对跨域提供支持
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Headers",
httpServletRequest.getHeader("Access-Control-Request-Headers"));
// 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
return super.preHandle(request, response);
}
}

 

99然后是我亲测的

WebMvcConfigurer中配置


import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;


@Configuration
@EnableConfigurationProperties(FileUploadProperties.class)
public class WebMvcConfigurer extends WebMvcConfigurationSupport {

/**
* 页面跨域访问Controller过滤
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
WebMvcConfigurer.super.addCorsMappings(registry);
// 设置允许跨域的路由
registry.addMapping("/**")
// 设置允许跨域请求的域名
.allowedOrigins("*")
//表示允许的请求头,默认允许所有的请求头信息
//允许请求头中的header,默认都支持
.allowedHeaders("*")
// 设置允许的方法
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
// 是否允许证书(cookies)
.allowCredentials(true)
//响应头中允许访问的header,默认为空
.exposedHeaders("Access-Control-Allow-Headers",
"Access-Control-Allow-Methods",
"Access-Control-Allow-Origin",
"Access-Control-max-Age",
"X-Frame-Options","Authorization")

//

///maxAge(3600)表明在3600秒内,不需要再发送预检验请求,可以缓存该结果


.maxAge(3600) ;
}

}

新加一个过滤器

CoresFilter

import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

import javax.servlet.Filter;


@Component
public class CoresFilter implements Filter {

@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void destroy() {

}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
//// 设置允许跨域请求的域名
response.setHeader("Access-Control-Allow-Origin", "*");
//// 设置允许的方法
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, HEAD");
///maxAge(3600)表明在3600秒内,不需要再发送预检验请求,可以缓存该结果
response.setHeader("Access-Control-Max-Age", "3600");
//表示允许的请求头,默认允许所有的请求头信息
//允许请求头中的header,默认都支持
response.setHeader("Access-Control-Allow-Headers", "Authorization,access-control-allow-origin, authority, content-type, version-info, X-Requested-With");
////响应头中允许访问的header,默认为空
response.addHeader("Access-Control-Expose-Headers", "Authorization");
filterChain.doFilter(servletRequest, servletResponse);
}



}

99.2补充新增一个白名单

RefererInterceptor文件中设置

import cn.hutool.core.util.StrUtil;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.net.MalformedURLException;

@Component
public class RefererInterceptor extends HandlerInterceptorAdapter {
/**
* 白名单
*/
private String[] refererDomain = new String[]{"127.0.0.1","localhost"};
/**
* 是否开启referer校验
*/
private Boolean check =false;


@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
if (!check) {
return true;
}
String referer = req.getHeader("referer");
String host = req.getServerName();
if (StrUtil.isBlank(referer)) {
return true;
}
java.net.URL url = null;
try {
url = new java.net.URL(referer);
} catch (MalformedURLException e) {
// URL解析异常,也置为403
resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
resp.setCharacterEncoding("UTF-8");
resp.setContentType("application/json; charset=utf-8");
PrintWriter out = resp.getWriter();
out.write("{\"code\":\"403\",\"msg\":\"无权限访问,请求被拒绝\"}");
out.flush();
out.close();
return false;
}
// 首先判断请求域名和referer域名是否相同
if (!host.equals(url.getHost())) {
// 如果不等,判断是否在白名单中
if (refererDomain != null) {
for (String s : refererDomain) {
if (s.equals(url.getHost())) {
return true;
}
}
}
resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
resp.setCharacterEncoding("UTF-8");
resp.setContentType("application/json; charset=utf-8");
PrintWriter out = resp.getWriter();
out.write("{\"code\":\"403\",\"msg\":\"无权限访问,请求被拒绝\"}");
out.flush();
out.close();
return false;
}
return true;
}
}

 

 然后在WebMvcConfigurer 文件中设置

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.zhc.gds.common.interceptor.RefererInterceptor;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.*;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
@Configuration
@EnableConfigurationProperties(FileUploadProperties.class)
public class WebMvcConfigurer extends WebMvcConfigurationSupport {

@Resource
private RefererInterceptor fefererInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(fefererInterceptor);
}
}

 

3.前端配置跨域

 

  • 一、 利用webpack反向代理(此方法适合利用框架搭建的项目,以下例子基于vue搭建的项目

    1. 找到已经搭建完成的项目,进入config文件找到index.js如图

    2. 进入index.js 文件后,开始配置代码如下

    proxyTable: {
          '/api': {
            target: "http://127.0.0.1",    //配置需要跨域的IP地址         
            secure: false,                 //https协议需要配置,如果是https此处改为true
            changeOrigin: true,            //是否跨域,true为允许跨域
            pathRewrite: {
              '^/api': '/'
            }
          }
    }
    

     3. 配置完成之后,在文件中引用的写法如下图

    此处“/api/”是配置跨域时候的“/api/”名字可以改动,并不是固定不变的,但是引用的名字必须和配置时候的名字一样,否则报错

    二、利用Nginx 反向代理

     1. 首先下载Nginx。下载地址:https://github.com/LHDGH/nginx.git

     2. 下载完成,进入config 文件夹,然后找到nginx.config文件,如下图

    3. 打开nginx.config进行配置,如图

    3. 然后在公共文件(所以页面都要引入的文件)当中重新定义IP,如下

    const baseUrl = "http://localhost:8888/xxx";
      其中:
        localhost为第二步当中所配置的server_name;
        8888为第二步中所配置的listen;  
        xxx为第二步中所设置匹配的url,如下图
    4. 配置完成之后,利用终端进入包含有nginx.exe文件的文件夹启动nginx(启动命令如下)
    nginx.exe 回车
    

     如果之前已启动,则需要重启即可,如下

    nginx.exe -s reload

    完成以上步骤就可以用nginx反向代理,解决跨域问题了

    三、利用http-server反向代理

    1. 安装(全局安装加 -g)

    全局安装:npm install http-server -g
    

      在安装完成之后,进入文件执行第2步的时候可能会报错,提示http-server没有安装,我自己的解决方案就是重新安装了一下就好了

      具体想要研究可以访问:https://blog.csdn.net/sinat_41747081/article/details/90301155

    2. 进入需要代理的文件(也就是需要启动服务的文件夹)执行以下命令

    http-server 回车(此步骤默认端口8080)

    如果直接执行此步骤,本地文件可以通过默认的服务器地址为:localhost:8080或127.0.0.1:8080访问但并不能解决跨域问题

    3. 如果想要解决跨域问题,则需要执行以下命令

    http-server -P 需要代理的IP地址 -p 端口号 (可以修改默认的端口)

    例如

    http-server -P https://baidu.com -p 9999 -c-1
    
     
      https://baidu.com 代理的IP地址
      9999 访问的端口号
      -c-1  利用http-server每次浏览器会有缓存,不会及时相应(需要清空缓存)而-c-1 的作用浏览器不会存在缓存
      执行以上命令可以通过 localhost:9999.文件名 访问页面
      在公共js中定义IP 如
     
    const baseUrl = "http://localhost:9999"; 

    完成以上步骤就可以通过http-server实现本地代理进行跨域

    访问本地文件: loaclhost:9999/xxxx.html

    以下是http-server的命令

  •  
  1. function loginOn(){  
  2. var data_={  
  3. username:"admin",  
  4. password:"admin"  
  5. }  
  6. $.ajax({  
  7.     type: "POST",  
  8.     url: "http://192.168.2.240:8091/login",  
  9.     data:data_,  
  10.     xhrFields: {  
  11.         withCredentials: true // 携带跨域cookie  
  12.     },  
  13.     /*processData: false,*/  //测试时,这个如果带上,返回头Response里面会拿不到Cookie,Cookie里面存放了sessionid  
  14.     success: function(data) {  
  15.         console.log(data);    
  16.     }  
  17. });  
  18. }  

 

posted @ 2022-09-27 10:25  全琪俊  阅读(5910)  评论(0编辑  收藏  举报