通用后端跨域方法
1、@crossorigin 注解
在spring boot 中给我们提供了一个注解 @crossorigin 来实现跨域,这个注解可以实现方法级别的细粒度的跨域控制。
我们可以在类或者方添加该注解,如果在类上添加该注解,该类下的所有接口都可以通过跨域访问,如果在方法上添加注解,那么仅仅只限于加注解的方法可以访问。
@slf4j
@restcontroller
@requestmapping(value = apppath.service_location_url + "/appointment")
@api(value = "appointmentcontroller",tags = "预约列表接口")
@crossorigin
public class appointmentcontroller {
@autowired
private liveappointmentservice appointmentservice;
@requestmapping
@apioperation(value = "预约列表分页查询", response = csliveappointmentdto.class)
public jsonresult<pageinfo> getappointmentlist(appointmentlistdto dto){
log.info("getappointmentlist vo:{}", jsonutil.tojsonstr(dto));
pageinfo<csliveappointmentdto> appointmentlist = appointmentservice.getappointmentlist(dto);
return jsonresult.success(appointmentlist);
}
}@crossorigin 注解不生效问题
在spring框架4.2版本后,spring给出了注解的方式解决问题。
即在controller控制器中,在controller注解上方添加@crossorigin注解。
但是使用这种方式后也有可能仍然出现跨域问题,解决方案就是:
- 在@requestmapping注解中没有指定get、post方式,或者使用@getmapping或者@post mapping
- 在@crossorigin(methods = {requestmethod.post})指定方法
@slf4j
@restcontroller
@requestmapping(value = apppath.service_location_url + "/appointment")
@api(value = "appointmentcontroller",tags = "预约列表接口")
@crossorigin
public class appointmentcontroller {
@autowired
private liveappointmentservice appointmentservice;
@apioperation(value = "预约列表分页查询", response = csliveappointmentdto.class)
//@getmapping("getlist")
@requestmapping(method = requestmethod.get)
public jsonresult<pageinfo> getappointmentlist(appointmentlistdto dto){
log.info("getappointmentlist vo:{}", jsonutil.tojsonstr(dto));
pageinfo<csliveappointmentdto> appointmentlist = appointmentservice.getappointmentlist(dto);
return jsonresult.success(appointmentlist);
}
}2、springboot2.0 实现webmvcconfigurer 实现跨域
@configuration
public class webmvcconfig implements webmvcconfigurer {
@override
public void addcorsmappings(corsregistry registry) {
registry.addmapping("/**")
.allowedorigins("*")
.allowedmethods("post","get","options")
.allowedheaders("*")
.allowcredentials(false).maxage(3600);
}
}
3、过滤器实现跨域
@webfilter(filtername = "corsfilter")
@configuration
@order(ordered.highest_precedence)
@slf4j
public class corsfilter implements filter {
@value("${allow.headers:x-requested-with,authorization,content-type}")
private string allowheaders;
@value("${allow.origin:https://xxx.com}")
private string alloworigin;
@override
public void dofilter(servletrequest req, servletresponse res, filterchain chain) throws ioexception, servletexception {
httpservletresponse response = (httpservletresponse) res;
// response.setheader("access-control-allow-origin", "*");
response.setheader("access-control-allow-origin", "http://xxx:9091");
response.setheader("access-control-allow-credentials", "true");
response.setheader("access-control-allow-methods", "post, get, patch,options, delete, put");
response.setheader("access-control-max-age", "3600");
response.setheader("access-control-allow-headers", allowheaders);
// response.setheader("access-control-allow-headers", "*");
log.info("corsfilter res {},{}", response.getheader("access-control-allow-origin"), response.containsheader("access-control-allow-origin"));
chain.dofilter(req, res);
}
}
跨域不生效问题
(1)、@order(ordered.highest_precedence)如果有登录拦截,要将跨域filter等级提升为最高优先级
(2)、 response.setheader(“access-control-allow-headers”, “");
- access-control-allow-headers: * 在部分客户端上有兼容问题,mdn中介绍 access-control-allow-headers: * 有两重意思。
- 一个是在服务端设置access-control-allow-credentials: true的时候这个 * 只会被客户端当做字符串 * (我们不希望的,会出错的)。
- 另一个是没有这个设置则会被当做通配符(我们希望的,不会出错的)。
- 猜测是客户端对于 * 的实现上有兼容性问题,所以建议不要这样设置,用到什么设置什么最好,例如:access-control-allow-headers: content-type,x-requested-with,authorization。
(3)、 response.setheader(“access-control-allow-origin”, "”)
//指定允许其他域名访问 ‘access-control-allow-origin:http://172.80.0.206'//一般用法(,指定域,动态设置),3是因为不允许携带认证头和cookies //是否允许后续请求携带认证信息(cookies),该值只能是true,否则不返回
(4)、 response.setheader(“access-control-allow-methods”, “post, get, patch,options, delete, put”);options 在预检请求复杂请求中也会使用到
(5)、 如果有spring security结合使用需要添加该过滤器
@configuration
public class securityconfig extends websecurityconfigureradapter {
@override
protected void configure(httpsecurity security) throws exception {
security.csrf().disable();
security.headers().frameoptions().disable();
//加入过滤器
security.addfilterbefore(new corsfilter(), usernamepasswordauthenticationfilter.class);
}
}4、定制化参数实现跨域
前面要么是*,实际需求是根据业务参数定制化
@webfilter(filtername = "corsfilter", urlpatterns = "/*",
initparams = {@webinitparam(name = "alloworigin", value = "*"),
@webinitparam(name = "allowmethods", value = "get,post,put,delete,options"),
@webinitparam(name = "allowcredentials", value = "true"),
@webinitparam(name = "allowheaders", value = "content-type,x-token")})
public class corsfilter implements filter {
private string alloworigin;
private string allowmethods;
private string allowcredentials;
private string allowheaders;
private string exposeheaders;
@override
public void init(filterconfig filterconfig) throws servletexception {
alloworigin = filterconfig.getinitparameter("alloworigin");
allowmethods = filterconfig.getinitparameter("allowmethods");
allowcredentials = filterconfig.getinitparameter("allowcredentials");
allowheaders = filterconfig.getinitparameter("allowheaders");
exposeheaders = filterconfig.getinitparameter("exposeheaders");
}
@override
public void dofilter(servletrequest servletrequest, servletresponse servletresponse, filterchain filterchain) throws ioexception, servletexception {
httpservletrequest request = (httpservletrequest) servletrequest;
httpservletresponse response = (httpservletresponse) servletresponse;
if (!stringutils.isempty(alloworigin)) {
if(alloworigin.equals("*")){
// 设置哪个源可以访问
response.setheader("access-control-allow-origin", alloworigin);
}else{
list<string> alloworiginlist = arrays.aslist(alloworigin.split(","));
if (alloworiginlist != null && alloworiginlist.size() > 0) {
string currentorigin = request.getheader("origin");
if (alloworiginlist.contains(currentorigin)) {
response.setheader("access-control-allow-origin", currentorigin);
}
}
}
}
if (!stringutils.isempty(allowmethods)) {
//设置哪个方法可以访问
response.setheader("access-control-allow-methods", allowmethods);
}
if (!stringutils.isempty(allowcredentials)) {
// 允许携带cookie
response.setheader("access-control-allow-credentials", allowcredentials);
}
if (!stringutils.isempty(allowheaders)) {
// 允许携带哪个头
response.setheader("access-control-allow-headers", allowheaders);
}
if (!stringutils.isempty(exposeheaders)) {
// 允许携带哪个头
response.setheader("access-control-expose-headers", exposeheaders);
}
filterchain.dofilter(servletrequest, servletresponse);
}
@override
public void destroy() {
}
}5、 使用springcloud网关gateway实现跨域
原理和前面类似
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.web.cors.corsconfiguration;
import org.springframework.web.cors.reactive.corswebfilter;
import org.springframework.web.cors.reactive.urlbasedcorsconfigurationsource;
import org.springframework.web.util.pattern.pathpatternparser;
@configuration
public class corsconfig {
@bean
public corswebfilter corsfilter() {
corsconfiguration config = new corsconfiguration();
config.setallowcredentials(boolean.true);//允许cookie跨域
config.addallowedmethod("*");
config.addallowedorigin("*");
config.addallowedheader("*");//不要设置成*,参考前面
urlbasedcorsconfigurationsource source = new urlbasedcorsconfigurationsource(new pathpatternparser());
source.registercorsconfiguration("/**", config);
return new corswebfilter(source);
}
}
注:在下层服务不需要在做任何跨域配置,例如注解@crossorigin,否则会由于配置冲突导致依然出现跨域问题
6、nginx配置代理解决跨域问题
server {
listen 8000;
server_name localhost;
# / 表示匹配路径为/的url
location / {
proxy_pass http://需要跨域的域名:5500;
}
# /user 表示访问以/user 开头 的地址 如/username,/user/find等
location /user {
proxy_pass http://需要跨域的域名:3000;
}
}7、nginx配置响应头允许跨域
#
# wide-open cors config for nginx
#
location / {
#### 对options请求,会设置很多的请求头,并返回204
if ($request_method = 'options') {
add_header 'access-control-allow-origin' '*';
add_header 'access-control-allow-methods' 'get, post, options';
#
# custom headers and headers various browsers *should* be ok with but aren't
#
add_header 'access-control-allow-headers' 'dnt,user-agent,x-requested-with,if-modified-since,cache-control,content-type,range';
#
# tell client that this pre-flight info is valid for 20 days
#
add_header 'access-control-max-age' 1728000;
add_header 'content-type' 'text/plain; charset=utf-8';
add_header 'content-length' 0;
return 204;
}
if ($request_method = 'post') {
add_header 'access-control-allow-origin' '*';
add_header 'access-control-allow-methods' 'get, post, options';
add_header 'access-control-allow-headers' 'dnt,user-agent,x-requested-with,if-modified-since,cache-control,content-type,range';
add_header 'access-control-expose-headers' 'content-length,content-range';
}
if ($request_method = 'get') {
add_header 'access-control-allow-origin' '*';
add_header 'access-control-allow-methods' 'get, post, options';
add_header 'access-control-allow-headers' 'dnt,user-agent,x-requested-with,if-modified-since,cache-control,content-type,range';
add_header 'access-control-expose-headers' 'content-length,content-range';
}
}
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论