• 学成在线第二天


    机构查询

    效果展示:
    在这里插入图片描述
    YAPI接口文档:
    在这里插入图片描述
    在这里插入图片描述
    代码展示:
    首先使用mybatis-plus代码生成器,自动生成模板

    代码生成器
    在这里插入图片描述
    controller层:
    在这里插入图片描述

    虽然这样写对于这个功能来说没有问题,但是我每个Controller都需要获取token进行验证是否登录。就会出现一下问题:

    • 代码繁琐,每个Controller都需要获取token,验证用户是否登录
    • token解析不方便
      在这里插入图片描述

    统一Token处理(重点)

    解决思路:
    我们可以使用网关,进行token的获取和解析,并判断登录状态,提取用户信息,将其写入请求头在转发到springMVC的拦截器,并将数据绑定到ThreadLocal线程池中,以后可以在线程中获取信息,示意图如下:
    在这里插入图片描述
    在这里插入图片描述
    首先在网关模块,搭建全局过滤器
    在这里插入图片描述

    package com.xuecheng.gate.filter;
    
    import com.alibaba.fastjson.JSON;
    import com.xuecheng.commons.model.vo.AuthInfo;
    import com.xuecheng.commons.utils.JwtUtils;
    import io.jsonwebtoken.Claims;
    import lombok.SneakyThrows;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;
    
    import java.net.URLEncoder;
    
    //网关校验
    @Component
    public class AuthFilter implements GlobalFilter,Ordered {
    
        @Override
        @SneakyThrows
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            ServerHttpRequest request = exchange.getRequest();
            ServerHttpResponse response = exchange.getResponse();
            String path = request.getURI().getPath();
            //判断不要验证的,直接放行
            if(path.contains("coursePub/preview") ||
                    path.contains("auth/login") ||
                    path.contains("basic/dictionary") ||
                    path.contains("category/tree-nodes")||
                    path.contains("course/upload")||
                    path.contains("search/")){
                return chain.filter(exchange);
            }
            String token = request.getHeaders().getFirst("Authorization");
            Boolean aBoolean = JwtUtils.verifyToken(token);
            if (!aBoolean){
                //4、校验token,对于token异常的,返回401状态码
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return response.setComplete();
            }
            Claims claims = JwtUtils.parserToken(token).getBody();
            Long userId = claims.get("userId", Long.class);
            Long companyId = claims.get("companyId",Long.class);
            String companyName = claims.get("companyName",String.class);
    
            AuthInfo authInfo = new AuthInfo();
            authInfo.setUid(userId);
            authInfo.setCompanyId(companyId);
            authInfo.setCompanyName(companyName);
            //解决中文乱码
            String json = URLEncoder.encode(JSON.toJSONString(authInfo), "UTF-8");
            //7、向下传递,到其他微服务
            //向请求头对象,设置用户信息
            ServerHttpRequest httpRequest = request.mutate().headers(httpHeaders -> {
                httpHeaders.add("payload", json);
            }).build();
            //设置到请求体里
            exchange.mutate().request(httpRequest);
    
            return chain.filter(exchange);
        }
    
        @Override
        public int getOrder() {
            return 0;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    然后在统一web处理模块,配置拦截器
    在这里插入图片描述

    package com.xuecheng.web.filter;
    
    import com.alibaba.fastjson.JSON;
    
    import com.xuecheng.commons.model.vo.AuthInfo;
    import com.xuecheng.commons.utils.AuthInfoHolder;
    import org.springframework.util.StringUtils;
    import org.springframework.web.servlet.HandlerInterceptor;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.net.URLDecoder;
    
    
    /**
     * 自定义拦截器
     */
    public class AuthInfoInInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            String payload = request.getHeader("payload");
            if(StringUtils.isEmpty(payload)) {
                return true;
            }
            String json = URLDecoder.decode(payload, "utf-8");
            AuthInfo authInfo = JSON.parseObject(json, AuthInfo.class);
            AuthInfoHolder.setAuthInfo(authInfo);
            return true;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    package com.xuecheng.web.filter;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    import javax.servlet.annotation.WebFilter;
    
    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
    
            registry.addInterceptor(new AuthInfoInInterceptor())
                    .addPathPatterns("/**");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    最后设置ThreadLocal工具类:
    在这里插入图片描述

    package com.xuecheng.commons.utils;
    
    
    import com.xuecheng.commons.model.vo.AuthInfo;
    
    /**
     * 存放用户id的容器
     */
    public class AuthInfoHolder {
    
        //定义ThreadLocal
        private final static ThreadLocal<AuthInfo> threadLocal = new ThreadLocal<>();
    
        /**
         * 获取线程中的用户
         */
        public static AuthInfo getAuthInfo() {
            return threadLocal.get();
        }
    
        /**
         * 设置当前线程中的用户
         */
        public static void setAuthInfo(AuthInfo info) {
            threadLocal.set(info);
        }
    
        public static Long getUserId() {
            return threadLocal.get().getUid();
        }
    
        public static Long getCompanyId() {
            if(threadLocal.get() != null) {
                return threadLocal.get().getCompanyId();
            }else {
                return null;
            }
    
        }
    
        public static void remove(){threadLocal.remove();}
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    引入依赖,并修改启动类:

       <dependency>
                <groupId>com.xuecheng</groupId>
                <artifactId>xc-framework-web</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    package com.xuecheng.system;
    
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.ComponentScan;
    
    @SpringBootApplication
    @MapperScan("com.xuecheng.system.mappers")
    @ComponentScan(basePackages = {
            "com.xuecheng.web",
            "com.xuecheng.system"
    
    })
    public class SystemApplication {
        public static void main(String[] args) {
            SpringApplication.run(SystemApplication.class, args);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    修改后的controller层:

    package com.xuecheng.system.controller;
    
    import com.xuecheng.commons.enums.ErrorCode;
    import com.xuecheng.commons.model.vo.AuthInfo;
    import com.xuecheng.commons.model.vo.CompanyVo;
    import com.xuecheng.commons.model.vo.ResponseResult;
    import com.xuecheng.commons.utils.AuthInfoHolder;
    import com.xuecheng.commons.utils.JwtUtils;
    import com.xuecheng.web.exception.BusinessException;
    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jws;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestHeader;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import com.xuecheng.system.service.CompanyService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import sun.net.httpserver.AuthFilter;
    
    /**
     * 

    * 教育机构前端控制器 *

    * * @author itheima */
    @Slf4j @RestController @RequestMapping("/company") public class CompanyController { @Autowired private CompanyService companyService; //查询我的机构信息 @GetMapping("/mine") public ResponseResult mine(@RequestHeader("payload") String payload){ AuthInfo authInfo = AuthInfoHolder.getAuthInfo(); Long companyId = authInfo.getCompanyId(); CompanyVo vo =companyService.mine(companyId); return ResponseResult.okResult(vo); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    serviceimpl层:

    package com.xuecheng.system.service.impl;
    
    import cn.hutool.core.bean.BeanUtil;
    import com.xuecheng.commons.enums.ErrorCode;
    import com.xuecheng.commons.model.vo.CompanyVo;
    import com.xuecheng.commons.utils.BeanHelper;
    import com.xuecheng.system.domain.Company;
    import com.xuecheng.system.mappers.CompanyMapper;
    import com.xuecheng.system.service.CompanyService;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.xuecheng.web.exception.BusinessException;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    /**
     * 

    * 教育机构 服务实现类 *

    * * @author itheima * @since 2022-07-24 */
    @Service public class CompanyServiceImpl extends ServiceImpl<CompanyMapper, Company> implements CompanyService { @Autowired private CompanyMapper companyMapper; @Override public CompanyVo mine(Long companyId) { Company company = companyMapper.selectById(companyId); if (BeanUtil.isEmpty(company)){ throw new BusinessException(ErrorCode.COMPANYERROR); } CompanyVo vo = BeanHelper.copyProperties(company, CompanyVo.class); return vo; } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    课程查询

    效果展示

    在这里插入图片描述
    YAPI文档接口:
    在这里插入图片描述
    需要的表:

    在这里插入图片描述
    代码展示:
    使用mybatis-plus代码生成器生成基本架构,带入相关依赖和yml配置文件,太多了这里我就不显示了
    配置mybatis-plus分页插件
    在这里插入图片描述

    package com.xuecheng.mybatis.config;
    
    import cn.hutool.core.util.ArrayUtil;
    import cn.hutool.core.util.ObjectUtil;
    import com.baomidou.mybatisplus.annotation.DbType;
    import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
    import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
    import com.xuecheng.commons.utils.AuthInfoHolder;
    import lombok.extern.slf4j.Slf4j;
    import net.sf.jsqlparser.expression.Expression;
    import net.sf.jsqlparser.expression.StringValue;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Slf4j
    @Configuration
    public class MybatisPlusConfig {
    
        private static String [] tables = new String[]{"tb_dictionary","tb_category",
                "undo_log","tb_application","tb_company","tb_resource","tb_role_resource","tb_user_role"};
        //注册mybatis的插件
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor(){
            MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
    //多租户        mybatisPlusInterceptor.addInnerInterceptor(tenantEnterpriseInterceptor());
            //分页
            mybatisPlusInterceptor.addInnerInterceptor(paginationInnerInterceptor());
            return mybatisPlusInterceptor;
        }
        //分页的插件配置
        @Bean
        public PaginationInnerInterceptor paginationInnerInterceptor(){
            return new PaginationInnerInterceptor(DbType.MYSQL);
        }
        }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    web层:

    package com.xuecheng.content.controller;
    
    import com.xuecheng.commons.model.dto.CourseDto;
    import com.xuecheng.commons.model.dto.PageRequest;
    import com.xuecheng.commons.model.vo.ResponseResult;
    import javafx.geometry.Pos;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import com.xuecheng.content.service.CourseBaseService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    
    /**
     * 

    * 课程基本信息前端控制器 *

    * * @author itheima */
    @Slf4j @RestController @RequestMapping("/course") public class CourseBaseController { @Autowired private CourseBaseService courseBaseService; //分页查询课程列表 @PostMapping("/list") public ResponseResult courseList(PageRequest pageRequest, @RequestBody CourseDto courseDto){ return courseBaseService.courseList(pageRequest,courseDto); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    serviceIml层:

    package com.xuecheng.content.service.impl;
    
    import cn.hutool.core.collection.CollUtil;
    import cn.hutool.core.util.StrUtil;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.core.metadata.IPage;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import com.xuecheng.commons.model.dto.CourseDto;
    import com.xuecheng.commons.model.dto.PageRequest;
    import com.xuecheng.commons.model.vo.PageResponseResult;
    import com.xuecheng.commons.model.vo.ResponseResult;
    import com.xuecheng.commons.utils.AuthInfoHolder;
    import com.xuecheng.content.domain.CourseBase;
    import com.xuecheng.content.mappers.CourseBaseMapper;
    import com.xuecheng.content.service.CourseBaseService;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    /**
     * 

    * 课程基本信息 服务实现类 *

    * * @author itheima * @since 2022-07-27 */
    @Service public class CourseBaseServiceImpl extends ServiceImpl<CourseBaseMapper, CourseBase> implements CourseBaseService { @Override public ResponseResult courseList(PageRequest pageRequest, CourseDto courseDto) { //分页 Page<CourseBase> page = new Page<>(pageRequest.getPageNo(), pageRequest.getPageSize()); //根据条件插叙 LambdaQueryWrapper<CourseBase> wrapper = new LambdaQueryWrapper<>(); //hutool工具包 if (StrUtil.isNotEmpty(courseDto.getCourseName())){ wrapper.like(CourseBase::getName,courseDto.getCourseName()); } if (StrUtil.isNotEmpty(courseDto.getAuditStatus())){ wrapper.eq(CourseBase::getAuditStatus,courseDto.getAuditStatus()); } if (StrUtil.isNotEmpty(courseDto.getTeachmode())){ wrapper.eq(CourseBase::getTeachmode,courseDto.getTeachmode()); } //设置机构ID,要不然就全查出来了 wrapper.eq(CourseBase::getCompanyId, AuthInfoHolder.getCompanyId()); //数据分页 IPage<CourseBase> courseBasePage = this.page(page, wrapper); return PageResponseResult.okResult(courseBasePage.getTotal(), courseBasePage.getRecords()); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    代码优化:
    在这里插入图片描述
    这里其实有点小问题,为了确保数据的安全性和隔离性,我们在执行SQL时,往往都会设置上租户id(即企业ID),针对大面积的操作就比较繁琐和枯燥。查询,删除,更新,插入操作的时候都得加上 企业ID这个条件
    在这里插入图片描述
    那mybatis-plus提供了多租户插件,借助SQL拦截技术,可以自动对SQL语句添加租户ID

    具体如下:
    首先注解掉以前的代码
    在这里插入图片描述
    其次在mybatis-plus的配置在添加多租户sql拦截器

    在这里插入图片描述
    然后加上
    在这里插入图片描述

     /**
         * 多租户的插件配置
         *    getTenantId: 租户数据的来源
         *    getTenantIdColumn: 拼接的SQL语句的租户字段
         *    ignoreTable: 判断表时候需要租户配置
         */
        @Bean
        public TenantLineInnerInterceptor tenantEnterpriseInterceptor(){
            return new TenantLineInnerInterceptor(new TenantLineHandler() {
                //获取线程中的企业id
                public Expression getTenantId() {
                    Long companyId = AuthInfoHolder.getCompanyId();
                    if(ObjectUtil.isEmpty(companyId)) {
                        return null;
                    }else {
                        return new StringValue(companyId.toString());
                    }
                }
    
                //租户的字段
                public String getTenantIdColumn() {
                    return "company_id";
                }
    
                //设置忽略的表,true忽略,false-拼接
                @Override
                public boolean ignoreTable(String tableName) {
                    // 是否需要需要过滤某一张表
                    if (ArrayUtil.contains(tables,tableName)){
                        return true;
                    }
                    //拼接的sql多租户字段标示对应的值不能为空
                    Expression tenantId = this.getTenantId();
                    if (ObjectUtil.isEmpty((tenantId))){
                        return true;
                    }
                    return false;
                }
            });
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    效果展示:
    在这里插入图片描述

    第二天任务完成

  • 相关阅读:
    DotNetGuide新增C#/.NET/.NET Core充电站(让你学习不迷路)
    Leetcode.122 买卖股票的最佳时机 II
    notepad++编辑多个位置
    2023最新Nmap安装和使用详解,超详细教程
    SpringBoot实战(1)
    SSL加速是什么,有什么优势?
    推荐系统常见算法分类
    优化冗余的if-else,“责任树模式”是个不错的选择
    【学习笔记】Kafka学习
    【C++】—— 模板进阶
  • 原文地址:https://blog.csdn.net/weixin_44233087/article/details/126073822