springboot案例-tlias智能学习辅助系统

原型展示

 准备工作

需求 

  环境搭建

 一:创建数据库
-- 部门管理
create table dept(
                     id int unsigned primary key auto_increment comment '主键ID',
                     name varchar(10) not null unique comment '部门名称',
                     create_time datetime not null comment '创建时间',
                     update_time datetime not null comment '修改时间'
) comment '部门表';

insert into dept (id, name, create_time, update_time) values(1,'学工部',now(),now()),(2,'教研部',now(),now()),(3,'咨询部',now(),now()), (4,'就业部',now(),now()),(5,'人事部',now(),now());



-- 员工管理(带约束)
create table emp (
                     id int unsigned primary key auto_increment comment 'ID',
                     username varchar(20) not null unique comment '用户名',
                     password varchar(32) default '123456' comment '密码',
                     name varchar(10) not null comment '姓名',
                     gender tinyint unsigned not null comment '性别, 说明: 1 男, 2 女',
                     image varchar(300) comment '图像',
                     job tinyint unsigned comment '职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管, 5 咨询师',
                     entrydate date comment '入职时间',
                     dept_id int unsigned comment '部门ID',
                     create_time datetime not null comment '创建时间',
                     update_time datetime not null comment '修改时间'
) comment '员工表';

INSERT INTO emp
(id, username, password, name, gender, image, job, entrydate,dept_id, create_time, update_time) VALUES
(1,'jinyong','123456','金庸',1,'1.jpg',4,'2000-01-01',2,now(),now()),
(2,'zhangwuji','123456','张无忌',1,'2.jpg',2,'2015-01-01',2,now(),now()),
(3,'yangxiao','123456','杨逍',1,'3.jpg',2,'2008-05-01',2,now(),now()),
(4,'weiyixiao','123456','韦一笑',1,'4.jpg',2,'2007-01-01',2,now(),now()),
(5,'changyuchun','123456','常遇春',1,'5.jpg',2,'2012-12-05',2,now(),now()),
(6,'xiaozhao','123456','小昭',2,'6.jpg',3,'2013-09-05',1,now(),now()),
(7,'jixiaofu','123456','纪晓芙',2,'7.jpg',1,'2005-08-01',1,now(),now()),
(8,'zhouzhiruo','123456','周芷若',2,'8.jpg',1,'2014-11-09',1,now(),now()),
(9,'dingminjun','123456','丁敏君',2,'9.jpg',1,'2011-03-11',1,now(),now()),
(10,'zhaomin','123456','赵敏',2,'10.jpg',1,'2013-09-05',1,now(),now()),
(11,'luzhangke','123456','鹿杖客',1,'11.jpg',5,'2007-02-01',3,now(),now()),
(12,'hebiweng','123456','鹤笔翁',1,'12.jpg',5,'2008-08-18',3,now(),now()),
(13,'fangdongbai','123456','方东白',1,'13.jpg',5,'2012-11-01',3,now(),now()),
(14,'zhangsanfeng','123456','张三丰',1,'14.jpg',2,'2002-08-01',2,now(),now()),
(15,'yulianzhou','123456','俞莲舟',1,'15.jpg',2,'2011-05-01',2,now(),now()),
(16,'songyuanqiao','123456','宋远桥',1,'16.jpg',2,'2007-01-01',2,now(),now()),
(17,'chenyouliang','123456','陈友谅',1,'17.jpg',NULL,'2015-03-21',NULL,now(),now());



二:创建springboot工程

三:配置properties依赖
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url
spring.datasource.url=jdbc:mysql://localhost:3306/tlias
#连接数据库的用户名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password=1234


#输出日志到控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

#开启驼峰自动映射
mybatis.configuration.map-underscore-to-camel-case=true
 四:添加三层架构所需文件

package com.itheima.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Result {
    private Integer code;//响应码
    private String msg;//响应信息
    private Object data;//返回响应数据

    //增删改 成功响应
    public static Result success(){
        return new Result(1,"success",null);
    }
    //查询 成功响应
    public static Result success(Object data){
        return new Result(1,"success",data);
    }
    //失败响应
    public static Result error(String msg){
        return new Result(0,msg,null);
    }
}
package com.itheima.controller;

import org.springframework.web.bind.annotation.RestController;

@RestController
public class DeptController {
}
package com.itheima.mapper;


import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface DeptMapper {
}

package com.itheima.service.impl;

import com.itheima.service.DeptService;
import org.springframework.stereotype.Service;

@Service
public class DeptServiceImpl implements DeptService {

}

开发规范

开发流程

接口开发 

一:部门管理

查询部门
 @Autowired
    private DeptService deptService;

    @GetMapping("/depts")
    public Result list(){
        log.info("查询全部部门数据");
        List<Dept> list=deptService.list();
        return Result.success(list);
    }
 @Autowired
    private DeptMapper deptMapper;
    @Override
    public List<Dept> list() {
       return deptMapper.list();
    }
@Mapper
public interface DeptMapper {
    @Select("select * from dept")
    List<Dept> list();
}

删除部门 

 

/**
     * 根据ID删除对应的部门
     */
    @DeleteMapping("/depts/{id}")
    public Result deleteById(@PathVariable Integer id){
        log.info("根据id删除部门:{}",id);
        deptService.deleteById(id);
        return Result.success();
    }

/**
     * 根据id删除部门
     */
    @Override
    public void deleteById(Integer id) {
        deptMapper.deleteById(id);
    }
 @Delete("delete from dept where id=#{id}")
    void deleteById(Integer id);

新增部门  

修改部门
 /**
     * 修改部门
     * @param dept
     * @return
     */
    @PutMapping()
    public Result modify(@RequestBody Dept dept){
         log.info("修改部门:{}",dept.getName());
         deptService.modifyDept(dept);
         return Result.success();
    }

    /**
     * 根据id查询部门
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public Result GetById(@PathVariable Integer id){
        log.info("查询部门:{}",id);
        Dept dept=deptService.GetById(id);
        return Result.success(dept);
    }

    
    //service层
    @Override
    public void modifyDept(Dept dept) {
        dept.setUpdateTime(LocalDateTime.now());
        deptMapper.modifyDept(dept);
    }

    @Override
    public Dept GetById(Integer id) {
        Dept dept=deptMapper.GetById(id);
        return dept;
    }

    //mapper层
    @Update("update dept set name=#{name},update_time=#{updateTime} where id=#{id}")
    void modifyDept(Dept dept);

    @Select("select * from dept where id=#{id}")
    Dept GetById(Integer id);

二:员工管理

分页查询

 

package com.itheima.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

/**
 * 该类是封装要返回的数据总数和数据列表
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageBean {
    private long totals;//总记录数
    private List rows;//要返回的数据列表
}

controller层
/**
     *
     * @param page
     * 接收前端的页码
     * @param pageSize
     * 接收前端的页面大小
     * @return
     */
    @GetMapping("/emps")
    public Result page(@RequestParam(defaultValue = "1") Integer page,
                       @RequestParam(defaultValue = "10") Integer pageSize){
        PageBean pageBean=empService.page(page,pageSize);
        return Result.success(pageBean);

    }

service层
 @Override
    public PageBean page(Integer page, Integer pageSize) {
        Integer start=(page-1)*pageSize;
        List<Emp> list=empMapper.page(start,pageSize);
        Long count=empMapper.count();
        PageBean pageBean=new PageBean(count,list);
        return pageBean;
    }

mapper层
  /**
     * 查询并返回emp表中的数据数
     * @return
     */
    @Select("select count(*) from emp")
    public long count();

    /**
     * 返回当前页的结果列表
     * @param开始索引
     * @parampageSize页面大小
     * @return
     */
    @Select("select * from emp limit #{start},#{pageSize}")
    public List<Emp> page(Integer start,Integer pageSize);
使用pagehelper插件
 <!--分页查询插件-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.4.6</version>
        </dependency>
service层 
@Override
    public PageBean page(Integer page, Integer pageSize) {
        //1.设置分页参数
        PageHelper.startPage(page,pageSize);

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

        //3.封装PageBean对象
        PageBean pageBean=new PageBean(p.getTotal(),p.getResult());
        return pageBean;
    }

mapper层
 /**
     * 查询所有员工
     * @return
     */
    @Select("select * from emp")
    List<Emp> list();

分页查询(带条件)

<?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.itheima.mapper.EmpMapper">
    <select id="list" resultType="com.itheima.pojo.Emp">
        select * from emp
        <where>
            <if test="name!=null">
                name like concat('%',#{name},'%')
            </if>
            <if test="gender!=null">
                and gender=#{gender}
            </if>
            <if test="begin!=null and end!=null">
                and entrydate between #{begin} and #{end}
            </if>
        </where>
        order by update_time desc
    </select>

</mapper>

controller层
/**
     *
     * @param page
     * 接收前端的页码
     * @param pageSize
     * 接收前端的页面大小
     * @return
     */
    @GetMapping("/emps")
    public Result page(@RequestParam(defaultValue = "1") Integer page,
                       @RequestParam(defaultValue = "10") Integer pageSize,
                       Short gender,
                       String name,
                       @DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate begin,
                        @DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate end
                       )
    {
        PageBean pageBean=empService.page(page,pageSize,gender,name,begin,end);
        return Result.success(pageBean);

service层
@Override
    public PageBean page(Integer page, Integer pageSize,
                         Short gender,
                         String name,
                         LocalDate begin,
                         LocalDate end
    )
    {
        //1.设置分页参数
        PageHelper.startPage(page,pageSize);

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

        //3.封装PageBean对象
        PageBean pageBean=new PageBean(p.getTotal(),p.getResult());
        return pageBean;
    }

mapper层
List<Emp> list( Short gender,
                    String name,
                    LocalDate begin,
                    LocalDate end);
删除员工

controller层
 @DeleteMapping("/emps/{ids}")
    public Result delete(@PathVariable List<Integer> ids){
        empService.delete(ids);
        return Result.success();
    }

service层
@Override
    public void delete(List<Integer> ids) {
        empMapper.delete(ids);
    }

mapper层
 <delete id="delete">
        delete from emp where id in
        <foreach collection="ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </delete>
 新增员工

controller层
@PostMapping("/emps")
    public Result save(@RequestBody Emp emp){
        empService.save(emp);
        return Result.success();
   }

service层
@Override
    public void save(Emp emp) {
        emp.setCreateTime(LocalDateTime.now());
        emp.setUpdateTime(LocalDateTime.now());
        empMapper.save(emp);
    }

mapper层
@Insert("insert into emp(username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time) " +
            "values (#{username},#{password},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})")
    void save(Emp emp);

 文件上传
 

 

本地存储 

@Slf4j
@RestController
public class UploadController {
    @PostMapping("/upload")
    public Result upload(MultipartFile image) throws IOException {
        log.info("文件上传:{}",image);
        //获取原始文件名
        String originalFilename= image.getOriginalFilename();
        //构建新的文件名
        String newFilename= UUID.randomUUID().toString()+originalFilename.substring(originalFilename.lastIndexOf("."));
        //将文件保存在服务端E:/images/目录下
        image.transferTo(new File("D:/images/"+newFilename));

        return Result.success();
    }
}

若要修改上传文件大小,则要在properties文件下进行以下配置

#配置单个文件最大上传大小
spring.servlet.multipart.max-file-size=10MB

#配置单个请求最大上传大小(一次请求可以上传多个文件)
spring.servlet.multipart.max-request-size=100MB

MutipartFile类常用方法

 阿里云oss

@Autowired
    private AliOSSUtils aliOSSUtils;
    @PostMapping("/upload")
    public Result upload(MultipartFile image) throws IOException {
        log.info("文件上传:{}",image.getOriginalFilename());
        String url = aliOSSUtils.upload(image);
        log.info("url:{}",url);
        return Result.success(url);

    }
 查询回显

contr层
@GetMapping("/emps/{id}")
    public Result getById(@PathVariable Integer id){
        Emp emp=empService.getById(id);
        return Result.success(emp);
    }

service层
@Override
    public Emp getById(Integer id) {
        Emp emp=empMapper.getByID(id);
        return emp;
    }

mapper层
@Select("select * from emp where id=#{id}")
    Emp getByID(Integer id);
 修改员工

 @PutMapping("/emps")
    public Result update(@RequestBody Emp emp){
        empService.update(emp);
        return Result.success();
    }

service层
@Override
    public void update(Emp emp) {
        emp.setUpdateTime(LocalDateTime.now());
        empMapper.update(emp);
    }

<update id="update">
        update emp
        <set>
            <if test="username!=null and username!='' ">
                username=#{username},
            </if>
            <if test="password!=null and password!=''">
                 password=#{password},
            </if>
            <if test="name!=null and name!=''">
                  name=#{name},
            </if>
            <if test="gender!=null and gender!=''">
                 gender=#{gender},
            </if>
            <if test="image!=null and image!=''">
                  image=#{image},
            </if>
            <if test="job != null">
                job = #{job},
            </if>
            <if test="entrydate != null">
                entrydate = #{entrydate},
            </if>
            <if test="deptId != null">
                dept_id = #{deptId},
            </if>
            <if test="updateTime != null">
                update_time = #{updateTime}
            </if>

        </set>

        where id=#{id}
    </update>

配置文件参数配置化

yml配置文件 

 

#对象/map集合
user:
  name: jack
  age: 18
  password: 123456

#数组/List/set集合
hobby:
  -java
  -game
  -sport

 configurationProperties

 基础登录功能

登录

controller层
package com.itheima.controller;


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

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

    }
}

service层
@Override
    public Emp login(Emp emp) {
        Emp e=empMapper.GetByPasswordAndUsername(emp);
        return e;
    }

mapper层
 @Select("select * from emp where username=#{username} and password=#{password}")
    Emp GetByPasswordAndUsername(Emp emp);

 登录校验

 

会话技术 

 http session演示

package com.itheima;

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

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

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

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

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



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

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

    @GetMapping("/s2")
    public Result session2(HttpServletRequest request){
        HttpSession session = request.getSession();
        log.info("HttpSession-s2: {}", session.hashCode());

        Object loginUser = session.getAttribute("loginUser"); //从session中获取数据
        log.info("loginUser: {}", loginUser);
        return Result.success(loginUser);
    }
}

浏览器第一次请求服务器,服务器设置cookie

 客户端浏览器将cookie存储在本地

当客户端继续请求服务端时会携带cookie 

 会话跟踪方案对比

JWT令牌

payload 有效荷载采用base64编码,数字签名的加密算法采用Header里面声明的算法

 jwt令牌生成和效验

 @Test
    public void genJwt(){
        Map<String,Object> claims=new HashMap<>();
        claims.put("id",1);
        claims.put("name","jack");
        String jwt = Jwts.builder()
                .setClaims(claims)//设置自定义内容
                .signWith(SignatureAlgorithm.HS256, "itheima")//设置加密算法和秘钥
                .setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))//设置有效期
                .compact();
        System.out.println(jwt);

    }

 @Test
    public void parseJwt(){
        Claims claims = Jwts.parser()
                .setSigningKey("itheima")//指定签名秘钥
                .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiamFjayIsImlkIjoxLCJleHAiOjE2OTMyMTUxODB9.2ZiD6WRVMjyjtAdAgAt4o5xaX5YWZoJ6izbOT1QU_Fg")
                .getBody();
        System.out.println(claims);
    }

 

package com.itheima.controller;


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

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

@Slf4j
@RestController
public class LoginController {
    @Autowired
    private EmpService empService;
    @PostMapping("/login")
    public Result Login(@RequestBody Emp emp){
        log.info("员工登录:{}",emp);
        Emp e=empService.login(emp);
        if(e!=null){
            Map<String,Object> claims=new HashMap<>();
            claims.put("id",e.getId());
            claims.put("name",e.getName());
            claims.put("username",e.getUsername());
            String jwt = JwtUtils.generateJwt(claims);
            return Result.success(jwt);
        }
        return Result.error("用户名或密码错误!");

    }
}

token存储在了本地

过滤器Filter

因为Filter不属于spring boot 组件里面的,是属于java web 里面的三大组件之一,所以要加上ServletComponentScan注解在

@SpringBootApplication上

package com.itheima.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(urlPatterns = "/*")//拦截所有的请求
public class DemoFilter implements Filter{

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("此请求被拦截!");
    }
}

过滤器链

执行流程

请求--》执行前逻辑--》放行--》资源--》执行后逻辑 

package com.itheima.filter;

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

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

@Slf4j
@WebFilter(urlPatterns = "/*")//拦截所有的请求
public class LoginCheckFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //1.获取请求的URL
        HttpServletRequest srq = (HttpServletRequest) servletRequest;
        HttpServletResponse srp = (HttpServletResponse) servletResponse;
        String url = srq.getRequestURL().toString();
        log.info("请求的路径为:{}",url);

        //2.判断请求的URL中是否包含login,如果包含,说明是登录操作,放行。
        if(url.contains("login")){
            log.info("登录操作,放行");
            filterChain.doFilter(servletRequest,servletResponse);
            return;
        }
        //3.获取请求头中的令牌(token)
        String jwt = srq.getHeader("token");
        //4.判断令牌是否存在,如果不存在,返回错误结果(未登录)
        if(!StringUtils.hasLength(jwt)){
            log.info("令牌不存在,错误");
            Result error = Result.error("NOT_LOGIN");
            //手动转化为JSON格式的字符串返回给前端
            String jsonString = JSONObject.toJSONString(error);
            //响应头对象调取getwriter获取写入流响应回前端
            srp.getWriter().write(jsonString);
            return;
        }
        //5.解析token,如果解析失败,返回错误结果(未登录)
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("令牌不存在,错误");
            Result error = Result.error("NOT_LOGIN");
            //手动转化为JSON格式的字符串返回给前端
            String jsonString = JSONObject.toJSONString(error);
            //响应头对象调取getwriter获取写入流响应回前端
            srp.getWriter().write(jsonString);
            return;
        }
        //6.放行
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

登录效验案例

用postman测试

查询部门

解析成功放行

这里咱们把令牌故意改错

令牌解析错误 

拦截器Interceptor

1.建立一个新包interceptor,然后添加一个新类,实现HanderInterceptor,按下快捷键Ctrl加O来重写方法

package com.itheima.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component//加入IOC容器
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override//目标资源方法运行前运行,返回true:放行,返回false:不放行
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle....");
        return true;
    }

    @Override//目标资源方法运行后运行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle....");
    }

    @Override//视图渲染完毕后运行,最后运行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion....");
    }
}

2.在config包里面注册拦截器

package com.itheima.config;

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

@Configuration//配置类
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;

    @Override//注册拦截器
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");
    }
}

3.测试代码

拦截路径 

过滤器和拦截器执行流程
 登录校验
package com.itheima.interceptor;

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

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component//加入IOC容器
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override//目标资源方法运行前运行,返回true:放行,返回false:不放行
    public boolean preHandle(HttpServletRequest srq, HttpServletResponse srp, Object handler) throws Exception {
        //1.获取请求的URL

        String url = srq.getRequestURL().toString();
        log.info("请求的路径为:{}",url);

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

            return true;
        }
        //3.获取请求头中的令牌(token)
        String jwt = srq.getHeader("token");
        //4.判断令牌是否存在,如果不存在,返回错误结果(未登录)
        if(!StringUtils.hasLength(jwt)){
            log.info("令牌不存在,错误");
            Result error = Result.error("NOT_LOGIN");
            //手动转化为JSON格式的字符串返回给前端
            String jsonString = JSONObject.toJSONString(error);
            //响应头对象调取getwriter获取写入流响应回前端
            srp.getWriter().write(jsonString);
            return false;
        }
        //5.解析token,如果解析失败,返回错误结果(未登录)
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("令牌不存在,错误");
            Result error = Result.error("NOT_LOGIN");
            //手动转化为JSON格式的字符串返回给前端
            String jsonString = JSONObject.toJSONString(error);
            //响应头对象调取getwriter获取写入流响应回前端
            srp.getWriter().write(jsonString);
            return false;
        }
        //6.放行
        return true;
    }

    @Override//目标资源方法运行后运行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle....");
    }

    @Override//视图渲染完毕后运行,最后运行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion....");
    }
}

异常处理

package com.itheima.exception;

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

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

事务

 一旦出现异常就会出现事务的回滚

优化删除部门操作,删除部门的同时该部门下员工也要删除

 /**
     * 根据id删除部门
     */
    @Transactional
    @Override
    public void deleteById(Integer id) {

        deptMapper.deleteById(id);
        empMapper.deleteDeptEmpById(id);
    }

把代码改成这种,就会出现事务回滚,不会在数据库中删除 

@Transactional
    @Override
    public void deleteById(Integer id) {

        deptMapper.deleteById(id);
        int i=1/0;
        empMapper.deleteDeptEmpById(id);
    }

事务进阶

rollbackFor

/**
     * 根据id删除部门
     */
    @Transactional(rollbackFor = Exception.class)//配置事务处理,默认是运行时异常才会处理,这里指定所有异常都会回滚
    @Override
    public void deleteById(Integer id) {

        deptMapper.deleteById(id);

        empMapper.deleteDeptEmpById(id);
    }

propagation 

 一般会用前两个就行了

下面来看一个案例

 /**
     * 根据id删除部门
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)//配置事务处理,默认是运行时异常才会处理,这里指定所有异常都会回滚
    @Override
    public void deleteById(Integer id) {

        try {
            deptMapper.deleteById(id);
            int i=1/0;
            empMapper.deleteDeptEmpById(id);
        } finally {

            DeptLog deptLog=new DeptLog();
            deptLog.setCreateTime(LocalDateTime.now());
            deptLog.setDescription("执行了解散部门操作,此次解散的是"+id+"号部门");
            deptLogService.insert(deptLog);
        }

    }

 加上@Transactional(propagation = Propagation.REQUIRES_NEW)后会在下面这个方法中创建一个新事物,上面方法报错回滚不会影响下面

 @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public void insert(DeptLog deptLog) {
        deptLogMapper.insert(deptLog);
    }

AOP基础

 案例

添加依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
package com.itheima.aop;


import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Slf4j
@Aspect//加入使用AOP
public class TimeAspect {
    @Around("execution(* com.itheima.service.*.*(..))")
    public Object recordTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        //开始时间
        long begin=System.currentTimeMillis();
       //执行原始方法
        Object object=proceedingJoinPoint.proceed();
        long end=System.currentTimeMillis();
        //获取方法结束时间,输出到日志
        log.info(proceedingJoinPoint.getSignature()+"执行耗时:{}ms",end-begin);
        return object;
    }
}

 

Aop运用场景和优势

AOP核心概念 

会先进入代理对象的方法,之后进入原来方法

通知类型

 

刷新查询部门,在调用接口方法前before和around执行

 之后这些方法执行

package com.itheima.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * 该类用于演示通知类型
 */
@Component
@Aspect
@Slf4j
public class MyAspect {
    //将公共的切点表达式抽取出来,需要用到时引用该切点表达式即可。
    @Pointcut("execution(* com.itheima.service.impl.DeptServiceImpl.*(..))")
    public void pt(){}
/*
前置通知
 */
    @Before("pt()")
    public void before(){
        log.info("before........");
    }

    /**
     * 后置通知
     */
    @After("pt()")
    public void after(){
        log.info("after........");
    }

    /**
     * 环绕通知
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("pt()")
    public Object before(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("Aroundbefore........");//最先通知
        Object proceed = proceedingJoinPoint.proceed();
        log.info("AroundAfter........");//最后通知
        return proceed;
    }

    /**
     * 返回后通知
     */
    @AfterReturning("pt()")
    public void AfterReturning(){
        log.info("AfterReturning........");
    }


}

通知顺序

切入点表达式 

可以用||来匹配多个切入点

@annotation

@annotation(里面指定自定义注解路径) 

连接点

代码

package com.itheima.aop;



import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 该类用于演示通知类型
 */
@Component
@Aspect
@Slf4j
public class MyAspect2 {
    //将公共的切点表达式抽取出来,需要用到时引用该切点表达式即可。
    @Pointcut("execution(* com.itheima.service.impl.DeptServiceImpl.*(..))")
    public void pt(){}
    /*
    前置通知
     */
    @Before("pt()")
    public void before(JoinPoint joinPoint){
        log.info("Aspect2 before........");
    }



    /**
     * 环绕通知
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("pt()")
    public Object before(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        log.info("Aspect2 Around before........");//最先通知
        //1.获取目标对象类名
        String classname = proceedingJoinPoint.getTarget().getClass().getName();
        log.info("目标对象类名为:{}",classname);

        //2.获取目标对象方法名
        String methodname = proceedingJoinPoint.getSignature().getName();
        log.info("目标对象方法名为:{}",methodname);

        //3.获取目标对象运行时传入的参数
        Object [] args=proceedingJoinPoint.getArgs();
        log.info("目标方法运行时传入的参数:{}", Arrays.toString(args));

        //4.放行目标方法执行
        Object result=proceedingJoinPoint.proceed();

        //5.获取目标方法运行的返回值
        log.info("目标方法运行的返回值:{}",result);
        log.info("Aspect2 AroundAfter........");//最后通知

        return result;

    }




}

测试删除部门

 //删除部门
    @Test
    public void AOPdeleteTest()  {
        deptService.delete(1);

    }

 AOP案例

引入AOP依赖

<!--        AOP依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

 创建数据库表和对应实体类

-- 操作日志表
create table operate_log(
    id int unsigned primary key auto_increment comment 'ID',
    operate_user int unsigned comment '操作人ID',
    operate_time datetime comment '操作时间',
    class_name varchar(100) comment '操作的类名',
    method_name varchar(100) comment '操作的方法名',
    method_params varchar(1000) comment '方法参数',
    return_value varchar(2000) comment '返回值',
    cost_time bigint comment '方法执行耗时, 单位:ms'
) comment '操作日志表';



package com.itheima.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class OperateLog {
    private Integer id; //ID
    private Integer operateUser; //操作人ID
    private LocalDateTime operateTime; //操作时间
    private String className; //操作类名
    private String methodName; //操作方法名
    private String methodParams; //操作方法参数
    private String returnValue; //操作方法返回值
    private Long costTime; //操作耗时
}

自定义注解log

package com.itheima.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)//用来指定该注解在方法执行时生效
@Target(ElementType.METHOD)//用来指定该注解作用在方法上
public @interface Log {
}

自定义切面类

package com.itheima.aop;

import com.alibaba.fastjson.JSONObject;
import com.itheima.mapper.OperateLogMapper;
import com.itheima.pojo.OperateLog;
import com.itheima.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.spel.ast.OpMinus;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Arrays;

@Slf4j
@Component
@Aspect//切面类
public class LogAspect {
    
    @Autowired
    private OperateLogMapper operateLogMapper;
    
    @Autowired
    private HttpServletRequest httpServletRequest;

    @Around("@annotation(com.itheima.anno.Log)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        
        OperateLog operateLog=new OperateLog();
        //获取操作人员ID,可以通过注入HttpServletRequest对象来获取token请求头中的jwt信息来获取,然后解析jwt令牌
        String jwt = httpServletRequest.getHeader("token");
        Claims claims = JwtUtils.parseJWT(jwt);
        Integer operateUser=(Integer)claims.get("id");
        operateLog.setOperateUser(operateUser);
        //设置操作时间
        LocalDateTime operateTime=LocalDateTime.now();
        operateLog.setOperateTime(operateTime);
        //设置操作类名
        String className=joinPoint.getTarget().getClass().getName();
        operateLog.setClassName(className);
        //设置操作方法名
        String methodName=joinPoint.getSignature().getName();
        operateLog.setMethodName(methodName);
        //设置操作方法参数
        String methodParams= Arrays.toString(joinPoint.getArgs());
        operateLog.setMethodParams(methodParams);
        //设置操作方法返回值
        Long begin=System.currentTimeMillis();
        Object result = joinPoint.proceed();
        //转化为json格式的字符串
        String returnValue = JSONObject.toJSONString(result);
        operateLog.setReturnValue(returnValue);

        //设置操作时间
        Long end=System.currentTimeMillis();
        Long costTime=end-begin;
        operateLog.setCostTime(costTime);

        //插入数据
        operateLogMapper.insertOperateLog(operateLog);
        log.info("记录AOP日志:{}",operateLog);
        return result;
    }
}

测试:增加保卫部然后删除保卫部

日志

数据库 

spring boot 项目配置 

springboot支持三种配置文件方式

配置优先级

 Bean管理

package com.itheima;

import com.itheima.controller.DeptController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;

@SpringBootTest
public class BeanTest {

    @Autowired
    private ApplicationContext applicationContext;

    @Test
    void testGetBean(){
        //根据name获取bean
       DeptController bean1 = (DeptController)applicationContext.getBean("deptController");
        System.out.println(bean1);
        //根据类型获取bean
        DeptController bean2 = applicationContext.getBean(DeptController.class);
        System.out.println(bean2);

        //根据名字和类型获取bean
        DeptController bean3 = applicationContext.getBean("deptController", DeptController.class);
        System.out.println(bean3);


    }
}

默认为单例模式,只会生成一个对象 

bean作用域

管理第三方bean

 

@Configuration
public class CommonConfig {

    //声明第三方bean
    @Bean//将第三方bean加入IOC容器
    public SAXReader saxReader(){
        return new SAXReader();
    }
}

 

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Springboot CAS-Client 是一个基于Springboot框架集成CAS(Central Authentication Service)的客户端。 CAS是一种单点登录(Single Sign-On)协议,它允许用户在一次登录后就能够访问多个应用,而无需重新认证。 Springboot CAS-Client 的作用是充当CAS服务端和应用系统之间的中间件,它负责向CAS服务端发送认证请求,并根据认证结果来管理用户的登录状态。 为了集成CAS,我们首先需要在Springboot项目中引入相应的依赖,例如spring-boot-starter-web和spring-boot-starter-security。接着,我们需要配置CAS服务端的地址信息,包括CAS服务端的登录URL、登出URL以及验证票据的URL等。 在Springboot CAS-Client中,我们也可以自定义一些过滤器和拦截器来实现相关的功能。例如,我们可以编写一个CAS认证过滤器来拦截所有的请求,并判断用户的登录状态。如果用户未登录,则跳转到CAS服务端进行认证;如果用户已登录,则直接放行请求。此外,我们还可以编写一个CAS登出拦截器来处理用户的登出请求,并在登出完成后将用户重定向到指定的页面。 总的来说,Springboot CAS-Client 提供了一个简洁、灵活的方式来集成CAS协议,使得我们的Springboot应用能够享受到单点登录带来的便利。通过它,我们可以轻松地实现用户认证、登录状态管理以及注销等功能,提升用户体验并提高开发效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值