目录
2.CorsFilter , spring webmvc 中给出过滤器层面的跨域
3.CorsWebFilter ,spring webflux中的过滤器
6.继承 HandlerInterceptorAdapter
一、跨域
1. @CrossOrigin
解决单个controller跨域问题,直接在需要跨域的Controller的类上添加 @CrossOrigin 跨域注解即可。
@CrossOrigin //在controller类上添加此注解
@CrossOrigin
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/{id}")
public User get(@PathVariable Long id) {
}
}
2.CorsFilter , spring webmvc 中给出过滤器层面的跨域
当一个模块中controlller过多时,添加注解过于繁琐,可以创建一个配置类对象,进行跨域设置,'' FilterRegistrationBean<Corsfilter> ''。
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* FilterRegistrationBean<CorsFilter> 这个是web MVC中给出的过滤器
* 解决多个Controller中的跨域问题
*/
@Configuration
public class CorsFilterConfig {
@Bean
public FilterRegistrationBean<CorsFilter>
filterFilterRegistrationBean(){
UrlBasedCorsConfigurationSource configSource =
new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration =
new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.setAllowCredentials(true);
configSource.registerCorsConfiguration("/**",
corsConfiguration);
FilterRegistrationBean<CorsFilter> fBean =
new FilterRegistrationBean<>(
new CorsFilter(configSource));
fBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return fBean;
}
}
3.CorsWebFilter ,spring webflux中的过滤器
一个网关管理多个服务时,设置此配置类对象,可以从网关层面解决多个服务的跨域问题,这就不需要每个服务都写一遍跨域了。
import org.springframework.context.annotation.Bean;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
/**
* CorsWebFilter这个是spring webflux中的过滤器
* 可以解决网关层面多个服务的跨域问题
*/
@Configuration
public class CorsFilterConfig {
@Bean
public CorsWebFilter corsWebFilter(){
UrlBasedCorsConfigurationSource configSource =
new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.setAllowCredentials(true);
configSource.registerCorsConfiguration("/**",config);
return new CorsWebFilter(configSource);
}
}
4.配置文件 进行跨域设置
spring:
cloud:
gateway:
globalcors: #跨域配置
corsConfigurations:
'[/**]':
allowedOrigins: "*"
allowedHeaders: "*"
allowedMethods: "*"
allowCredentials: true
5.Filter方式进行设置
@WebFilter
public class CorsFilter implements Filter {
@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-Methods", "*");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(req, res);
}
}
6.继承 HandlerInterceptorAdapter
@Component
public class CrossInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
return true;
}
}
7.实现 WebMvcConfigurer
@Configuration
@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
public class AppConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 拦截所有的请求
.allowedOrigins("http://www.abc.com") // 可跨域的域名,可以为 *
.allowCredentials(true)
.allowedMethods("*") // 允许跨域的方法,可以单独配置
.allowedHeaders("*"); // 允许跨域的请求头,可以单独配置
}
}
8.SpringBoot跨域配置
8.1
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;
/**
* @Author:
* @CreateTime: 2022-11-28 13:26
* @Description: 配置跨域
*/
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.setAllowCredentials(true);
config.addAllowedMethod("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/**", config);
return new CorsFilter(configSource);
}
}
8.2
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;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class CorsConfig{
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true); // 允许cookies跨域
config.addAllowedOrigin("*");// #允许向该服务器提交请求的URI,*表示全部允许,自定义可以添加多个,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
config.addAllowedHeader("header1");// #允许访问的头信息,*表示全部,可以添加多个
config.setMaxAge(18000L);// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
config.addAllowedMethod("OPTIONS");// 允许提交请求的方法,*表示全部允许,一般OPTIONS,GET,POST三个够了
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
二、拦截器
1.实现WebMvcConfigurer接口的配置类,重写其中的addCorsMappings()方法【配置跨域信息】和addInterceptors()方法【配置拦截器信息,如拦截路径和开放路径等】
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @Author:
* @CreateTime: 2022-11-24 14:08
* @Description: 定义拦截器
*/
@Configuration//标识这是一个配置类
public class InterceptorAdapterConfig implements WebMvcConfigurer {
/**
* 重写addCorsMappings()解决跨域问题
* 配置:允许http请求进行跨域访问
*
* @param registry
* @Author
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")//指哪些接口URL需要增加跨域设置
.allowedOrigins("*")//指的是前端哪些域名被允许跨域
.allowCredentials(true)//需要带cookie等凭证时,设置为true,就会把cookie的相关信息带上
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")//指的是允许哪些方法
.maxAge(3600);//cookie的失效时间,单位为秒(s),若设置为-1,则关闭浏览器就失效
}
/**
* 重写addInterceptors()实现拦截器
* 配置:要拦截的路径以及不拦截的路径
*
* @param registry
* @Author
*/
@Override
public void addInterceptors(InterceptorRegistry interceptorRegistry) {
//注册Interceptor拦截器(Interceptor这个类是我们自己写的拦截器类)
InterceptorRegistration registry = interceptorRegistry.addInterceptor(new InterceptorConfigurer());
// 拦截所有请求 添加需要拦截的路径
String[] addPaths = {
"/**"
};
//放过的请求路径 添加不拦截的路径
String[] excludePaths = {
"/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg", "/rescue/admin/loginAccount", "/rescue/admin/loginRest",
"/rescue/admin/activated", "/rescue/admin/reset", "/rescue/admin/set", "/rescue/admin/note", "/rescue/admin/captcha",
"/rescue/admin/account/avatar", "/rescue/admin/resource/photo", "/rescue/admin/resource/getCityCode",
"/rescue/admin/product/photoProduct"
};
registry.addPathPatterns(addPaths);
registry.excludePathPatterns(excludePaths);
}
}
2.实现HandlerInterceptor接口的自定义拦截器,重写其中的preHandle()方法,方法内容为拦截到请求后的处理
import com.admin.api.base.Result;
import com.admin.api.base.SystemCode;
import com.baomidou.mybatisplus.extension.exceptions.ApiException;
import lombok.extern.log4j.Log4j2;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @Author:
* @CreateTime: 2022-11-24 14:07
* @Description: 拦截器配置
*/
@Log4j2
public class InterceptorConfigurer implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) {
try {
// 逻辑处理
Result result = new Result();
String token = TokenUtils.getCookieToken(request);
log.info("token=" + token);
if (StringUtils.isNotBlank(token)) {
int userId = TokenUtils.getUserId(token);
if (userId <= 0) {//判断用户是否登录
result = Result.fail(SystemCode.UNAUTHORIZED.getCode(), SystemCode.UNAUTHORIZED.getMessage());
}
} else {
判断token是否过期
result = Result.fail(SystemCode.LOSE_TOKEN.getCode(), SystemCode.LOSE_TOKEN.getMessage());
}
if (result.getCode() == 401 || result.getCode() == 420) {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=UTF-8");
PrintWriter out = null;
try {
out = response.getWriter();
out.println(JSONObject.fromObject(result));
} catch (IOException ioException) {
ioException.printStackTrace();
}
return false;
}
// 不做拦截的地址
if (!checkRequestURI(request)) {
}
//这里设置拦截以后重定向的页面,一般设置为登陆页面地址
response.sendRedirect(request.getContextPath() + "/user/login");
//如果设置为false时,被请求时,拦截器执行到此处将不会继续操作
//如果设置为true时,请求将会继续执行后面的操作
return true;
} catch (Exception e) {
throw new ApiException(e.getMessage(), e);
}
}
// 不做拦截的地址
public Boolean checkRequestURI(HttpServletRequest request) {
log.info(request.getRequestURI());
if (request.getRequestURI().equals("")) {
return true;
}
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) {
}
}
三、过滤器
1.代码实现
过滤器的本质就是一个实现了 Filter 接口的 Java 类
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/*")//过滤路径
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器初始化了........init... "+filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("过滤前........doFilter "); //放行
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("过滤后.......doFilter");
}
@Override
public void destroy() {
System.out.println("销毁了.....destroy");
}
}
- doFilter(ServletRequest, ServletResponse, FilterChain):这是一个完成过滤行为的方法。这同样是上游过滤器调用的方法。引入的FilterChain对象提供了后续过滤器所要调用的信息。如果该过滤器是过滤器链中的最后一个过滤器,则将请求交给被请求资源。也可以直接给客户端返回响应信息。
- init(FilterConfig):由Web容器来调用完成过滤器的初始化工作。它保证了在第一次doFilter()调用前由容器调用。您能获取在web.xml 文件中指定的初始化参数。
- destroy():由Web容器来调用来释放资源,doFilter()中的所有活动都被该实例终止后,调用该方法。
2.通过web.xml文件配置
<filter>
<filter-name>FilterDemo1</filter-name>
<filter-class>FilterDemo1</filter-class>
<init-param>
<param-name>word_file</param-name>
<param-value>/WEB-INF/word.txt</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>FilterDemo1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>指定一个过滤器。
<filter-name>用于为过滤器指定一个名字,该元素的内容不能为空。
<filter-class>元素用于指定过滤器的完整的限定类名。
<init-param>元素用于为过滤器指定初始化参数
<param-name>指定参数的名字,
<param-value>指定参数的值。在过滤器中,可以使用FilterConfig接口对象来访问初始化参数。
<filter-mapping>元素用于设置一个 Filter 所负责拦截的资源。一个Filter拦截的资源可通过两种方式来指定:Servlet 名称和资源访问的请求路径
<filter-name>子元素用于设置filter的注册名称。该值必须是在<filter>元素中声明过的过滤器的名字
<url-pattern>设置 filter 所拦截的请求路径(过滤器关联的URL样式)<servlet-name>指定过滤器所拦截的Servlet名称。
<dispatcher>指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST。用户可以设置多个<dispatcher>子元素用来指定 Filter 对资源的多种调用方式进行拦截。<dispatcher>子元素可以设置的值及其意义
REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。
INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
拦截方式配置:dispatcher
拦截方式配置也就是资源被访问的形式,有这么几个属性
REQUEST:默认值,浏览器直接请求资源
FORWARD:转发访问资源 : RequestDispatcher.forward();
INCLUDE:包含访问资源 : RequestDispatcher.include();
ERROR:错误跳转资源 : 被声明式异常处理机制调用的时候
补充:声明式异常处理即:在web.xml中通过配置来确定不同的异常类型将如何被处理,最后跳转到哪个页面,也就是我们常常看到的一些404错误页面
<error-page>
<!--异常的类-->
<exception-type>xxx</exception-type>
<!--异常发生时跳转的页面-->
<location>xxx</location>
</error-page>
多个Filter的执行顺序
如果一定要确保执行顺序,就要对配置进行修改了,执行顺序如下:
- 在web.xml中,filter执行顺序跟的顺序有关,先声明的先执行。
- 使用注解配置的话,filter的执行顺序跟名称的字母顺序有关,例如AFilter会比BFilter先执行。
- 如果既有在web.xml中声明的Filter,也有通过注解配置的Filter,那么会优先执行web.xml中配置的Filter
先执行带有url-pattern标签的filter,再执行带有servlet-name标签的filter 如果同为url-pattern或servlet-name,则会按照在web.xml中的声明顺序执行
// ATestFilter
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("in ATestFilter");
chain.doFilter(request, response);
}
// TestFilter
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("in TestFilter");
chain.doFilter(request, response);
}
// ServletFilter
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("in ServletFilter");
chain.doFilter(request, response);
}
<filter>
<filter-name>servletFilter</filter-name>
<filter-class>com.test.servletFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>servletFilter</filter-name>
<servlet-name>MVC-dispatchar</servlet-name>
</filter-mapping >
<filter>
<filter-name>testFilter</filter-name>
<filter-class>com.test.testFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter- mapping>
<filter-name>testFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>atestFilter</filter-name>
<filter-class>com.test.atestFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter- mapping>
<filter-name>atestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
配置中先有两个/*路径,这两个先执行
in TestFilter
in ATestFilter
in ServletFilter
3.中文乱码过滤器
论在做什么web应用的开发中,中文乱码的问题是很常见的,所以这类问题应该被重视。中文乱码出现的场景非常多,每次都去处理很麻烦,还有可能出现遗漏的情况,所以一个处理中文乱码的过滤器就能很好的解决这个问题。
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name><!--用来指定一个具体的字符集-->
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<!--true:无论request是否指定了字符集,都是用encoding;false:如果request已指定一个字符集,则不使用encoding-->
<param-value>false</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>