准备工作
idea中alt+enter快速生成方法
查询端口占用情况
cmd中netstat -ano | findstr :8080找到pid最后在任务管理器中结束该项目
部门管理
需求,环境搭建
准备数据库表(dept,emp)
-- 创建新的部门表
CREATE TABLE dept
(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(10) NOT NULL,
operation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -- 操作时间字段
);
-- 插入部门信息
INSERT INTO dept (name)
VALUES ('人力资源部'),
('财务部'),
('市场部'),
('研发部'),
('销售部');
-- 创建新的员工表
CREATE TABLE emp
(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20) NOT NULL,
password varchar(32) default '123456',
position VARCHAR(50),
dept_id INT,
photo varchar(300), -- 图片字段,使用BLOB类型存储二进制数据
gender tinyint, -- 性别字段,1男 2女
date DATE, -- 入职日期字段
operation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -- 操作时间字段
);
INSERT INTO emp (name, position, dept_id, photo, gender, date)
VALUES ('张三', '经理', 1, NULL, 1, '2020-01-15')
, ('李四', '会计', 2, NULL, 2, '2019-05-20')
, ('王五', '市场专员', 3, NULL, 1, '2021-03-10')
, ('赵六', '软件工程师', 4, NULL, 1, '2018-11-05')
, ('孙七', '销售代表', 5, NULL, 2, '2022-02-28')
, ('周八', '人力资源专员', 1, NULL, 2, '2020-06-12')
, ('吴九', '财务分析师', 2, NULL, 2, '2019-09-30')
, ('郑十', '市场经理', 3, NULL, 1, '2021-07-25')
, ('冯十一', '高级软件工程师', 4, NULL, 1, '2018-04-18')
, ('陈十二', '销售经理', 5, NULL, 2, '2022-01-03');
创建springboot工程,引入对应的起步依赖(web,mybatis,mysql驱动,lombok)
配置application.properties中引入mybatis的配置信息,准备对应的实体类
spring.application.name=studylearn
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url
spring.datasource.url=jdbc:mysql://localhost:3306/studylearn
#连接数据库的用户名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password=123456
#配置mybatis的日志,指定输出到控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#开启mybatis的驼峰命名自动映射开关
mybatis.configuration.map-underscore-to-camel-case=true
#pojo中emp实体类
package com.example.studylearn.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp {
private Integer id;
private String name;
private String password;
private Short gender;
private String photo;
private Integer deptid;
private Short position;
private LocalDate date;
private LocalDateTime operationtime;
}
#pojo中dept实体类
package com.example.studylearn.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dept {
private Integer id;
private String name;
private LocalDateTime operationtime;
}
准备对应的mapper,service(接口,实现类),controller基础结构
#service接口和类
package com.example.studylearn.service.impl;
import com.example.studylearn.service.DeptService;
import org.springframework.stereotype.Service;
@Service
public class DeptServiceImpl implements DeptService {
}
package com.example.studylearn.service;
public interface DeptService {
}
#pojo中实体类
package com.example.studylearn.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp {
private Integer id;
private String name;
private String password;
private Short gender;
private String photo;
private Integer deptid;
private Short position;
private LocalDate date;
private LocalDateTime operationtime;
}
#mapper中接口
package com.example.studylearn.mapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface EmpController {
}
#controller中类
package com.example.studylearn.controller;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DeptController {
}
开发规范restful
representational state transfer ,表述性状态转换,它是一种软件架构风格
pojo中实体类result
package com.example.studylearn.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
private Integer code;//响应吗:1成功,0失败
private String msg;//响应信息 描述字符串
private Object data;//返回的数据
//增删改 成功响应
public static Result success() {
return new Result (1,"success", null);
}
//查询 成功响应
public static Result success(Object data) {
return new Result(1,"success",data);
}
//失败响应
public static Result error(String msg) {
return new Result(0, msg,null);
}
}
如果出错可能是pox.xml中导入依赖的版本不匹配
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>studylearn</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>studylearn</name>
<description>Demo project for Spring Boot</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>22</java.version>
</properties>
<dependencies>
<!-- web起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mybatis起步依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>3.0.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
流程
部门查询
controller层代码
package com.example.learn_system.controller;
import com.example.learn_system.pojo.Dept;
import com.example.learn_system.pojo.Result;
import com.example.learn_system.service.DeptService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Slf4j
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
// @RequestMapping(value = "/depts",method = RequestMethod.GET )
@GetMapping("/depts")
public Result list(){
log.info("查询全部部门数据");
//调用service查询部门数据
List<Dept> deptList=deptService.list();
return Result.success(deptList);
}
}
dept接口方法 service
package com.example.learn_system.service;
import com.example.learn_system.pojo.Dept;
import java.util.List;
public interface DeptService {
/*查询全部部门数据*/
List<Dept> list();
}
deptservice实现类
package com.example.learn_system.service.impl;
import com.example.learn_system.mapper.DeptMapper;
import com.example.learn_system.pojo.Dept;
import com.example.learn_system.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptMapper deptMapper;
@Override
public List<Dept> list(){
return deptMapper.list();
}
}
dept mapper操作数据库
package com.example.learn_system.mapper;
import com.example.learn_system.pojo.Dept;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface DeptMapper {
/*查询全部部门*/
@Select("select * from dept")
List<Dept> list();
}
前后端联调
部署nginx把部署的nginx启动
访问http://local:90
删除部门
//controller 中
/*删除部门*/
@DeleteMapping("/depts/{id}")
public Result delete(@PathVariable Integer id){
log.info("根据id删除部门,{}",id);
//调用service删除部门
deptService.delete(id);
return Result.success();
}
//service接口
// 删除部门
void delete(Integer id);
//service实现类
@Override
public void delete(Integer id) {
deptMapper.deleteById(id);
}
//mapper接口操作数据库
// 根据id删除部门
@Delete("delete from dept where id = #{id}")
void deleteById(Integer id);
新增部门
//controller层
// 新增部门
@PostMapping("/depts")
public Result add(@RequestBody Dept dept){
log.info("新增部门{}",dept);
//调用service新增部门
deptService.add(dept);
return Result.success();
}
//service接口
//新增部门
void add(Dept dept);
//service实现类
@Override
public void add(Dept dept) {
dept.setOperationTime(LocalDateTime.now());
deptMapper.insert(dept);
}
//mapper
//新增部门
@Insert("insert into dept(name ,operation_time) value (#{name},#{operationTime})")
void insert(Dept dept);
优化
员工管理
分页查询
原始方法
//pojo中首先新建实体类
/*分页查询结果封装类*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageBean {
private Long total;//总记录数
private List rows;//数据列表
}
//emp mapper中
// 查询总记录数
@Select("select count(*) from emp")
public Long count();
// 分页查询,获取列表数据
@Select("select * from emp limit #{start},#{pageSize}")
public List<Emp> page(Integer start, Integer pageSize)
//emp中controller
@Autowired
private EmpService empService;
@GetMapping("/emps")
public Result page(@RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue ="10") Integer pageSize) {
log.info("分页查询,参数{},{}", page, pageSize);
//调用service分页查询
PageBean pageBean = empService.page(page, pageSize);
return Result.success(pageBean);
}
//emp的serverce接口
public interface EmpService {
PageBean page(Integer page, Integer pageSize);
}
//emp的service
@Autowired
private EmpMapper empMapper;
@Override
public PageBean page(Integer page, Integer pageSize) {
//获取总记录数
Long count=empMapper.count();
//获取分页查询结果列表
Integer start=(page-1)*pageSize;
List<Emp> empList=empMapper.page(start,pageSize);
//封装PageBean对象
PageBean pageBean=new PageBean(count,empList);
return pageBean;
}
分页插件PageHelper
//pom.xml中
<!--pagehelper分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>
//empmapper中接口信息
// 员工信息的查询
@Select("select * from emp")
public List<Emp> list();
//empservice中
@Override
public PageBean page(Integer page, Integer pageSize) {
//设置分页参数
PageHelper.startPage(page,pageSize);
//执行查询
List<Emp> empList = empMapper.list();
Page<Emp> p=(Page<Emp>) empList;
//封装PageBean对象
PageBean pageBean = new PageBean(p.getTotal(), p.getResult());
return pageBean;
}
//其余的不变
分页查询(待条件)
//empcontroller层
@Autowired
private EmpService empService;
@GetMapping("/emps")
public Result page(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "5") Integer pageSize,
String name, Short gender,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {
log.info("分页查询,参数{},{},{},{},{},{}", page, pageSize,name,gender,begin,end);
//调用service分页查询
PageBean pageBean = empService.page(page, pageSize,name,gender, begin, end);
return Result.success(pageBean);
//empservice接口
PageBean page(Integer page, Integer pageSize, String name, Short gender, LocalDate begin,LocalDate end);
//empservice类中
List<Emp> empList = empMapper.list(name, gender, begin, end);
多条件查询需要用到xml
resources中新建empmapper的.xml文件(同包同名)
//mapper接口
public interface EmpMapper {
public List<Emp> list(String name, Short gender, LocalDate begin, LocalDate end);
}
//EmpMapper.xml
<?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">
<!--namespace路径与EmpMapper接口类path一致-->
<mapper namespace="com.example.learn_system.mapper.EmpMapper">
<!--id与mapper接口的方法名一致
resultType为单条记录所返回的类型
-->
<select id="list" resultType="com.example.learn_system.pojo.Emp">
select * free emp
-- where判断是否生成where和and和or子句
<where>
<if test="name !=null and name !=''">
name like concat('%',#{name},'%')
</if>
<if test="gender !=null">
and gender=#{gender}
</if>
<if test="begin!=null and end!=null">
and date between #{begin} and #{end}
</if>
</where>
order by date desc
</select>
</mapper>
删除员工
//controller
@DeleteMapping("/{ids}")
public Result delete(@PathVariable List<Integer> ids){
log.info("批量删除操作,ids:{}",ids);
empService.delete(ids);
return Result.success();
}
//service接口
void delete(List<Integer> ids);
//service实现类
@Override
public void delete(List<Integer> ids) {
empMapper.delete(ids);
}
//mapper接口
//批量删除
void delete(List<Integer> ids);
//mapper.xml映射文件
<!-- 皮批量删除 (1,2,3) collection里的是集合名-->
<delete id="delete">
delete from emp
where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
新增员工
//controller
@PostMapping
public Result save(@RequestBody Emp emp){
log.info("新增员工,emp:{}",emp);
empService.save(emp);
return Result.success();
}
//service接口
void save(Emp emp);
//service实现类
@Override
public void save(Emp emp) {
emp.setDate(LocalDateTime.now());
emp.setOperationTime(LocalDateTime.now());
empMapper.insert(emp);
}
//mapper接口
@Insert("insert into emp(name, password, position,dept_id,photo, gender, date) " +
"value (#{name},#{password},#{position},#{deptId},#{photo},#{gender},#{date})")
void insert(Emp emp);
文件上传
简介
文件上传,是指将本地图片视频,音频等文件上传到服务器,供其他用户浏览或下载的过程
前端和service控制层 上传文件 的 名字必须要一致
本地存储
服务端,接收到上传上来的文件之后,将文件存储在本地服务器磁盘中
在springboot中,文件上传,默认单个文件允许最大大小为1M。如果需要上传大文件,可以如下配置
//application.properties中设置(在resources中)
#配置单个文件上传大小限制
spring.servlet.multipart.max-file-size=10MB
#配置单个请求最大大小的限制(一次请求中是可以上传多个文件的)
spring.servlet.multipart.max-request-size=100MB
//controller层
@Slf4j
@RestController
public class UploadController {
@PostMapping("/upload")
public Result upload(String name, MultipartFile photo) throws IOException {
log.info("文件上传:{},{}",name,photo);
//获取原始文件名
String originalFilename = photo.getOriginalFilename();
//构造唯一的文件名(不能重复) --uuid(通用唯一识别码)
int index = originalFilename.lastIndexOf(".");
String extname = originalFilename.substring(index);
String newFileName=UUID.randomUUID().toString()+extname;
log.info("新的文件名:{}",newFileName);
//将文件存储在服务器的磁盘目录中E:\javaweb\photo
photo.transferTo(new File("E:\\javaweb\\photo\\"+newFileName));
return Result.success();
}
}
阿里云OSS 使用步骤
object storage service是一款海量,安全,成本低,高可靠的云存储服务。使用oss可通过网络随时存储和调用包括文本,图片,音频,视频等各种文件
bucket:存储空间是用户用于存储对象(object就是文件)的容器,所有的对象都必须隶属于某个存储空间
SDK:software Development Kit的缩写,软件开发工具包,包括辅助软件开发的依赖(jar包),代码实例等,都可以叫做SDK
点开阿里云中oss服务下载sdk,点开文档操作在pom.xml中配置
在学习文档中找方法写入,注意修改内容
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
String endpoint = "https://oss-cn-beijing.aliyuncs.com";
// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
// 填写Bucket名称,例如examplebucket。
String bucketName = "examplebucket";
// 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
String objectName = "exampledir/exampleobject.txt";
// 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。
// 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。
String filePath= "D:\\localpath\\examplefile.txt";
案例集成OSS
引入阿里云oss上传文件工具类(官方代码改造而来)
上传图片接口开发
编辑员工
查询回显
//controller
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id){
log.info("根据id查询员工信息,id:{}",id);
Emp emp=empService.getById(id);
return Result.success(emp);
}
//service接口
Emp getById(Integer id);
//service实现类
@Override
public Emp getById(Integer id) {
return empMapper.getById(id);
}
//mapper接口方法
@Select("select * from emp where id-#{id}")
Emp getById(Integer id);
修改员工
//controller层
@PostMapping
public Result update(@RequestBody Emp emp){
log.info("更新员工:{}",emp);
empService.update(emp);
return Result.success();
//service接口
void update(Emp emp);
//service实体类
@Override
public void update(Emp emp) {
emp.setOperationTime(LocalDateTime.now());
empMapper.update(emp);
}
//mapper文件
void update(Emp emp);
//xml文件放在resource文件新建的包下
<update id="update">
update emp
<set>
<if test="name !=null and name !=''">
name=#{name},
</if>
<if test="password !=null and password !=''">
password=#{password},
</if>
<if test="position !=null and position !=''">
position=#{position},
</if>
<if test="deptId !=null">
dept_id=#{deptId},
</if>
<if test="photo !=null and photo !=''">
photo=#{photo},
</if>
<if test="gender !=null">
gender=#{gender},
</if>
<if test="date !=null and date !=''">
date=#{date}
</if>
</set>
where id =#{id}
</update>
配置文件
参数配置化
yml配置文件
配置格式
yml基本语法
大小写敏感
数之前必须有空格,作为分隔符
使用缩进表示层级关系,缩进时,不允许使用tab,只能用空格(idea中会自动将tab转化为空格)
缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
#表示注释,从这个字符一直到行尾,都会被解析器忽略
#resources文件中新建application.yml的file
#对象/Map集合:
user:
name: zhangsan
age: 12
password: 123456
#数组/List/Set集合
hobby:
- java
- game
#例子
spring:
#数据库连接信息
datasource:
#驱动类名称
driver-class-name: com.mysql.cj.jdbc.Driver
#数据库连接的url
url: jdbc:mysql://localhost:3306/study_system
#连接数据库的用户名
username: root
#连接数据库的密码
password: 123456
servlet:
multipart:
#配置单个文件上传大小限制
max-file-size: 10MB
#配置单个请求最大大小的限制(一次请求中是可以上传多个文件的)
max-request-size: 100MB
mybatis:
configuration:
#配置mybatis的日志,指定输出到控制台
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#开启mybatis的驼峰命名自动映射开关
map-underscore-to-camel-case: true
#阿里云OSS
aliyun:
oss:
emdpoint:
- sport
@ConfigurationProperties
@ConfigurationProperties与@Value
相同点:都是用来注入外部配置的属性的
不同点:@Value注解只能一个一个的进行外部属性的注入
@ConfigurationProperties可以批量的将外部的属性配置注入到bean对象的属性中
登录功能
//controller曾新建方法
@Slf4j
@RestController
public class LoginController {
@Autowired
private EmpService empService;
public Result login(@RequestBody Emp emp){
log.info("员工登录:{}",emp);
Emp e=empService.login(emp);
return e !=null?Result.success():Result.error("用户名或密码错误");
}
}
//service接口
Emp login(Emp emp);
//service实现类
@Override
public Emp login(Emp emp) {
return empMapper.getByNameAndPassword(emp);
}
//mapper接口
@Select("select * from emp where name=#{name} and password =#{password}")
Emp getByNameAndPassword(Emp emp);
登录校验
会话技术
会话:
用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。再一次会话中可以包含 多次 请求和响应
会话跟踪
一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间 共享数据
会话跟踪方案:
客户端会话跟踪技术:Cookie
@Slf4j
@RestController
public class SessionController {
//设置cookie
@GetMapping("/c1")
public Result cookie1(HttpServletResponse response){
response.addCookie(new Cookie("login_name","itheima"));
return Result.success();
}
//获取cookie
@GetMapping("/c2")
public Result cookie2(HttpServletRequest request){
Cookie[] cookies= request.getCookies();//获取所有的cookie
for (Cookie cookie:cookies){
if (cookie.getName().equals("login_name")){
//输出那么为login_name的cookie
System.out.println("login_name:"+cookie.getValue());
}
}
return Result.success();
}
}
服务端会话跟踪技术:Session
//在HttpSession中存储值
@GetMapping("/s1")
public Result session1(HttpSession session){
log.info("HttpSession-s1:{}",session.hashCode());
session.setAttribute("loginUser","tom");//session中存储数据
return Result.success();
}
//从httpsession中获取值
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);
}
令牌技术
JWT令牌
简介
一般用于登陆后产生令牌返回浏览器,之后每次请求都会携带令牌去校验,成功后通过
jwt生成(入门)
//pom.xml文件中引入
<!-- JWT令牌-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
//JWT令牌生成与解析
// 生成JWT
@Test
public void testGenJwt(){
Map<String, Object> claims=new HashMap<>();
claims.put("id",1);
claims.put("name","tom");
String jwt=Jwts.builder()
.signWith(SignatureAlgorithm.ES256,"itheima")//签名算法
.setClaims(claims)//自定义内容(载荷)
.setExpiration(new Date(System.currentTimeMillis()+3600*1000))//设置有效期1h
.compact();
System.out.println(jwt);
}
// 解析jwt
@Test
public void testParseJwt(){
Claims claims=Jwts.parser()
.setSigningKey("itheima")
.parseClaimsJws("")//里面填要解析的加密令牌
.getBody();
System.out.println(claims);
}
登录-生成令牌
//java中引入令牌操作工具类
public class JwtUtils {
private static String signKey="itheima";
private static Long expire=43200000L;
// 生成JWT令牌 claims jwt第二部分负载 payload 中存储的内容
public static String generateJwt(Map<String, Object> claims){
String jwt= Jwts.builder()
.addClaims(claims)
.signWith(SignatureAlgorithm.ES256,signKey)//签名算法
.setExpiration(new Date(System.currentTimeMillis()+expire))//设置有效期1h
.compact();
return jwt;
}
// 解析jwt令牌 jwt第二部分负载 payload 中存储的内容
public static Claims ParseJwt(String jwt){
Claims claims=Jwts.parser()
.setSigningKey(signKey)
.parseClaimsJws(jwt)//里面填要解析的加密令牌
.getBody();
return claims;
}
}
//登陆完成后,调用工具类生成JWT令牌,并返回 login类
@Slf4j
@RestController
public class LoginController {
@Autowired
private EmpService empService;
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());
String jwt = JwtUtils.generateJwt(claims);//jwt包含了当前登录的员工信息
return Result.success(jwt);
}
//登陆失败,返回错误信息
return Result.error("用户名或密码错误");
}
}
过滤器Filter
快速入门
//新建的实现类
@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销毁方法执行了");
}
}
//springboot启动类上加入
@ServletComponentScan//开启了对servlet组件的支持
详解(执行流程,拦截路径,过滤器链)
拦截路径:
filter可以根据需求,配置不同的拦截资源路径
拦截具体路径 urlPatterns值 /login 只有访问/login路径时,才会被拦截
目录拦截 urlPatterns值/emps/* 访问/emps下的所有资源,都会被拦截
拦截所有 urlPatterns值 /* 访问所有资源,都会被拦截
过滤器链
登录校验-Filter
//filter中
@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter{
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req=(HttpServletRequest) servletRequest;
HttpServletResponse resp=(HttpServletResponse) servletResponse;
//获取请求url
String url = req.getRequestURL().toString();
log.info("请求的url:{}",url);
//判断请求url中是否包含login,若包含,说明是登陆操作,放行
if (url.contains("login")){
log.info("登陆操作,放行。。。");
filterChain.doFilter(servletRequest, servletResponse);
return;
}
//获取请求头中的令牌(token)
String jwt = req.getHeader("token");
//判断令牌是否存在,若不存在,返回错误结果(未登录)
if (!StringUtils.hasLength(jwt)){
log.info("请求头token为空,返回未登陆的信息");
Result error = Result.error("NOT_LOGIN");
//手动转换 对象json ---》阿里巴巴fastJSON 去pom.xml文件配置
String notLogin= JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return;
}
//解析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;
}
//放行
log.info("令牌合法,放行");
filterChain.doFilter(servletRequest, servletResponse);
}
}
//pom.xml文件
<!-- fastJSON-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastJSON</artifactId>
<version>1.2.76</version>
</dependency>
拦截器Interceptor
简介 快速入门
概念:是一种动态拦截方法调用的机制,类似于过滤器。spring框架中提供的,用来动态拦截控制器方法的执行
作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码(跟过滤器类似)
//定义拦截器,实现HandlerInterceptor接口,并重写其所有方法
//新建包和方法
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {//ctrl+o快速创建方法
@Override//目标资源方法运行前运行,返回true,放行。返回false,不放行
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 ...");
}
}
//注册拦截器
@Configuration//配置类
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginCheckInterceptor loginCheckInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");
}
}
详解
拦截路径
执行流程
登录校验 -interceptor
@Component
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {//ctrl+o快速创建方法
@Override//目标资源方法运行前运行,返回true,放行。返回false,不放行
public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
//获取请求url
String url = req.getRequestURL().toString();
log.info("请求的url:{}",url);
//判断请求url中是否包含login,若包含,说明是登陆操作,放行
if (url.contains("login")){
log.info("登陆操作,放行。。。");
return true;
}
//获取请求头中的令牌(token)
String jwt = req.getHeader("token");
//判断令牌是否存在,若不存在,返回错误结果(未登录)
if (!StringUtils.hasLength(jwt)){
log.info("请求头token为空,返回未登陆的信息");
Result error = Result.error("NOT_LOGIN");
//手动转换 对象json ---》阿里巴巴fastJSON 去pom.xml文件配置
String notLogin= JSONObject.toJSONString(error);
resp.getWriter().write(notLogin);
return false;
}
//解析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;
}
//放行
log.info("令牌合法,放行");
return true;
}
异常处理
全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class) //捕获所有异常
public Result ex(Exception ex){
ex.printStackTrace();
return Result.error("对不起,操作失败,请联系管理员");
}
}