[JavaWeb]【十一】web后端开发-SpringBootWeb案例(登录)

目录

一、登录功能

[1.1 思路](#1.1 思路)

[1.2 LoginController](#1.2 LoginController)

[1.3 EmpService](#1.3 EmpService)

[1.4 EmpServiceImpl](#1.4 EmpServiceImpl)

[1.5 EmpMapper](#1.5 EmpMapper)

[1.6 启动服务-测试](#1.6 启动服务-测试)

[1.7 前后端联调](#1.7 前后端联调)

二、登录校验(重点)

[2.1 问题](#2.1 问题)

[2.2 问题分析](#2.2 问题分析)

[2.3 登录校验​编辑](#2.3 登录校验编辑)

[2.4 会话技术](#2.4 会话技术)

[2.4.1 会话技术](#2.4.1 会话技术)

[2.4.2 会话跟踪方案对比](#2.4.2 会话跟踪方案对比)

[2.5 JWT令牌](#2.5 JWT令牌)

[2.5.1 简介](#2.5.1 简介)

[2.5.2 应用场景](#2.5.2 应用场景)

[2.5.3 jwt-生成](#2.5.3 jwt-生成)

[5.5.3.1 引入包](#5.5.3.1 引入包)

[5.5.3.2 生成JWT](#5.5.3.2 生成JWT)

[5.5.3.3 解析JWT](#5.5.3.3 解析JWT)

[2.6 案例实现JWT](#2.6 案例实现JWT)

[2.6.1 新建JwtUtils工具类](#2.6.1 新建JwtUtils工具类)

[2.6.2 LoginController](#2.6.2 LoginController)

[2.6.3 启动服务-测试](#2.6.3 启动服务-测试)

[2.6.4 前后端联调](#2.6.4 前后端联调)

[2.7 过滤器Filter](#2.7 过滤器Filter)

[2.7.1 概述](#2.7.1 概述)

[2.7.2 快速入门](#2.7.2 快速入门)

[2.7.2.1 新增DemoFilter](#2.7.2.1 新增DemoFilter)

[2.7.2.2 SpringBootProjectTestApplication](#2.7.2.2 SpringBootProjectTestApplication)

[2.7.2.3 启动服务-测试](#2.7.2.3 启动服务-测试)

[2.7.2.4 总结](#2.7.2.4 总结)

[2.7.3 详解(执行流程、拦截路径、过滤器链)](#2.7.3 详解(执行流程、拦截路径、过滤器链))

[2.7.3.1 执行流程](#2.7.3.1 执行流程)

[2.7.3.2 Filter-拦截路径 ​编辑](#2.7.3.2 Filter-拦截路径 编辑)

[2.7.3.3 过滤器链(优先级按过滤器名自然排序)](#2.7.3.3 过滤器链(优先级按过滤器名自然排序))

[2.7.3.4 总结](#2.7.3.4 总结)

[2.7.4 案例-登录校验-Filter](#2.7.4 案例-登录校验-Filter)

[2.7.4.1 思路](#2.7.4.1 思路)

[2.7.4.2 pom.xml引入依赖fastjson](#2.7.4.2 pom.xml引入依赖fastjson)

[2.7.4.3 新建工具类拦截器LoginCheckFilter](#2.7.4.3 新建工具类拦截器LoginCheckFilter)

[2.7.4.4 启动服务-测试](#2.7.4.4 启动服务-测试)

[2.8 拦截器Interceptor](#2.8 拦截器Interceptor)

[2.8.1 简介](#2.8.1 简介)

[2.8.2 快速入门](#2.8.2 快速入门)

[2.8.2.1 新建LoginCheckInterceptor](#2.8.2.1 新建LoginCheckInterceptor)

[2.8.2.2 WebConfig](#2.8.2.2 WebConfig)

[2.8.2.3 启动服务测试](#2.8.2.3 启动服务测试)

[2.8.3 详解](#2.8.3 详解)

[2.8.3.1 拦截路径](#2.8.3.1 拦截路径)

[2.8.3.2 拦截器- 执行流程](#2.8.3.2 拦截器- 执行流程)

[2.8.4 案例-登录校验-Interceptor](#2.8.4 案例-登录校验-Interceptor)

[2.8.4.1 LoginCheckInterceptor](#2.8.4.1 LoginCheckInterceptor)

[2.8.4.2 WebConfig](#2.8.4.2 WebConfig)

[2.8.4.3 启动服务-测试](#2.8.4.3 启动服务-测试)

三、异常处理

[3.1 异常现象--新增重复名称部门](#3.1 异常现象--新增重复名称部门)

[3.2 思考如何处理](#3.2 思考如何处理)

[3.3 全家异常处理](#3.3 全家异常处理)

[3.3.1 异常类GlobalExceptionHandler](#3.3.1 异常类GlobalExceptionHandler)

[3.3.2 启动服务-测试](#3.3.2 启动服务-测试)

[3.3.3 总结](#3.3.3 总结)


前言:实现案例的登录功能、登录校验和异常处理

一、登录功能

1.1 思路

1.2 LoginController

java 复制代码
package com.runa.controller;

import com.runa.pojo.Emp;
import com.runa.pojo.Result;
import com.runa.service.EmpService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
public class LoginController {

    @Autowired
    private EmpService empService;
    @PostMapping("/login")
    public Result login(@RequestBody Emp emp){
        log.info("登录的用户:{}",emp);
        Emp e = empService.login(emp);
        return e != null ? Result.success():Result.error("用户名或密码错误");

    }
}

1.3 EmpService

java 复制代码
package com.runa.service;

import com.runa.pojo.Emp;
import com.runa.pojo.PageBean;
import org.springframework.format.annotation.DateTimeFormat;

import java.time.LocalDate;
import java.util.List;

public interface EmpService {



    /**
     * 分页查询  不带条件
     * @param page
     * @param pageSize
     * @return
     */
//    PageBean page(Integer page, Integer pageSize);

    /**
     * 分页查询 带条件
     * @param page
     * @param pageSize
     * @return
     */
    PageBean page(Integer page, Integer pageSize,String name, Short gender, LocalDate begin, LocalDate end);

    /**
     * 批量删除员工
     * @param ids
     */
    void delete(List<Integer> ids);

    /**
     * 新增员工
     * @param emp
     */
    void save(Emp emp);

    /**
     * 根据id查询员工
     * @param id
     * @return
     */
    Emp getById(Integer id);

    /**
     * 修改员工
     * @param emp
     */
    void update(Emp emp);

    /**
     * 登录
     * @param emp
     * @return
     */
    Emp login(Emp emp);
}

1.4 EmpServiceImpl

java 复制代码
package com.runa.service.impl;



import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.runa.mapper.EmpMapper;
import com.runa.pojo.Emp;
import com.runa.pojo.PageBean;
import com.runa.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

@Service
public class EmpServiceImpl implements EmpService {

    @Autowired
    private EmpMapper empMapper;

    /**
     * 分页查询 pagehelper+组合查询
     * @param page
     * @param pageSize
     * @param name
     * @param gender
     * @param begin
     * @param end
     * @return
     */
    @Override
    public PageBean page(Integer page, Integer pageSize, String name, Short gender, LocalDate begin, LocalDate end) {
        // 1 设置分页参数
        PageHelper.startPage(page, pageSize);

        // 2 执行查询
        List<Emp> empList = empMapper.list(name, gender, begin, end);
        Page<Emp> p = (Page<Emp>) empList;

        // 3 封装PangeBean对象
        PageBean pageBean = new PageBean(p.getTotal(),((Page<Emp>) empList).getResult());
        return pageBean;
    }

    /**
     * 批量删除员工
     * @param ids
     */
    @Override
    public void delete(List<Integer> ids) {
        empMapper.delete(ids);

    }

    /**
     * 新增员工
     * @param emp
     */
    @Override
    public void save(Emp emp) {
        emp.setCreateTime(LocalDateTime.now());
        emp.setUpdateTime(LocalDateTime.now());
        empMapper.insert(emp);

    }

    /**
     * 根据ID查询员工
     * @param id
     * @return
     */
    @Override
    public Emp getById(Integer id) {

        return empMapper.getByID(id);
    }

    /**
     * 修改员工
     * @param emp
     */
    @Override
    public void update(Emp emp) {
        emp.setUpdateTime(LocalDateTime.now());
        empMapper.update(emp);

    }

    /**
     * 登录
     * @param emp
     * @return
     */
    @Override
    public Emp login(Emp emp) {

        return empMapper.getByUsernameAndPassword(emp);
    }


    /**
     * 分页查询  加pagehelper
     * @param page
     * @param pageSize
     * @return
     */
//    @Override
//    public PageBean page(Integer page, Integer pageSize) {
//        // 1 设置分页参数
//        PageHelper.startPage(page, pageSize);
//
//        // 2 执行查询
//        List<Emp> empList = empMapper.list();
//        Page<Emp> p = (Page<Emp>) empList;
//
//        // 3 封装PangeBean对象
//        PageBean pageBean = new PageBean(p.getTotal(),((Page<Emp>) empList).getResult());
//        return pageBean;
//    }

    /**
     * 分页查询
     * @param page
     * @param pageSize
     * @return
     */
//    @Override
//    public PageBean page(Integer page, Integer pageSize) {
//        // 1 获取总记录数
//        Long count = empMapper.count();
//
//        // 2 获取分页查询结果列表
//        Integer start = (page - 1) * pageSize;
//        List<Emp> empList = empMapper.page(start, pageSize);
//
//        // 3 封装PangeBean对象
//        PageBean pageBean = new PageBean(count,empList);
//    }


}

1.5 EmpMapper

java 复制代码
package com.runa.mapper;

import com.runa.pojo.Emp;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.time.LocalDate;
import java.util.List;

/**
 * 员工管理
 */
@Mapper
public interface EmpMapper {


    /**
     * 查询总记录数
     * @return
     */
//    @Select("select count(*) from emp")
//    public Long count();

    /**
     * 分页查询 获取列表数据
     * @param start
     * @param pageSize
     * @return
     */
//    @Select("select * from emp limit #{start}, #{pageSize}")
//    public List<Emp> page(Integer start, Integer pageSize);
    /**
     * 使用pagehelper的员工信息查询
     * @return
     */
//    @Select("select * from emp")
//    public List<Emp> list();

    /**
     * 使用pagehelper的员工信息查询(带条件)--动态sql
     * 使用xml注解sql
     * @return
     */

    public List<Emp> list(String name, Short gender, LocalDate begin, LocalDate end);

    /**
     * 批量删除员工
     * @param ids
     */
    void delete(List<Integer> ids);

    /**
     * 新增员工
     * @param emp
     */
    @Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)" +
            " values(#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})")
    void insert(Emp emp);

    /**
     * 根据id查询员工
     * @param id
     * @return
     */
    @Select("select * from emp where id = #{id}")
    Emp getByID(Integer id);

    /**
     * 修改员工
     * @param emp
     */
    void update(Emp emp);



    /**
     * 登录
     * @param emp
     * @return
     */
    @Select("select * from emp where username = #{username} and password = #{password}")
    Emp getByUsernameAndPassword(Emp emp);
}

1.6 启动服务-测试

1.7 前后端联调

二、登录校验(重点)

2.1 问题

2.2 问题分析

2.3 登录校验

2.4 会话技术

2.4.1 会话技术

2.4.2 会话跟踪方案对比

Cookies与Session例子,启动服务http://localhost:8080/c1

java 复制代码
package com.runa.controller;

import com.runa.pojo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

/**
 * HttpSession演示
 */
@Slf4j
@RestController
public class SessionController {

    //设置Cookie
    @GetMapping("/c1")
    public Result cookie1(HttpServletResponse response){
        response.addCookie(new Cookie("login_username","itheima")); //设置Cookie/响应Cookie
        return Result.success();
    }

    //获取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();
    }



    @GetMapping("/s1")
    public Result session1(HttpSession session){
        log.info("HttpSession-s1: {}", session.hashCode());

        session.setAttribute("loginUser", "tom"); //往session中存储数据
        return Result.success();
    }

    @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);
    }
}

2.5 JWT令牌

2.5.1 简介

2.5.2 应用场景

2.5.3 jwt-生成

5.5.3.1 引入包

XML 复制代码
        <!--JWT令牌-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

5.5.3.2 生成JWT

记得注释他//@SpringBootTest

java 复制代码
package com.runa;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

//@SpringBootTest
class SpringBootProjectTestApplicationTests {

//    @Test
//    void contextLoads() {
//    }

    /**
     * 测试JWT令牌的生成
     */
    @Test
    public void testGenJwt(){
        Map<String, Object> claims = new HashMap<>();
        claims.put("id",1);
        claims.put("name","bocai");
        String jwt = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, "runa") // 签名算法 算法有哪些上官网
                .setClaims(claims) //自定义的内容(载荷)
                .setExpiration(new Date(System.currentTimeMillis() + 3600* 1000)) // 设置有效期为1h
                .compact();
        System.out.println(jwt);

    }

}

生成的jwt令牌上官网

5.5.3.3 解析JWT

java 复制代码
package com.runa;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

//@SpringBootTest
class SpringBootProjectTestApplicationTests {

//    @Test
//    void contextLoads() {
//    }

    /**
     * 生成Jwt令牌
     */
    @Test
    public void testGenJwt(){
        Map<String, Object> claims = new HashMap<>();
        claims.put("id",1);
        claims.put("name","bocai");
        String jwt = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, "runa") // 签名算法 算法有哪些上官网
                .setClaims(claims) //自定义的内容(载荷)
                .setExpiration(new Date(System.currentTimeMillis() + 3600* 1000)) // 设置有效期为1h
                .compact();
        System.out.println(jwt);

    }

    /**
     * 解析Jwt
     */
    @Test
    public void testPareJwt(){
        Claims claims = Jwts.parser()
                .setSigningKey("runa") //runa要与前面生成一致
                .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiYm9jYWkiLCJpZCI6MSwiZXhwIjoxNjkyNzc3MzAwfQ.KPqgKc5JS8j7GN7aPQ0GwQnUaGm78WWbzf2N7LGq34g")
                .getBody();
        System.out.println(claims);

    }

}

2.6 案例实现JWT

2.6.1 新建JwtUtils工具类

java 复制代码
package com.runa.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.Map;

public class JwtUtils {

    private static String signKey = "runa";
    private static Long expire = 43200000L;  // 12h

    /**
     * 生成JWT令牌
     * @param claims JWT第二部分负载 payload 中存储的内容
     * @return
     */
    public static String generateJwt(Map<String, Object> claims){
        String jwt = Jwts.builder()
                .addClaims(claims)
                .signWith(SignatureAlgorithm.HS256, signKey)
                .setExpiration(new Date(System.currentTimeMillis() + expire))
                .compact();
        return jwt;
    }

    /**
     * 解析JWT令牌
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser()
                .setSigningKey(signKey)
                .parseClaimsJws(jwt)
                .getBody();
        return claims;
    }
}

2.6.2 LoginController

java 复制代码
package com.runa.controller;

import com.runa.pojo.Emp;
import com.runa.pojo.Result;
import com.runa.service.EmpService;
import com.runa.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@RestController
public class LoginController {

    @Autowired
    private EmpService empService;
    @PostMapping("/login")
    public Result login(@RequestBody Emp emp){
        log.info("登录的用户:{}",emp);
        Emp e = empService.login(emp);

        // 登录成功,生成令牌,下发令牌
        if(e != null){
            Map<String, Object> claims = new HashMap<>();
            claims.put("id",e.getId());
            claims.put("name", e.getName());
            claims.put("username", e.getUsername());

            String jwt = JwtUtils.generateJwt(claims);
            return Result.success(jwt);
        }
        // 登录失败,返回错误信息

        return Result.error("用户名或密码错误");

    }
}

2.6.3 启动服务-测试

2.6.4 前后端联调

2.7 过滤器Filter

2.7.1 概述

2.7.2 快速入门

2.7.2.1 新增DemoFilter

java 复制代码
package com.runa.filter;

import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;

import java.io.IOException;

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

    @Override // 拦截到请求之后调用,调用多次
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("拦截到请求了~~~");
        // 放行
        filterChain.doFilter(servletRequest,servletResponse);
    }

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

2.7.2.2 SpringBootProjectTestApplication

java 复制代码
package com.runa;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@ServletComponentScan  //开启了对servlet组件支持
@SpringBootApplication
public class SpringBootProjectTestApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootProjectTestApplication.class, args);
    }

}

2.7.2.3 启动服务-测试

2.7.2.4 总结

2.7.3 详解(执行流程、拦截路径、过滤器链)

2.7.3.1 执行流程

2.7.3.2 Filter-拦截路径

2.7.3.3 过滤器链(优先级按过滤器名自然排序)

java 复制代码
package com.runa.filter;

import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;

import java.io.IOException;

@WebFilter(urlPatterns = "/*")
public class SecondFilter implements Filter {

    @Override // 拦截到请求之后调用,调用多次
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("SecondFilter 拦截---2到请求了~~~放行之前逻辑");
        // 放行
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("SecondFilter拦截---2到请求了~~~放行之后逻辑");
    }
}

2.7.3.4 总结

2.7.4 案例-登录校验-Filter

2.7.4.1 思路

2.7.4.2 pom.xml引入依赖fastjson

XML 复制代码
        <!--fastJSON-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>

2.7.4.3 新建工具类拦截器LoginCheckFilter

java 复制代码
package com.runa.filter;

import com.alibaba.fastjson.JSONObject;
import com.runa.pojo.Result;
import com.runa.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
//@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        //1.获取请求url。
        String url = req.getRequestURL().toString();
        log.info("请求的url: {}",url);

        //2.判断请求url中是否包含login,如果包含,说明是登录操作,放行。
        if(url.contains("login")){
            log.info("登录操作, 放行...");
            chain.doFilter(request,response);
            return;
        }

        //3.获取请求头中的令牌(token)。
        String jwt = req.getHeader("token");

        //4.判断令牌是否存在,如果不存在,返回错误结果(未登录)。
        if(!StringUtils.hasLength(jwt)){
            log.info("请求头token为空,返回未登录的信息");
            Result error = Result.error("NOT_LOGIN");
            //手动转换 对象--json --------> 阿里巴巴fastJSON
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            return;
        }

        //5.解析token,如果解析失败,返回错误结果(未登录)。
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {//jwt解析失败
            e.printStackTrace();
            log.info("解析令牌失败, 返回未登录错误信息");
            Result error = Result.error("NOT_LOGIN");
            //手动转换 对象--json --------> 阿里巴巴fastJSON
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            return;
        }

        //6.放行。
        log.info("令牌合法, 放行");
        chain.doFilter(request, response);

    }
}

2.7.4.4 启动服务-测试

记得将 DemoFilter 与SecondFilter 的@WebFilter(urlPatterns = "/*") 注释掉

2.8 拦截器Interceptor

2.8.1 简介

2.8.2 快速入门

2.8.2.1 新建LoginCheckInterceptor

java 复制代码
package com.runa.interceptor;


import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;


@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override //目标资源方法运行前运行, 返回true: 放行, 放回false, 不放行
    public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, 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...");
    }
}

2.8.2.2 WebConfig

java 复制代码
package com.runa.config;

import com.runa.interceptor.LoginCheckInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration //配置类
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");
    }
}

2.8.2.3 启动服务测试

将过滤器 LoginCheckFilter @WebFilter(urlPatterns = "/*") 注释掉

2.8.3 详解

2.8.3.1 拦截路径

2.8.3.2 拦截器- 执行流程

2.8.4 案例-登录校验-Interceptor

2.8.4.1 LoginCheckInterceptor

java 复制代码
package com.runa.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.runa.pojo.Result;
import com.runa.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override //目标资源方法运行前运行, 返回true: 放行, 放回false, 不放行
    public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
        //1.获取请求url。
        String url = req.getRequestURL().toString();
        log.info("请求的url: {}",url);

        //2.判断请求url中是否包含login,如果包含,说明是登录操作,放行。
        if(url.contains("login")){
            log.info("登录操作, 放行...");
            return true;
        }

        //3.获取请求头中的令牌(token)。
        String jwt = req.getHeader("token");

        //4.判断令牌是否存在,如果不存在,返回错误结果(未登录)。
        if(!StringUtils.hasLength(jwt)){
            log.info("请求头token为空,返回未登录的信息");
            Result error = Result.error("NOT_LOGIN");
            //手动转换 对象--json --------> 阿里巴巴fastJSON
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            return false;
        }

        //5.解析token,如果解析失败,返回错误结果(未登录)。
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {//jwt解析失败
            e.printStackTrace();
            log.info("解析令牌失败, 返回未登录错误信息");
            Result error = Result.error("NOT_LOGIN");
            //手动转换 对象--json --------> 阿里巴巴fastJSON
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            return false;
        }

        //6.放行。
        log.info("令牌合法, 放行");
        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...");
    }
}

2.8.4.2 WebConfig

java 复制代码
package com.runa.config;

import com.runa.interceptor.LoginCheckInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration //配置类
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**").excludePathPatterns("/login");
    }
}

2.8.4.3 启动服务-测试

注释Filter

三、异常处理

3.1 异常现象--新增重复名称部门

3.2 思考如何处理

3.3 全家异常处理

3.3.1 异常类GlobalExceptionHandler

java 复制代码
package com.runa.exception;

import com.runa.pojo.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 全局异常处理器
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)//捕获所有异常
    public Result ex(Exception ex){
        ex.printStackTrace();
        return Result.error("对不起,操作失败,请联系管理员");
    }

}

3.3.2 启动服务-测试

新建重复部门

3.3.3 总结

相关推荐
花花鱼2 分钟前
vue3 基于element-plus进行的一个可拖动改变导航与内容区域大小的简单方法
前端·javascript·elementui
k09335 分钟前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
激流丶12 分钟前
【Kafka 实战】如何解决Kafka Topic数量过多带来的性能问题?
java·大数据·kafka·topic
Themberfue16 分钟前
Java多线程详解⑤(全程干货!!!)线程安全问题 || 锁 || synchronized
java·开发语言·线程·多线程·synchronized·
EricWang135827 分钟前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
September_ning27 分钟前
React.lazy() 懒加载
前端·react.js·前端框架
让学习成为一种生活方式32 分钟前
R包下载太慢安装中止的解决策略-R语言003
java·数据库·r语言
web行路人37 分钟前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架
晨曦_子画38 分钟前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
超雄代码狂1 小时前
ajax关于axios库的运用小案例
前端·javascript·ajax