SpringBoot阶段总结

SpringBoot阶段总结

1.序言

学习Java到现在,也快过去一年了,在这里写下这篇文章,算是自己的一个阶段性的学习总结,也是为了方便后续的复习,以下是我认为比较有用的知识点。

2.拦截器

创建一个SpringBoot项目,导入下列依赖

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!--lombok-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>

先创建一个实体类,用来模拟登录用户:

@Data
public class User {
    private Integer id;
    private String username;
    private String password;
}

创建拦截器,用于拦截用户请求,我们的拦截器基础自HandlerInterceptor,实现下面三个方法

  1. preHandle
    调用时间:Controller方法处理之前
    执行顺序:链式Intercepter情况下,Intercepter按照注册声明的顺序一个接一个执行,若返回false,则中断执行。
    备注:不会进入afterCompletion方法
  2. postHandle
    调用前提:preHandle返回true
    调用时间:在Controller方法处理完之后,DispatcherServlet进行视图的渲染之前执行,即可以在这个方法中对ModelAndView进行操作
    执行顺序:链式Intercepter情况下,Intercepter按照注册声明的顺序倒着执行。
    备注:postHandle虽然post打头,但post、get方法都能处理
  3. afterCompletion
    调用前提:preHandle返回true
    调用时间:DispatcherServlet进行视图的渲染之后
    执行顺序:链式Intercepter情况下,Intercepter按照注册声明的顺序倒着执行。
    备注:多用于清理资源
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler)throws Exception{
        //这里的登录逻辑是,先判断HttpSession会话中是否有用户信息,没有说明还未登录
        HttpSession session=request.getSession();
        User user=(User)session.getAttribute("user");
        if(user==null){
            //没有用户,请求被拦截
            System.out.println("请求被拦截=============");
            return false;
        }else{
            //用户已登录,将请求放行
            System.out.println("请求被放行============");
            return true;
        }
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception{
        System.out.println("处理完成进入");
    }
    @Override
    public void afterCompletion(HttpServletRequest request,HttpServletResponse response,
                                Object handler, Exception ex)throws Exception{
        System.out.println("preHandler返回true后进入");
    }
}

创建配置类

@Configuration
public class WebConfiguration implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry){
        //配置放行的静态资源
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry){
    //拦截除了login和register外的所有请求
        registry.addInterceptor(loginInterceptor).addPathPatterns("/*")
                .excludePathPatterns("/login","/register");
    }
}

请求类

@RestController
public class UserController {
    @RequestMapping("/login")
    public String login(HttpServletRequest request, @RequestParam(value = "username",required = true)String username,
                        @RequestParam(value = "password",required = true)String password){
        System.out.println("登录验证开始==========");
        if(username.equals("admin")&&password.equals("123456")){
            User user=new User();
            user.setId(1);
            user.setUsername(username);
            user.setPassword(password);
            //设置到httpSession会话中
            request.getSession().setAttribute("user",user);
            return "登录成功========";
        }else{
            return "登录失败==========";
        }
    }
    @RequestMapping("/index")
    public String index(){
        return "index";
    }
}

下面是配置文件application.yml

server:
  port: 8081

启动项目,打开postman进行接口测试:
先测试http://localhost:8081/index
在这里插入图片描述
在这里插入图片描述
请求被拦截,我们进行登录:http://localhost:8081/login?username=admin&password=123456
在这里插入图片描述
再访问index
在这里插入图片描述
在这里插入图片描述

2.JdbcTemplate

之前我们有解除过JDBC,就是使用java来访问数据库,而JdbcTemplate是对JDBC的进一步封装,使用起来更加的方便
创建一个springboot项目,导入下列依赖

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!--lombok-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<!--jdbc-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<!--mysql-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>

修改配置文件application.yml

server:
  port: 8081
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/young?useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

先打开Navicat,在young数据库中创建一个user表

在这里插入图片描述
创建一个实体类:

@Data
public class User {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
}

dao层:

@Repository
public class UserDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    //添加用户
    public int saveUser(User user){
        //插入SQL语句
        String sql="insert into user(username,password,age) values(?,?,?);";
        //主键获取
        KeyHolder keyHolder=new GeneratedKeyHolder();
        jdbcTemplate.update(new PreparedStatementCreator() {
            @Override
            public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
                //指定主键
                PreparedStatement preparedStatement=con.prepareStatement(sql,new String[]{"id"});
                preparedStatement.setString(1,user.getUsername());
                preparedStatement.setString(2,user.getPassword());
                preparedStatement.setInt(3,user.getAge());
                return preparedStatement;
            }
        },keyHolder);
        return keyHolder.getKey().intValue();
    }
    //修改用户
    public int updateUser(User user){
        String sql="update user set password=? where id=?";
        int res=jdbcTemplate.update(sql,user.getPassword(),user.getId());
        return res;
    }
    //删除用户
    public int deleteUser(Integer id){
        String sql="delete from user where id=?";
        int res=jdbcTemplate.update(sql,id);
        return res;
    }
   //获取用户详情
    public Map<String, Object> getUserById(Integer id){
        String sql="select * from user where id="+id;
        Map<String,Object>res =jdbcTemplate.queryForMap(sql);
        return res;
    }
    //获取用户列表
    public List<User>getUserList(){
        String sql="select * from user";
        List<User>list=jdbcTemplate.query(sql, new RowMapper<User>() {
            @Override
            public User mapRow(ResultSet rs, int rowNum) throws SQLException {
                User user=new User();
                user.setId(rs.getInt("id"));
                user.setUsername(rs.getString("username"));
                user.setPassword(rs.getString("password"));
                user.setAge(rs.getInt("age"));
                return user;
            }
        });
        return list;
    }
}

控制层:

@RestControler
public class UserController {
    @Autowired
    private UserDao userDao;
    @RequestMapping("/saveUser")
    public int saveUser(@RequestBody User user){
        return userDao.saveUser(user);
    }
    @RequestMapping("/updateUser")
    public int updateUser(@RequestBody User user){
        return userDao.updateUser(user);
    }
    @RequestMapping("/deleteUser")
    public int deleteUser(int id){
        return userDao.deleteUser(id);
    }
    @RequestMapping("/getUserById")
    public Map<String, Object> getUserById(int id){
        return userDao.getUserById(id);
    }
    @RequestMapping("/getUserList")
    public List<User>getUserList(){
        return userDao.getUserList();
    }
}

运行项目:
添加用户
在这里插入图片描述
修改用户
在这里插入图片描述
查询用户
在这里插入图片描述
查询用户列表:
在这里插入图片描述
删除用户
在这里插入图片描述
更多的jdbcTemplate操作,可以参考下列文章
SpringBoot高级篇JdbcTemplate之数据插入使用姿势详解
JdbcTemplate基本使用
虽然JdbcTemplate不需要向JDBC一样一直创建连接、关闭连接,但是在使用上还是比较繁琐,因此不推荐大家使用,我们有方便的框架,如Mybatis,Mybatis-plus

3.接口返回值

在刚才的接口测试中,我们发现,返回值有时是一个数字或一个字符串,有时是一个json格式的数据,而在实际开发中,我们后端返回的数据,通常还需要一个状态码和消息提示来返回给前端,所以接下来,我们就来将一下接口该如何返回信息
创建一个springboot项目,引入下列依赖

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!--json-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.51</version>
		</dependency>
		<!--lombok-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>

创建一个user实体类

@Data
public class User {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
}

创建一个ResultVO.java,用于返回结果

/**
 * @param <T>
 *     code 表示状态码
 *     msg 表示提示信息
 *     data 表示返回的数据
 */
@Data
public class ResultVO <T>{
    private Integer code;
    private String msg;
    private T data;
    public ResultVO(){}
    public ResultVO(Integer code,String msg){
        this.code=code;
        this.msg=msg;
        this.data=null;
    }
    public ResultVO(Integer code,String msg,T data){
        this.code=code;
        this.msg=msg;
        this.data=data;
    }
}

创建一个ResultVOUtil.java工具类

public class ResultVOUtil {
    public static ResultVO success(){
        return new ResultVO<>(200,"操作成功");
    }
    public static ResultVO success(Object data){
        return new ResultVO<>(200,"操作成功",data);
    }
    public static ResultVO error(Integer code,String msg){
        return new ResultVO<>(code,msg);
    }
}

创建一个控制类

@RestController
public class UserController {
    @RequestMapping("/success")
    public ResultVO success(){
        return ResultVOUtil.success();
    }
    @RequestMapping("/fail")
    public ResultVO fail(){
        return ResultVOUtil.error(404,"找不到页面");
    }
    @RequestMapping("/getUser")
    public ResultVO getUser(){
        User user=new User();
        user.setId(1);
        user.setUsername("张三");
        user.setPassword("123456");
        user.setAge(18);
        return ResultVOUtil.success(user);
    }
    @RequestMapping("/getUserList")
    public ResultVO getUserList(){
        List<User>list=new ArrayList<>();
        for(int i=0;i<4;i++){
            User user=new User();
            user.setId(i+1);
            user.setUsername("username"+i);
            user.setPassword("123"+i);
            user.setAge(18+i);
            list.add(user);
        }
        return ResultVOUtil.success(list);
    }
}

运行项目
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

4.SpringBoot 整合Mybatis

创建一个springboot项目,将上一个案例的User,ResultVO,ResultVOUtil类复制到当前项目,然后引入下列依赖

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!--json-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.51</version>
		</dependency>
		<!--lombok-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</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>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>

修改application.yml配置文件

server:
  port: 8081
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/young?useSSL=false&serverTimezone=UTC
    username: root
    password: 3fa4d180
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  mapper-locations: mapper/*.xml

创建mapper软件包,在该包下创建UserMapper.java

@Mapper
public interface UserMapper {
    public int saveUser(User user);
    public int updateUser(User user);
    public int deleteUser(Integer id);
    public User getUserById(Integer id);
    public List<User>getUserList();
}

创建service软件包,在该包下创建UserService.java接口

public interface UserService {
    ResultVO saveUser(JSONObject jsonObject);
    ResultVO updateUser(JSONObject jsonObject);
    ResultVO deleteUser(Integer id);
    ResultVO getUserById(Integer id);
    ResultVO getUserList();
}

创建UserServiceImpl实现UserService接口

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public ResultVO saveUser(JSONObject jsonObject) {
        try{
            String username=jsonObject.getString("username");
            String password=jsonObject.getString("password");
            Integer age=jsonObject.getInteger("age");
            User user=new User();
            user.setUsername(username);
            user.setPassword(password);
            user.setAge(age);
            int addNum=userMapper.saveUser(user);
            if(addNum<=0){
                return ResultVOUtil.error(500,"添加用户失败");
            }
            Map<String,Object>res=new HashMap<>();
            res.put("addNum",addNum);
            res.put("id",user.getId());
            return ResultVOUtil.success(res);
        }catch (Exception e){
            e.printStackTrace();
            return ResultVOUtil.error(500,"添加用户数据库操作失败");
        }
    }
    @Override
    public ResultVO updateUser(JSONObject jsonObject) {
        try{
            Integer id=jsonObject.getInteger("id");
            String username=jsonObject.getString("username");
            String password=jsonObject.getString("password");
            Integer age=jsonObject.getInteger("age");
            User user=new User();
            user.setUsername(username);
            user.setPassword(password);
            user.setAge(age);
            user.setId(id);
            int updateNum=userMapper.updateUser(user);
            if(updateNum<=0){
                return ResultVOUtil.error(500,"修改用户失败");
            }
            Map<String,Object>res=new HashMap<>();
            res.put("updateNum",updateNum);
            return ResultVOUtil.success(res);
        }catch (Exception e){
            e.printStackTrace();
            return ResultVOUtil.error(500,"修改用户数据库操作失败");
        }
    }
    @Override
    public ResultVO deleteUser(Integer id) {
        try{
            int deleteNum=userMapper.deleteUser(id);
            if(deleteNum<=0){
                return ResultVOUtil.error(500,"删除用户失败");
            }else{
                Map<String,Object>res=new HashMap<>();
                res.put("deleteNum",deleteNum);
                return ResultVOUtil.success(res);
            }
        }catch (Exception e){
            e.printStackTrace();
            return ResultVOUtil.error(500,"删除用户失败");
        }
    }
    @Override
    public ResultVO getUserById(Integer id) {
        try{
            User user=userMapper.getUserById(id);
            return ResultVOUtil.success(user);
        }catch (Exception e){
            e.printStackTrace();
            return ResultVOUtil.error(500,"获取用户详情失败");
        }
    }
    @Override
    public ResultVO getUserList() {
        try{
            List<User>list=userMapper.getUserList();
            return ResultVOUtil.success(list);
        }catch (Exception e){
            e.printStackTrace();
            return ResultVOUtil.error(500,"获取用户列表失败");
        }
    }
}

创建UserController.java控制类

@RestController
@Slf4j
public class UserController {
    @Autowired
    private UserService userService;
    @RequestMapping(value = "/saveUser",method = RequestMethod.POST)
    public ResultVO saveUser(@RequestBody JSONObject jsonObject){
        log.info("-------saveUser-------");
        return userService.saveUser(jsonObject);
    }
    @RequestMapping(value = "/updateUser",method = RequestMethod.PUT)
    public ResultVO updateUser(@RequestBody JSONObject jsonObject){
        log.info("-------updateUser-------");
        return userService.updateUser(jsonObject);
    }
    @RequestMapping(value = "/deleteUser",method = RequestMethod.DELETE)
    public ResultVO deleteUser(@RequestParam(name = "id",required = true)Integer id){
        log.info("------deleteUser---------");
        return userService.deleteUser(id);
    }
    @RequestMapping(value = "/getUserById",method = RequestMethod.GET)
    public ResultVO getUserById(@RequestParam(name = "id",required = true)Integer id){
        log.info("------getUserById-------");
        return userService.getUserById(id);
    }
    @RequestMapping(value = "/getUserList",method = RequestMethod.GET)
    public ResultVO getUserList(){
        log.info("-------getUserList-------");
        return userService.getUserList();
    }
}

在resource目录下,创建mapper目录,在该目录下创建UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.young.demo19.mapper.UserMapper">
    <insert id="saveUser" parameterType="com.young.demo19.entity.User" useGeneratedKeys="true" keyProperty="id">
        insert into user(id,username,password,age)
        values(#{id},#{username},#{password},#{age})
    </insert>
    <update id="updateUser" parameterType="com.young.demo19.entity.User">
        update user
        set
        <if test="username!=null and username!=''">
             username=#{username},
        </if>
        <if test="password!=null and password!=''">
            password=#{password},
        </if>
        <if test="age!=null">
            age=#{age},
        </if>
        <if test="1!=0">
            id=#{id}
        </if>
        where id=#{id}
    </update>
    <delete id="deleteUser" parameterType="int">
        delete from user
        where id=#{id}
    </delete>
    <select id="getUserById" parameterType="int" resultType="com.young.demo19.entity.User">
        select *
        from user
        where id=#{id}
    </select>
    <select id="getUserList" resultType="com.young.demo19.entity.User">
        select *
        from user
    </select>
</mapper>

启动项目
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
刚才总结的,都是单个表的查询,接下来讲述多表查询的操作,我们先创建一个权限表
在这里插入图片描述
在该表中插入一些数据
在这里插入图片描述
创建Role实体类

@Data
public class Role {
    private Integer id;
    private String name;
}

修改User实体类

@Data
public class User {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    private List<Role>roles;
}

当在数据库进行SQL查询时,如果有一些列的名称相同,我们可以使用别名,在多表查询中,我们一般不会返回resultType,而是返回ResultMap,ResultMap将SQL查询到的列与实体类对应的属性一一对应。
修改UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.young.demo19.mapper.UserMapper">
    <resultMap id="userBean" type="com.young.demo19.entity.User">
        <id property="id" column="uid"/>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <result property="age" column="age"/>
        <collection property="roles" ofType="com.young.demo19.entity.Role">
            <id property="id" column="rid"/>
            <result property="name" column="name"/>
        </collection>
    </resultMap>
    <insert id="saveUser" parameterType="com.young.demo19.entity.User" useGeneratedKeys="true" keyProperty="id">
        insert into user(id,username,password,age)
        values(#{id},#{username},#{password},#{age})
    </insert>
    <update id="updateUser" parameterType="com.young.demo19.entity.User">
        update user
        set
        <if test="username!=null and username!=''">
             username=#{username},
        </if>
        <if test="password!=null and password!=''">
            password=#{password},
        </if>
        <if test="age!=null">
            age=#{age},
        </if>
        <if test="1!=0">
            id=#{id}
        </if>
        where id=#{id}
    </update>
    <delete id="deleteUser" parameterType="int">
        delete from user
        where id=#{id}
    </delete>
    <select id="getUserById" parameterType="int" resultMap="userBean">
        select u.id uid,u.username,
        u.password,u.age,
        r.id rid,r.name
        from user u left join user_role ur
        on u.id=ur.uid
        left join role r
        on r.id=ur.rid
        where u.id=#{id}
    </select>
    <select id="getUserList" resultMap="userBean">
        select u.id uid,u.username,
        u.password,u.age,
        r.id rid,r.name
        from user u left join user_role ur
        on u.id=ur.uid
        left join role r
        on r.id=ur.rid
    </select>
</mapper>

创建一个user_role中间表
在这里插入图片描述
插入一些数据
在这里插入图片描述
运行项目
在这里插入图片描述
在这里插入图片描述
参考文章:
MyBatis快速入门第一讲–使用传统的JDBC编程带来的问题
推荐这个作者写的SSM系列的专栏,写的很详细

5.Restful风格和分页

RESTFUL特点包括:
1、每一个URI代表1种资源;
2、客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源;
3、通过操作资源的表现形式来操作资源;
4、资源的表现形式是XML或者 JSON;
5、客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息
顺便在这里补充一个内容,就是我们的接口的URL,可以写成这种形式/user/{id},下面是基于上个例子修改而来的实例
修改UserController.java

@RestController
@Slf4j
public class UserController {
    @Autowired
    private UserService userService;
    @RequestMapping(value = "/saveUser",method = RequestMethod.POST)
    public ResultVO saveUser(@RequestBody JSONObject jsonObject){
        log.info("-------saveUser-------");
        return userService.saveUser(jsonObject);
    }
    @RequestMapping(value = "/updateUser",method = RequestMethod.PUT)
    public ResultVO updateUser(@RequestBody JSONObject jsonObject){
        log.info("-------updateUser-------");
        return userService.updateUser(jsonObject);
    }
    @RequestMapping(value = "/deleteUser",method = RequestMethod.DELETE)
    public ResultVO deleteUser(@RequestParam(name = "id",required = true)Integer id){
        log.info("------deleteUser---------");
        return userService.deleteUser(id);
    }
    @RequestMapping(value = "/getUserById",method = RequestMethod.GET)
    public ResultVO getUserById(@RequestParam(name = "id",required = true)Integer id){
        log.info("------getUserById-------");
        return userService.getUserById(id);
    }
    @RequestMapping(value = "/getUserList",method = RequestMethod.GET)
    public ResultVO getUserList(){
        log.info("-------getUserList-------");
        return userService.getUserList();
    }
    @RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
    public ResultVO getUserById1(@PathVariable(name = "id",required = true)Integer id){
        log.info("-----getUserById1---------");
        return userService.getUserById(id);
    }
}

在这里插入图片描述
刚才整合Mybatis时,忘记讲分页的操作了,在这里补充一下
修改UserMapper.java,添加一个方法

public int getTotal();

修改UserMapper.xml,添加以下内容

<select id="getTotal" resultType="int">
        select count(*)
        from user
    </select>

修改UserService.java,添加一个方法

public ResultVO getUserList(int pageNum,int pageSize);

修改UserServiceImpl.java

@Override
    public ResultVO getUserList(Integer pageNum,Integer pageSize){
        try{
            PageHelper.startPage(pageNum,pageSize);
            List<User>list=userMapper.getUserList();
            int total=userMapper.getTotal();
            Map<String,Object>res=new HashMap<>();
            res.put("total",total);
            res.put("list",list);
            return ResultVOUtil.success(res);
        }catch (Exception e){
            e.printStackTrace();
            return ResultVOUtil.error(500,"分页获取失败");
        }
    }

修改UserController.java

@RequestMapping(value = "/user/getUserList/{pageNum}/{pageSize}",method = RequestMethod.GET)
    public ResultVO getUserList(@PathVariable(name = "pageNum",required = true)Integer pageNum,
                                @PathVariable(name = "pageSize",required = true)Integer pageSize){
        log.info("----getUserList1-------");
        return userService.getUserList(pageNum,pageSize);
    }

运行项目
在这里插入图片描述

6.Cookie和Session

创建一个springboot项目
实体类User.java,用来模拟用户登录

@Data
public class User {
    private Integer id;
    private String username;
    private String password;
}

创建拦截器LoginInterceptor.java

@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler)throws Exception{
        //获取cookie
        Cookie[]cookies=request.getCookies();
        for(Cookie cookie:cookies){
            //判断是否存在对应的cookie
            if(cookie.getName().equals(WebConfiguration.COOKIE_NAME)){
                //判断session中是否有user
                HttpSession session=request.getSession();
                //模拟通过数据库获取用户信息
                User user=(User)session.getAttribute(WebConfiguration.SESSION_NAME);
                if(user==null){
                    user=new User();
                    user.setId(1);
                    user.setUsername("张三");
                    user.setPassword("123456");
                    //将用户存储到session中
                    session.setAttribute(WebConfiguration.SESSION_NAME,user);
                }
                System.out.println("放行========");
                return true;
            }
        }
        System.out.println("拦截成功===========");
        return false;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception{
    }
    @Override
    public void afterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex){
    }
}

配置类WebConfiguration.java

@Configuration
public class WebConfiguration implements WebMvcConfigurer {
    public final static String COOKIE_NAME="user_cookie";
    public final static String SESSION_NAME="user_session";
    @Autowired
    private LoginInterceptor loginInterceptor;
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry){
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/*")
                .excludePathPatterns("/login");
    }
}

控制类UserController.java

@RestController
public class UserController {
    @RequestMapping("/login")
    public Cookie login(@RequestParam(value = "username",required = true)String username,
                        @RequestParam(value = "password",required = true)String password,
                        HttpServletResponse response){
        if(username.equals("admin")&&password.equals("123456")){
            //登录成功后添加cookie
            Cookie cookie=new Cookie(WebConfiguration.COOKIE_NAME,username);
            cookie.setMaxAge(30);
            cookie.setPath("/");
            response.addCookie(cookie);
            return cookie;
        }else{
            return null;
        }
    }
    @RequestMapping("/index")
    public User index(HttpServletRequest request){
        HttpSession session=request.getSession();
        User user=(User) session.getAttribute(WebConfiguration.SESSION_NAME);
        return user;
    }
    @RequestMapping("/logout")
    public String logout(HttpServletRequest request,HttpServletResponse response){
        Cookie[]cookies=request.getCookies();
        for(Cookie cookie:cookies){
            if(cookie.getName().equals(WebConfiguration.COOKIE_NAME)){
                request.getSession().removeAttribute(WebConfiguration.SESSION_NAME);
                cookie.setMaxAge(0);
                response.addCookie(cookie);
                return "注销成功";
            }
        }
        return "注销失败";
    }
}

运行项目
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
30秒后,在访问index
在这里插入图片描述

7.AOP切面

AOP(Aspect Oriented Programming),面向切面思想,是Spring的三大核心思想之一(两外两个:IOC-控制反转、DI-依赖注入)。
AOP我一般是用来打印日志,当然它还有许多功能,如权限检验等,下面是整合步骤:
创建SpringBoot项目,引入下列依赖:

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<!--json-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.51</version>
		</dependency>
		<!--lombok-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>

创建一个切面类:

@Aspect
@Component
@Slf4j
public class MyAop{
    @Pointcut("execution(public * com.young.demo19.controller.*.*(..))")
    public void log(){
        log.info("切入方法");
    }
    //前置通知
    @Before("log()")
    public void doBefore(JoinPoint joinPoint)throws Throwable{
        log.info("------------- start --------------");
        ServletRequestAttributes attributes=(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request=attributes.getRequest();
        log.info("URL:"+request.getRequestURL());
        log.info("METHOD:"+request.getMethod());
        log.info("CLASS_METHOD:"+joinPoint);
        log.info("ARGS:"+Arrays.toString(joinPoint.getArgs()));
    }
    //返回通知
    @AfterReturning(returning = "ret",pointcut = "log()")
    public void afterReturning(Object ret){
        log.info("返回值是:"+ret);
    }
    //异常通知
    @AfterThrowing(throwing = "ex",pointcut = "log()")
    public void throwss(JoinPoint joinPoint,Exception ex){
        log.info("异常通知-----------");
        log.info("产生异常的方法:"+joinPoint);
        log.info("错误类型:"+ex);
    }
    //后置通知
    @After("log()")
    public void after(JoinPoint joinPoint){
        log.info("------------end----------------");
    }
}

HelloController.java

@RestController
public class HelloController {
    @RequestMapping("/helloAop")
    public Object helloAop(){
        return "helloAop";
    }
    @RequestMapping("/helloError")
    public Object helloError(){
        return 1/0;
    }
}

运行项目,先访问http://localhost:8080/helloAop
在这里插入图片描述
访问http://localhost:8080/helloError

在这里插入图片描述
参考文章:SpringBoot3切面AOP实现权限检验
SpringBoot的AOP使用

8.异步编程注解Async

AsyncService.java

@Service
@Slf4j
public class AsyncService {
    @Async
    public void doTask1(){
        try{
            long t1=System.currentTimeMillis();
            Thread.sleep(2000L);
            long t2=System.currentTimeMillis();
            log.info("线程{}用时{}",Thread.currentThread().getName(),t2-t1);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    @Async
    public void doTask2(){
        try{
            long t1=System.currentTimeMillis();
            Thread.sleep(3000L);
            long t2=System.currentTimeMillis();
            log.info("线程{}用时{}",Thread.currentThread().getName(),t2-t1);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

AsyncController.java

@RestController
@Slf4j
public class AsyncController {
    @Autowired
    private AsyncService asyncService;
    @RequestMapping("/doTask")
    public void doTask(){
        long t1=System.currentTimeMillis();
        asyncService.doTask1();
        asyncService.doTask2();
        try{
            Thread.sleep(1000L);
        }catch (Exception e){
            e.printStackTrace();
        }
        long t2=System.currentTimeMillis();
        log.info("线程{}用时{}",Thread.currentThread().getName(),t2-t1);
    }
}

运行项目,访问/doTask
在这里插入图片描述
发现虽然有Async注解, 但是还是同步进行,原因是我们没有添加配置类
AsyncConfiguration.java

@EnableAsync
@Configuration
public class AsyncConfiguration {
}

重新运行,访问doTask
在这里插入图片描述
不过现在还存在问题,因为默认的是直接创建线程,也就是说,当用户很多而且同时发出请求时,有可能因为创建太多线程而导致内存不足,我们修改配置类

@EnableAsync
@Configuration
public class AsyncConfiguration  implements AsyncConfigurer{
    //核心线程数
    private final static int CORE_NUM=5;
    //最大线程数
    private final static int POOL_NUM=10;
    //队列容量
    private final static int QUEUE_CAPACITY=50;
    //存活时间
    private final static int ALIVE_TIME=200;
    //线程前缀名
    private final static String PRE_NAME="myThread_";
    @Bean
    public ThreadPoolTaskExecutor executor(){
        ThreadPoolTaskExecutor executor=new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(CORE_NUM);
        executor.setMaxPoolSize(POOL_NUM);
        executor.setQueueCapacity(QUEUE_CAPACITY);
        executor.setKeepAliveSeconds(ALIVE_TIME);
        executor.setThreadNamePrefix(PRE_NAME);
        //设置当缓存队列已满并且线程中的线程数目达到MAX_NUM时,采取的行动
        /**
         * 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略
         * 通常有以下四种策略:
         * ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
         * ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
         * ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
         * ThreadPoolExecutor.CallerRunsPolicy:重试添加当前的任务,自动重复调用 execute() 方法,直到成功
         */
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

9.邮件发送

创建一个Springboot姓名,引入依赖:

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<!--json-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.51</version>
		</dependency>
		<!--lombok-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<!--redis-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<!--邮件服务-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-mail</artifactId>
		</dependency>

application.yml

spring:
  redis:
    username: root
    host: localhost
    port: 6379
    database: 0
    jedis:
      pool:
        max-idle: 8
        min-idle: 0
        max-wait: 1000ms
    timeout: 5000ms
  mail:
    username: 2827523200@qq.com
    password: 邮箱验证后的密码
    host: smtp.qq.com
    properties:
      mail:
        smtp:
          ssl:
            enable: true
    default-encoding: UTF-8
server:
  port: 8081

MailProperties.java

@ConfigurationProperties(prefix = "spring.mail")
@Component
public class MailProperties {
    private String username;
    public void setUsername(String username){
        this.username=username;
    }
    public String getUsername(){
        return this.username;
    }
}

CodeUtil.java验证码工具类

public class CodeUtil {
    public static String getCode(int n){
        char[]chars="0123456789".toCharArray();
        StringBuilder sb=new StringBuilder();
        for(int i=0;i<n;i++){
            sb.append(new Random().nextInt(chars.length));
        }
        return sb.toString();
    }
}

RedisUtil.java

@Component
public class RedisUtil {
    @Autowired
    private StringRedisTemplate redisTemplate;
    private String PRE="";
    public <K,V> boolean set(K key,V value,long time){
        if(key==null){
            return false;
        }
        boolean res=false;
        try{
            redisTemplate.opsForValue().set(PRE+key, JSONObject.toJSONString(value),time, TimeUnit.SECONDS);
            res=true;
        }catch (Exception e){
            e.printStackTrace();
        }
        return res;
    }
    public <K,V> V get(K key,Class<V>clazz){
        if(key==null){
            return null;
        }
        String value=redisTemplate.opsForValue().get(PRE+key);
        return JSONObject.parseObject(value,clazz);
    }
}

MailController.java

@RestController
public class MailController {
    @Autowired
    private JavaMailSenderImpl mailSender;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private MailProperties mailProperties;
    @RequestMapping(value = "/sendMail",method = RequestMethod.POST)
    public String sendMail(@RequestParam(value = "to",required = true)String to)throws Exception{
        MimeMessage message=mailSender.createMimeMessage();
        MimeMessageHelper helper=new MimeMessageHelper(message);
        helper.setFrom(mailProperties.getUsername());
        helper.setSubject("发送验证码");
        helper.setTo(to);
        String code= CodeUtil.getCode(4);
        helper.setText("验证码为:<h2>"+code+"</h2>请勿泄露");
        mailSender.send(message);
        redisUtil.set("code",code,60);
        return "发送成功";
    }
    @RequestMapping(value = "/verify",method = RequestMethod.POST)
    public String verify(@RequestParam(value = "code",required = true)String code){
        String code1=redisUtil.get("code",String.class);
        if(code1==null||code1==""){
            return "验证码已过期";
        }
        if(code1.equals(code)){
            return "验证成功";
        }else{
            return "验证失败";
        }
    }
}

运行项目
在这里插入图片描述
打开QQ邮箱
在这里插入图片描述
参考文章:SpringBoot实现邮箱验证

10.SpringBoot整合Redis

在上一个案例中,我们看到一个RedisUtil工具类,Redis是一个缓存中间件,接下来我们简单介绍一下Redis的一些使用操作
引入依赖:

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<!--redis-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<!--json-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.51</version>
		</dependency>
		<!--lombok-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>

下面简单介绍一个StringRedisTemplate的实例,我们在Redis中,以String类型进行存储,如果要存储的对象不是String类型,我们通过JSONObject.toString()进行转换
RedisUtil.java

@Component
public class RedisUtil {
   @Autowired
    private StringRedisTemplate redisTemplate;
    private final String PRE="";
    public <K,V> boolean set(K key,V value){
        boolean res=false;
        try{
            redisTemplate.opsForValue().set(PRE+key,JSONObject.toJSONString(value));
            res=true;
        }catch (Exception e){
            e.printStackTrace();
        }
        return res;
    }
    public <K,V> boolean set(K key,V value,long time){
        if(time<0){
            return this.set(key,value);
        }
        boolean res=false;
        try{
            redisTemplate.opsForValue().set(PRE+key,JSONObject.toJSONString(value),time,TimeUnit.SECONDS);
            res=true;
        }catch (Exception e){
            e.printStackTrace();
        }
        return res;
    }
    public <K,V> V get(K key,Class<V>clazz){
        if(key==null){
            return null;
        }
        try{
            String value=redisTemplate.opsForValue().get(PRE+key);
            return JSONObject.parseObject(value,clazz);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
    public <K,V> List<V> getList(K key,Class<V>clazz){
        if(key==null){
            return Collections.emptyList();
        }
        try{
            String value=redisTemplate.opsForValue().get(PRE+key);
            List<V>list=JSONObject.parseArray(value,clazz);
            return list;
        }catch (Exception e){
            e.printStackTrace();
            return Collections.emptyList();
        }
    }
    public <K> boolean setExpire(K key,long time){
        boolean res=false;
        if(key==null){
            return false;
        }
        try{
            res=redisTemplate.expire(PRE+key,time,TimeUnit.SECONDS);
            res=true;
        }catch (Exception e){
            e.printStackTrace();
        }
        return res;
    }
    public <K> long getExpire(K key){
        return redisTemplate.getExpire(PRE+key);
    }
    public <K> boolean del(K key){
        try{
            redisTemplate.delete(PRE+key);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    public <K> boolean hasKey(K key){
        if(key==null){
            return false;
        }
        try{
            return redisTemplate.hasKey(PRE+key);
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
}

测试类:

@SpringBootTest
class Demo19ApplicationTests {
	@Autowired
	private RedisUtil redisUtil;
	@Test
	public void set(){
		boolean res=redisUtil.set("num",100);
		System.out.println(res);
		res=redisUtil.set("str","helloWorld",100);
		System.out.println(res);
		res=redisUtil.set("expire","Expire",10);
		System.out.println(res);
		List<String>list=new ArrayList<>();
		list.add("one");
		list.add("two");
		list.add("three");
		res=redisUtil.set("list",list);
		System.out.println(res);
	}
	@Test
	public void get(){
		Integer num=redisUtil.get("num",Integer.class);
		System.out.println(num);
		String str=redisUtil.get("str",String.class);
		System.out.println(str);
		long time=redisUtil.getExpire("str");
		System.out.println(time);
		String expire=redisUtil.get("expire",String.class);
		System.out.println(expire);
		List<String>list=redisUtil.getList("list",String.class);
		for(String s:list){
			System.out.print(s+" ");
		}
		System.out.println();
	}
	@Test
	void contextLoads() {
	}
}

先测试set方法
在这里插入图片描述
10秒后打开RedisDesktopManager,因为expire设置存活时间是10秒,所以看不到
在这里插入图片描述
测试get方法
在这里插入图片描述
StringRedisTemplate的使用相对比较简单,但是每次都需要将数据转为String类型进行存储,不太方便,而且对数组等类型的支持也不太方便,所以接下来我们介绍RedisTemplate的序列化存储
RedisConfiguration.java

@Configuration
public class RedisConfiguration {
    @Bean
    public RedisTemplate<String,Object>redisTemplateBean(RedisConnectionFactory connectionFactory){
        RedisTemplate<String,Object>redisTemplate=new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer=new Jackson2JsonRedisSerializer<>(Object.class);
        StringRedisSerializer stringRedisSerializer=new StringRedisSerializer();
        ObjectMapper om=new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        //设置序列化方式
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

UserInfo.java,注意,要存储的对象必须要实现序列化接口

@Data
public class UserInfo implements Serializable {
    private Integer id;
    private String username;
    private String password;
}

RedisUtil.java

@Component
public class RedisUtil {
    @Autowired
    private RedisTemplate<String,Object>redisTemplate;
    private final String PRE="";
    public  boolean set(Object key,Object value){
        if(key==null){
            return false;
        }
        try{
            redisTemplate.opsForValue().set(PRE+key,value);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    public boolean set(Object key,Object value,long time){
        if(time<=0){
            return this.set(key,value);
        }
        if(key==null){
            return false;
        }
        try{
            redisTemplate.opsForValue().set(PRE+key,value,time,TimeUnit.SECONDS);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    public  boolean setExpire(Object key,long time){
        if(key==null){
            return false;
        }
        try{
            return redisTemplate.expire(PRE+key,time,TimeUnit.SECONDS);
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    public  long getExpire(Object key){
        if(key==null){
            return -1;
        }
        try{
            return redisTemplate.getExpire(PRE+key,TimeUnit.SECONDS);
        }catch (Exception e){
            e.printStackTrace();
            return -1;
        }
    }
    public Object get(Object key){
        if(key==null){
            return null;
        }
        try{
            return redisTemplate.opsForValue().get(PRE+key);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
    public  boolean hasKey(Object key){
        if(key==null){
            return false;
        }
        try{
            return redisTemplate.hasKey(PRE+key);
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    public boolean del(Object key){
        if(key==null){
            return false;
        }
        try{
            return redisTemplate.delete(PRE+key);
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    public  boolean leftPush(Object key,Object value){
        if(key==null){
            return false;
        }
        try{
            redisTemplate.opsForList().leftPush(PRE+key,value);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    public boolean rightPush(Object key,Object value){
        if(key==null){
            return false;
        }
        try{
            redisTemplate.opsForList().rightPush(PRE+key,value);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    public  Object getList(Object key){
        List<Object>list=Collections.emptyList();
        try{
            list=redisTemplate.opsForList().range(PRE+key,0,-1);
        }catch (Exception e){
            e.printStackTrace();
        }
        return list;
    }
}

测试类

@SpringBootTest
class Demo19ApplicationTests {
	@Autowired
	private RedisUtil redisUtil;
	@Test
	public void set(){
		UserInfo userInfo=new UserInfo();
		userInfo.setId(1);
		userInfo.setUsername("zhangsan");
		userInfo.setPassword("123456");
		boolean res=redisUtil.set("userInfo",userInfo);
		System.out.println(res);
		for(int i=0;i<4;i++){
			UserInfo userInfo1=new UserInfo();
			userInfo1.setId(i+1);
			userInfo1.setUsername("admin"+i);
			userInfo1.setPassword("12345"+i);
			redisUtil.leftPush("userList",userInfo1);
		}
	}
	@Test
	public void get(){
		UserInfo userInfo=(UserInfo) redisUtil.get("userInfo");
		System.out.println(userInfo);
		List<UserInfo>list=(List<UserInfo>) redisUtil.getList("userList");
		for(UserInfo userInfo1:list){
			System.out.println(userInfo1);
		}
	}
	@Test
	void contextLoads() {
	}
}

先测试set
在这里插入图片描述
再测试get
在这里插入图片描述
参考文章:SpringBoot整合Redis使用介绍
获取数据时,直接访问数据库是比较耗时的工作,我们可以将数据存入Redis缓存中间件中,从而提高访问速度,接下来,我们来将Redis和mysql进行一个整合
在上一个案例的基础上,我们在pom.xml添加下列信息

<!--mysql-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<!--mybatis-plus-->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.5.2</version>
		</dependency>

修改UserInfo.java

@Data
@TableName(value = "user")
public class UserInfo implements Serializable {
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String username;
    private String password;
    private Integer age;
}

修改application.yml

spring:
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    jedis:
      pool:
        max-idle: 8
        min-idle: 0
        max-wait: 1000ms
    timeout: 5000ms
  datasource:
    username: root
    password: 数据库密码
    url: jdbc:mysql://localhost:3306/young?useSSL=false&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
  mapper-locations: mapper/*.xml

ResultVO.java

/**
 * @param <T>
 *     code 表示状态码
 *     msg 表示提示信息
 *     data 表示返回的数据
 */
@Data
public class ResultVO <T>{
    private Integer code;
    private String msg;
    private T data;
    public ResultVO(){}
    public ResultVO(Integer code,String msg){
        this.code=code;
        this.msg=msg;
        this.data=null;
    }
    public ResultVO(Integer code,String msg,T data){
        this.code=code;
        this.msg=msg;
        this.data=data;
    }
}

ResultVOUtil.java

public class ResultVOUtil {
    public static ResultVO success(){
        return new ResultVO<>(200,"操作成功");
    }
    public static ResultVO success(Object data){
        return new ResultVO<>(200,"操作成功",data);
    }
    public static ResultVO error(Integer code,String msg){
        return new ResultVO<>(code,msg);
    }
}

UserInfoMapper.java

public interface UserInfoMapper extends BaseMapper<UserInfo> {
}

UserInfoService.java

public interface UserService {
    ResultVO saveUser(JSONObject jsonObject);
    ResultVO updateUser(JSONObject jsonObject);
    ResultVO deleteUser(Integer id);
    ResultVO getUserById(Integer id);
    ResultVO getUserList();
}

UserInfoServiceImpl.java

@Service
public class UserInfoServiceImpl implements UserInfoService {
    @Autowired
    private UserInfoMapper userInfoMapper;
    @Resource
    private RedisUtil redisUtil;
    private final String PRE="userInfo_";
    private final String USERINFOLIST="userInfoList";
    @Override
    public ResultVO saveUser(JSONObject jsonObject) {
        try{
            String username=jsonObject.getString("username");
            String password=jsonObject.getString("password");
            Integer age=jsonObject.getInteger("age");
            UserInfo userInfo=new UserInfo();
            userInfo.setUsername(username);
            userInfo.setPassword(password);
            userInfo.setAge(age);
            int addNum=userInfoMapper.insert(userInfo);
            if (addNum <= 0) {
                return ResultVOUtil.error(403,"添加用户失败");
            }
            Map<String,Object>res=new HashMap<>();
            res.put("addNum",addNum);
            res.put("id",userInfo.getId());
            //将用户信息放入缓存
            redisUtil.set(PRE+userInfo.getId(),userInfo);
            //更新缓存列表
            this.updateUserList();
            return ResultVOUtil.success(res);
        }catch (Exception e){
            e.printStackTrace();
            return ResultVOUtil.error(403,"添加用户失败");
        }
    }
    @Override
    public ResultVO updateUser(JSONObject jsonObject) {
        try{
            String password=jsonObject.getString("password");
            Integer id=jsonObject.getInteger("id");
            UserInfo userInfo=new UserInfo();
            userInfo.setId(id);
            userInfo.setPassword(password);
            UpdateWrapper<UserInfo>wrapper=new UpdateWrapper<>();
            wrapper.eq("id",id);
            int updateNum=userInfoMapper.update(userInfo,wrapper);
            if (updateNum <= 0) {
                return ResultVOUtil.error(403,"修改用户失败");
            }
            Map<String,Object>res=new HashMap<>();
            res.put("updateNum",updateNum);
            //修改缓存
            userInfo=userInfoMapper.selectById(id);
            //更新用户信息缓存
            redisUtil.set(PRE+userInfo.getId(),userInfo);
            //更新缓存列表
            this.updateUserList();
            return ResultVOUtil.success(res);
        }catch (Exception e){
            e.printStackTrace();
            return ResultVOUtil.error(403,"修改用户失败");
        }
    }
    @Override
    public ResultVO deleteUser(Integer id) {
        try{
            int deleteNum=userInfoMapper.deleteById(id);
            if(deleteNum<=0){
                return ResultVOUtil.error(403,"删除用户失败");
            }
            this.updateUserList();
            Map<String,Object>res=new HashMap<>();
            res.put("deleteNum",deleteNum);
            return ResultVOUtil.success(res);
        }catch (Exception e){
            e.printStackTrace();
            return ResultVOUtil.error(403,"删除用户失败");
        }
    }
    @Override
    public ResultVO getUserById(Integer id) {
        try{
            //先从redis中获取
            UserInfo userInfo=(UserInfo) redisUtil.get(PRE+id);
            if(userInfo==null){
                System.out.println("从数据库中获取");
                userInfo=userInfoMapper.selectById(id);
                //获取成功后存入缓存
                if(userInfo!=null){
                    redisUtil.set(PRE+userInfo.getId(),userInfo);
                }
            }else{
                System.out.println("从缓存中获取");
            }
            return ResultVOUtil.success(userInfo);
        }catch (Exception e){
            e.printStackTrace();
            return ResultVOUtil.error(403,"缓存用户详情失败");
        }
    }
    @Override
    public ResultVO getUserList() {
        try{
            //先从缓存获取
            List<UserInfo>userInfoList=(List<UserInfo>) redisUtil.getList(USERINFOLIST);
            if(userInfoList==null){
                System.out.println("从数据库中获取");
                userInfoList=userInfoMapper.selectList(null);
                //存入缓存
                if(userInfoList!=null){
                    for(UserInfo userInfo:userInfoList){
                        redisUtil.rightPush(USERINFOLIST,userInfo);
                    }
                }
            }else{
                System.out.println("从缓存中获取用户列表");
            }
            return ResultVOUtil.success(userInfoList);
        }catch (Exception e){
            e.printStackTrace();
            return ResultVOUtil.error(403,"获取用户列表失败");
        }
    }
    @Async
    private void updateUserList(){
        System.out.println("更新缓存");
        List<UserInfo>userInfoList=userInfoMapper.selectList(null);
        for(UserInfo userInfo:userInfoList){
            redisUtil.rightPush(USERINFOLIST,userInfo);
        }
    }
}

AsyncConfiguration.java

@Configuration
@EnableAsync
public class AsyncConfiguration {
}

UserInfoController.java

@RestController
@Slf4j
public class UserInfoController {
    @Autowired
    private UserInfoService userInfoService;
    @RequestMapping(value = "/saveUser",method = RequestMethod.POST)
    public ResultVO saveUser(@RequestBody JSONObject jsonObject){
        log.info("--------saveUser----------");
        return userInfoService.saveUser(jsonObject);
    }
    @RequestMapping(value = "/updateUser",method = RequestMethod.PUT)
    public ResultVO updateUser(@RequestBody JSONObject jsonObject){
        log.info("--------updateUser----------");
        return userInfoService.updateUser(jsonObject);
    }
    @RequestMapping(value = "/deleteUser",method = RequestMethod.DELETE)
    public ResultVO deleteUser(@RequestParam(value = "id",required = true)Integer id){
        log.info("----------deleteUser-----------");
        return userInfoService.deleteUser(id);
    }
    @RequestMapping(value = "/getUserById",method = RequestMethod.GET)
    public ResultVO getUserById(@RequestParam(value = "id",required = true)Integer id) {
        log.info("----------getUserById-----------");
        return userInfoService.getUserById(id);
    }
    @RequestMapping(value = "/getUserList",method = RequestMethod.GET)
    public ResultVO getUserList(){
        log.info("---------getUserList----------");
        return userInfoService.getUserList();
    }
}

修改启动类

@SpringBootApplication
@MapperScan("com.young.demo19.mapper")
public class Demo19Application {
	public static void main(String[] args) {
		SpringApplication.run(Demo19Application.class, args);
	}

}

数据库内容:
在这里插入图片描述

运行项目
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里添加、修改、删除后都要更新缓存,我们查看控制台如下,确实更新了三次缓存
在这里插入图片描述
第一次获取id=2的用户信息
在这里插入图片描述
在这里插入图片描述
第二次获取id=2的用户信息
在这里插入图片描述
获取用户列表
在这里插入图片描述
在这里插入图片描述
Redis还有一个发布订阅的功能
创建一个springboot项目
pom.xml

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!--lombok-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<!--redis-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<!--json-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.51</version>
		</dependency>

MessageReceiver.java

@Service
public class MessageReceiver {
    public void receiveMessage(String message){
        System.out.println("接收信息:"+message);
    }
}

RedisConfig.java

@Configuration
public class RedisConfig {
    //订阅的频道名
    private static String topic="chat";
    @Bean
    public StringRedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
        return new StringRedisTemplate(redisConnectionFactory);
    }
    //配置监听适配器
    @Bean
    public MessageListenerAdapter messageListenerAdapter(MessageReceiver receiver){
        MessageListenerAdapter adapter=new MessageListenerAdapter(receiver,"receiveMessage");
        return adapter;
    }
    //配置监听容器
    @Bean
    public RedisMessageListenerContainer getRedisMessageListenerContainer(RedisConnectionFactory connectionFactory,MessageListenerAdapter listenerAdapter){
        RedisMessageListenerContainer container=new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        //订阅主题,可以将多个频道订阅到一个容器中
        container.addMessageListener(listenerAdapter,new PatternTopic(RedisConfig.topic));
        return container;
    }
}

MessageController.java

@RestController
public class MessageController {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @GetMapping("/sendMessage")
    public void sendMessage(String message){
        //chat是订阅的频道名
        redisTemplate.convertAndSend("chat",message+new Date());
    }
}

application.yml

server:
  port: 8081
spring:
  redis:
    host: localhost
    port: 6379
    database: 0
    lettuce:
      pool:
        max-active: 8
        min-idle: 0
        max-idle: 8
        max-wait: -1

运行项目:
在这里插入图片描述
在这里插入图片描述
参考文章:SpringBoot整合Redis实现发布订阅功能

11.SpringBoot整合MyBatis-plus

在上一个案例中,我们没有使用Mybatis来访问数据库,而是使用Mybatis-plus,Mybatis-plus是mybatis的升级版,mybatis有的,Mybatis-plus都有,而且Mybatis-plus对单表的查询更加的方便,不用写那么多sql语句
创建一个Springboot项目,引入下列依赖

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<!--json-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.51</version>
		</dependency>
		<!--lombok-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<!--mysql-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<!--mybatis-plus-->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.5.2</version>
		</dependency>

application.yml

spring:
  datasource:
    username: root
    password: 数据库密码
    url: jdbc:mysql://localhost:3306/young?useSSL=false&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
  mapper-locations: mapper/*.xml
  global-config:
    db-config:
      logic-delete-value: 1
      logic-not-delete-value: 0

在这里插入图片描述
下面是建表的SQL语句:

CREATE TABLE `t_user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(64) NOT NULL,
  `password` varchar(64) NOT NULL,
  `create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  `version` int NOT NULL DEFAULT '0',
  `is_delete` int NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

User.java

@TableName(value = "t_user")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String username;
    private String password;
    @TableField(value = "create_time",fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(value = "update_time",fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    @Version
    private Integer version;
    @TableLogic
    @TableField(value = "is_delete")
    private Integer isDelete;
}

UserMapper.java

public interface UserMapper extends BaseMapper<User> {
    Page<User>getUserList(Page<User>pages);
}

UserMapper.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">
<mapper namespace="com.young.demo19.mapper.UserMapper">
    <select id="getUserList" resultType="com.young.demo19.entity.User">
        select *
        from t_user
        where is_delete=0
    </select>
</mapper>

在启动类添加注解

@SpringBootApplication
@MapperScan("com.young.demo19.mapper")
public class Demo19Application {
	public static void main(String[] args) {
		SpringApplication.run(Demo19Application.class, args);
	}
}

UserService.java

public interface UserService {
    ResultVO saveUser(User user);
    ResultVO updateUser(User user);
    ResultVO deleteUser(Integer id);
    ResultVO getUserById(Integer id);
    ResultVO getUserList(Integer pageNum,Integer pageSize);
}

UserServiceImpl.java

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public ResultVO saveUser(User user) {
        try{
            int addNum=userMapper.insert(user);
            if(addNum<=0){
                return ResultVOUtil.error(403,"添加失败");
            }
            Integer id=user.getId();
            Map<String,Object>res=new HashMap<>();
            res.put("id",id);
            res.put("addNum", addNum);
            return ResultVOUtil.success(res);
        }catch (Exception e){
            e.printStackTrace();
            return ResultVOUtil.error(403,"添加失败");
        }
    }
    @Override
    public ResultVO updateUser(User user) {
        try{
            int updateNum=userMapper.updateById(user);
            if(updateNum<=0){
                return ResultVOUtil.error(403,"修改失败");
            }
            Map<String,Object>res=new HashMap<>();
            res.put("updateNum",updateNum);
            return ResultVOUtil.success(res);
        }catch (Exception e){
            e.printStackTrace();
            return ResultVOUtil.error(403,"修改失败");
        }
    }
    @Override
    public ResultVO deleteUser(Integer id) {
        try{
            int deleteNum=userMapper.deleteById(id);
            if(deleteNum<=0){
                return ResultVOUtil.error(403,"删除失败");
            }
            Map<String,Object>res=new HashMap<>();
            res.put("deleteNum",deleteNum);
            return ResultVOUtil.success(res);
        }catch (Exception e){
            e.printStackTrace();
            return ResultVOUtil.error(403,"删除失败");
        }
    }
    @Override
    public ResultVO getUserById(Integer id) {
        try{
            User user=userMapper.selectById(id);
            return ResultVOUtil.success(user);
        }catch (Exception e){
            e.printStackTrace();
            return ResultVOUtil.error(403,"获取用户信息失败");
        }
    }
    @Override
    public ResultVO getUserList(Integer pageNum, Integer pageSize) {
        try{
            Page<User>pages=new Page<>(pageNum,pageSize);
            pages=userMapper.getUserList(pages);
            List<User>userList=pages.getRecords();
            Long total=pages.getTotal();
            Map<String,Object>res=new HashMap<>();
            res.put("total",total);
            res.put("list",userList);
            return ResultVOUtil.success(res);
        }catch (Exception e){
            e.printStackTrace();
            return ResultVOUtil.error(403,"获取用户列表失败");
        }
    }
}

MyMetaObjectHandler.java

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime",new Date(),metaObject);
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
}

MybatisPlusConfiguration.java

@Configuration
public class MybatisPlusConfiguration {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor=new MybatisPlusInterceptor();
        //配置分页
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        //配置乐观锁
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}

ResultVO.java

@Data
public class ResultVO <T>{
    private Integer code;
    private String msg;
    private T data;
    public ResultVO(){}
    public ResultVO(Integer code,String msg){
        this.code=code;
        this.msg=msg;
        this.data=null;
    }
    public ResultVO(Integer code,String msg,T data){
        this.code=code;
        this.msg=msg;
        this.data=data;
    }
}

ResultVOUtil.java

public class ResultVOUtil {
    public static ResultVO success(){
        return new ResultVO<>(200,"操作成功");
    }
    public static ResultVO success(Object data){
        return new ResultVO<>(200,"操作成功",data);
    }
    public static ResultVO error(Integer code,String msg){
        return new ResultVO<>(code,msg);
    }
}

UserController.java

@RestController
public class UserController {
    @Autowired
    private UserService userService;
    @RequestMapping(value = "/saveUser",method = RequestMethod.POST)
    public ResultVO saveUser(@RequestBody User user){
        return userService.saveUser(user);
    }
    @RequestMapping(value = "/updateUser",method = RequestMethod.PUT)
    public ResultVO updateUser(@RequestBody User user){
        return userService.updateUser(user);
    }
    @RequestMapping(value = "/deleteUser",method = RequestMethod.DELETE)
    public ResultVO deleteUser(@RequestParam(value = "id",required = true)Integer id){
        return userService.deleteUser(id);
    }
    @RequestMapping(value = "/getUserById",method = RequestMethod.GET)
    public ResultVO getUserById(@RequestParam(value = "id",required = true)Integer id){
        return userService.getUserById(id);
    }
    @RequestMapping(value = "/getUserList",method = RequestMethod.GET)
    public ResultVO getUserList(@RequestParam(value = "pageNum",required = true)Integer pageNum,
                                @RequestParam(value = "pageSize",required = true)Integer pageSize){
        return userService.getUserList(pageNum,pageSize);
    }
}

运行项目
先添加多条数据
在这里插入图片描述
修改用户信息
在这里插入图片描述
在这里插入图片描述
删除用户
在这里插入图片描述
在这里插入图片描述
因为我们用TableLogic来定义逻辑删除,所以数据不会被物理删除,0表示存在,1表示删除
获取用户信息
在这里插入图片描述
获取用户列表
在这里插入图片描述
上面涉及的都是单表操作,多表操作的话,就我个人而言,还是使用xml文件来进行书写sql语句进行多表操作,这里就不累述了,有兴趣的可以参考之前提到的SpringBoot整合Mybatis的案例
下面来介绍Mybatis-plus的事务操作
修改saveUser方法

@Override
    @Transactional(rollbackFor = {RuntimeException.class,Exception.class})
    public ResultVO saveUser(User user) {
        try{
            int addNum=userMapper.insert(user);
            if(addNum<=0){
                throw new RuntimeException();
            }
            Integer id=user.getId();
            Map<String,Object>res=new HashMap<>();
            res.put("id",id);
            res.put("addNum", addNum);
            return ResultVOUtil.success(res);
        }catch (Exception e){
            e.printStackTrace();
            //事务回顾
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResultVOUtil.error(403,"添加失败");
        }
    }

添加数据
在这里插入图片描述
操作成功,不回滚,接着我们试着抛出一个错误
修改saveUser方法

@Override
    @Transactional(rollbackFor = {RuntimeException.class,Exception.class})
    public ResultVO saveUser(User user) {
        try{
            int addNum=userMapper.insert(user);
            int i=1/0;
            if(addNum<=0){
                throw new RuntimeException();
            }
            Integer id=user.getId();
            Map<String,Object>res=new HashMap<>();
            res.put("id",id);
            res.put("addNum", addNum);
            return ResultVOUtil.success(res);
        }catch (Exception e){
            e.printStackTrace();
            //事务回顾
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResultVOUtil.error(403,"添加失败");
        }
    }

添加数据
在这里插入图片描述
查看数据库,发现确实回滚了在这里插入图片描述
参考文章:SpringBoot整合Mybatis-plus
mybatis-plus超详细讲解

12. httpClient

在实际项目中,我们可能需要在代码中发起http请求,而httpClient就是用来协作我们进行http请求的
我们先修改上一个案例的代码,修改UserController.java,我们等下要用httpClient来访问它

@RestController
public class UserController {
    @Autowired
    private UserService userService;
    @Autowired
    private UserMapper userMapper;
    @RequestMapping(value = "/saveUser",method = RequestMethod.POST)
    public ResultVO saveUser(@RequestBody User user){
        return userService.saveUser(user);
    }
    @RequestMapping(value = "/updateUser",method = RequestMethod.PUT)
    public ResultVO updateUser(@RequestBody User user){
        return userService.updateUser(user);
    }
    @RequestMapping(value = "/deleteUser",method = RequestMethod.DELETE)
    public ResultVO deleteUser(@RequestParam(value = "id",required = true)Integer id){
        return userService.deleteUser(id);
    }
    @RequestMapping(value = "/getUserById",method = RequestMethod.GET)
    public ResultVO getUserById(@RequestParam(value = "id",required = true)Integer id){
        return userService.getUserById(id);
    }
    @RequestMapping(value = "/getUserList",method = RequestMethod.GET)
    public ResultVO getUserList(@RequestParam(value = "pageNum",required = true)Integer pageNum,
                                @RequestParam(value = "pageSize",required = true)Integer pageSize){
        return userService.getUserList(pageNum,pageSize);
    }
    @RequestMapping(value = "/getUserList",method = RequestMethod.POST)
    public ResultVO getUserList1(@RequestParam(value = "pageNum",required = true)Integer pageNum,
                                @RequestParam(value = "pageSize",required = true)Integer pageSize){
        return userService.getUserList(pageNum,pageSize);
    }
    @RequestMapping(value = "/getUserById1",method = RequestMethod.POST)
    public ResultVO getUserById1(@RequestParam(value = "id",required = true)Integer id){
        return userService.getUserById(id);
    }
    @RequestMapping(value = "/getUserList1",method = RequestMethod.GET)
    public ResultVO getUserList1(){
        QueryWrapper<User>wrapper=new QueryWrapper<>();
        wrapper.eq("is_delete",0);
        List<User>list=userMapper.selectList(wrapper);
        return ResultVOUtil.success(list);
    }
    @RequestMapping(value = "/getUserList2",method = RequestMethod.POST)
    public ResultVO getUserList2(){
        QueryWrapper<User>wrapper=new QueryWrapper<>();
        wrapper.eq("is_delete",0);
        List<User>list=userMapper.selectList(wrapper);
        return ResultVOUtil.success(list);
    }
     @RequestMapping(value = "/saveUser1",method = RequestMethod.POST)
    public ResultVO saveUser1(@RequestBody JSONObject jsonObject){
        String username=jsonObject.getString("username");
        String password=jsonObject.getString("password");
        User user=new User();
        user.setUsername(username);
        user.setPassword(password);
        int addNum=userMapper.insert(user);
        Map<String,Object>res=new HashMap<>();
        res.put("addNum",addNum);
        res.put("id",user.getId());
        return ResultVOUtil.success(res);
    }
}

创建一个SpringBoot项目(注意,这个项目和上一个案例不是同一个项目),导入下列依赖:

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!--lombok-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<!--fastjson-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.51</version>
		</dependency>
		<!--httpclient-->
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.5.13</version>
		</dependency>

测试Get无参请求

@Test
	public void doGetTest1(){
		//Http客户端
		CloseableHttpClient httpClient= HttpClientBuilder.create().build();
		//get请求
		HttpGet httpGet=new HttpGet("http://localhost:8080/getUserList1");
		//响应模型
		CloseableHttpResponse response=null;
		try{
			response=httpClient.execute(httpGet);
			if(response!=null){
				System.out.println("响应状态:"+response.getStatusLine());
				HttpEntity responseEntity=response.getEntity();
				System.out.println("响应内容长度:"+responseEntity.getContentLength());
				System.out.println("响应内容:"+ EntityUtils.toString(responseEntity));
			}
		}catch (ClientProtocolException e){
			e.printStackTrace();
		}catch (ParseException e){
			e.printStackTrace();
		}catch (IOException e){
			e.printStackTrace();
		}finally {
			try{
				if(httpClient!=null){
					httpClient.close();
				}
				if(response!=null){
					response.close();
				}
			}catch (IOException e){
				e.printStackTrace();
			}
		}
	}

在这里插入图片描述
测试get有参请求

@Test
	public void doGetTest2(){
		//获取http客户端
		CloseableHttpClient httpClient=HttpClientBuilder.create().build();
		CloseableHttpResponse response=null;
		//参数
		StringBuffer param=new StringBuffer();
		try{
			//对字符编码
			param.append("id="+ URLEncoder.encode("1","utf-8"));
		}catch (UnsupportedEncodingException e){
			e.printStackTrace();
		}
		//get请求
		HttpGet httpGet=new HttpGet("http://localhost:8080/getUserById?"+param);
		try{
			//配置
			RequestConfig requestConfig=RequestConfig.custom()
					.setConnectTimeout(5000)
					.setConnectionRequestTimeout(5000)
					.setSocketTimeout(5000)
					.build();
			httpGet.setConfig(requestConfig);
			response=httpClient.execute(httpGet);
			System.out.println("响应状态:"+response.getStatusLine());
			HttpEntity responseEntity=response.getEntity();
			if(responseEntity!=null){
				System.out.println("响应内容长度:"+responseEntity.getContentLength());
				System.out.println("响应内容:"+EntityUtils.toString(responseEntity));
			}
		}catch (ClientProtocolException e){
			e.printStackTrace();
		}catch (ParseException e){
			e.printStackTrace();
		}catch (IOException e){
			e.printStackTrace();
		}finally {
			try{
				if(httpClient!=null){
					httpClient.close();
				}
				if(response!=null){
					response.close();
				}
			}catch (Exception e){
				e.printStackTrace();
			}
		}
	}

在这里插入图片描述
测试post无参请求

@Test
	public void doPostTest1(){
		CloseableHttpClient httpClient=HttpClientBuilder.create().build();
		HttpPost httpPost=new HttpPost("http://localhost:8080/getUserList2");
		CloseableHttpResponse response=null;
		try{
			response=httpClient.execute(httpPost);
			HttpEntity httpEntity =response.getEntity();
			System.out.println("响应状态:"+response.getStatusLine());
			if(httpEntity!=null){
				System.out.println("响应内容长度:"+httpEntity.getContentLength());
				System.out.println("响应内容:"+EntityUtils.toString(httpEntity));
			}
		}catch (ClientProtocolException e){
			e.printStackTrace();
		}catch (ParseException e){
			e.printStackTrace();
		}catch (IOException e){
			e.printStackTrace();
		}finally {
			try{
				if(httpClient!=null){
					httpClient.close();
				}
				if(response!=null){
					response.close();
				}
			}catch (IOException e){
				e.printStackTrace();
			}
		}
	}

在这里插入图片描述
测试POST有参请求

@Test
	public void doPostTest2(){
		CloseableHttpClient httpClient=HttpClientBuilder.create().build();
		StringBuffer param=new StringBuffer();
		try{
			param.append("pageNum="+URLEncoder.encode("1","utf-8"));
			param.append("&");
			param.append("pageSize=4");
		}catch (UnsupportedEncodingException e){
			e.printStackTrace();
		}
		HttpPost httpPost=new HttpPost("http://localhost:8080/getUserList?"+param);
		httpPost.setHeader("Content-Type","application/json;charset=utf8");
		CloseableHttpResponse response=null;
		try{
			response=httpClient.execute(httpPost);
			HttpEntity httpEntity=response.getEntity();
			System.out.println("响应状态:"+response.getStatusLine());
			if(httpEntity!=null){
				System.out.println("响应内容长度:"+httpEntity.getContentLength());
				System.out.println("响应内容:"+EntityUtils.toString(httpEntity));
			}
		}catch (ClientProtocolException e){
			e.printStackTrace();
		}catch (ParseException e){
			e.printStackTrace();
		}catch (IOException e){
			e.printStackTrace();
		}finally {
			try{
				if(httpClient!=null){
					httpClient.close();
				}
				if(response!=null){
					response.close();
				}
			}catch (IOException e){
				e.printStackTrace();
			}
		}
	}

在这里插入图片描述
测试post请求体请求

@Test
	public void doPostTest3(){
		CloseableHttpClient httpClient=HttpClientBuilder.create().build();
		JSONObject jsonObject=new JSONObject();
		jsonObject.put("username","hahaha");
		jsonObject.put("password","115116");
		String json= jsonObject.toString();
		HttpPost httpPost=new HttpPost("http://localhost:8080/saveUser1");
		CloseableHttpResponse response=null;
		try{
			StringEntity entity=new StringEntity(json,"UTF-8");
			httpPost.setEntity(entity);
			httpPost.setHeader("Content-Type","application/json;charset=utf8");
			response=httpClient.execute(httpPost);
			System.out.println("响应状态:"+response.getStatusLine());
			HttpEntity responseEntity=response.getEntity();
			if(responseEntity!=null){
				System.out.println("响应内容长度为:"+responseEntity.getContentLength());
				System.out.println("响应内容:"+EntityUtils.toString(responseEntity));
			}
		}catch (ClientProtocolException e){
			e.printStackTrace();
		}catch (ParseException e){
			e.printStackTrace();
		}catch (IOException e){
			e.printStackTrace();
		}finally {
			try{
				if(httpClient!=null){
					httpClient.close();
				}
				if(response!=null){
					response.close();
				}
			}catch (Exception e){
				e.printStackTrace();
			}
		}
	}

在这里插入图片描述
接下来我们将刚才的请求过程封装为一个工具类
先创建一个接收类:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class HttpClientResult implements Serializable {
    public HttpClientResult(Integer code){
        this.code=code;
        this.content=null;
    }
    //响应状态码
    private Integer code;
    //响应数据
    private String content;
}

HttpClientUtil.java工具类:

public class HttpClientUtil {
    private static final String ENCODING="UTF-8";
    private static final int CONNECT_TIMEOUT=5000;
    private static final int CONNECT_REQUEST_TIMEOUT=5000;
    private static final int SOCKET_TIMEOUT=5000;
    public static HttpClientResult doGet(String url)throws Exception{
        return doGet(url,null,null);
    }
    public static HttpClientResult doGet(String url, Map<String,String>params)throws Exception{
        return doGet(url,null,params);
    }
    public static HttpClientResult doGet(String url,Map<String,String>headers,Map<String,String>params)throws Exception{
        //创建httpClient
        CloseableHttpClient httpClient= HttpClientBuilder.create().build();
        //创建访问地址
        URIBuilder uriBuilder=new URIBuilder(url);
        params.forEach((k,v)->uriBuilder.setParameter(k,v));
        //创建http对象
        HttpGet httpGet=new HttpGet(uriBuilder.build());
        //配置
        RequestConfig requestConfig=RequestConfig.custom()
                .setConnectTimeout(CONNECT_TIMEOUT)
                .setConnectionRequestTimeout(CONNECT_REQUEST_TIMEOUT)
                .setSocketTimeout(SOCKET_TIMEOUT)
                .build();
        httpGet.setConfig(requestConfig);
        //设置请求头
        packageHeader(headers,httpGet);
        //创建httpResponse对象
        CloseableHttpResponse httpResponse=null;
        try{
            //执行请求
            return getHttpClientResult(httpResponse,httpClient,httpGet);
        }finally {
            //释放资源
            release(httpResponse,httpClient);
        }
    }
    public static HttpClientResult doPost(String url)throws Exception{
        return doPost(url,null,null);
    }
    public static HttpClientResult doPost(String url,Map<String,String>params)throws Exception{
        return doPost(url,null,params);
    }
    public static HttpClientResult doPost(String url,Map<String,String>headers,Map<String,String>params)throws Exception{
        //创建httpClient
        CloseableHttpClient httpClient=HttpClientBuilder.create().build();
        //创建http对象
        HttpPost httpPost=new HttpPost(url);
        RequestConfig requestConfig=RequestConfig
                .custom()
                .setConnectTimeout(CONNECT_TIMEOUT)
                .setConnectionRequestTimeout(CONNECT_REQUEST_TIMEOUT)
                .setSocketTimeout(SOCKET_TIMEOUT)
                .build();
        httpPost.setConfig(requestConfig);
        //设置请求头
        packageHeader(headers,httpPost);
        //封装请求参数
        if(params!=null){
            List<NameValuePair>list=new ArrayList<>();
            params.forEach((k,v)->list.add(new BasicNameValuePair(k,v)));
            httpPost.setEntity(new UrlEncodedFormEntity(list,ENCODING));
        }
        //响应结果
        CloseableHttpResponse httpResponse=null;
        try{
            return getHttpClientResult(httpResponse,httpClient,httpPost);
        }finally {
            release(httpResponse,httpClient);
        }
    }
    //封装头部
    public static void packageHeader(Map<String,String>headers, HttpRequestBase httpMethod){
        if(headers!=null){
            headers.forEach((k,v)->httpMethod.setHeader(k,v));
        }
    }
    //获取响应结果
    public static HttpClientResult getHttpClientResult(CloseableHttpResponse httpResponse,
                                                       CloseableHttpClient httpClient,
                                                       HttpRequestBase httpMethod)throws Exception{
        httpResponse=httpClient.execute(httpMethod);
        if(httpResponse!=null&&httpResponse.getStatusLine()!=null){
            String content="";
            if(httpResponse.getEntity()!=null){
                content= EntityUtils.toString(httpResponse.getEntity(),ENCODING);
            }
            return new HttpClientResult(httpResponse.getStatusLine().getStatusCode(),content);
        }
        return new HttpClientResult(HttpStatus.SC_INTERNAL_SERVER_ERROR);
    }
    public static void release(CloseableHttpResponse httpResponse,CloseableHttpClient httpClient)throws Exception{
        if(httpResponse!=null){
            httpResponse.close();
        }
        if(httpClient!=null){
            httpClient.close();
        }
    }
}

测试无参get

@Test
	public void doGet(){
		try{
			HttpClientResult clientResult= HttpClientUtil.doGet("http://localhost:8080/getUserList1");
			System.out.println(JSON.toJSONString(clientResult));
		}catch (Exception e){
			e.printStackTrace();
		}
	}

在这里插入图片描述
测试有参get

@Test
	public void doGet1(){
		try{
			Map<String,String>param=new HashMap<>();
			param.put("pageNum","1");
			param.put("pageSize","2");
			HttpClientResult clientResult= HttpClientUtil.doGet("http://localhost:8080/getUserList",param);
			System.out.println(JSON.toJSONString(clientResult));
		}catch (Exception e){
			e.printStackTrace();
		}
	}

在这里插入图片描述
参考文章:
HttpClient信息使用示例

13.WebService

pom.xml依赖:

<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-frontend-jaxws</artifactId>
			<version>3.5.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-transports-http</artifactId>
			<version>3.5.3</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-bindings-soap</artifactId>
			<version>3.5.3</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.51</version>
		</dependency>

实体类
User.java

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String username;
    private String password;
}

Admin.java

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Admin {
    private Integer id;
    private String name;
}

service层
AdminService.java

@WebService
public interface AdminService {
    @WebMethod
    String getAdminList();
}

AdminServiceImpl.java

@Component
@WebService(targetNamespace = "http://service.demo15.young.com/", endpointInterface = "com.young.demo15.service.AdminService")
public class AdminServiceImpl implements AdminService {
    @Override
    public String getAdminList() {
        List<Admin>list=new ArrayList<>();
        list.add(new Admin(1,"admin1"));
        list.add(new Admin(2,"admin2"));
        return JSON.toJSONString(list);
    }
}

UserService.java

@WebService
public interface UserService {
    @WebMethod
    String getUserList();
}

UserServiceImpl.java

@Component
@WebService(targetNamespace = "http://service.demo15.young.com/",endpointInterface = "com.young.demo15.service.UserService")
public class UserServiceImpl implements UserService {
    @Override
    public String getUserList() {
        List<User>list=new ArrayList<>();
        list.add(new User(1,"user1","pwd1"));
        list.add(new User(2,"user2","pwd2"));
        list.add(new User(3,"user3","pwd3"));
        return JSONObject.toJSONString(list);
    }
}

配置类,WebServiceConfig.java

@Configuration
public class WebServiceConfig {
    @Bean(name = SpringBus.DEFAULT_BUS_ID)
    public SpringBus springBus(){
        return new SpringBus();
    }
    @Bean(name = "wbsBean")
    public ServletRegistrationBean registrationBean(){
        ServletRegistrationBean registrationBean=new ServletRegistrationBean<>(new CXFServlet(),"/webService/*");
        return registrationBean;
    }
    @Bean
    public Endpoint endpoint1(SpringBus springBus, UserService userService){
        EndpointImpl endpoint=new EndpointImpl(springBus,userService);
        endpoint.publish("/userServer");
        System.out.println("http://localhost:8080/webService/userServer?wsdl");
        return endpoint;
    }
    @Bean
    public Endpoint endpoint2(SpringBus springBus, AdminService adminService){
        EndpointImpl endpoint=new EndpointImpl(springBus,adminService);
        endpoint.publish("/adminServer");
        System.out.println("http://localhost:8080/webService/adminServer?wsdl");
        return endpoint;
    }
}

工具类ClientUtil.java

public class ClientUtil {
    public static String getResult(String wsdUrl,String methodName,String...params)throws Exception{
        JaxWsDynamicClientFactory proxyFactoryBean=JaxWsDynamicClientFactory.newInstance();
        Client client=proxyFactoryBean.createClient(wsdUrl);
        Object[]objects;
        if(params==null||params.length==0){
            objects=client.invoke(methodName);
        }else{
            objects=client.invoke(methodName,params);
        }
        return JSONObject.toJSONString(objects);
    }
}

TestController.java

@RestController
public class TestController {
    @RequestMapping("/getUserList")
    public String getUserList(){
        String wsdUrl="http://localhost:8080/webService/userServer?wsdl";
        String methodName="getUserList";
        try{
            return ClientUtil.getResult(wsdUrl,methodName,null);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
    @RequestMapping("/getAdminList")
    public String getAdminList(){
        String wsdUrl="http://localhost:8080/webService/adminServer?wsdl";
        String methodName="getAdminList";
        try{
            return ClientUtil.getResult(wsdUrl,methodName,null);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
}

运行项目
访问http://localhost:8080/webService/userServer?wsdl
在这里插入图片描述
访问http://localhost:8080/webService/adminServer?wsdl
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
参考文章聊聊日常开发中,如何对接WebService

14.WebSocket

创建一个SpringBoot项目,引入下列依赖:

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!--lombok-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<!--fastjson-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.51</version>
		</dependency>
		<!--websocket-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-websocket</artifactId>
		</dependency>

websocketConfig.java

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

MyWebSocket.java

@Component
@ServerEndpoint(value = "/webSocket/{userId}")
@Slf4j
public class MyWebSocket {
    private Session session;
    private String userId;
    private static int onlineCount=0;
    private static ConcurrentMap<String,MyWebSocket>map=new ConcurrentHashMap<>();
    @OnOpen
    public void onOpen(Session session,@PathParam(value = "userId") String userId){
        if(map.containsKey(userId)){
            log.info("用户{}已在线",userId);
        }else{
            this.session=session;
            this.userId=userId;
            map.put(userId,this);
            addOnlineCount();
            log.info("用户{}进入连接,当前人数:{}",userId,getOnlineCount());
        }
    }
    @OnClose
    public void onClose(){
        if(map.containsKey(this.userId)){
            map.remove(this.userId);
            subOnlineCount();
            log.info("用户{}退出连接,当前人数:{}",this.userId,getOnlineCount());
        }
    }
    @OnError
    public void onError(Session session,Throwable throwable){
        log.info("用户{}发送错误{}",this.userId,throwable.getMessage());
    }
    @OnMessage
    public void onMessage(Session session,String message){
        log.info("来自客户端用户{}的消息{}",this.userId,message);
    }
    public void sendMessage(String message)throws Exception{
        this.session.getBasicRemote().sendText(message);
    }
    public void sendMessageTo(String userId,String message)throws Exception{
        if (map.containsKey(userId)) {
            log.info("发送消息{} 给用户{}",message,userId);
            map.get(userId).sendMessage(message);
        }
    }
    public void sendMessageAll(String message)throws Exception{
        log.info("群发信息{}",message);
        for(String id:map.keySet()){
            map.get(id).sendMessage(message);
        }
    }
    public void sendMessageByJson(JSONObject jsonObject)throws Exception{
        String fromId=jsonObject.getString("fromId");
        String toId=jsonObject.getString("toId");
        String message=jsonObject.getString("message");
        message=fromId+":"+message;
        this.sendMessageTo(toId,message);
    }
    public synchronized static void addOnlineCount(){
        onlineCount++;
    }
    public synchronized static void subOnlineCount(){
        onlineCount--;
    }
    public synchronized static int getOnlineCount(){
        return onlineCount;
    }
}

WebSocketController.java

@RestController
public class WebSocketController {
    @Autowired
    private MyWebSocket myWebSocket;

    @RequestMapping(value = "/sendMessageToId")
    public void sendMessageToId(@RequestParam(value = "userId", required = true) String userId,
                                @RequestParam(value = "message", required = true) String message) throws Exception {
        myWebSocket.sendMessageTo(userId, message);
    }
    @RequestMapping(value = "/sendMessageToAll")
    public void sendMessageAll(@RequestParam(value = "message", required = true) String message) throws Exception {
        myWebSocket.sendMessageAll(message);
    }
    @RequestMapping(value = "/sendMessage")
    public void sendMessage(@RequestBody JSONObject jsonObject)throws Exception{
        myWebSocket.sendMessageByJson(jsonObject);
    }
}

运行项目,搜索http://websocket.jsonin.com/
输入ws://localhost:8080/webSocket/用户
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
参考文章:SpringBoot整合WebSocket
SpringBoot整合websocket

15.Shiro安全框架

创建一个SpringBoot项目,引入依赖:

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!--thymeleaf-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<!--thymeleaf-shiro-->
		<dependency>
			<groupId>com.github.theborakompanioni</groupId>
			<artifactId>thymeleaf-extras-shiro</artifactId>
			<version>2.1.0</version>
		</dependency>
		<!--shiro-->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring-boot-starter</artifactId>
			<version>1.5.3</version>
		</dependency>
		<!--mysql-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<!--mybatis-plus-->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.5.2</version>
		</dependency>
		<!--shiro-cache-->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-ehcache</artifactId>
			<version>1.5.3</version>
		</dependency>
		<!--lombok-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<!--fastjson-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.51</version>
		</dependency>

实体类:Role.java

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "t_role")
public class Role implements Serializable {
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String name;
}

User.java

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "t_user")
public class User implements Serializable {
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String username;
    private String password;
    private String salt;
    private Integer age;
    private String email;
    private String address;
    @TableField(exist = false)
    private List<Role>roles;
}

UserRoleId.java

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "t_user_role")
public class UserRoleId {
    @TableId(type = IdType.AUTO)
    private Integer id;
    @TableField(value ="user_id")
    private Integer userId;
    @TableField(value = "role_id")
    private Integer roleId;
}

UserMapper.java

@Mapper
public interface UserMapper extends BaseMapper<User> {
    @Select("select * from t_user where username=#{username}")
    User findUserByUsername(String username);
}

RoleMapper.java

@Mapper
public interface RoleMapper extends BaseMapper<Role> {
    @Select("select r.id,r.name from t_role r left join t_user_role ur on r.id=ur.role_id where ur.user_id=#{userId}")
    List<Role>getRoleByUserId(Integer id);
}

UserRoleIdMapper.java

@Mapper
public interface UserRoleIdMapper extends BaseMapper<UserRoleId> {
}

UserServic.java

public interface UserService {
    int register(User user);
    User findUserByUsername(String username);
}

UserServiceImpl.java

@Service("userService")
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserRoleIdMapper userRoleIdMapper;
    @Override
    public int register(User user) {
        String salt= SaltUtil.getSalt(ShiroConstant.SHIRO_LENGTH);
        Md5Hash md5Hash=new Md5Hash(user.getPassword(),salt,ShiroConstant.SHIRO_ITERATORS);
        String password=md5Hash.toHex();
        user.setSalt(salt);
        user.setPassword(password);
        //添加用户
        userMapper.insert(user);
        //添加权限
        UserRoleId userRoleId=new UserRoleId();
        userRoleId.setUserId(user.getId());
        userRoleId.setRoleId(2);
        userRoleIdMapper.insert(userRoleId);
        return 1;
    }
    @Override
    public User findUserByUsername(String username) {
        return userMapper.findUserByUsername(username);
    }
}

RoleService.java

public interface RoleService {
    List<Role>getRolesByUserId(Integer userId);
}

RoleServiceImpl.java

@Service("roleService")
public class RoleServiceImpl implements RoleService {
    @Autowired
    private RoleMapper roleMapper;
    @Override
    public List<Role> getRolesByUserId(Integer userId) {
        return roleMapper.getRoleByUserId(userId);
    }
}

SaltUtil.java 生成加密盐的工具类

public class SaltUtil {
    public static String getSalt(int n){
        char[]chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890".toCharArray();
        StringBuffer sf=new StringBuffer();
        for(int i=0;i<n;i++){
            int j=new Random().nextInt(chars.length);
            sf.append(chars[j]);
        }
        return sf.toString();
    }
}

CustomerByteSource.java,将其他类型转换为byte类型

public class CustomerByteSource implements ByteSource {
    private byte[]bytes;
    public CustomerByteSource(byte[]bytes){
        this.bytes=bytes;
    }
    public CustomerByteSource(char[]chars){
        this.bytes= CodecSupport.toBytes(chars);
    }
    public CustomerByteSource(String str){
        this.bytes=CodecSupport.toBytes(str);
    }
    public CustomerByteSource(ByteSource byteSource){
        this.bytes=byteSource.getBytes();
    }
    @Override
    public byte[] getBytes() {
        return this.bytes;
    }
    @Override
    public String toHex() {
        return Hex.encodeToString(bytes);
    }
    @Override
    public String toBase64() {
        return Base64.encodeToString(bytes);
    }
    @Override
    public boolean isEmpty() {
        return bytes==null||bytes.length==0;
    }
}

ApplicationContextUtil.java

@Component
public class ApplicationContextUtil implements ApplicationContextAware {
    private static ApplicationContext context;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context=applicationContext;
    }
    public static Object getBean(String beanName){
        return context.getBean(beanName);
    }
}

ShiroConstant.java,定义一些常量

public class ShiroConstant {
    public final static int SHIRO_ITERATORS=1024;
    public final static String SHIRO_ALGORITHM="MD5";
    public final static int SHIRO_LENGTH=8;
}

CustomerRealm.java自定义realm

public class CustomerRealm extends AuthorizingRealm {
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String principal=(String)principalCollection.getPrimaryPrincipal();
        UserService userService=(UserService) ApplicationContextUtil.getBean("userService");
        User user=userService.findUserByUsername(principal);
        if(!Objects.isNull(user)){
            RoleService roleService=(RoleService) ApplicationContextUtil.getBean("roleService");
            List<Role>roles=roleService.getRolesByUserId(user.getId());
            SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
            roles.forEach((role)->{
                authorizationInfo.addRole(role.getName());
            });
            return authorizationInfo;
        }
        return null;
    }
    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String principal=(String) authenticationToken.getPrincipal();
        //获取用户
        UserService userService=(UserService) ApplicationContextUtil.getBean("userService");
        User user=userService.findUserByUsername(principal);
        if(user==null){
            return null;
        }
        return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),new CustomerByteSource(user.getSalt()),this.getName());
    }
}

ShiroConfiguration.java shiro的配置类

@Configuration
public class ShiroConfiguration {
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean filterFactoryBean=new ShiroFilterFactoryBean();
        filterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        Map<String,String>map=new HashMap<>();
        //设置受限资源和开放资源
        map.put("/index","authc");
        map.put("/login","anon");
        map.put("/register","anon");
        filterFactoryBean.setLoginUrl("/login");
        filterFactoryBean.setFilterChainDefinitionMap(map);
        return filterFactoryBean;
    }
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(Realm realm){
        DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }
    @Bean
    public Realm realm(){
        CustomerRealm customerRealm=new CustomerRealm();
        //设置加密管理器
        HashedCredentialsMatcher credentialsMatcher=new HashedCredentialsMatcher();
        //设置迭代次数
        credentialsMatcher.setHashIterations(ShiroConstant.SHIRO_ITERATORS);
        //设置加密算法
        credentialsMatcher.setHashAlgorithmName(ShiroConstant.SHIRO_ALGORITHM);
        customerRealm.setCredentialsMatcher(credentialsMatcher);
        //设置缓存管理器
        customerRealm.setCacheManager(new EhCacheManager());
        //开启全局缓存
        customerRealm.setCachingEnabled(true);
        customerRealm.setAuthorizationCachingEnabled(true);
        customerRealm.setAuthenticationCachingEnabled(true);
        customerRealm.setAuthorizationCacheName("authorizationCache");
        customerRealm.setAuthenticationCacheName("authenticationCache");
        return customerRealm;
    }
    //解决shiro标签在thymeleaf失效的问题
    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }
}

UserController.java

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    @RequestMapping("/login")
    public ModelAndView login(User user){
        ModelAndView modelAndView=new ModelAndView();
        try{
            //获取主体
            Subject subject= SecurityUtils.getSubject();
            //设置令牌
            UsernamePasswordToken token=new UsernamePasswordToken(user.getUsername(),user.getPassword());
            //登录
            subject.login(token);
            //登录成功,跳转到主页
            modelAndView.setViewName("index");
            modelAndView.addObject("user",user);
        }catch (UnknownAccountException e){
            e.printStackTrace();
            System.out.println("用户名错误");
            modelAndView.setViewName("redirect:/login");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("密码错误");
            modelAndView.setViewName("redirect:/login");
        }catch (Exception e){
            e.printStackTrace();
            modelAndView.setViewName("redirect:/login");
        }
        return modelAndView;
    }
    @RequestMapping("/logout")
    public ModelAndView logout(){
        Subject subject=SecurityUtils.getSubject();
        subject.logout();
        ModelAndView modelAndView=new ModelAndView();
        modelAndView.setViewName("redirect:/login");
        return modelAndView;
    }
    @RequestMapping("/register")
    public ModelAndView register(User user){
        ModelAndView modelAndView=new ModelAndView();
        try{
            userService.register(user);
            modelAndView.setViewName("redirect:/login");
        }catch (Exception e){
            e.printStackTrace();
            modelAndView.setViewName("redirect:/register");
        }
        return modelAndView;
    }
}

LoginController.java

@Controller
public class LoginController {
    @RequestMapping("index")
    public String index(){
        return "index";
    }
    @RequestMapping("login")
    public String login(){
        return "login";
    }
    @RequestMapping("register")
    public String register(){
        return "register";
    }
}

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8">
    <title>主页</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <style>
        #bodyLeft{
            width:20%;
            border-right:1px solid gray;
            float:left;
            height:100%;
            margin-left:5%;
        }
        #bodyRight{
            width:65%;
             float:right;
             margin-right:5%;
             height:100%;
        }
        #foot{
            width:100%;
            height:100px;
            font-size:12px;
            position:absolute;
            bottom:0px;
            left:0px;
            background:#333;
        }
        #foot ul{
            margin-left:20px;
        }
        #foot li{
            color:white;
        }
    </style>
</head>
<body>
<div id="bodyLeft">
    <h1>管理</h1>
    <ul>
        <li><a th:href="@{/user/login}">退出登录</a></li>
        <shiro:hasRole name="admin">
            <li>
                <a href="">用户管理</a>
            </li>
            <li>
                <a href="">图书管理</a>
            </li>
        </shiro:hasRole>
        <shiro:hasAnyRoles name="admin,user">
            <li><a href="">图书借阅</a></li>
            <li><a href="">我的借阅</a></li>
        </shiro:hasAnyRoles>
    </ul>
</div>
<div id="foot">
    <ul>
        <li>@CopyRight</li>
        <li>版权所有:Young</li>
        <li>2022-5-25</li>
    </ul>
</div>
</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>login</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
<div class="container">
    <form th:action="@{/user/login}" method="post">
        <label for="username">
            用户名<input type="text" id="username" name="username" placeholder="请输入用户名"/>
        </label>
        <label for="password">
            密码<input type="password" id="password" name="password" placeholder="请输入密码"/>
        </label>
        <input type="submit" value="登录"/>
    </form>
    <a th:href="@{/register}" style="float:right">注册--></a>
</div>
</body>
</html>

register.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>注册</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
<div class="container">
    <form th:action="@{/user/register}" method="post">
        <label for="username">
            用户名<input type="text" id="username" name="username"/>
        </label>
        <label for="password">
            密码<input type="password" id="password" name="password"/>
        </label>
        <input type="submit" value="注册"/>
    </form>
    <a th:href="@{/login}" style="float:left;"><--返回</a>
</div>
</body>
</html>

application.yml

spring:
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/young?useSSL=false&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
server:
  port: 8081

数据库内容如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
运行项目:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
参考文章:一篇适合小白的Shiro教程

16.SpringSecurity安全框架

创建项目,引入依赖:

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!--lombok-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<!--mysql-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<!--mybatis-plus-->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.5.2</version>
		</dependency>
		<!--security-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<!--json-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.51</version>
		</dependency>

创建控制器

@Controller
public class MyController {
    @RequestMapping("/hello")
    public String hello(){
        return "redirect:/hello.html";
    }
}

hello.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h3>Hello Spring Security</h3>
</body>
</html>

配置application.yml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/young?useSSL=false&serverTimezone=UTC
    username: root
    password: 1q2w3e
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
  mapper-locations: mapper/*.xml

运行项目,在浏览器中输入网址:http://localhost:8080/hello
在这里插入图片描述
username为user,password为控制台的密码
在这里插入图片描述
输入用户和密码后,跳转到如下页面
在这里插入图片描述
接下来我们自定义用户和权限
创建一个配置类SecurityConfig

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http)throws Exception{
        http.authorizeRequests()
                //设置相应的访问权限
                .antMatchers("/admin/*").hasRole("ADMIN")
                .antMatchers("/user/*").hasAnyRole("ADMIN","USER")
                .anyRequest().authenticated()
                .and()
                .formLogin()//表单登录
                .and()
                .httpBasic();

    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth)throws Exception{
        auth.inMemoryAuthentication()
                .withUser("admin")
                //noop表示密码没有加密
                .password("{noop}adminpass")
                .roles("ADMIN","USER")
                .and()
                .withUser("spring")
                .password("{noop}123456")
                .roles("USER");
    }
}

修改控制器

@Controller
public class MyController {
    @RequestMapping("/hello")
    public String hello(){
        return "redirect:/hello.html";
    }
    @RequestMapping("/admin/home")
    @ResponseBody
    public String adminHome(){
        return "this is admin home";
    }
    @RequestMapping("/user/info")
    @ResponseBody
    public String userInfo(){
        String username="";
        Object principal=SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if(principal instanceof UserDetails){
            username=((UserDetails)principal).getUsername();
        }else{
            username=principal.toString();
        }
        return "this is user info:"+username;
    }
}

运行项目
在浏览器输入http://localhost:8080/admin/home,将spring用户的密码输入
在这里插入图片描述
访问失败,因为spring没有admin权限,输入http://localhost:8080/user/info
在这里插入图片描述
访问成功,我们关闭浏览器,然后使用admin用户进行登录
在这里插入图片描述
在这里插入图片描述
接下来我们自定义登录页面
修改配置类SecurityConfig.java

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http)throws Exception{
        //表单提交
        http.formLogin()
                .loginPage("/login.html")
                 .usernameParameter("username")
                .passwordParameter("password")
                 .loginProcessingUrl("/login")                                                   .successForwardUrl("/toMain")                                        .failureForwardUrl("/toError");
        http.authorizeRequests()
                //login.html不需要被认证
                .antMatchers("/login.html").permitAll()
                //error.html不需要被认证
                .antMatchers("/error.html").permitAll()
                        .anyRequest().authenticated();
        //关闭CSRF防护
        http.csrf().disable();
    }
    @Bean
    public PasswordEncoder getPW(){
        return new BCryptPasswordEncoder();
    }
}

修改控制类

@Controller
public class MyController {
    @RequestMapping("/toMain")
    public String toMain(){
        return "redirect:/main.html";
    }
    @RequestMapping("/toError")
    public String error(){
        return "redirect:/error.html";
    }
}

添加UserLoginServiceImpl类,实现UserDetailsService接口

@Service
public class UserLoginServiceImpl implements UserDetailsService {
    @Autowired
    private PasswordEncoder pw;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    //模拟根据用户名获取对应的数据库信息
        if(!"admin".equals(username)){
            throw new UsernameNotFoundException("用户名不存在");
        }
        String password=pw.encode("123456");
        return new User(username,password,
                AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal"));
    }
}

在这里插入图片描述
输入错误的密码或用户名后,跳转到失败页面
在这里插入图片描述
重新输入正确用户名和密码
在这里插入图片描述
接下来讲解根据数据库获取用户信息进行登录
编写一个测试方法,用来获取加密后的密码

@SpringBootTest
class Demo08ApplicationTests {
	@Test
	public void test(){
		PasswordEncoder pw=new BCryptPasswordEncoder();
		String encode=pw.encode("123456");
		System.out.println(encode);
		boolean matches=pw.matches("123456",encode);
		System.out.println("=========="+matches);
	}
	@Test
	void contextLoads() {
	}
}

在这里插入图片描述
数据库表
在这里插入图片描述
在这里插入图片描述

实体类UserEntity.java

@TableName(value = "user")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserEntity implements Serializable {
    @TableId(type = IdType.AUTO)
    private Integer id;
    @TableField(value = "username")
    private String username;
    @TableField(value = "password")
    private String password;
    @TableField(value = "role")
    private String role;
}

创建对应的dao(mapper)层
UserEntityMapper.java,继承BaseMapper

public interface UserEntityMapper extends BaseMapper<UserEntity> {
}

然后在启动类添加扫描,扫描mapper所在的包

@SpringBootApplication
@MapperScan("com.young.demo08.mapper")
public class Demo08Application {

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

这里发现一个问题,我扫描后但是mapper仍然注入失败,所以我创建一个工具类
ApplicationContextUtil.java

@Component
public class ApplicationContextUtil implements ApplicationContextAware {
    private static ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }
    public static Object getBean(String beanName){
        return applicationContext.getBean(beanName);
    }
}

修改UserLoginServiceImpl.java

@Service
public class UserLoginServiceImpl implements UserDetailsService {
    @Autowired
    private PasswordEncoder pw;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserEntityMapper userEntityMapper=(UserEntityMapper) ApplicationContextUtil.getBean("userEntityMapper");
        QueryWrapper<UserEntity>wrapper=new QueryWrapper<>();
        wrapper.eq("username",username);
        UserEntity userEntity=userEntityMapper.selectOne(wrapper);
        if(Objects.isNull(userEntity)){
            throw new UsernameNotFoundException("用户不存在");
        }
        return new User(username,userEntity.getPassword(),
                AuthorityUtils.commaSeparatedStringToAuthorityList(userEntity.getRole()));
    }
}

修改控制类

@Controller
public class MyController {
    @RequestMapping("/toMain")
    public String toMain(){
        return "redirect:/main.html";
    }
    @RequestMapping("/toError")
    public String error(){
        return "redirect:/error.html";
    }
    @RequestMapping("/admin/home")
    @ResponseBody
    public String adminHome(){
        return "this is admin home";
    }
    @RequestMapping("/user/info")
    @ResponseBody
    public String userInfo(){
        String username="";
        Object principal=SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if(principal instanceof UserDetails){
            username=((UserDetails)principal).getUsername();
        }else{
            username=principal.toString();
        }
        return "this is user info:"+username;
    }
}

修改SecurityConfig.java

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http)throws Exception{
        //表单提交
        http.formLogin()
                .loginPage("/login.html")
                .usernameParameter("username")
                .passwordParameter("password")
                        .loginProcessingUrl("/login")
                                .successForwardUrl("/toMain")
                                        .failureForwardUrl("/toError");
        http.authorizeRequests()
                //login.html不需要被认证
                .antMatchers("/login.html").permitAll()
                //error.html不需要被认证
                .antMatchers("/error.html").permitAll()
                //认证
                .antMatchers("/admin/**").hasAuthority("ADMIN")
                .antMatchers("/user/**").hasAnyAuthority("ADMIN","USER")
                        .anyRequest().authenticated();
        //关闭CSRF防护
        http.csrf().disable();
    }
    @Bean
    public PasswordEncoder getPW(){
        return new BCryptPasswordEncoder();
    }
}

运行项目,输入admin及其密码

登录成功
在这里插入图片描述
点击admin/home页面
在这里插入图片描述
点击user/info页面
在这里插入图片描述
我们使用spring重新登录
登录成功后,点击admin/home页面
在这里插入图片描述
点击user/info页面
在这里插入图片描述
接下来我们整合JWT实现前后端分离
我们重新创建一个springboot项目,引入下列依赖

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</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>
		<!--json-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.51</version>
		</dependency>
		<!--mysql-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<!--mybatis-->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.2.2</version>
		</dependency>
		<!--security-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<!--jdbc-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<!--jwt-->
		<dependency>
			<groupId>com.auth0</groupId>
			<artifactId>java-jwt</artifactId>
			<version>3.19.2</version>
		</dependency>

application.yml

server:
  port: 8080
spring:
  datasource:
    username: root
    password: 1q2w3e
    url: jdbc:mysql://localhost:3306/young?useSSL=false&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
jwt:
  header: Authorization
  secret: 1q2w3e
  expire-time: 14400000

创建一个JwtProperties.java用来获取和jwt有关的配置信息

@Data
@Configuration
@ConfigurationProperties(prefix = "jwt")
public class JwtProperties {
    private String header;
    private String secret;
    private Integer expireTime;
}

一般情况下,我们要创建一个实体类来实现UserDetails接口,之前的操作没有实现UserDetails接口也可以,只是转换比较麻烦,现在我们创建一个实体类JwtUser

@Data
@AllArgsConstructor
public class JwtUser implements UserDetails {
    private String username;
    private String password;
    private Collection<? extends GrantedAuthority>grantedAuthorities;
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.grantedAuthorities;
    }
    @Override
    public String getPassword() {
        return this.password;
    }
    @Override
    public String getUsername() {
        return this.username;
    }
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return true;
    }
}

创建一个ResultVO.java用来保存返回结果

public class ResultVO <T>{
    private Integer code;
    private String msg;
    private T data;
    public ResultVO(){}
    public ResultVO(Integer code,String msg){
        this.code=code;
        this.msg=msg;
    }
    public ResultVO(Integer code,String msg,T data){
        this.code=code;
        this.msg=msg;
        this.data=data;
    }
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }
}

ResultVOUtil.java工具类

public class ResultVOUtil {
    public static ResultVO success(){
        return new ResultVO<>(200,"操作成功");
    }
    public static ResultVO success(Object data){
        return new ResultVO<>(200,"操作成功",data);
    }
    public static ResultVO error(Integer code,String msg){
        return new ResultVO<>(code,msg);
    }
}

JwtUtil.java工具类

@Component
public class JwtUtil {
    @Resource
    private JwtProperties jwtProperties;
    public String createToken(Map<String,String>payload){
        JWTCreator.Builder builder= JWT.create();
        Calendar calendar=Calendar.getInstance();
        calendar.add(Calendar.SECOND,jwtProperties.getExpireTime());
        builder.withIssuedAt(new Date());
        builder.withExpiresAt(calendar.getTime());
        payload.forEach((k,v)->builder.withClaim(k,v));
        return builder.sign(Algorithm.HMAC256(jwtProperties.getSecret()));
    }
    public Map<String,String>resolveToken(String token){
        if(token==null){
            return null;
        }
        JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(jwtProperties.getSecret())).build();
        DecodedJWT decodedJWT=jwtVerifier.verify(token);
        Map<String,Claim>map=decodedJWT.getClaims();
        Map<String,String>res=new HashMap<>();
        map.forEach((k,v)->res.put(k,v.asString()));
        return res;
    }
}

JwtUserDetailsService.java,实现UserDetailsService接口

@Service
public class JwtUserDetailsService implements UserDetailsService {
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        if(!username.equals("admin")){
            return null;
        }
        return new JwtUser("admin",passwordEncoder.encode("123456"),
                AuthorityUtils.commaSeparatedStringToAuthorityList("user"));
    }
}

JwtAccessDeniedHandler处理没有权限访问的情况

@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        ResultVO resultVO= ResultVOUtil.error(403,"没有权限访问");
        String json= JSON.toJSONString(resultVO);
        response.setContentType("text/json;charset=utf-8");
        response.getWriter().write(json);
    }
}

JwtAuthenticationEntryPoint.java处理缺少凭证的情况

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        ResultVO resultVO= ResultVOUtil.error(404,"缺少凭证");
        String json= JSON.toJSONString(resultVO);
        response.setContentType("text/json;charset=utf-8");
        response.getWriter().write(json);
    }
}

LoginFailHandler.java处理登录失败的情况

@Component
public class LoginFailHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        ResultVO resultVO=ResultVOUtil.error(500,"用户名或密码错误");
        String json= JSON.toJSONString(resultVO);
        response.setContentType("text/json;charset=utf-8");
        response.getWriter().write(json);
    }
}

JwtAuthenticationSuccessHandler.java处理登录成功的情况

@Component
public class JwtAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Resource
    private JwtUtil jwtUtil;
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        Object principal=authentication.getPrincipal();
        JwtUser jwtUser=(JwtUser) principal;
        String username=jwtUser.getUsername();
        String password=jwtUser.getPassword();
        Map<String,String>payload=new HashMap<>();
        payload.put("username",username);
        payload.put("password",password);
        String token=jwtUtil.createToken(payload);
        //将生成的authentication放入容器中,生成安全的上下文
        SecurityContextHolder.getContext().setAuthentication(authentication);
        ResultVO resultVO=new ResultVO<>();
        resultVO= ResultVOUtil.success(token);
        String json= JSON.toJSONString(resultVO);
        response.setContentType("text/json;charset=utf-8");
        response.getWriter().write(json);
    }
}

JwtAuthenticationTokenFilter.java

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Resource
    private UserDetailsService userDetailsService;
    @Resource
    private JwtUtil jwtUtil;
    @Resource
    private JwtProperties jwtProperties;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token=request.getHeader(jwtProperties.getHeader());
        Map<String,String>res=jwtUtil.resolveToken(token);
        if(res!=null){
            String username=res.get("username");
            if(username!=null&& SecurityContextHolder.getContext().getAuthentication()==null){
                UserDetails userDetails=this.userDetailsService.loadUserByUsername(username);
                if(!Objects.isNull(userDetails)){
                    UsernamePasswordAuthenticationToken authenticationToken=
                            new UsernamePasswordAuthenticationToken(userDetails.getUsername(),null,userDetails.getAuthorities());
                    authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                }
            }
        }
        filterChain.doFilter(request,response);
    }
}

WebSecurityConfig.java

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    @Resource
    private JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler;
    @Resource
    private LoginFailHandler loginFailHandler;
    @Resource
    private JwtAccessDeniedHandler jwtAccessDeniedHandler;
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    @Bean
    public JwtAuthenticationTokenFilter authenticationTokenFilterBean()throws Exception{
        return new JwtAuthenticationTokenFilter();
    }
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean()throws Exception{
        return super.authenticationManagerBean();
    }
    @Override
    protected void configure(HttpSecurity httpSecurity)throws Exception{
        httpSecurity.formLogin()
                .successHandler(jwtAuthenticationSuccessHandler)
                .failureHandler(loginFailHandler)
                .loginProcessingUrl("/login")
                .and()
                .csrf().disable()
                .exceptionHandling()
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                .accessDeniedHandler(jwtAccessDeniedHandler)
                .and()
                //设置无状态连接,即不创建session
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated();
        //解决跨域问题
        httpSecurity.cors().and().csrf().disable();
        //配置自己的jwt验证过滤器
        httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
        httpSecurity.headers().cacheControl();
    }
}

运行项目
在这里插入图片描述
在这里插入图片描述
参考文章:SpringSecurity整合JWT实现前后端分离认证和权限管理
SpringSecurity入门原理及实战
SpringSecurity安全框架详解

17.结束语

SpringBoot内容还是相当多的,上面只是一些我学习过并且认为挺有用的知识点,不过有些知识点因为还没有在项目中使用过,所以难免会有一些错误,希望谅解,欢迎指正,不喜勿喷。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值