【登录问题:拦截器和过滤器的应用】

2. 登录拦截或过滤

2.1 问题描述

在项目中,不进行登录验证也可直接进行主页面的访问;

在过滤器、拦截器中拦截前端发起的请求,判断用户是否已经完成登录;登陆了就放行,如果没有登录则返回提示信息,跳转到登录页面。

2.2 过滤器

过滤器具体的处理逻辑如下:

  1. 获取本次请求的URI

  2. 判断本次请求, 是否需要登录, 才可以访问(如果是拦截器,不需要该步骤)

  3. 如果不需要,则直接放行

  4. 判断登录状态,如果已登录,则直接放行

  5. 如果未登录, 则返回未登录结果

2.2.1 代码实现登录过滤器

在yml文件中存入放行路径

excludePath: '/employee/login,/employee/logout,/backend/**,/front/**,favicon.ico'

过滤器

import com.alibaba.fastjson.JSON;
import com.itheima.reggie.utils.common.BaseContext;
import com.itheima.reggie.utils.common.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Arrays;

/**
 * 登录校验
 */
@WebFilter("/*") // 表示该类是一个filter,拦截所有请求
@Slf4j
public class LoginCheckFilter implements Filter {

    // 2.2 创建路径匹配器对象
    AntPathMatcher apm = new AntPathMatcher();

    @Value("${excludePath}")
    String excludePath;

    /**
     * 判断是否登录,并确定是否放行
     *
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain filterChain) throws IOException, ServletException {

        //1. 获取本次请求的URI
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        String requestURI = req.getRequestURI();

        //2. 判断本次请求, 是否需要登录, 才可以访问
        // 2.1 解析要放行的所有请求
        String[] urls = excludePath.split(",");

        // 2.3 检查是否在放行范围
        if (checkUrl(urls, requestURI)) {
            //3. 如果不需要,则直接放行
            filterChain.doFilter(request, response);
            return; // 后面代码不需要执行,直接结束
        }

        //4. 判断登录状态,如果已登录,则直接放行
        HttpSession session = req.getSession();
        Long employeeId = (Long) session.getAttribute("employee");

        if (employeeId != null) {

            long id = Thread.currentThread().getId();
            log.info("线程id为:{}",id);

            //已经登录,将id存入线程工具类中
            BaseContext.setCurrentId(employeeId);

            // 放行
            filterChain.doFilter(request, response);
            return; // 后面代码不需要执行,直接结束
        }

        //5. 如果未登录, 则返回未登录结果
        // 前端页面写死的,只识别NOTLOGIN;接受到该符号后,跳转到登录页面
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));

    }

    /**
     * 判断某个请求是否在不登录的时候就可以放行
     *
     * @param urls
     * @param requestURI
     * @return
     */
    private boolean checkUrl(String[] urls, String requestURI) {

        boolean matchResult = false;
        for (String url : urls) {

            // 匹配 本次请求的requestURI  是否符合 url
            matchResult = apm.match(url, requestURI);

            if (matchResult) {
                return true;
            }
        }

        log.info("本次请求url为:{},是否需要放行:{}", requestURI, matchResult);
        return false;
    }
}

2.2.2 开启组件扫描

需要在引导类上, 加上Servlet组件扫描的注解, 来扫描过滤器配置的@WebFilter注解, 扫描上之后, 过滤器在运行时就生效了。

@Slf4j
@SpringBootApplication
@ServletComponentScan
public class ReggieApplication {
    public static void main(String[] args) {
        SpringApplication.run(ReggieApplication.class,args);
        log.info("项目启动成功...");
    }
}

@ServletComponentScan 的作用:

在SpringBoot项目中, 在引导类/配置类上加了该注解后, 会自动扫描项目中(当前包及其子包下)的

​ @WebServlet ,

​ @WebFilter ,

​ @WebListener 注解, 自动注册Servlet的相关组件 ;

2.3 拦截器

拦截器具体的处理逻辑如下:

  1. 获取本次请求的URI
  2. 判断登录状态,如果已登录,则直接放行
  3. 如果未登录, 则返回未登录结果
  4. 把拦截器配置进容器,并指定拦截规则

2.3.1 代码实现拦截器

import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.reggie.utils.common.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

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

/**
 * 员工登录拦截器
 */
@Slf4j
@Component
public class EmployeeInterceptor implements HandlerInterceptor {

    /**
     * 重写preHandle方法,校验登录状态,如果已经登录,则放行;否则跳转到登录页面
     *
     * @param request  请求对象
     * @param response 响应对象
     * @param handler  处理器封装对象
     * @return
     * @throws Exception 1. 获取本次请求的URI
     *                   2. 判断登录状态,如果已登录,则直接放行
     *                   3. 如果未登录, 则返回未登录结果
     *                   4. 把拦截器配置进容器,并指定拦截规则
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 1. 获取本次请求的URI
        String requestURI = request.getRequestURI();

        // 2. 放行不需要拦截的资源  /employee/login
        /* if ("/employee/login".equals(requestURI)) {
            return true;
        } */
        log.info("本次请求URI:{}", requestURI);
        Long employeeId = (Long) request.getSession().getAttribute("employee");
        // 3. 如果未登录, 则返回未登录结果
        if (employeeId == null) {

            // 前端跳转到登录页面,需要用到用到一个特殊标识:NOTLOGIN
            // 因为preHandle返回值类型为boolean,所以不能return RObj
            // 使用response原生API
            // 创建R对象,并且手动序列化为JSON格式字符串

            /**
             * writeValueAsString(obj)  把obj序列化为JSON格式字符串
             * writeValue(writer, obj)  把obj序列化为JSON格式字符串,然后写入writer
             */
            new ObjectMapper().writeValue(response.getWriter(), R.error("NOTLOGIN"));

            // 拦截
            return false;
        }

        // 4. 判断登录状态,如果已登录,则直接放行
        // 放行
        return true;

        // 5. 把拦截器配置进容器,并指定拦截规则
    }
}

2.3.2 web容器中配置拦截器

在配置文件写入要排除的路径

excludePath: '/employee/login,/employee/logout,/backend/**,/front/**,/employee/login,favicon.ico'

web初始化配置配置要拦截的路径

import com.itheima.reggie.web.interceptor.EmployeeInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import java.util.Arrays;

@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {

    @Autowired
    EmployeeInterceptor employeeInterceptor;

    @Value("${excludePath}")
    String excludePath;

    /**
     * 配置拦截器,并映射拦截路径
     *
     * @param registry
     */
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        log.info("开始加载拦截器...");
        String[] urls = excludePath.split(",");
        log.info("直接放行的路径是:{}", Arrays.toString(urls));
        registry
                .addInterceptor(employeeInterceptor)
                // 本拦截器拦截的资源路径
                .addPathPatterns("/employee/**")
                // 本拦截器不拦截的资源路径
                // .excludePathPatterns("/employee/login");
                .excludePathPatterns(urls);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江桥诗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值