分页查询

@GetMapping()
public Result list(Integer page, Integer pagesize){
    log.info("分页查询:page:{} pagesize{}", page, pagesize);
   PageEmp<Emp> result = empService.findAll(page,pagesize);
    return Result.success(result);
}
        @Override
        public PageEmp<Emp> findAll(Integer page, Integer pagesize) {
                int start=(page-1)*pagesize;
                long total=deptMapper.count();
                List<Emp> data=deptMapper.findAll(start,pagesize);
            return new PageEmp<>(total,data);
        }
​
@Mapper
public interface EmpMapper {
​
    @Select("select * from emp e left join dept d on e.dept_id = d.id limit #{start},#{pagesize}")
    List<Emp> findAll(int start, Integer pagesize);
​
    @Select("select count(*) from emp e left join dept d on e.dept_id = d.id ")
    long count();
}

PageHelper

  • 简单分页查询

@Mapper
public interface EmpMapper {
    @Select("select * from emp e left join dept d on e.dept_id = d.id ")
    List<Emp> findAll();
}
​
@Service
public class EmpServiceImpl implements EmpService {
        @Autowired
        private EmpMapper empMapper;
​
        @Override
        public PageEmp<Emp> findAll(Integer page, Integer pagesize) {
                PageHelper.startPage(page,pagesize);
                List<Emp> empList = empMapper.findAll();
                Page<Emp> p = (Page<Emp>) empList;
                return new PageEmp<>(p.getTotal(),p.getResult());
        }
        
}
  • 条件分页查询

   @GetMapping()
    public Result list(@RequestParam(defaultValue = "1")Integer page,
                       @RequestParam(defaultValue = "10")Integer pageSize,
                       Integer  id,  String name,
                       @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
                       @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end)
    {
        log.info("分页查询:{},{},{},{},{},{}", page, pageSize,id,name,begin,end);
       PageEmp<Emp> result = empService.findAll(page,pageSize,id,name,begin,end);
        return Result.success(result);
    }
   public PageEmp<Emp> findAll(Integer page, Integer pageSize, Integer id, String name, LocalDate begin, LocalDate end) {
                PageHelper.startPage(page,pageSize);
                List<Emp> empList = empMapper.findAll(id, name, begin, end);
                Page<Emp> p = (Page<Emp>) empList;
                return new PageEmp<>(p.getTotal(),p.getResult());
        }
@Mapper
public interface EmpMapper {
​
    List<Emp> findAll(Integer id, String name, LocalDate begin, LocalDate end);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
​
<mapper namespace="com.itwyz.demo2.mapper.EmpMapper">
​
    <select id="findAll" resultType="com.itwyz.demo2.pojo.Emp">
        select e.* from emp e left join dept d on e.dept_id=d.id
        where
        <if test="name!=null and name!=''">
             e.name like concat('%',#{name},'%')
        </if>
          <if test="id!=null">
            and e.id=#{id}
        </if>
        <if test="begin!=null">
            and e.create_time &gt;= #{begin}
        </if>
    </select>
</mapper>
​
  • 条件查询优化

public class EmpQueryParam {
    private Integer page=1;
    private Integer pageSize=10;
    private String name;
    private Integer age;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
    private String deptName;
​
}
    @GetMapping()
    public Result list(EmpQueryParam empQueryParam)
    {
        log.info("分页查询:{}", empQueryParam);
       PageEmp<Emp> result = empService.findAll(empQueryParam);
        return Result.success(result);
    }
        @Override
        public PageEmp<Emp> findAll(EmpQueryParam empQueryParam) {
                PageHelper.startPage(empQueryParam.getPage(),empQueryParam.getPageSize());
                List<Emp> empList = empMapper.findAll(empQueryParam);
                Page<Emp> p = (Page<Emp>) empList;
                return new PageEmp<>(p.getTotal(),p.getResult());
        }
@Mapper
public interface EmpMapper {
​
    List<Emp> findAll(EmpQueryParam empQueryParam);
}
​
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
​
<mapper namespace="com.itwyz.demo2.mapper.EmpMapper">
​
    <select id="findAll" resultType="com.itwyz.demo2.pojo.Emp">
        select e.* from emp e left join dept d on e.dept_id=d.id
        where
        <if test="name!=null and name!=''">
             e.name like concat('%',#{name},'%')
        </if>
          <if test="age!=null">
            and e.age=#{age}
        </if>
        <if test="createTime!=null">
            and e.create_time = #{createTime}
        </if>
        <if test="updateTime!=null">
            and e.create_time = #{updateTime}
        </if>
    </select>
</mapper>
​

新增(批量保存)

    @PostMapping()
    public Result add(@RequestBody Emp emp)
    {
        log.info("新增员工:{}", emp);
        empService.save(emp);
        return Result.success();
    }
       public void save(Emp emp) {
                //1.保存员工基本信息
                emp.setCreateTime(LocalDateTime.now());
                emp.setUpdateTime(LocalDateTime.now());
                empMapper.insert(emp);
                List<Expr> exprList = emp.getExprList();
                if(!exprList.isEmpty()){
                        exprList.forEach(expr->{
                                expr.setEmpId(emp.getId());
                        });
                }
                        empMapper.insertBach(exprList);
​
        }
    @Options(useGeneratedKeys = true,keyProperty = "id")//获取到生成的主键
    @Insert("insert into emp(name,age,dept_id,create_time,update_time) values(#{name},#{age},#{deptId},#{createTime},#{updateTime})")
    void insert(Emp emp);
​
    void insertBach(List<Expr> exprList);
<mapper namespace="com.itwyz.demo2.mapper.EmpMapper">
    <insert id="insertBach">
        insert into expr (emp_id,expr) values
        <foreach collection="exprList" item="expr" separator=",">
            (#{expr.empId},#{expr.expr})
        </foreach>
    </insert>

不用xml映射文件

    @PostMapping()
    public Result add(@RequestBody Emp emp)
    {
        log.info("新增员工:{}", emp);
        empService.save(emp);
        return Result.success();
    }

      public void save(Emp emp) {
                //1.保存员工基本信息
                emp.setCreateTime(LocalDateTime.now());
                emp.setUpdateTime(LocalDateTime.now());
                empMapper.insert(emp);
                //2.保存员工的工作信息
                emp.getExprList().forEach(expr->{
                        expr.setEmpId(emp.getId());
                        empMapper.insertbach(expr);
                });
                
        }
    @Options(useGeneratedKeys = true,keyProperty = "id")//获取到生成的主键
    @Insert("insert into emp(name,age,dept_id,create_time,update_time) values(#{name},#{age},#{deptId},#{createTime},#{updateTime})")
    void insert(Emp emp);
​
    @Insert("insert into expr(emp_id,expr) values(#{empId},#{expr})")
    void insertbach(Expr expr);

事务

事务是一组操作的集合,它是一个不可分割的单位。这些操作要么同时成功,要么同时失败。

  • 开启:start transaction

  • 提交:commit

  • 回滚:rollback

  • REQUIRED(默认)

  • REQUIRED_NEW(需要在一个新事务中运行)

文件上传

本地存储

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文件上传</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
    姓名:<input type="text" name="name"><br>
    年龄:<input type="text" name="age"><br>
    头像:<input type="file" name="file"><br>
    <input type="submit" value="提交">
</form>
</body>
</html>
    @PostMapping("/upload")
    public Result upload(String name,Integer age,MultipartFile file) throws IOException {
        log.info("接受参数:{}{}{}",name,age,file);
        String originalFilename = file.getOriginalFilename();
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
        String newName= UUID.randomUUID() +suffix;
        file.transferTo(new File("D:\\Code\\JAVA\\Web\\demo2\\src\\main\\File\\"+newName));
        return Result.success();
    }

阿里云OSS

阿里云对象存储OSS,是一款海量、安全、低成本、高可靠的云存储服务。使用OSS,可以通过网络存储和调用包括文本、图片、音频和视频在内的各种文件。

创建

  • 创建Bucket

填入Buket名称,其它都不用动

关闭阻止公共访问

设置公共读

创建Accesskey

配置AccessKey

set OSS_ACCESS_KEY_ID=YOUR_ACCESS_KEY_ID
set OSS_ACCESS_KEY_SECRET=YOUR_ACCESS_KEY_SECRET

让更改生效

setx OSS_ACCESS_KEY_ID "%OSS_ACCESS_KEY_ID%"
setx OSS_ACCESS_KEY_SECRET "%OSS_ACCESS_KEY_SECRET%"

验证环境变量是否生效

echo %OSS_ACCESS_KEY_ID%
echo %OSS_ACCESS_KEY_SECRET%
案例
  • 磁盘存储

package com.itwyz.demo2.controller;
​
import java.io.*;
import java.nio.file.Files;
import java.util.Random;
​
import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectListing;
import com.aliyun.oss.model.OSSObjectSummary;
import com.aliyun.oss.common.comm.SignVersion;
​
public class OssJavaSdkQuickStart {
    public static void main(String[] args) throws com.aliyuncs.exceptions.ClientException {
        // 设置 OSS Endpoint 和 Bucket 名称
        String endpoint = "https://oss-cn-beijing.aliyuncs.com";
        String bucketName = "web52";
        String objectName = "001.jpg";
        // 替换为您的 Bucket 区域
        String region = "cn-beijing";
​
        // 从环境变量中获取访问凭证。运行本代码示例之前,请先配置环境变量
        EnvironmentVariableCredentialsProvider credentialsProvider =
                CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
​
        // 创建 OSSClient 实例
        ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
        // 显式声明使用 V4 签名算法
        clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
        OSS ossClient = OSSClientBuilder.create()
                .endpoint(endpoint)
                .credentialsProvider(credentialsProvider)
                .region(region)
                .build();
        try {
            // 1. 创建存储空间(Bucket)
            ossClient.createBucket(bucketName);
            System.out.println("1. Bucket " + bucketName + " 创建成功。");
            // 2. 上传文件
            File file=new File("C:\\Users\\86131\\Pictures\\Screenshots\\屏幕截图 2025-04-15 163125.png");
            byte[] content= Files.readAllBytes(file.toPath());
            ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(content));
            System.out.println("2. 文件 " + objectName + " 上传成功。");
​
​
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException | IOException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}
  • 上传阿里云存储

package com.itwyz.demo2.utils;
​
import java.io.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
​
import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.common.comm.SignVersion;
import org.springframework.stereotype.Component;
​
@Component
public class AliyunOSSOperator {
    public String upload(byte[]content,String originalFilename) throws com.aliyuncs.exceptions.ClientException {
        // 设置 OSS Endpoint 和 Bucket 名称
        String endpoint = "https://oss-cn-beijing.aliyuncs.com";
        String bucketName = "web-52";
        // 替换为您的 Bucket 区域
        String region = "cn-beijing";
//        获取当前系统日期的字符串,格式为 yyyy/MM
        String dir= LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM"));
//        生成一个不重复的文件名
        String newFileName= UUID.randomUUID()+"."+originalFilename.substring(originalFilename.lastIndexOf("."));
        String objectName=dir+"/"+newFileName;
        // 从环境变量中获取访问凭证。运行本代码示例之前,请先配置环境变量
        EnvironmentVariableCredentialsProvider credentialsProvider =
                CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
​
        // 创建 OSSClient 实例
        ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
        // 显式声明使用 V4 签名算法
        clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
        OSS ossClient = OSSClientBuilder.create()
                .endpoint(endpoint)
                .credentialsProvider(credentialsProvider)
                .region(region)
                .build();
        try {
            // 1. 创建存储空间(Bucket)
            ossClient.createBucket(bucketName);
            System.out.println("1. Bucket " + bucketName + " 创建成功。");
            // 2. 上传文件
            ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(content));
            System.out.println("2. 文件 " + objectName + " 上传成功。");
​
​
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return endpoint.split("//")[0]+"//"+bucketName+"."+endpoint.split("//")[1]+"/"+objectName;
    }
}
    @Autowired
    private  AliyunOSSOperator aliyunOSSOperator;
    @PostMapping("/upload2")
    public Result upload(MultipartFile file) throws Exception{
        log.info("文件上传:{}",file.getOriginalFilename());
        String url=aliyunOSSOperator.upload(file.getBytes(), file.getOriginalFilename());
        log.info("文件上传成功:{}",url);
        return Result.success(url);
    }

优化后(参数信息从配置文件读取)

参数配置化
  • 指将一些灵活变化的参数,配置在配置文件中,然后通过@Value注解来注入外部配置的属性

  • @Value

     

  • @ConfigurationProperties

@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliyunOSSProperties {
    private String endpoint;
    private String buketName;
    private String region;
}
package com.itwyz.demo2.utils;

import java.io.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.common.comm.SignVersion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class AliyunOSSOperator {
    @Autowired
    private AliyunOSSProperties aliyunOSSProperties;
    public String upload(byte[]content,String originalFilename) throws com.aliyuncs.exceptions.ClientException {
        String endpoint = aliyunOSSProperties.getEndpoint();
        String bucketName = aliyunOSSProperties.getBuketName();
        String region = aliyunOSSProperties.getRegion();

//        获取当前系统日期的字符串,格式为 yyyy/MM
        String dir= LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM"));
//        生成一个不重复的文件名
        String newFileName= UUID.randomUUID()+"."+originalFilename.substring(originalFilename.lastIndexOf("."));
        String objectName=dir+"/"+newFileName;
        // 从环境变量中获取访问凭证。运行本代码示例之前,请先配置环境变量
        EnvironmentVariableCredentialsProvider credentialsProvider =
                CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();

        // 创建 OSSClient 实例
        ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
        // 显式声明使用 V4 签名算法
        clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
        OSS ossClient = OSSClientBuilder.create()
                .endpoint(endpoint)
                .credentialsProvider(credentialsProvider)
                .region(region)
                .build();
        try {
            // 1. 创建存储空间(Bucket)
            ossClient.createBucket(bucketName);
            System.out.println("1. Bucket " + bucketName + " 创建成功。");
            // 2. 上传文件
            ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(content));
            System.out.println("2. 文件 " + objectName + " 上传成功。");


        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return endpoint.split("//")[0]+"//"+bucketName+"."+endpoint.split("//")[1]+"/"+objectName;
    }
}

删除

逻辑实现


    @PostMapping("/user")
    public Result addUser(@RequestBody User user){
        System.out.println("添加用户数据"+user);
        userService.addUser(user);
        return Result.success();
    }
        @Transactional(rollbackFor = Exception.class)
        @Override
        public void delete(List<Integer> ids) {
                //删除员工基本信息
                empMapper.delete(ids);

                //删除员工工作信息
                empExprMapper.delete(ids);
        }

编辑

查询回显

 <resultMap id="empResultMap" type="com.itwyz.demo2.pojo.Emp">
        <id column="id" property="id"/>
        <result column ="name" property="name"/>
        <result column="age" property="age"/>
        <result column="dept_id" property="deptId"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
        <collection property="exprList" ofType="com.itwyz.demo2.pojo.Expr">
            <result column="emp_id" property="empId"/>
            <result column="expr" property="expr"/>
        </collection>
    </resultMap>

    <select id="getInfo" resultMap="empResultMap">
    select e.*,ee.emp_id,ee.expr from emp e left join expr ee on e.id=ee.emp_id where e.id=#{id}
    </select>

修改

    @PutMapping()
    public Result update(@RequestBody Emp emp){
        log.info("修改员工:{}", emp);
        empService.update(emp);
        return Result.success();
    }
        @Transactional(rollbackFor = Exception.class)
        @Override
        public void update(Emp emp) {
                //1.更新员工基本信息
                emp.setUpdateTime(LocalDateTime.now());
                empMapper.updateById(emp);
                //2.更新员工工作信息
                empExprMapper.delete(Arrays.asList(emp.getId()));
                if (!CollectionUtils.isEmpty(emp.getExprList())) {
                        emp.getExprList().forEach(expr->{
                                expr.setEmpId(emp.getId());
                                empMapper.insertbach(expr);
                        });
                }
        }
    <update id="updateById">
        update emp
        <set>
            <if test="name != null and name != ''">
                name = #{name},
            </if>
            <if test="age != null and age != 0">
                age = #{age},
            </if>
            <if test="deptId != null">
                dept_id = #{deptId},
            </if>
            <if test="updateTime != null">
                update_time = #{updateTime}
            </if>
        </set>
        where id = #{id}
    </update>

全局异常处理器

项目中出现异常,例如mapper出现异常,异常会由mapper->service->controller然后返回一个JSON格式的Exception响应给前端

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler
    public Result handleException(Exception e) {
        log.error("服务器异常:{}", e);
        return Result.error("服务器异常");
    }

    @ExceptionHandler
    public Result handleException(DuplicateKeyException e) {
        log.error("业务异常:{}", e);
        String s=e.getMessage();
        int i = s.indexOf("Duplicate entry");
        String errMsg=s.substring(i);
        String[] arr=errMsg.split(" ");
        return Result.error(arr[2]+"已存在");
    }
}

信息统计

解决mybatisX误报

三种方案

  • 删除MybatisX

  • 添加@MapKey注解

  • 如下设置

职位统计

结合Echarts的柱状图

    public JobOption getEmpJobData() {
        List<Map<String, Object>> list = empMapper.countEmpJobData();
        List<Object> jobList = list.stream().map(dataMap -> dataMap.get("pos")).toList();
        List<Object> dataList = list.stream().map(dataMap -> dataMap.get("num")).toList();
        return new JobOption(jobList,dataList);
    }
    @MapKey("pos")
    List<Map<String,Object>> countEmpJobData();
    <select id="countEmpJobData" resultType="java.util.Map">
        select
            case dept_id when 1 then '技术部'
                         when 2 then '财务部'
                         when 3 then '人事部'
                         when 4 then '市场部'
                         else '其他' end as pos,
            count(*) num
        from emp group by dept_id order by num;
    </select>

性别统计

image-20250427160109152

登录

    @PostMapping("/login")
    public Result Login(@RequestBody User user){
        log.info("登录信息:{}", user);
        LoginInfo info =userService.login(user);
        if(info!=null){
            return Result.success(info);
        }
        return Result.error("用户名或密码错误");
    }
    @Override
    public LoginInfo login(User user) {
        // 根据用户名和密码查询员工信息
        User u =userMapper.selectByUsernameAndPassword(user.getUsername(),user.getPassword());
        //判断是否存在这个员工,如果存在,组装登录成功信息
        if(u!=null){
            return new LoginInfo(u.getUserId(),u.getUsername(),u.getPassword(),"00000");
        }
        //不存在返回null
        return null;
    }
    @Select("select user_id id,username,password from user where username=#{username} and password=#{password}")
    User selectByUsernameAndPassword(String username, String password);

登录校验

会话

Cookie

三个自动

  • 服务器端创建好cookie后会自动响应给浏览器

  • 浏览器会自动将cookie存储在浏览器本地

  • 后续请求中cookie会自动携带到浏览器

Session

底层是基于cookie,不同的是cookie中存储的是服务端会话对象session的id值

令牌
  • 登录成功后创建令牌,响应给客户端

  • 客户端存储令牌

  • 之后每一次请求都携带令牌

  • 服务器端校验有效性,有效放行,无效返回错误结果

JWT

  • 引入jjwt的依赖

    <dependency>
    			<groupId>io.jsonwebtoken</groupId>
    			<artifactId>jjwt</artifactId>
    			<version>0.9.1</version>
    		</dependency>
  • 调用官方提供的工具类Jwts来生成jwt令牌

    public void testGenerateJwt() {
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("id", 1);
        dataMap.put("username", "wyz");
        String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256, "aXR3eXo=")
                .addClaims(dataMap)
                .setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))
                .compact();
        System.out.println(jwt);
    }
    public void testParseJwt() {
        String token="eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJ3eXoiLCJleHAiOjE3NDY1NDU1ODV9.vR9wAR5Q1wQDDDfqTE9NMdB3DwzhZZNqSXRe0BGpCqw";
        Claims claims = Jwts.parser().setSigningKey("aXR3eXo=")
                .parseClaimsJws(token)
                .getBody();
        System.out.println(claims);
    }
  • 登录完成后生成令牌

    • 定义jwt令牌生成工具类

    • 登录完成后,调用工具类生成jwt令牌并返回

package com.itwyz.demo2.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 final String SECRET_KEY = "aXR3eXo=";

    // 过期时间:12小时
    private static final long EXPIRATION = 12 * 60 * 60 * 1000;

    /**
     * 生成 JWT 令牌
     *
     * @param claims 自定义数据
     * @return 返回生成的令牌字符串
     */
    public static String generateToken(Map<String, Object> claims) {
        return Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .addClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
                .compact();
    }

    /**
     * 解析 JWT 令牌
     *
     * @param token 要解析的令牌字符串
     * @return 返回解析后的 Claims 数据
     */
    public static Claims parseToken(String token) {
        return Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody();
    }
}
@Slf4j
@RestController()
@RequestMapping()
public class LoginController {

    @Autowired
    private UserService userService;
    @PostMapping("/login")
    public LoginInfo Login(@RequestBody User user){
        LoginInfo info =userService.login(user);
        if(info!=null){
            log.info("登录成功{}",info);
            //生成Jwt令牌
            Map<String,Object> claims=new HashMap<>();
            claims.put("id",info.getId());
            claims.put("username",info.getUsername());
            String jwt = JwtUtils.generateToken(claims);
            return new LoginInfo(info.getId(),info.getUsername(), info.getPassword(), jwt);
        }
        return null;
    }

}
Filter

Filter过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能

启动类上加@ServeletComponentScan

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

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
       log.info("过滤器初始化");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("过滤器开始执行");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
        log.info("过滤器销毁");
    }
}
令牌校验
package com.itwyz.demo2.filter;

import com.itwyz.demo2.utils.JwtUtils;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;

@WebFilter(urlPatterns = "/*")
@Slf4j
public class TokenFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        //1.获取到请求路径
        String requestURI = request.getRequestURI();
        //2.判断是否是登录请求,如果路径包含/login,放行
        if(requestURI.contains("/login")){
            filterChain.doFilter(request,response);
            return;
        }
        //3.获取请求头中的token
        String token =request.getHeader("token");
        //4.判读token是否为空,如果为空,返回错误信息,
        if(token==null|| token.isEmpty()){
            log.info("令牌为空,响应401");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }
        //5.如果Token存在,校验令牌,如果校验失败,返回错误信息
        try {
            JwtUtils.parseToken(token);
        } catch (Exception e) {
            log.info("令牌校验不通过,响应401");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }
        //6.校验通过,放行
        log.info("令牌校验通过,放行");
        filterChain.doFilter(request,response);
    }
}
  • 放行后访问对应资源,访问完成后,还会回到Filter中,并执行之后的操作

  • 一个web应用中可以配置多个过滤器,这多个过滤器形成了一个过滤器链

  • 注解配置的Filter,优先级是按照过滤器类名的自然排序

Interceptor

package com.itwyz.demo2.intercepter;

import com.itwyz.demo2.utils.JwtUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

@Slf4j
@Component
public class TokenIntercepter implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //3.获取请求头中的token
        String token =request.getHeader("token");
        //4.判读token是否为空,如果为空,返回错误信息,
        if(token==null || token.isEmpty()){
            log.info("令牌为空,响应401");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }
        //5.如果Token存在,校验令牌,如果校验失败,返回错误信息
        try {
            JwtUtils.parseToken(token);
        } catch (Exception e) {
            log.info("令牌校验不通过,响应401");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }
        //6.校验通过,放行
        log.info("令牌校验通过,放行");
        return true;
    }
}
package com.itwyz.demo2.config;

import com.itwyz.demo2.intercepter.TokenIntercepter;
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 TokenIntercepter tokenIntercepter;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(tokenIntercepter)
                .addPathPatterns("/**")         //拦截所有请求
                .excludePathPatterns("/login"); //不拦截登录请求
    }
}

更多推荐