登录功能:
 
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginInfo {
    private Integer id;  //用户ID
    private String username; //用户名
    private String name;  //姓名
    private String token;  // 令牌
}empService接口:

    /**
     * 登录
     * @param emp
     * @return
     */
    LoginInfo login(Emp emp);
}empServiceImpl实现类:
  aempMapper接口:

    /**
     * 根据用户名和密码查询
     * @param emp
     * @return
     */
    @Select("select * from emp where username = #{username} and password = #{password}")
    Emp getByUserNameAndPwd(Emp emp);
}empServiceImpl实现类:

  @Override
    public LoginInfo login(Emp emp) {
        Emp e =  empMapper.getByUserNameAndPwd(emp);
        if (e != null){  //登录成功  --生成令牌
            Map<String,Object> map = new HashMap<>();  //设置 payload的数据
            map.put("id",e.getId());
            map.put("username",e.getUsername());
            map.put("name",e.getName());
            String jwt = JwtUtils.generateJwt(map);
            return new LoginInfo(e.getId(),e.getUsername(),e.getName(),jwt);
        }
        return null;
    }LoginController:

    @Autowired
    private EmpService empService;
    @PostMapping("/login")
    public Result login(@RequestBody Emp emp){
        log.info("用户进行登录了...");
       LoginInfo loginInfo =  empService.login(emp);
     if (loginInfo != null){
         return Result.success(loginInfo);
     }
     return Result.error("用户名或密码错误~~");
    }引子:
登录校验:

     会话技术:
 
 
简单的理解,一次会话,就像我们的一次打电话。

断开了一个。

会话跟踪:
         会话跟踪方案:
 
             客户端会话跟踪技术:Cookie
 

cookie的三个自动:
优点:
                 缺点:
 
 
编写SessionController控制器层:
 
    //设置Cookie
    @GetMapping("/c1")
    public Result cookie1(HttpServletResponse response){
        response.addCookie(new Cookie("login_username","sde666")); //设置Cookie/响应Cookie
        return Result.success();
    }启动项目,在浏览器控制栏,输入:http://localhost:8080/s1 访问
 

在SessionController类中,编写接口 c2 获取cookie

    //获取Cookie
    @GetMapping("/c2")
    public Result cookie2(HttpServletRequest request){
        Cookie[] cookies = request.getCookies();
        for (Cookie cookie : cookies) {
            if(cookie.getName().equals("login_username")){
                System.out.println("login_username: "+cookie.getValue()); //输出name为login_username的cookie
            }
        }
        return Result.success();
    }执行 http://localhost:8080/c2 获取cookie

看看浏览器控制台:
                 区分跨域的三个维度:
 
 
             服务端会话跟踪技术:Session
 
优点:
                 缺点:
 
 
编写 SessionController控制器:
 
    /**
     * 存session
     * @param session
     * @return
     */
    @GetMapping("/s1")
    public Result session1(HttpSession session){
        log.info("HttpSession-s1: {}", session.hashCode());
        session.setAttribute("loginUser", "tom"); //往session中存储数据
        return Result.success();
    }在浏览器输入http://localhost:8080/s1访问

编写:http://localhost:8080/s2 取session

    /**
     * 取session
     * @param request
     * @return
     */
    @GetMapping("/s2")
    public Result session2(HttpServletRequest request){
        HttpSession session = request.getSession();
        log.info("HttpSession-s2: {}", session.hashCode());
        Object loginUser = session.getAttribute("loginUser"); //从session中获取数据
        log.info("loginUser: {}", loginUser);
        return Result.success(loginUser);
    }通过浏览器访问:

通过idea控制台观看:

JWT令牌:

         优点:
 
 
缺点:
全称:
组成:
jwt依赖:
   
<dependency> 
     <groupId>io.jsonwebtoken</groupId>
     <artifactId>jjwt</artifactId> 
     <version>0.9.1</version>
</dependency>生成令牌:
编写JwtTest类:

      
    /**
     * 生成令牌
     */
    @Test
    void testGenJwt(){
        Map<String,Object> claims = new HashMap<>();
        claims.put("id",18);
        claims.put("username","孙尚香");
        String jwt = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256,"c2Rl")   //设置令牌的签名算法和密钥
                .setExpiration(new Date(System.currentTimeMillis() + 2 * 3600 * 1000))  //设置过期时间
                .addClaims(claims)  //往指定令牌存入数据
                .compact();  //生成令牌
        System.out.println(jwt);
    }运行测试类:
 
解析令牌:
把上面刚刚,生成的令牌进行校验

    /**
     * 解析令牌
     */
    @Test
    void testParseJwt(){
        Claims claims = Jwts.parser()
                .setSigningKey("c2Rl")  //
                .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MDI3MDc1NjksImlkIjoxOCwidXNlcm5hbWUiOiLlrZnlsJrpppkifQ.XW__woxh-1YVdR2rILavgr3tMPCqKjPjTioFC02MaZ4")
                .getBody(); //获取自定义内容
        System.out.println(claims);
    }看idea控制台:

jwt令牌解析失败的4个情况:
演示一:(改写jwt令牌header部分)

测试效果:

演示二(更改令牌Payload部分)

效果测试:

演示三(更改signature部分)

效果演示:

演示四(jwt令牌过期)

运行这个单元测试方法,生成jwt令牌,放到下面的程序中解析。

进行解析测试:

完成登录成功下发Jwt令牌的操作:

测试效果:

测试一下登录失败的情况。

过滤器Filter:

             概念:
 
 
快速操作:

我们可以看到, 当我们实现Filter接口时,程序会报错。

上面有三个方法,要实现,也可以只实现一个方法,那就是doFilter方法。因为init方法和destory方法都是默认实现了已经。


  
@Slf4j
public class DemoFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("DemoFilter init 方法执行了... ");
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        log.info("DemoFilter diFilter 方法执行了 放行前执行...");
        chain.doFilter(servletRequest,servletResponse); //放行
        log.info("DemoFilter diFilter 方法执行了 放行后执行...");
    }
    @Override
    public void destroy() {
        log.info("DemoFilter destory 方法执行了...");
    }
}配置Filter:

引导类上加注解:

@ServletComponentScan    //开启spring boot对javaweb组件的支持(filter)
@SpringBootApplication
public class TilasRun {
    public static void main(String[] args) {
        SpringApplication.run(TilasRun.class, args);
    }
}运行测试:
在Apifox里面发送请求进行测试:

关系应用程序:
 
         Filter详解:
 
 
执行流程:

过滤器的放行:
 
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        log.info("DemoFilter diFilter 方法执行了 放行前执行...");
        chain.doFilter(servletRequest,servletResponse); //放行
        log.info("DemoFilter diFilter 方法执行了 放行后执行...");
    }如果我们没有调用 FilterChain接口中的 doFilter方法,那么就代表没有放行。
 
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        log.info("DemoFilter diFilter 方法执行了 放行前执行...");
       // chain.doFilter(servletRequest,servletResponse); // 我把这一行注释了,表示不放行,那么请求就被拦截在这了。
        log.info("DemoFilter diFilter 方法执行了 放行后执行...");
    }重启服务器测试:
 调用查询全部的部门接口
调用登录接口:
 
看看 idea的控制台信息:

             拦截路径解析:
 
        
                 拦截具体路径:
 
 
@WebFilter("/login") //拦截 login请求
public class DemoFilter implements Filter {
}测试:
查询全部的部门信息:

登录接口:
                 目录拦截:
 
 
@Slf4j
//@WebFilter(urlPatterns = "/*")
//@WebFilter("/login") //拦截 login请求
@WebFilter(urlPatterns = "/depts/*")  //拦截depts目录下的所有请求
public class DemoFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("DemoFilter init 方法执行了... ");
    }
}测试:
登录接口:
查看所有部门接口:
 
                 拦截所有:
 
 
             过滤器链:
 

                 介绍:
                 顺序:
创建AbcFilter类:

@Slf4j
 @WebFilter("/*")
public class AbcFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        log.info("AbcFilter doFilter 方法执行了 放行前");
        chain.doFilter(servletRequest,servletResponse); // 放行
        log.info("AbcFilter doFilter 方法执行了 放行后");
    }
}还有一个filter类就是上面用的DemoFilter类

@Slf4j
@WebFilter(urlPatterns = "/*")
//@WebFilter("/login") //拦截 login请求
//@WebFilter(urlPatterns = "/depts/*")  //拦截depts目录下的所有请求
public class DemoFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("DemoFilter init 方法执行了... ");
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        log.info("DemoFilter diFilter 方法执行了 放行前执行...");
        chain.doFilter(servletRequest,servletResponse); //放行
        log.info("DemoFilter diFilter 方法执行了 放行后执行...");
    }
    @Override
    public void destroy() {
        log.info("DemoFilter destory 方法执行了...");
    }
}这两个过滤器,就成了过滤器链了,我们启动程序观察执行流程。
开始:
在Apifox里面发送请求测试:

第一步:

第二步:

第三步:

第四步:

第五步:

最后:

看看idea的控制台:

登录校验-Filter:
登录拦截步骤:
1,获取请求的url,2,判断请求头中是否包含login

 @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
            //获取请求url。
            String url = request.getRequestURL().toString();
            //判断请求url中是否包含login,如果包含,说明是登录操作,放行。
            if (url.contains("/login")){
                log.info("是登录请求,直接放行{}",url);
                chain.doFilter(request,response);
                return;
            }
}3,获取请求头中的令牌:

     //获取请求头中的令牌(token)。
             String jwt = request.getHeader("token");4,判断令牌是否存在:

      //判断令牌是否存在,如果不存在,响应401。
                if (!StringUtils.hasLength(jwt)){
                    log.info("没有携带jwt令牌",jwt);
                    //如果没有 jwt令牌,设置响应码为 401
                    response.setStatus(401);
                    return;
                }5,解析令牌:

        //解析token,如果解析失败,响应401 。
            try {
                JwtUtils.parseJWT(jwt);
            } catch (Exception e) {
                log.info("令牌解析出错{}",e);
                response.setStatus(401);
                return;
            }6,放行:

        //放行。
        chain.doFilter(request,response);登录测试:
我在Apifox里面进行登录测试,看看拦截器是否直接放行了,登录接口。

查看拦截器:

直接放行,访问web资源(LoginController)

测试,jwt令牌为null的情况。

拦截到了请求:

令牌为null

看Apifox返回的信息:

测试不合法的jwt令牌:
拦截,因为不是登录请求。

有jwt令牌:

非法的jwt令牌,解析出现异常。

在Apifox查看效果:

成功测试-jwt令牌合法
不是登录请求,拦截。

有令牌:

合法的令牌:

成功访问到web资源

Apifox的返回数据:

拦截器Interceptor:

        
             概念:
 
 
             作用:
 
 
 
 
1,定义一个类,DemoInterceptor实现 HandlerInterceptor接口。

@Slf4j
@Component
public class DemoInterceptor implements HandlerInterceptor {
    // controller 方法运行之前执行  返回值是 boolean类型 true表示放行 false表示不放行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("DemoInterceptor preHandle 方法执行了");
        return true;
    }
    // controller 方法运行之后执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
       log.info("DemoInterceptor postHandle 方法执行了");
    }
    // 最后运行的
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("DemoInterceptor afterCompletion 方法执行了");
    }
}2,注册拦截器:

/**
 * 配置类---配置拦截器
 */
@Slf4j
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private DemoInterceptor demoInterceptor;
        @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(demoInterceptor).addPathPatterns("/**");  //表示拦截所有请求的路径
    }
}在Apifox测试,是否能够拦截到我的所有请求。

方法执行前拦截到了。

web资源,运行后执行。

访问到了deptController:

最后执行的方法也执行了。

查看Apifox的返回数据。

查看一下,执行流程。

拦截器:HandlerInterceptor接口解析:
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		return true;
	}
后两个方法:
// handlerInterceptor接口的第二个方法。
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
	}
//  handlerInterceptor接口的第三个方法。
	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}

过滤器路径详解:
一级路径:
任意路径:
        @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(demoInterceptor).addPathPatterns("/**");  //表示拦截所有请求的路径
    }测试效果:
登录接口拦截:

idea效果:

查询所有部门信息接口:

idea效果:

目录匹配一级:

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
            // 拦截除了登录以外的所有路径
        registry.addInterceptor(demoInterceptor).addPathPatterns("/depts/*");
    }测试查询单个emp的接口:
 
 
idea效果:

测试根据id查询部门信息接口:
idea效果:

目录匹配任意级:

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
            // 拦截除了登录以外的所有路径
        registry.addInterceptor(demoInterceptor).addPathPatterns("/depts/**");
    }过滤器,拦截路径的排除(excludePathPatterns):
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(demoInterceptor).addPathPatterns("/**").excludePathPatterns("/login"); //我在preHandle方法中已经判断了 .excludePathPatterns("/login");
    }测试:
 
ieal效果:

 
然后在到LoginContriller接口:

             Filter和interceptot的区别:























