目录
二、先回顾一下zuul网关的过滤器链(摘自springCloud笔记)
2.3注意:上面的配置类可以改为文件配置,在springmvc的配置文件中进行配置::
3.1HandlerInterceptor接口的三个方法preHandle,postHandle和afterCompletion
3.如果不配置启动类扫描,也可以在配置类(casConfig.java)注册
2.@ServletComponentScan、@WebServlet、@WebFilter、@WebListener
一、参考文章
过滤器与拦截器详解_鲨鱼辣椒灬的博客-CSDN博客_过滤器和拦截器
https://blog.csdn.net/qq_43542296/article/details/127810032
二、先回顾一下zuul网关的过滤器链(摘自springCloud笔记)
ZUUL网关,创建cloud-zuul-7780项目(需注册到eureka):
项目目的/Zuul作用:
1.正如在此项目yml中配置的那样
《#consumer服务名称:cloud-consumer-7750 多个服务时往下加就行
cloud-consumer-7750: /user/** #只要前缀以user开头 (请求当前服务接口必须 加user)》可以使得一个consumer项目中的多个接口的url在外部暴露时前面追加一个user路径参数,统一于一个父路径方便前端调用(访问不带user则404)。2.zuul的过滤器链可对请求参数是否传递,是否已登录等进行一些验证。
3. 输入经过网关的路径,网关可实现对生产者请求的负载均衡:详见第7步。
1.删除test包,pom文件修改boot版本号为:
<version>1.5.9.RELEASE</version>
修改cloud版本号为:<spring-cloud.version>Dalston.SR4</spring-cloud.version>
2.修改pom文件jar包名:
<!--Zuul网关项目,需注册,是注册中心客户端-->【去掉netflix和client】
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter--eureka-netflix</artifactId>client
</dependency>
<!--zuul网关jar-->【去掉netflix】
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter--zuul</artifactId>netflix
</dependency>
3.配置yml文件:
server:
port: 7780
spring:
application:
name: cloud-zuul-7780
eureka:
client:
service-url:
defaultZone: http://root:123456@127.0.0.1:7777/eureka
zuul:
routes:
#consumer服务名称:cloud-consumer-7750 多个服务时往下加就行
cloud-consumer-7750: /user/** #只要前缀以user开头 (请求当前服务接口必须 加user)
4.启动类添加注解:
@EnableZuulProxy //开启zuul注册中心;
//不必再加@EnableDiscoveryClient注解--因为@EnableZuulProxy包含了@EnableDiscoveryClient
5.创建zuul包并创建三个java类文件FirstFilter,SecondFilter,ThirdFilter类,并都继承(extends) ZuulFilter实现其四个方法,都加@Component注解,形成了有三个过滤器的一条过滤器链 。
【设置各个过滤器的级别分别为,FirstFilter=0,SecondFilter=5,ThirdFilter=10
执行顺序为0->5->10 设置每个过滤器类型都为PRE】
1.FirstFilter.java
package com.hz.cloudzuul7780.zuul;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* @author hqw OK
* @date 2020-12-30 16:56
* @project springcloud
**/
//filterType [有pre、route、post、error四种
//filterOrder [权重:在同一类型中,数小靠前
//shouldFilter [控制过滤器是否执行,true表示执行,false表示不执行
//run [filter需要执行的具体操作
@Component
public class FirstFilter extends ZuulFilter {
@Override
public String filterType() {/*filterType [有pre、route、post、error四种*/
//return "pre";
return FilterConstants.PRE_TYPE;//FilterConstants.PRE_TYPE 等同 "pre"
}
@Override
public int filterOrder() {/*filterOrder [权重:在同一类型中,数值越小优先级越高[和线程相反
*/
return 0;
}
@Override
public boolean shouldFilter() {/*shouldFilter [控制过滤器是否执行,true表示执行,false表示不执行*/
RequestContext rc = RequestContext.getCurrentContext();
System.err.println("==过滤器1是否执行++====="+rc.sendZuulResponse());
//sendZuulResponse():全局变量【可以在其它过滤中和本过滤器中获取到】
return rc.sendZuulResponse(); //true执行此过滤器 false不执行此过滤器 [sendZuulResponse初始为true]
//return false;
}
@Override
public Object run() {/*run [filter需要执行的具体操作:这里是判断url是否携带参数id*/
System.err.println("====进入过滤器1+++====》》》》");
//怎么获取url上参数:id?
//怎么拦截 url没有传id参数怎么给用户返回信息 不再调用服务
RequestContext rc = RequestContext.getCurrentContext();/*【RequestContext.getCurrentContext()】来自import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
作用1.获取url参数/请求头参数
2.设置sendZuulResponse全局属性用于控制过滤器是否执行
3.setResponseBody("error:不含id参数!");setResponseStatusCode(401)
可在浏览器页面返回信息。可见过滤器可以改变和传出获取的值*/
HttpServletRequest request = rc.getRequest();
String strid = request.getParameter("id");//如果获取token用request.getHeader();
System.err.println("id========"+strid);
if(strid==null)
{
rc.setSendZuulResponse(false); //全局变量【可以在其它过滤中和本过滤器中获取到--设为false:《过滤器链上后面的低权重的过滤器的shouldFilter()方法查看到此属性为false,它们就不会再执行》】
rc.setResponseBody("error:不含id参数!");/**/
rc.setResponseStatusCode(401);
}
/*【strid != null】啥也不做直接放行,可进入下一个过滤器(进行登录判断或其它判断)*/
return null;//return null或者什么都可以
}
}
2.SecondFilter.java
package com.hz.cloudzuul7780.zuul;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.stereotype.Component;
/**
* @author hqw OK
* @date 2020-12-30 16:57
* @project springcloud
**/
//filterType [有pre、route、post、error四种
//filterOrder [在同一类型中,数小靠前
//shouldFilter [控制过滤器是否执行,true表示执行,false表示不执行
//run [filter需要执行的具体操作
@Component
public class SecondFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return 5;
}
@Override
public boolean shouldFilter() {
RequestContext rc = RequestContext.getCurrentContext();
System.err.println("==过滤器2是否执行++====="+rc.sendZuulResponse());
//sendZuulResponse():全局变量【可以在其它过滤中和本过滤器中获取到】
return rc.sendZuulResponse(); //true执行此过滤器 false不执行此过滤器
//return false;
}
@Override
public Object run() {
System.err.println("====进入过滤器2+++====》》》》");
//登录判断或其它判断
//已登录,啥也不做放行到下一个过滤器
//未登录,设置setSendZuulResponse(false);
return null;
}
}
3. ThirdFilter.java
package com.hz.cloudzuul7780.zuul;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.stereotype.Component;
/**
* @author hqw OK
* @date 2020-12-30 16:57
* @project springcloud
**/
//filterType [有pre、route、post、error四种
//filterOrder [在同一类型中,数小靠前
//shouldFilter [控制过滤器是否执行,true表示执行,false表示不执行
//run [filter需要执行的具体操作
@Component
public class ThirdFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return 10;
}
@Override
public boolean shouldFilter() {
RequestContext rc = RequestContext.getCurrentContext();
System.err.println("==过滤器3是否执行++====="+rc.sendZuulResponse());
//sendZuulResponse():全局变量【可以在其它过滤中和本过滤器中获取到】
return rc.sendZuulResponse(); //true执行此过滤器 false不执行此过滤器
//return false;
}
@Override
public Object run() {
System.err.println("====进入过滤器3+++====》》》》");
//
return null;
}
}
6.测试:
依次启动注册中心7777,provider7760,provider7761,consumer7750,Zuul7780;
访问注册中心服务端:http://wzy1:7777/ ,可见:
1)原始方式直接访问consumer7750: http://127.0.0.1:7750/findId?id=1 ,仍可访问成功(正常,这个原始不经过网关的路径不给前端;我们配置的/user/**只在访问zuul项目的7780端口这个路径时才需要带上,而这个经过网关的路径才是给前端的接口路径;这两个路径实质上调用的是一个生产者接口,再由ribbon组件负载均衡到不同的生产者项目):
2)访问经过网关(配置了端口7780,路径加user)的路径: http://127.0.0.1:7780/user/findId?id=1 ,仍可见:【这个是给前端的】
若访问http://127.0.0.1:7780/user/findId 不带id参数(可见我们FirstFilter.java过滤器配置过的错误信息:rc.setResponseBody("error:不含id参数!");):
若访问http://127.0.0.1:7780/findId?id=1不带yml文件配置过的user路径(cloud-consumer-7750: /user/** #只要前缀以user开头 (请求当前服务接口必须 加user)):404
测试达到了效果。
三、再回顾一下之前的拦截器案例笔记
1.创建拦截器的两种方式
- 继承HandlerInterceptorAdapter抽象类。
- 实现HandlerInterceptor接口,推荐使用实现接口的方式。
Spring MVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器(即controller)进行预处理和后处理。将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是AOP思想的具体实现。
2.创建拦截器
2.1在utils包里建一个拦截器类(添加注解@Component)【实现HandlerInterceptor接口-->重写其方法-->return true(到下一个过滤器,不能传出值)/重定向return false】
HandlerInterceptor接口的三个方法preHandle,postHandle和afterCompletion都是defalult修饰的,实现类不需要全部实现。
preHandle():在请求被处理之前进行操作,预处理。
postHandle():在请求被处理之后,但结果还没有响应前进行操作,可以改变响应结果,后处理。
afterCompletion():所有请求响应结束后执行善后工作,清理对象、关闭资源,最终处理。
如果想设置响应头,请在postHandle中进行,因为当afterCompletion被调用时,响应已经被提交了!
2.2在config包里建一个拦截器配置类【实现WebMvcConfigurer接口-->实例化拦截器类-->重写addInterceptors(InterceptorRegistry registry)方法-->使用registry添加拦截器(可添加多个)并配置拦截规则 (只能拦截controller方法)】,也可采用配置文件方式
2.3注意:上面的配置类可以改为文件配置,在springmvc的配置文件中进行配置::
<!--拦截器的配置-->
<mvc:interceptors>
<!--单个拦截器配置-->
<mvc:interceptor>
<!--配置拦截的路径:
/** : 拦截当前路径以及后台所有
-->
<mvc:mapping path="/**"/>
<!--配置拦截器-->
<bean class="com.fs.interceptors.AInterceptor"></bean>
</mvc:interceptor>
<mvc:interceptor>
<!--配置拦截的路径:
/** : 拦截当前路径以及后台所有
-->
<mvc:mapping path="/**"/>
<!--配置拦截器-->
<bean class="com.fs.interceptors.BInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
3.拦截器执行顺序
3.1HandlerInterceptor接口的三个方法preHandle,postHandle和afterCompletion
HandlerInterceptor接口的三个方法preHandle,postHandle和afterCompletion都是defalult修饰的,实现类不需要全部实现。
preHandle():在请求被处理之前进行操作,预处理。
postHandle():在请求被处理之后,但结果还没有渲染前进行操作,可以改变响应结果,后处理。
afterCompletion():所有请求响应结束后执行善后工作,清理对象、关闭资源,最终处理。
如果想设置响应头,请在postHandle中进行,因为当afterCompletion被调用时,响应已经被提交了!
如果只配置了一个拦截器,那么执行顺序应该是这样的:preHandle
-> dohandle
(处理请求)-> postHandle
-> render
(渲染视图)-> afterCompletion
3.2 拦截器执行顺序和设置顺序(如果同时命中了几个拦截器的拦截规则)
1.拦截器的preHandle()执行顺序和拦截器在springMVC配置文件中添加的顺序或者配置类中配置的顺序相同
是按照springmvc的XML文件添加拦截器的顺序或者配置类中配置的拦截器顺序执行的。
注册拦截器时可以使用 InterceptorRegistry#order(int) 方法指定顺序,值越小优先级越高。
2.拦截器的postHandle()方法是否执行,由拦截器链中所有拦截器的preHandle()的返回值决定,
都返回true才执行,只要有一个返回false就不执行
3.拦截器的afterCompletion()方法是否执行,由自己拦截器的preHandle()的返回值决定,
返回true: 执行,返回false: 不执行
4.postHandle(), afterCompletion() 的执行顺序与拦截器在配置文件中添加的顺序或者配置类中配置的顺序相反。
四、再回顾一下之前的过滤器案例笔记
(培训时工程位置:peixun\Java\Javacode\eclipse-workspace)
如何创建Filter?
类似于servlet,需两步
第一步:实现javax.servlet.Filter接口,该接口中定义了三个方法都需要重写:
(1) void init(FilterConfig config):用于Filter的初始化。FilteConfig用于访问Filter的配置信息。
(2) void destroy():Filter销毁前的操作,例如完成某些资源的回收。
(3) void doFilter(ServletRequest request,ServletResponse response,FilterChain chain):
实现过滤功能的核心方法,可以实现对请求request进行预处理,也可以实现对服务器响应response进行后处理---它们的分界线为是否调用了chain.doFilter(request,response),执行该方法之前,即对用户请求request进行预处理,执行该方法之后,即对服务器响应response进行后处理。
第二步:Web.xml文件中配置Filter 或者 添加@WebFilter注解
Filter配置与Servlet的配置非常相似,区别在于Servlet通常只配置一个URL,而Filter可以同时配置多个请求的URL。配置Filter有两种方式:
(1). 在Filter类中通过Annotation配置,即@WebFilter,javax.servlet.annotation.WebFilter。
(2). 在web.xml文件中通过配置文件进行配置。
这里我们使用web.xml这种配置方式,下面重点介绍<filter>内包含的一些元素。
(1).<filter-name>用来定义过滤器的名称,该名称在整个程序中都必须唯一。
(2).<filter-class>元素指定过滤器类的完全限定的名称,即Filter的实现类。
(3). <init-param>为Filter配置参数,与<context-param>具有相同的元素描述符<param-name>和<param-value>。
- context-param和init-param区别:
- context-param是application范围内的参数,存放在servletcontext中
- init-param是servlet和filter范围内的参数,只能在servlet或filter的init()方法中取得
(4). <filter-mapping>元素用来声明Web应用中的过滤器映射。这个过滤器的<filter>和<filter-mapping>必须具有相同的<filter-name>,指定该Filter所拦截的URL。过滤是按照部署描述符的<filter-mapping>出现的顺序执行的。
<!-- <filter>
<filter-name>LoginFilter</filter-name>
<filter-class>com.hz.filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<url-pattern>/user/*</url-pattern>
</filter-mapping> -->
@WebFilter("/*") // "/*"设置本Filter 应用范围(过滤所有请求响应)
@WebFilter("/user/*") //@WebFilter注解【过滤user文件夹下的页面等静态资源,同时过滤含user前缀的servlet请求】@WebServlet("/user/IndexServlet") //【给IndexServlet设置user前缀】
package com.hz.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
/**
* Servlet Filter implementation class CharacterEncodingFilter
*/
@WebFilter("/*") // "/*" 设置本Filter 应用范围(过滤所有请求响应)
public class CharacterEncodingFilter implements Filter {//implements Filter
/**
* Default constructor.
*/
public CharacterEncodingFilter() {
// TODO Auto-generated constructor stub
}
/**
* @see Filter#destroy()
*/
public void destroy() {
// TODO Auto-generated method stub
}
/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");//放上放下都可以。还是放上,看着顺
// // TODO Auto-generated method stub
// // place your code here
//
// // pass the request along the filter chain
chain.doFilter(request, response);//放行:调用web资源或者其它过滤器(调用你指定调用的请求servlet)
}
/**
* @see Filter#init(FilterConfig)
*/
public void init(FilterConfig fConfig) throws ServletException {
// TODO Auto-generated method stub
}
}
五、【boot过滤器写法】
1.MyFilter.java
实现javax.servlet.Filter接口重写其方法
添加@WebFilter(filterName = "MyFilter",urlPatterns = "/*")注解
package com.genertech.plm.aia.login.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
//过滤路径
@WebFilter(filterName = "MyFilter",urlPatterns = "/*")
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("my filter ....... ");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig config) throws ServletException {
}
}
2.启动类配置扫描
3.如果不配置启动类扫描,也可以在配置类(casConfig.java)注册
通过org.springframework.boot.web.servlet.FilterRegistrationBean注册Filter
六、总结
1.拦截器过滤器区别
2.@ServletComponentScan、@WebServlet、@WebFilter、@WebListener
/*servlet3.0后,使用@ServletComponentScan注解后,
Servlet可以直接通过@WebServlet注解自动注册、
Filter可以直接通过@WebFilter注解自动注册、
Listener可以直接通过@WebListener注解自动注册*/