登录校验2.0

登录校验2.0

Filter

Filter详解

过滤器Filter在使用中的一些细节,主要介绍一下3个方面的细节:

  1. 过滤器的执行流程
  2. 过滤器的拦截路径配置
  3. 过滤器链
执行流程

img

过滤器当中我们拦截到了请求之后,如果希望继续访问后面的web资源,就要执行放行操作,放行就是调用FilterChain对象当中的doFilter()方法,这个方法之前所编写的代码属于放行之前的逻辑。

在放行后访问完web资源之后还会回到过滤器当中,回到过滤器之后如果有需求还可以执行放行之后的逻辑,放行之后的逻辑写在doFilter()之后。

@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init 初始化方法执行了");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("DemoFilter 放行前逻辑....");
        
        // 放行请求
        chain.doFilter(request, response);

        System.out.println("DemoFilter 放行后逻辑....");
    }

    @Override // 销毁方法,只调用一次
    public void destroy() {
        System.out.println("destory 销毁方法执行了");
    }
}

img

拦截路径

Filter可以根据需求,配置不同的拦截资源路径:

拦截路径urlPatterns值含义
拦截具体路径/login只有访问/login路径时,才会被拦截
目录拦截/emps/*访问/emps下的所有资源,都会被拦截
拦截所有/*访问所有资源,都会被拦截

拦截具体路径

@WebFilter(urlPatterns = "/login")
public class DemoFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init 初始化方法执行了");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("DemoFilter 放行前逻辑....");

        // 放行请求
        chain.doFilter(request, response);

        System.out.println("DemoFilter 放行后逻辑....");
    }

    @Override // 销毁方法,只调用一次
    public void destroy() {
        System.out.println("destory 销毁方法执行了");
    }
}

目录拦截

@WebFilter(urlPatterns = "/depts/*")
public class DemoFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init 初始化方法执行了");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("DemoFilter 放行前逻辑....");

        // 放行请求
        chain.doFilter(request, response);

        System.out.println("DemoFilter 放行后逻辑....");
    }

    @Override // 销毁方法,只调用一次
    public void destroy() {
        System.out.println("destory 销毁方法执行了");
    }
}
过滤器链
介绍

过滤器链指的是在一个Web应用程序当中,可以配置多个过滤器,多个过滤器就形成了一个过滤器链

img

比如:在web服务器当中,定义了两个过滤器,这两个过滤器就形成了一个过滤器链。

而这个过滤器链上的过滤器在执行的时候回一个一个的执行,会先执行第一个filter,放行之后再来执行第二个filter,如果执行到了最后一个过滤器放行之后,才会访问对象的web资源。

访问web资源之后,按照过滤器的执行流程,还会回到过滤器当中来执行过滤器放行后的逻辑,而在执行后的逻辑的时候,顺序是反着的。

先执行过滤器2放行之后的逻辑,再来执行过滤器1放行之后的逻辑,在最后在给浏览器响应数据。

验证步骤:

  1. 在filter包下再新建一个Filter过滤器类:AbcFilter
  2. 在AbcFilter过滤器中编写放行前和放行后逻辑
  3. 配置AbcFilter过滤器拦截请求路径为:/*
  4. 重启SpringBoot服务,查看DemoFilter、AbcFilter的执行日志

AbcFilter过滤器

@WebFilter(urlPatterns = "/*")
public class AbcFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("Abc 拦截到了请求... 放行前逻辑");

        // 放行
        filterChain.doFilter(servletRequest, servletResponse);

        System.out.println("Abc 拦截到了请求...... 放行后逻辑");
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

打开浏览器访问登录接口

img

注意:以注解方式配置的Filter过滤器,它的执行优先级是按时过滤器类名的自动排序确定的,类名排名越靠前,优先级越高。

如果想让DemoFilter先执行,就需要修改类名

登录校验-Filter

分析

img

登录校验的基本流程:

  • 要进入到后台管理系统,必须先完成登录操作,此时就需要访问登录接口login
  • 登录成功之后,需要在服务端生成一个JWT令牌,并且把JWT令牌返回给前端,前端会将JWT令牌存储下来
  • 在后续的每一次请求当中,都会将JWT令牌携带到服务端,请求到达服务端之后,要想去访问对应的业务功能,此时必须先校验令牌的有效性
  • 对于校验令牌的操作,使用登录校验的过滤器,在过滤器当中来校验令牌的有效性。如果令牌是无效的,就响应一个错误信息,也不会再去放行访问对应的资源。如果令牌存在,并且有效,此时就会放行去访问对应的web资源,执行相应的业务操作。

问题:

  1. 所有请求,拦截到了之后,都需要校验令牌吗?
    • 登录请求例外
  2. 拦截到请求后,什么请求下才可以放行,执行业务操作?
    • 有令牌,且令牌校验通过(合法);否则都返回未登录错误结果。
具体流程

img

基于上面的业务流程,具体的操作步骤:

  1. 获取请求url
  2. 判断请求url中是否包含login,如果包含,说明是登录操作,放行
  3. 获取请求头中的令牌(token)
  4. 判断令牌是否存在,如果不存在,返回错误结果(未登录)
  5. 解析token,如果解析失败,返回错误结果(未登录)
  6. 放行

拦截器Interceptor

介绍

什么是拦截器?

  • 是一种动态拦截方法调用的机制,类似于过滤器
  • 拦截器是Spring框架中提供的,用来动态拦截控制器方法的执行

作用:

  • 拦截请求,在指定方法调用前后,根据业务需要执行预先设定的代码

    在拦截器中,通常也会做一些通用性的操作,如:通过拦截器来拦截前端发起的请求,将登录校验的逻辑全部编写在拦截器中。在校验的过程中,如发现用户登录了(携带JWT令牌且是合法令牌),就可以直接放行,去访问spring当中的资源。如果校验时发现并没有登录或是非法令牌,就可以直接给前端响应未登录的错误信息。

拦截器的使用步骤分为两步:

  1. 定义拦截器
  2. 注册配置拦截器

自定义拦截器:实现HandlerInterceptor接口,并重写其所有方法

@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");
        return true;
    }

    // 目标资源方法执行后执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle...");
    }

    // 视图渲染完毕后执行,最后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion...");
    }
}

注意

  • preHandle方法:目标资源方法执行前执行。返回true:放行;返回false:不放行
  • postHandle方法:目标资源方法执行后执行
  • afterCompletion方法:视图渲染完毕后执行,最后执行

注册配置拦截器:实现WebMvcConfigure接口,并重写addInterceptors方法

@Configuration
public class WebConfig implements WebMvcConfigurer {


    // 自定义的拦截器对象
    @Resource
    private LoginCheckInterceptor loginCheckInterceptor;


    // 注册自定义拦截器对象
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 设置拦截器拦截的请求路径(/** 表示拦截所有请求)
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");
    }
}

效果如图:

img

如果将拦截器中返回值改为false,再次发送请求后,没有响应数据,说明请求被拦截了没有放行

Interceptor详解

拦截器的使用细节:

  1. 拦截器的拦截路径配置
  2. 拦截器的执行流程
拦截路径

在注册配置拦截器的时候,要指定拦截器的路径,通过addPathOatterns("要拦截路径")方法,就可以指定要拦截哪些资源

/**表示拦截所有资源,而在配置拦截器时,不仅可以指定要拦截哪些资源,还可以指定不拦截哪些资源,只需要调用excludePathPatterns("不拦截路径")方法,指定哪些资源不需要拦截

@Configuration
public class WebConfig implements WebMvcConfigurer {


    // 自定义的拦截器对象
    @Resource
    private LoginCheckInterceptor loginCheckInterceptor;


    // 注册自定义拦截器对象
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 设置拦截器拦截的请求路径(/** 表示拦截所有请求)
        registry.addInterceptor(loginCheckInterceptor)
                .addPathPatterns("/**").excludePathPatterns("/login");
    }
}

在拦截器中除了可以设置/**拦截所有资源外,还有一些常见拦截路径设置:

拦截路径含义例子
/*一级路径能匹配/depts、/emps、/login,不能匹配/depts/1
/**任意级路径能匹配/depts、/depts/1、/depts/1/2
/depts/*/depts下的一级路径能匹配/depts/1、不能匹配/depts/1/2、/depts
/depts/**/depts下的任意级路径能匹配/depts、/depts/1、/depts/1/2、不能匹配/emps/1
执行流程

img

介绍
  • 当打开浏览器来访问部署在web服务器当中的web应用时,此时定义的拦截器会拦截到这次请求。拦截到这次请求之后,会先执行放行前的逻辑,然后再执行放行操作。由于是基于Springboot开发的,所以放行之后是进入到了spring的环境当中,也就是要来访问我们所定义的controller当中的接口方法。
  • Tomcat并不识别所编写的Controller程序,但是它识别Servlet程序,所以在Spring的Web环境中提供了一个非常核心的Servelt:DispatcherServlet(前端控制器),所有请求都会先进行到DispatcherServlet,再将请求转给Controller。
  • 当定义了拦截器之后,会在执行Controller的方法之前,请求被拦截器拦截住。执行preHandle()方法,这个方法执行完成后需要返回一个布尔类型的值,如果返回true,就表示放行本次操作,才会继续访问controller中的方法;如果返回false,则不会放行(controller中的方法也不会执行)
  • 在controller当中的方法执行完毕之后,再回来执行postHandle()这个方法以及afterCompletion()方法,然后再返回给DispatcherServlet,最终再来执行过滤器当中放行后的这一部分。执行完毕之后,最终给浏览器响应数据。
拦截器和过滤器的区别
  • 接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现HandlerInterceptor接口
  • 拦截范围不同:过滤器Filter会拦截所有资源,而Interceptor只会拦截环境中的资源

登录校验——Interceptor

登录校验的业务逻辑和登录校验Filter过滤器当中的逻辑是完全一致的。

登录校验拦截器

//自定义拦截器
@Component //当前拦截器对象由Spring创建和管理
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
	//前置方式
	@Override
	public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
		System.out.println("preHandle .... ");
		// 1.获取请求url
		// 2.判断请求url中是否包含login,如果包含,说明是登录操作,放行
		// 3.获取请求头中的令牌(token)
		String token = request.getHeader("token");
		log.info("从请求头中获取的令牌:{}",token);
		// 4.判断令牌是否存在,如果不存在,返回错误结果(未登录)
		if(!StringUtils.hasLength(token)){
			log.info("Token不存在");
			// 创建响应结果对象
			Result responseResult = Result.error("NOT_LOGIN");
            // 把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
		String json = JSONObject.toJSONString(responseResult);
		// 设置响应头(告知浏览器:响应的数据类型为json、响应的数据编码表为utf-8)
		response.setContentType("application/json;charset=utf-8");
		// 响应
		response.getWriter().write(json);
		return false;//不放行
 		}
		// 5.解析token,如果解析失败,返回错误结果(未登录)
		try {
			JwtUtils.parseJWT(token);
 		}catch (Exception e){
			log.info("令牌解析失败!");
			// 创建响应结果对象
			Result responseResult = Result.error("NOT_LOGIN");
			// 把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
			String json = JSONObject.toJSONString(responseResult);
			//设置响应头
			response.setContentType("application/json; charset=utf-8");
			//响应
			response.getWriter().write(json);
			return false;
 		}
		//6.放行
		return true;
}

注册配置拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {
	//拦截器对象
	@Autowired
	private LoginCheckInterceptor loginCheckInterceptor;
    @Override
	public void addInterceptors(InterceptorRegistry registry) {
		//注册自定义拦截器对象
		registry
	   .addInterceptor(loginCheckInterceptor)
 		.addPathPatterns("/**")
 		.excludePathPatterns("/login");
 	}
}

当用户没有登录,校验机制返回错误信息,前端页面根据返回的错误信息结果,自动跳转到登录页面

异常处理

当出现异常的时候,并且我们没有做任何异常处理的时候,三层架构处理异常的方案:

  • Mapper接口在操作数据库的时候出错了,此时异常会往上抛(谁调用Mapper就给谁抛),会抛给Service
  • Service中也存在异常了,会抛给controller
  • 而在controller当中,没有做任何的异常处理,所以最终异常会再往上抛,最终会抛给框架之后,框架就会返回一个JSON格式的数据,里面封装的就是错误信息,但是框架返回的JSON格式的数据并不符合开发规范。

解决方案:

在三层架构项目中,出现了异常,该如何处理?

  • 方案一:在所有Controller的所有方法中进行try…catch处理
    • 缺点:代码臃肿(不推荐)
  • 方案二:全局异常处理器
    • 好处:简单、优雅(推荐)

img

全局异常处理器

流程:

  • 定义全局异常处理器非常简单,就是定义一个类,在类上加上一个注释@RestControllerAdvice,加上这个注释就代表定义了一个全局异常处理器
  • 在全局异常处理器当中,需要定义一个方法来捕获异常,在这个方法上需要加上注解@ExceptionHandler。通过@ExceptionHandler注解当中的value属性来指定要捕获的是哪一类型的异常。
@RestControllerAdvice
public class GlobalExceptionHandler {
	//处理异常
	@ExceptionHandler(Exception.class) //指定能够处理的异常类型
	public Result ex(Exception e){
		e.printStackTrace();//打印堆栈中的异常信息
		//捕获到异常之后,响应一个标准的Result
		return Result.error("对不起,操作失败,请联系管理员");
     }
}

@RestControllerAdvice = @ControllerAdvice + @ResponseBody处理异常的方法返回值会转换为json后响应给前端

全局异常处理器的使用,主要涉及到两个注解:

  • @RestControllerAdvice // 表示当前类为全局异常处理器
  • @ExceptionHandler // 指定可以捕获哪种类型的异常进行处理
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值