首页 > 编程语言 >一个基于微服务架构的SpringBoot+vue2.0的在线教育系统【源码开源】【强烈建议收藏】

一个基于微服务架构的SpringBoot+vue2.0的在线教育系统【源码开源】【强烈建议收藏】

时间:2023-03-18 15:13:34浏览次数:41  
标签:return SpringBoot service stu 强烈建议 源码 import com public

今天给大家开源一个基于springboot+vue2.0的微服务在线教育平台系统,系统是攀登网的孟哥和汉远哥开发的,我进行了本版本的开发。

该系统完全免费、开源。

为防止刷着刷者找不到,大家点赞、收藏文章。

具体的介绍如下所示。

1. 技术介绍

注意Easy Mock、微信部分、消息队列部分没用到,因为微信部分需要企业申请一些东西。

2.功能介绍

本项目分前台用户界面功能和后台管理功能;

前台用户界面功能:

  滚动大条幅展示重要通知和课程或者活动;

  展示课程,根据实际业务需求,展示课程推荐,最新课程,免费课程,实战课程;

  课程搜索,用户输入指定课程关键字,可以搜索查询,也可以根据课程类别分类,和类型进行搜索;

  课程详细展示

  用户登陆等等

后台管理功能:

  权限管理

  讲师管理

  课程分类

  课程管理

  统计分析

  内容管理等等

前端截图如下

后端截图

3. 前端

3.1 首页 

3.2 课程

提供按照课程的类别,类型以及搜索框进行快速查询相关课程

3.3 登入注册

4. 后端

4.1 登录

4.2 权限管理

包括用户管理,角色管理,菜单管理,可以查看对应的信息并添加,修改或删除

角色管理界面可以为角色分配权限

4.3 课程管理

可以添加课程,对课程进行分类管理

4.4其他部分截图

5、系统的核心代码

统一结果集

package com.stu.service.base.result;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

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

/**

 - 全局统一返回结果
 */
@Data
@ApiModel(value = "全局统一返回结果")
public class R {

    @ApiModelProperty(value = "是否成功")
    private Boolean success;
    @ApiModelProperty(value = "返回码")
    private Integer code;

    @ApiModelProperty(value = "返回消息")
    private String message;

    @ApiModelProperty(value = "返回数据")
    private Map<String, Object> data = new HashMap<>();

    public R() {
    }

    public static R ok() {
        R r = new R();
        r.setSuccess(ResultCodeEnum.SUCCESS.getSuccess());
        r.setCode(ResultCodeEnum.SUCCESS.getCode());
        r.setMessage(ResultCodeEnum.SUCCESS.getMessage());
        return r;
    }

    public static R error() {
        R r = new R();
        r.setSuccess(ResultCodeEnum.UNKNOWN_REASON.getSuccess());
        r.setCode(ResultCodeEnum.UNKNOWN_REASON.getCode());
        r.setMessage(ResultCodeEnum.UNKNOWN_REASON.getMessage());
        return r;
    }

    public static R setResult(ResultCodeEnum resultCodeEnum) {
        R r = new R();
        r.setSuccess(resultCodeEnum.getSuccess());
        r.setCode(resultCodeEnum.getCode());
        r.setMessage(resultCodeEnum.getMessage());
        return r;
    }

    public R success(Boolean success) {
        this.setSuccess(success);
        return this;
    }

    public R message(String message) {
        this.setMessage(message);
        return this;
    }

    public R code(Integer code) {
        this.setCode(code);
        return this;
    }

    public R data(String key, Object value) {
        this.data.put(key, value);
        return this;
    }

    public R data(Map<String, Object> map) {
        this.setData(map);
        return this;
    }

}

枚举类

package com.stu.service.base.result;

import lombok.Getter;
import lombok.ToString;

/**
 * 前后端数据交换状态码
 */
@Getter
@ToString
public enum ResultCodeEnum {

    SUCCESS(true, 20000, "成功"),
    UNKNOWN_REASON(false, 20001, "未知错误"),
    UPDATE_ERROR(false, 20002, "更新失败"),

    BAD_SQL_GRAMMAR(false, 21001, "sql 语法错误"),
    JSON_PARSE_ERROR(false, 21002, "json 解析异常"),
    PARAM_ERROR(false, 21003, "参数不正确"),

    FILE_UPLOAD_ERROR(false, 21004, "文件上传错误"),
    FILE_DELETE_ERROR(false, 21005, "文件刪除错误"),
    EXCEL_DATA_IMPORT_ERROR(false, 21006, "Excel 数据导入错误"),

    VIDEO_UPLOAD_ALIYUN_ERROR(false, 22001, "视频上传至阿里云失败"),
    VIDEO_UPLOAD_TOMCAT_ERROR(false, 22002, "视频上传至业务服务器失败"),
    VIDEO_DELETE_ALIYUN_ERROR(false, 22003, "阿里云视频文件删除失败"),
    FETCH_VIDEO_UPLOADAUTH_ERROR(false, 22004, "获取上传地址和凭证失败"),
    REFRESH_VIDEO_UPLOADAUTH_ERROR(false, 22005, "刷新上传地址和凭证失败"),
    FETCH_PLAYAUTH_ERROR(false, 22006, "获取播放凭证失败"),

    URL_ENCODE_ERROR(false, 23001, "URL编码失败"),
    ILLEGAL_CALLBACK_REQUEST_ERROR(false, 23002, "非法回调请求"),
    FETCH_ACCESSTOKEN_FAILD(false, 23003, "获取 accessToken 失败"),
    FETCH_USERINFO_ERROR(false, 23004, "获取用户信息失败"),
    LOGIN_ERROR(false, 23005, "登录失败"),

    COMMENT_EMPTY(false, 24006, "评论内容必须填写"),

    PAY_RUN(false, 25000, "支付中"),
    PAY_UNIFIEDORDER_ERROR(false, 25001, "统一下单错误"),
    PAY_ORDERQUERY_ERROR(false, 25002, "查询支付结果错误"),

    ORDER_EXIST_ERROR(false, 25003, "课程已购买"),

    GATEWAY_ERROR(false, 26000, "服务不能访问"),

    CODE_ERROR(false, 28000, "验证码错误"),

    LOGIN_PHONE_ERROR(false, 28009, "手机号码不正确"),
    LOGIN_MOBILE_ERROR(false, 28001, "账号不正确"),
    LOGIN_PASSWORD_ERROR(false, 28008, "密码不正确"),
    LOGIN_DISABLED_ERROR(false, 28002, "该用户已被禁用"),
    REGISTER_MOBLE_ERROR(false, 28003, "手机号已被注册"),
    LOGIN_AUTH(false, 28004, "需要登录"),
    LOGIN_ACL(false, 28005, "没有权限"),
    SMS_SEND_ERROR(false, 28006, "短信发送失败"),
    SMS_SEND_ERROR_BUSINESS_LIMIT_CONTROL(false, 28007, "短信发送过于频繁"),

    DIVIDE_ZERO(false, 29001, "除零错误"),

    DATA_NULL(false, 30001, "数据不存在!"),
    DATA_EXITS(false, 30002, "数据已存在!"),
    DATA_NODE_EXITS(false, 30003, "该章节下存在视频课程,请先删除视频课程!");
    private final Boolean success;

    private final Integer code;

    private final String message;

    ResultCodeEnum(Boolean success, Integer code, String message) {
        this.success = success;
        this.code = code;
        this.message = message;
    }

}

token认证

package com.stu.service.base.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.joda.time.DateTime;
import org.springframework.util.StringUtils;

import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;

/******************************
 * 用途说明:jwt工具类
 * 作者姓名: Administrator
 * 创建时间: 2022-06-30 21:48
 ******************************/
public class JwtUtils {

    public static final String APP_SECRET = "sdfGRRD323FGSfdrtr4233";

    private static Key getKeyInstance() {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        byte[] bytes = DatatypeConverter.parseBase64Binary(APP_SECRET);
        return new SecretKeySpec(bytes, signatureAlgorithm.getJcaName());
    }

    /**
     * 获取 jwt token
     *
     * @param jwtInfo 实体
     * @param expire  过期时间 m
     * @return 生成的 token
     */
    public static String getJwtToken(JwtInfo jwtInfo, int expire) {

        String JwtToken = Jwts.builder()
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")
                // 主题
                .setSubject("javaclimb-user")
                // 颁发时间
                .setIssuedAt(new Date())
                // 过期时间
                .setExpiration(DateTime.now().plusSeconds(expire).toDate())
                // 用户id
                .claim("id", jwtInfo.getId())
                // 用户昵称
                .claim("nickName", jwtInfo.getNickName())
                // 用户头像
                .claim("avatar", jwtInfo.getAvatar())
                .signWith(SignatureAlgorithm.HS256, getKeyInstance())
                .compact();

        return JwtToken;
    }

    /**
     * 判断token是否存在与有效
     *
     * @param jwtToken
     * @return
     */
    public static boolean checkJwtToken(String jwtToken) {
        if (StringUtils.isEmpty(jwtToken)) {
            return false;
        }
        try {
            Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 判断token是否存在与有效
     *
     * @param request
     * @return
     */
    public static boolean checkJwtToken(HttpServletRequest request) {
        String jwtToken = request.getHeader("token");
        return checkJwtToken(jwtToken);
    }

    /**
     * 根据token获取会员信息
     *
     * @param request
     * @return
     */
    public static JwtInfo getMemberIdByJwtToken(HttpServletRequest request) {
        String jwtToken = request.getHeader("token");
        if (StringUtils.isEmpty(jwtToken)) {
            return null;
        }
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody();
        JwtInfo jwtInfo = new JwtInfo(claims.get("id").toString(), claims.get("nickName").toString(), claims.get(
                "avatar").toString());
        return jwtInfo;
    }
}

Redis

package com.stu.service.base.config;

/******************************
 * 用途说明:
 * 作者姓名: Administrator
 * 创建时间: 2022-06-29 23:15
 ******************************/

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.io.Serializable;
import java.time.Duration;

/**
 * Redis配置
 */
@Configuration
@EnableCaching
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory connectionFactory) {
        RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();
        // 配置数据连接池
        redisTemplate.setConnectionFactory(connectionFactory);

        // 序列化自定义
        // key 的序列化
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        // value 的序列化
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        return redisTemplate;
    }

    @Bean
    public CacheManager cacheManager(LettuceConnectionFactory connectionFactory) {

        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                //过期时间600秒
                .entryTtl(Duration.ofSeconds(600))
                // 配置序列化
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
                .disableCachingNullValues();

        RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }

}

swagger

package com.stu.service.base.config;


import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * @author javaclimb
 * swagger2 配置文件
 */
@Configuration
@EnableSwagger2
public class Swagger2Config {

    @Bean
    public Docket webApiConfig() {
        return new Docket(DocumentationType.SWAGGER_2)
                // 分组名
                .groupName("WebApi")
                .apiInfo(apiInfo())
                .select()
//                .paths(Predicates.not(PathSelectors.regex("/admin/.*")))
                .paths(Predicates.not(PathSelectors.regex("/error.*")))
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Java攀登网API文档")
                .description("描述服务端的 api 接口定义")
                .version("1.0")
                .contact(new Contact("javaclimb", "http://www.javaclimb.com", "[email protected]"))
                .build();
    }

}

全局异常和自定义异常

package com.stu.service.base.handler;

import com.stu.service.base.exception.CustomException;
import com.stu.service.base.result.R;
import com.stu.service.base.utils.ExceptionUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/******************************
 * 用途说明:统一异常处理
 * 作者姓名: Administrator
 * 创建时间: 2022-04-17 23:32
 ******************************/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {


    /***********************************
     * 用途说明:自定义异常
     * 返回值说明: com.stu.service.base.result.R
     ***********************************/
    @ExceptionHandler(CustomException.class)
    @ResponseBody
    public R customException(CustomException e){
//        e.printStackTrace();
        log.error(e.getMessage());
        log.error(ExceptionUtils.getMessage(e));
        return R.error().message(e.getMessage()).code(e.getCode());
    }
    /***********************************
     * 用途说明:数字异常
     * 返回值说明: com.stu.service.base.result.R
     ***********************************/
    @ExceptionHandler(ArithmeticException.class)
    @ResponseBody
    public R arithmeticException(Exception e){
        log.error(ExceptionUtils.getMessage(e));
        return R.error().message(e.getMessage());
    }
    /***********************************
     * 用途说明:全局异常
     * 返回值说明: com.stu.service.base.result.R
     ***********************************/
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public R error(Exception e){
        log.error(ExceptionUtils.getMessage(e));
        return R.error().message(e.getMessage());
    }

}
    package com.stu.service.base.exception;

    import com.stu.service.base.result.ResultCodeEnum;
    import lombok.Data;

    /******************************
     * 用途说明:自定义异常
     * 作者姓名: Administrator
     * 创建时间: 2022-04-18 0:00
     ******************************/
    @Data
    public class CustomException extends RuntimeException{

        private Integer code;

        public CustomException(ResultCodeEnum resultCodeEnum){
            super(resultCodeEnum.getMessage());
            //this.message = resultCodeEnum.getMessage();
            this.code = resultCodeEnum.getCode();

        }

    }

课程部分

package com.stu.service.edu.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.stu.service.base.dto.CourseDto;
import com.stu.service.base.exception.CustomException;
import com.stu.service.base.result.ResultCodeEnum;
import com.stu.service.edu.entity.Chapter;
import com.stu.service.edu.entity.Course;
import com.stu.service.edu.entity.CourseDescription;
import com.stu.service.edu.entity.constant.CourseConstant;
import com.stu.service.edu.entity.form.CourseInfoForm;
import com.stu.service.edu.entity.vo.ApiCourseVo;
import com.stu.service.edu.entity.vo.CoursePublishVo;
import com.stu.service.edu.entity.vo.CourseQueryVo;
import com.stu.service.edu.entity.vo.CourseVo;
import com.stu.service.edu.mapper.CourseMapper;
import com.stu.service.edu.service.ChapterService;
import com.stu.service.edu.service.CourseDescriptionService;
import com.stu.service.edu.service.CourseService;
import com.stu.service.edu.service.VideoService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * <p>
 * 课程 服务实现类
 * </p>
 *
 * @author stu
 * @since 2022-05-05
 */
@Service
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements CourseService {

    @Autowired
    private CourseDescriptionService courseDescriptionService;

    @Autowired
    private VideoService videoService;

    @Autowired
    private ChapterService chapterService;

    /***********************************
     * 用途说明:保存课程基本信息和课程简介
     * 返回值说明: com.stu.service.edu.entity.Course
     ***********************************/
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Course saveCourseInfo(CourseInfoForm courseInfoForm) {

        //保存课程基本信息
        Course course = new Course();
        BeanUtils.copyProperties(courseInfoForm, course);
        course.setStatus(CourseConstant.STATUS_DRAFT);//未发布
        baseMapper.insert(course);

        //保存课程简介
        CourseDescription courseDescription = new CourseDescription();
        courseDescription.setDescription(courseInfoForm.getDescription());
        //设置课程和描述是一对一的关系,即课程id和描述id相等。
        //EduCourseDescription实体类主键生成策略改为input。
        courseDescription.setId(course.getId());
        courseDescriptionService.save(courseDescription);

        return course;
    }

    /***********************************
     * 用途说明:取得课程的基本信息和课程简介
     * 返回值说明: com.stu.service.edu.entity.form.CourseInfoForm
     ***********************************/
    @Override
    public CourseInfoForm getCourseForm(String id) {
        return baseMapper.getCourseForm(id);
    }

    /***********************************
     * 用途说明:更新课程基本信息和课程简介
     * 返回值说明: boolean
     ***********************************/
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateCourseForm(CourseInfoForm courseInfoForm) {
        //课程基本信息
        Course course = new Course();
        BeanUtils.copyProperties(courseInfoForm, course);
        int countUpdate = baseMapper.updateById(course);

        //课程简介
        CourseDescription courseDescription = new CourseDescription();
        courseDescription.setId(course.getId());
        courseDescription.setDescription(courseInfoForm.getDescription());

        boolean countUpdateDescriptiong = courseDescriptionService.updateById(courseDescription);

        return (countUpdate > 0 && countUpdateDescriptiong);
    }

    /***********************************
     * 用途说明:
     * @param courseId
     * 返回值说明:查询课程发布信息
     * @return com.stu.service.edu.entity.vo.CoursePublishVo
     ***********************************/
    @Override
    public CoursePublishVo getCoursePublishById(String courseId) {
        return baseMapper.getCoursePublishById(courseId);
    }

    @Override
    public boolean publishCourse(String id) {
        Course course = new Course();
        course.setId(id);
        course.setStatus(CourseConstant.STATUS_NORMAL);
        return baseMapper.updateById(course) > 0;
    }

    /***********************************
     * 用途说明:课程分页
     * @param page
     * @param limit
     * @param courseQueryVo
     * 返回值说明:
     * @return com.baomidou.mybatisplus.core.metadata.IPage<com.stu.service.edu.entity.vo.CoursePublishVo>
     ***********************************/
    @Override
    public IPage<CourseVo> pageCourses(Long page, Long limit, CourseQueryVo courseQueryVo) {

        QueryWrapper<CourseVo> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("t1.gmt_modified").orderByDesc("t1.id");

        String title = courseQueryVo.getTitle();

        if (!StringUtils.isEmpty(title)) {
            queryWrapper.like("t1.title", title);
        }
        Page<CourseVo> pageParam = new Page<>(page, limit);
        List<CourseVo> coursePublishVos = baseMapper.pageCourses(pageParam, queryWrapper);
        pageParam.setRecords(coursePublishVos);
        return pageParam;
    }

    /***********************************
     * 用途说明:根據id刪除課程(包括课时,小节,详情等)
     * @param id
     * 返回值说明:
     * @return boolean
     ***********************************/
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteById(String id) {
        boolean result = false;
        //删除video课时
        boolean videoResult = videoService.removeVideoByCourseId(id);
        //删除description课程详情
        QueryWrapper<Chapter> chapterQueryWrapper = new QueryWrapper<Chapter>();
        chapterQueryWrapper.eq("course_id", id);
        boolean chapterResult = chapterService.remove(chapterQueryWrapper);

        boolean desResult = courseDescriptionService.removeById(id);

        //删除course课程
        int courseResult = baseMapper.deleteById(id);
        if (videoResult && chapterResult && courseResult > 0) {
            return true;
        } else {
            result = false;
            throw new CustomException(ResultCodeEnum.UPDATE_ERROR);


        }

    }

    /***********************************
     * 用途说明:根据游览数量取得热门课程
     * 返回值说明:
     * @return java.util.List<com.stu.service.edu.entity.Course>
     ***********************************/
    @Cacheable(value = "index", key = "'listHotCourses'")
    @Override
    public List<Course> listHotCourses() {
        QueryWrapper<Course> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("view_count");
        queryWrapper.last("limit 8");
        return baseMapper.selectList(queryWrapper);
    }

    /***********************************
     * 用途说明:根据讲师id查询所有课程
     * @param id
     * 返回值说明:
     * @return java.util.List<com.stu.service.edu.entity.Course>
     ***********************************/
    @Override
    public List<Course> listCourseByTeacherId(String id) {
        QueryWrapper<Course> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("gmt_modified");
        queryWrapper.eq("teacher_id", id);
        return baseMapper.selectList(queryWrapper);
    }

    /***********************************
     * 用途说明:前端课程分页nuxt
     * @param pageParam
     * @param courseQueryVo
     * 返回值说明:
     * @return java.util.Map<java.lang.String, java.lang.Object>
     ***********************************/
    @Override
    public Map<String, Object> listPageCourses(Page<Course> pageParam, CourseQueryVo courseQueryVo) {
        QueryWrapper<Course> queryWrapper = new QueryWrapper<>();
        if (null != courseQueryVo) {
            String subjectParentId = courseQueryVo.getSubjectParentId();
            String subjectId = courseQueryVo.getSubjectId();
            //"销量排序"
            String buyCountSort = courseQueryVo.getBuyCountSort();
            //"最新时间排序"
            String gmtCreateSort = courseQueryVo.getGmtCreateSort();
            //"价格排序"
            String priceSort = courseQueryVo.getPriceSort();

            if (StringUtils.isNotEmpty(subjectParentId)) {
                queryWrapper.eq("subject_parent_id", subjectParentId);
            }
            if (StringUtils.isNotEmpty(subjectId)) {
                queryWrapper.eq("subject_id", subjectId);
            }
            if (StringUtils.isNotEmpty(buyCountSort)) {
                queryWrapper.orderByDesc("buy_count");
            }
            if (StringUtils.isNotEmpty(gmtCreateSort)) {
                queryWrapper.orderByDesc("gmt_create");
            }
            if (StringUtils.isNotEmpty(priceSort)) {
                queryWrapper.orderByDesc("price");
            }
        }
        baseMapper.selectPage(pageParam, queryWrapper);
        Map<String, Object> mapParam = new HashMap<>();
        mapParam.put("items", pageParam.getRecords());//当前页记录
        mapParam.put("current", pageParam.getCurrent());//当前页
        mapParam.put("pages", pageParam.getPages());//共有多少页
        mapParam.put("size", pageParam.getSize());//每页的记录数
        mapParam.put("total", pageParam.getTotal());//总记录数
        mapParam.put("hasNext", pageParam.hasNext());//是否有上一页
        mapParam.put("hasPrevious", pageParam.hasPrevious());//是否有下一页
        return mapParam;
    }

    /***********************************
     * 用途说明:根据课程id查询详情nuxt
     * @param id
     * 返回值说明:
     * @return com.stu.service.edu.entity.vo.ApiCourseVo
     ***********************************/
    @Override
    public ApiCourseVo getApiCourseById(String id) {
        this.updateViewCount(id);
        return baseMapper.getApiCourseById(id);
    }

    /***********************************
     * 用途说明:更新游览数
     * @param id
     * 返回值说明:
     * @return boolean
     ***********************************/
    @Override
    public boolean updateViewCount(String id) {
        Course course = baseMapper.selectById(id);
        course.setViewCount(course.getViewCount() + 1);
        return baseMapper.updateById(course) > 0;
    }

    /***********************************
     * 用途说明:根据课程id查询订单相关的课程信息
     * @param id
     * 返回值说明:
     * @return com.stu.service.edu.entity.vo.CourseApiVo
     ***********************************/
    @Override
    public CourseDto getCourseById(String id) {
        return baseMapper.getCourseById(id);
    }

    /***********************************
     * 用途说明:更新課程銷售量
     * @param id
     * 返回值说明:
     * @return boolean
     ***********************************/
    @Override
    public boolean updateBuyCountById(String id) {
        Course course = baseMapper.selectById(id);
        course.setBuyCount(course.getBuyCount() + 1);
        return baseMapper.updateById(course) > 0;


    }
}

权限部分

package com.stu.service.acl.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.stu.service.acl.entity.Permission;
import com.stu.service.acl.entity.RolePermission;
import com.stu.service.acl.entity.User;
import com.stu.service.acl.mapper.PermissionMapper;
import com.stu.service.acl.service.PermissionService;
import com.stu.service.acl.service.RolePermissionService;
import com.stu.service.acl.service.UserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 权限 服务实现类
 * </p>
 *
 * @author stu
 * @since 2022-08-16
 */
@Service
public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permission> implements PermissionService {

    @Autowired
    private RolePermissionService rolePermissionService;

    @Autowired
    private UserService userService;

    /***********************************
     * 用途说明:查询所有权限菜单
     * 返回值说明:
     * @return java.util.List<com.stu.service.acl.entity.Permission>
     ***********************************/
    @Override
    public List<Permission> ListAllPermissions() {
        QueryWrapper<Permission> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("id");
        return bulidPermission(baseMapper.selectList(queryWrapper));
    }

    /***********************************
     * 用途说明:把返回所有菜单list集合进行封装的方法
     * @param list
     * 返回值说明:
     * @return java.util.List<com.stu.service.acl.entity.Permission>
     ***********************************/
    private List<Permission> bulidPermission(List<Permission> list) {
        //创建list集合,用于数据最终封装
        List<Permission> finalNode = new ArrayList<>();

        //把所有菜单list集合遍历,得到顶层菜单 pid=0菜单,设置level是1
        for (Permission permission : list) {
            //得到顶层菜单 pid=0菜单
            if ("0".equals(permission.getPid())) {
                permission.setLevel(1);
                //根据顶层菜单,向里面进行查询子菜单,封装到finalNode里面
                finalNode.add(selectChildren(permission, list));
            }
        }

        return finalNode;
    }

    /***********************************
     * 用途说明:递归查询下级菜单
     * @param permission
     * @param list
     * 返回值说明:
     * @return com.stu.service.acl.entity.Permission
     ***********************************/
    private Permission selectChildren(Permission permission, List<Permission> list) {
        //1 因为向一层菜单里面放二层菜单,二层里面还要放三层,把对象初始化
        permission.setChildren(new ArrayList<Permission>());
        //2 遍历所有菜单list集合,进行判断比较,比较id和pid值是否相同
        for (Permission child : list) {
            if (permission.getId().equals(child.getPid())) {
                child.setLevel(permission.getLevel() + 1);
                if (child.getChildren() == null) {
                    child.setChildren(new ArrayList<>());
                }
//                permission.getChildren().add(child);
//                selectChildren(child,list);
                permission.getChildren().add(selectChildren(child, list));
            }
        }
        return permission;

    }

    /***********************************
     * 用途说明:递归删除菜单
     * @param id
     * 返回值说明:
     * @return boolean
     ***********************************/
    @Override
    public boolean removeChildById(String id) {
        List<String> idList = new ArrayList<>();
        selectChildListById(id, idList);
        idList.add(id);
        return baseMapper.deleteBatchIds(idList) > 0;
    }

    /***********************************
     * 用途说明:根據角色獲取菜單
     * @param id
     * 返回值说明:
     * @return java.util.List<com.stu.service.acl.entity.Permission>
     ***********************************/
    @Override
    public List<Permission> listAllMenu(String id) {
        //获取所有菜单
        List<Permission> allPermissionList = baseMapper.selectList(new QueryWrapper<>());
        //根据角色id呼气角色权限列表
        List<RolePermission> rolePermissionsList = rolePermissionService
                .list(new QueryWrapper<RolePermission>().eq("role_id", id));
        //遍历所有菜单,获取每一项,看是否在权限列表,如果在,就标记
        List<String> permissionIdList = rolePermissionsList.stream().map(e -> e.getPermissionId()).collect(Collectors.toList());
        allPermissionList.forEach(permission -> {
            if (permissionIdList.contains(permission.getId())) {
                permission.setHasSelect(true);
            } else {
                permission.setHasSelect(false);
            }
        });
        /*for (int i = 0; i < allPermissionList.size(); i++) {
            Permission permission = allPermissionList.get(i);
            for (int m = 0; m < rolePermissionList.size(); m++) {
                RolePermission rolePermission = rolePermissionList.get(m);
                if(rolePermission.getPermissionId().equals(permission.getId())) {
                    permission.setSelect(true);
                }
            }
        }*/
        return bulidPermission(allPermissionList);
    }

    /***********************************
     * 用途说明:给角色分配菜单权限
     * @param roleId
     * @param permissionId
     * 返回值说明:
     * @return boolean
     ***********************************/
    @Override
    public boolean saveRolePermissionrelationShip(String roleId, String[] permissionId) {

        //删除旧的权限
        rolePermissionService.remove(new QueryWrapper<RolePermission>().eq("role_id", roleId));
        List<RolePermission> list = new ArrayList<>();
        for (String id : permissionId) {
            RolePermission rolePermission = new RolePermission();
            rolePermission.setRoleId(roleId);
            rolePermission.setPermissionId(id);
            list.add(rolePermission);
        }

        return rolePermissionService.saveBatch(list);
    }

    /***********************************
     * 用途说明:根据用户id查询有权限的菜单
     * @param id
     * 返回值说明:
     * @return java.util.List<java.lang.String>
     ***********************************/
    @Override
    public List<String> selectPermissionValueListByUserId(String id) {
        List<String> list;
        if (checkAdmin(id)) {
            //如果是超级管理员获取所有权限
            list = baseMapper.selectAllPermissionValue();
        } else {
            //根据用户id查询所有权限
            list = baseMapper.selectPermissionValueByUserId(id);
        }
        return list;
    }

    /***********************************
     * 用途说明:根据用户id查询所有权限的菜单详细列表
     * @param userId
     * 返回值说明:
     * @return java.util.List<org.json.JSONObject>
     ***********************************/
    @Override
    public List<JSONObject> selectPermissionByUserId(String userId) {
        List<Permission> selectPermissionList = null;
        if (checkAdmin(userId)) {
            //如果是超级管理员获取所有权限
            selectPermissionList = baseMapper.selectList(null);
        } else {
            //根据用户id查询所有权限
            selectPermissionList = baseMapper.selectPermissionByUserId(userId);
        }
        //先转换成树状
        List<Permission> permissionList = bulidPermission(selectPermissionList);
        //然后转化成前端需要的格式
        List<JSONObject> result = bulidJson(permissionList);

        return result;
    }

    /***********************************
     * 用途说明:转化成前端需要的格式
     * @param permissionList
     * 返回值说明:
     * @return java.util.List<org.json.JSONObject>
     ***********************************/
    private List<JSONObject> bulidJson(List<Permission> permissionList) {
        List<JSONObject> menus = new ArrayList<>();
        if (permissionList.size() == 1) {
            Permission topNode = permissionList.get(0);
            //组建左侧一级菜单
            List<Permission> oneMenuList = topNode.getChildren();
            for (Permission one : oneMenuList) {
                JSONObject oneMenu = new JSONObject();
                oneMenu.put("path", one.getPath());
                oneMenu.put("component", one.getComponent());
                oneMenu.put("redirect", "noredirect");//第一级不需要重定向
                oneMenu.put("name", "name_" + one.getId());
                oneMenu.put("hidden", false);//一级不需要因此,3级需要
                JSONObject oneMeta = new JSONObject();
                oneMeta.put("title", one.getName());
                oneMeta.put("icon", one.getIcon());
                oneMenu.put("meta", oneMeta);

                List<JSONObject> children = new ArrayList<>();
                List<Permission> twoMenuList = one.getChildren();//二级菜单
                for (Permission two : twoMenuList) {
                    JSONObject twoMenu = new JSONObject();
                    twoMenu.put("path", two.getPath());
                    twoMenu.put("component", two.getComponent());
//                    twoMenu.put("redirect", "noredirect");//第一级不需要重定向
                    twoMenu.put("name", "name_" + two.getId());
                    twoMenu.put("hidden", false);//一级不需要因此,3级需要
                    JSONObject twoMeta = new JSONObject();
                    twoMeta.put("title", two.getName());
                    twoMeta.put("icon", two.getIcon());
                    twoMenu.put("meta", twoMeta);
                    children.add(twoMenu);

                    //功能按钮
                    List<Permission> threeMenuList = two.getChildren();
                    for (Permission three : threeMenuList) {
                        if (StringUtils.isEmpty(three.getPath())) {
                            continue;
                        }
                        JSONObject threeMenu = new JSONObject();
                        threeMenu.put("path", three.getPath());
                        threeMenu.put("component", three.getComponent());
//                        threeMenu.put("redirect", "noredirect");//第一级不需要重定向
                        threeMenu.put("name", "name_" + three.getId());
                        threeMenu.put("hidden", true);//一级不需要因此,3级需要
                        JSONObject threeMeta = new JSONObject();
                        threeMeta.put("title", three.getName());
                        threeMeta.put("icon", three.getIcon());
                        threeMenu.put("meta", threeMeta);

                        children.add(threeMenu);
                    }

                }
                oneMenu.put("children", children);
                menus.add(oneMenu);

            }
        }
        return menus;
    }

    /***********************************
     * 用途说明:判断是否管理员
     * @param id
     * 返回值说明:
     * @return boolean
     ***********************************/
    private boolean checkAdmin(String id) {
        User user = userService.getById(id);
        if (user != null && "admin".equals(user.getUsername())) {
            return true;
        }
        return false;
    }

    /***********************************
     * 用途说明:根据当前菜单id查询他的子子孙孙id,封装到list集合
     * @param id
     * @param idList
     * 返回值说明:
     ***********************************/
    private void selectChildListById(String id, List<String> idList) {
        //查询当前菜单的下级
        QueryWrapper<Permission> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("pid", id);
        queryWrapper.select("id");
        List<Permission> childList = baseMapper.selectList(queryWrapper);
        //把childIdList里面菜单id值获取出来,封装idList里面,做递归查询
        childList.forEach(item -> {
            idList.add(item.getId());
            selectChildListById(item.getId(), idList);
        });

    }

}

角色

package com.stu.service.acl.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.stu.service.acl.entity.Permission;
import com.stu.service.acl.entity.Role;
import com.stu.service.acl.entity.RolePermission;
import com.stu.service.acl.entity.UserRole;
import com.stu.service.acl.mapper.RoleMapper;
import com.stu.service.acl.service.RoleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.stu.service.acl.service.UserRoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author stu
 * @since 2022-08-16
 */
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {

    @Autowired
    private UserRoleService userRoleService;

    /***********************************
     * 用途说明:根据用户获取角色
     * @param userId
     * 返回值说明:
     * @return java.util.List<com.stu.service.acl.entity.Permission>
     ***********************************/
    @Override
    public Map<String, Object> findRoleByUserId(String userId) {
        //获取所有角色
        List<Role> allRoleList = baseMapper.selectList(new QueryWrapper<>());
        //根据用户id获取角色列表
        List<UserRole> existUserRoleList = userRoleService
                .list(new QueryWrapper<UserRole>().eq("user_id", userId).select("role_id"));
        //遍历所有菜单,获取每一项,看是否在权限列表,如果在,就标记
        List<String> existRoleLists = existUserRoleList.stream().map(e -> e.getRoleId()).collect(Collectors.toList());

        List<Role> assignRoles = new ArrayList<>();
        allRoleList.forEach(role -> {
            if (existRoleLists.contains(role.getId())) {
                assignRoles.add(role);
            }
        });

        Map<String, Object> roleMap = new HashMap<>();
        roleMap.put("assignRoles", assignRoles);
        roleMap.put("allRoleList", allRoleList);
        return roleMap;
    }

    /***********************************
     * 用途说明:给用户分配角色权限
     * @param userId
     * @param roleIds
     * 返回值说明:
     * @return boolean
     ***********************************/
    @Override
    public boolean saveUserRelationShip(String userId, String[] roleIds) {
        //删除旧的所有角色权限
        userRoleService.remove(new QueryWrapper<UserRole>().eq("user_id", userId));
        List<UserRole> list = new ArrayList<>();
        for (String id : roleIds) {
            UserRole rolePermission = new UserRole();
            rolePermission.setRoleId(id);
            rolePermission.setUserId(userId);
            list.add(rolePermission);
        }

        return userRoleService.saveBatch(list);
    }

    /***********************************
     * 用途说明:根据userid获取用户信息
     * @param userId
     * 返回值说明:
     * @return java.util.List<com.stu.service.acl.entity.Role>
     ***********************************/
    @Override
    public List<Role> selectRoleByUserId(String userId) {
        //根据用户id获取角色列表
        List<UserRole> userRoleList = userRoleService
                .list(new QueryWrapper<UserRole>().eq("user_id", userId).select("role_id"));
        //遍历所有菜单,获取每一项,看是否在权限列表,如果在,就标记
        List<String> roleIdLists = userRoleList.stream().map(e -> e.getRoleId()).collect(Collectors.toList());
        List<Role> roleList = new ArrayList<>();
        if (roleIdLists.size() > 0) {
            roleList = baseMapper.selectBatchIds(roleIdLists);
        }

        return roleList;
    }
}

文件导入导出

package com.stu.service.edu.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.stu.service.base.exception.CustomException;
import com.stu.service.base.result.ResultCodeEnum;
import com.stu.service.edu.entity.Subject;
import com.stu.service.edu.entity.excel.SubjectExcelData;
import com.stu.service.edu.mapper.SubjectMapper;

/******************************
 * 用途说明:excel导入的listener
 * 作者姓名: Administrator
 * 创建时间: 2022-05-06 0:23
 ******************************/
public class SubJectExcelListener extends AnalysisEventListener<SubjectExcelData> {

    //因为SubJectExcelListener不能交给spring进行管理,需要自己new,不能注入其他对象
    //不能实现数据库操作 new mapper

    private SubjectMapper subjectMapper;

    //让对象可以使用start,创建一个有参构造和一个无参构造
    public SubJectExcelListener() {
    }

    public SubJectExcelListener(SubjectMapper subjectMapper) {
        this.subjectMapper = subjectMapper;
    }
    //让对象可以使用end

    /***********************************
     * 用途说明:遍历每一行数据
     * 返回值说明: void
     ***********************************/
    @Override
    public void invoke(SubjectExcelData subjectExcelData, AnalysisContext analysisContext) {

        if (subjectExcelData == null) {
            throw new CustomException(ResultCodeEnum.EXCEL_DATA_IMPORT_ERROR);
        }
        String oneSubjectTitle = subjectExcelData.getOneSubjectTitle();
        String twoSubjectTitle = subjectExcelData.getTwoSubjectTitle();

        //验证是否已经存在一级分类
        Subject oneSbuject = this.checkOneSubject(oneSubjectTitle);

        if (oneSbuject == null) {
            oneSbuject = new Subject();
            oneSbuject.setTitle(oneSubjectTitle);
            oneSbuject.setParentId("0");
            subjectMapper.insert(oneSbuject);
        }

        //取得二级分类的父id
        String pid = oneSbuject.getId();
        //验证是否已经存在二级分类
        Subject twoSbuject = this.checkTwoSubject(twoSubjectTitle, pid);
        if (twoSbuject == null) {
            twoSbuject = new Subject();
            twoSbuject.setTitle(twoSubjectTitle);
            twoSbuject.setParentId(pid);
            subjectMapper.insert(twoSbuject);
        }

    }

    /***********************************
     * 用途说明:所有数据读取之后的收尾工作
     * 返回值说明: void
     ***********************************/
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }

    /***********************************
     * 用途说明:验证是否已经存在一级分类
     * 返回值说明: com.stu.service.edu.entity.Subject
     ***********************************/
    private Subject checkOneSubject(String title) {
        QueryWrapper<Subject> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("title", title);
        return subjectMapper.selectOne(queryWrapper);

    }

    /***********************************
     * 用途说明:验证是否已经存在二级分类
     * 返回值说明: com.stu.service.edu.entity.Subject
     ***********************************/
    private Subject checkTwoSubject(String title, String pid) {
        QueryWrapper<Subject> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("title", title);
        queryWrapper.eq("parent_id", pid);
        return subjectMapper.selectOne(queryWrapper);

    }
}
package com.stu.service.edu.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.stu.service.edu.entity.Subject;
import com.stu.service.edu.entity.excel.SubjectExcelData;
import com.stu.service.edu.entity.vo.SubjectVo;
import com.stu.service.edu.listener.SubJectExcelListener;
import com.stu.service.edu.mapper.SubjectMapper;
import com.stu.service.edu.service.SubjectService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 课程科目 服务实现类
 * </p>
 *
 * @author stu
 * @since 2022-05-06
 */
@Service
public class SubjectServiceImpl extends ServiceImpl<SubjectMapper, Subject> implements SubjectService {

    @Autowired
    private SubjectMapper subjectMapper;

    @Override
    public void batchImport(MultipartFile file) {

        try {
            EasyExcel.read(file.getInputStream(), SubjectExcelData.class,
                    new SubJectExcelListener(subjectMapper)).excelType(ExcelTypeEnum.XLS).sheet().doRead();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public List<SubjectVo> getAllSubject() {
        //课程分类的树形结构集合
        List<SubjectVo> rootList = new ArrayList<>();

        Map<String, SubjectVo> tempMap = new HashMap<>();
        List<SubjectVo> tempList = new ArrayList<>();

        QueryWrapper<Subject> queryWrapper = new QueryWrapper<>();
        List<Subject> subjectList = baseMapper.selectList(queryWrapper);

        //组装树形结构数据的准备工作
        for (Subject subject : subjectList) {

            SubjectVo subjectVo = new SubjectVo();
            //返回的实体和前端需要的实体vo进行赋值

            BeanUtils.copyProperties(subject, subjectVo);
            tempMap.put(subjectVo.getId(), subjectVo);
            tempList.add(subjectVo);
        }
        //封装课程分类的树形结构数据
        for (SubjectVo vo : tempList) {
            SubjectVo child = vo;
            if ("0".equals(vo.getParentId())) {
                rootList.add(vo);
            } else {
                SubjectVo parent = tempMap.get(child.getParentId());
                parent.getChildList().add(child);
            }
        }

        return rootList;
    }
}

网关

package com.stu.infrastructure.confing;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.util.pattern.PathPatternParser;

/******************************
 * 用途说明:
 * 作者姓名: Administrator
 * 创建时间: 2022-07-27 23:16
 ******************************/
@Configuration
public class CorsConfig {
    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        // #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
        config.addAllowedOrigin("*");
        // #允许访问的头信息,*表示全部
        config.addAllowedHeader("*");
        // 允许提交请求的方法类型,*表示全部允许
        config.addAllowedMethod("*");

        org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource source =
                new org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);
        return new CorsWebFilter(source);
    }

}
package com.stu.infrastructure.filter;

/******************************
 * 用途说明:
 * 作者姓名: Administrator
 * 创建时间: 2022-07-27 23:35
 ******************************/

import com.stu.service.base.utils.JwtUtils;
import com.google.gson.JsonObject;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.List;

@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        // 获取请求对象
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();

        // 匹配路由
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        // 判断路径中如果含有 /api/**/auth/**,则需要鉴权
        if (antPathMatcher.match("/api/**/auth/**", path)) {
            // 获取 token
            List<String> tokenList = request.getHeaders().get("token");
            // 没有 token
            if (tokenList == null) {
                ServerHttpResponse response = exchange.getResponse();
                // 鉴权失败
                return this.out(response);
            }

            // token 检验失败
            boolean isCheck = JwtUtils.checkJwtToken(tokenList.get(0));
            if (!isCheck) {
                ServerHttpResponse response = exchange.getResponse();
                // 鉴权失败
                return this.out(response);
            }
        }
        // 放行: 使用过滤器链,将请求继续向下传递
        return chain.filter(exchange);
    }


    /**
     * 定义当前过滤器的优先级
     *  - 值越小, 优先级越高
     */
    @Override
    public int getOrder() {
        return 0;
    }

    /**
     * 使用 webflux 输入请求信息
     */
    private Mono<Void> out(ServerHttpResponse response) {

        JsonObject message = new JsonObject();
        message.addProperty("success", false);
        message.addProperty("code", 28004);
        message.addProperty("data", "");
        message.addProperty("message", "鉴权失败");
        byte[] bytes = message.toString().getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bytes);
        //指定编码,否则在浏览器中会中文乱码
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        //输出http响应
        return response.writeWith(Mono.just(buffer));
    }

}

SpringSecurity

package com.stu.security.config;

import com.stu.security.filter.TokenAuthenticationFilter;
import com.stu.security.filter.TokenLoginFilter;
import com.stu.security.security.DefaultPasswordEncoder;
import com.stu.security.security.TokenLogoutHandler;
import com.stu.security.security.TokenManager;
import com.stu.security.security.UnauthorizedEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;

/******************************
 * 用途说明:
 * 作者姓名: Administrator
 * 创建时间: 2022-09-01 9:30
 ******************************/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private DefaultPasswordEncoder defaultPasswordEncoder;
    @Autowired
    private TokenManager tokenManager;
    @Autowired
    private RedisTemplate redisTemplate;


    /***********************************
     * 用途说明:密码处理
     * @param auth
     * 返回值说明:
     ***********************************/
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(defaultPasswordEncoder);
    }

    /***********************************
     * 用途说明:配置设置
     * @param http
     * 返回值说明:
     ***********************************/
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.exceptionHandling().authenticationEntryPoint(new UnauthorizedEntryPoint())  //未授权异常处理
                .and().csrf().disable().authorizeRequests().anyRequest().authenticated() //取消跨域所有请求
                .and().logout().logoutUrl("/admin/acl/index/logout")
                .addLogoutHandler(new TokenLogoutHandler(tokenManager, redisTemplate))
                .and().addFilter(new TokenLoginFilter(authenticationManager(), tokenManager, redisTemplate))
                .addFilter(new TokenAuthenticationFilter(authenticationManager(), tokenManager, redisTemplate))
                .httpBasic();

    }

    /***********************************
     * 用途说明:配置哪些请求不拦截
     * @param web
     * 返回值说明:
     ***********************************/
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(
                "/api/**",
                "/swagger-resources/**",
                "/webjars/**",
                "/v2/**",
                "/swagger-ui.html/**"
        );
    }
}
package com.stu.security.entity;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/******************************
 * 用途说明:
 * 作者姓名: Administrator
 * 创建时间: 2022-09-01 9:38
 ******************************/
@Data
@Slf4j
public class SecurityUser implements UserDetails {

    //当前登录用户
    private transient User currentUserInfo;

    //当前登录用户权限
    private List<String> permissionValueList;

    public SecurityUser() {
    }

    public SecurityUser(User user) {
        if (user != null) {
            this.currentUserInfo = user;
        }
    }

    /***********************************
     * 用途说明:获取当前用户的所有权限
     * 返回值说明:
     * @return java.util.Collection<? extends org.springframework.security.core.GrantedAuthority>
     ***********************************/
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        for(String permissionValue:permissionValueList){
            if(StringUtils.isEmpty(permissionValue)){
                continue;
            }
            SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);
            authorities.add(authority);
        }
        return authorities;
    }

    @Override
    public String getPassword() {
        return currentUserInfo.getPassword();
    }

    @Override
    public String getUsername() {
        return currentUserInfo.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}
package com.stu.security.entity;

import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serializable;
import java.util.Date;

/******************************
 * 用途说明:
 * 作者姓名: Administrator
 * 创建时间: 2022-09-01 9:38
 ******************************/
@Data
@ApiModel(value = "User对象", description = "用户表")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "会员id")
    private String id;

    @ApiModelProperty(value = "用户名称")
    private String username;

    @ApiModelProperty(value = "密码")
    private String password;

    @ApiModelProperty(value = "昵称")
    private String nickName;

    @ApiModelProperty(value = "用户头像")
    private String salt;

    @ApiModelProperty(value = "用户签名")
    private String token;


}
package com.stu.security.filter;

import com.stu.security.security.TokenManager;
import com.stu.service.base.result.R;
import com.stu.service.base.utils.ResponseUtil;
import io.netty.util.internal.StringUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/******************************
 * 用途说明:
 * 作者姓名: Administrator
 * 创建时间: 2022-09-01 9:39
 ******************************/
public class TokenAuthenticationFilter extends BasicAuthenticationFilter {

    private AuthenticationManager authenticationManager;
    private TokenManager tokenManager;
    private RedisTemplate redisTemplate;

    public TokenAuthenticationFilter(AuthenticationManager authenticationManager,
                                     TokenManager tokenManager,
                                     RedisTemplate redisTemplate) {
        super(authenticationManager);
        this.tokenManager = tokenManager;
        this.redisTemplate = redisTemplate;

    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws IOException, ServletException {
        if (request.getRequestURI().indexOf("admin") == -1) {
            chain.doFilter(request, response);
            return;
        }
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = null;
        try {
            usernamePasswordAuthenticationToken = getAuthentication(request);
        } catch (Exception e) {
            ResponseUtil.out(response, R.error());
        }
        if (usernamePasswordAuthenticationToken != null) {
            SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        } else {
            ResponseUtil.out(response, R.error());
        }
        chain.doFilter(request, response);
    }

    /***********************************
     * 用途说明:从request获取token,根据token获取权限列表
     * 返回值说明:
     * @return org.springframework.security.authentication.UsernamePasswordAuthenticationToken
     ***********************************/
    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        String token = request.getHeader("token");
        if (token != null && !"".equals(token.trim())) {
            String userName = tokenManager.getUserFromToken(token);
            List<String> permissionValueList = (List<String>) redisTemplate.opsForValue().get(userName);
            Collection<GrantedAuthority> authorities = new ArrayList<>();
            for (String permissionValue : permissionValueList) {
                if (StringUtils.isEmpty(permissionValue)) {
                    continue;
                }
                SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(permissionValue);
                authorities.add(simpleGrantedAuthority);
            }

            return new UsernamePasswordAuthenticationToken(userName, token, authorities);
        }
        return null;
    }
}
package com.stu.security.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.stu.security.entity.SecurityUser;
import com.stu.security.entity.User;
import com.stu.security.security.TokenManager;
import com.stu.service.base.exception.CustomException;
import com.stu.service.base.result.R;
import com.stu.service.base.result.ResultCodeEnum;
import com.stu.service.base.utils.ResponseUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;

/******************************
 * 用途说明:登录过滤器,对用户名称和密码进行校验
 * 作者姓名: Administrator
 * 创建时间: 2022-09-01 9:39
 ******************************/
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;
    private TokenManager tokenManager;
    private RedisTemplate redisTemplate;

    public TokenLoginFilter(AuthenticationManager authenticationManager,
                            TokenManager tokenManager,
                            RedisTemplate redisTemplate) {
        this.authenticationManager = authenticationManager;
        this.tokenManager = tokenManager;
        this.redisTemplate = redisTemplate;
        //设置访问方式,可以post以为的访问
        this.setPostOnly(false);
        this.setRequiresAuthenticationRequestMatcher(
                new AntPathRequestMatcher("/admin/acl/login", "POST"));
    }

    /***********************************
     * 用途说明:验证账号密码是否正确
     * @param request
     * @param response
     * 返回值说明:
     * @return org.springframework.security.core.Authentication
     ***********************************/
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        try {
            User user = new ObjectMapper().readValue(request.getInputStream(), User.class);
            return authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), new ArrayList<>()));
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException(ResultCodeEnum.LOGIN_MOBILE_ERROR);
        }
    }

    /***********************************
     * 用途说明:登录成功
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * 返回值说明:
     ***********************************/
    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {
        SecurityUser user = (SecurityUser) authResult.getPrincipal();
        String token = tokenManager.createToken(user.getCurrentUserInfo().getUsername());
        redisTemplate.opsForValue().set(user.getCurrentUserInfo().getUsername(), user.getPermissionValueList());
        ResponseUtil.out(response, R.ok().data("token", token));
    }

    /***********************************
     * 用途说明:登录失败
     * @param request
     * @param response
     * @param e
     * 返回值说明:
     ***********************************/
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                              AuthenticationException e) throws IOException, ServletException {
        ResponseUtil.out(response, R.error());
    }
}
package com.stu.security.security;


import com.stu.service.base.utils.MD5;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

/******************************
 * 用途说明:
 * 作者姓名: Administrator
 * 创建时间: 2022-09-01 9:39
 ******************************/
@Component
public class DefaultPasswordEncoder implements PasswordEncoder {

    /**
     * 加密密码
     *
     * @param rawPassword
     */
    @Override
    public String encode(CharSequence rawPassword) {
        return MD5.encrypt(rawPassword.toString());
    }

    /**
     * 判断密码是否正确
     *
     * @param rawPassword     the raw password to encode and match
     * @param encodedPassword the encoded password from storage to compare with
     * @return true if the raw password, after encoding, matches the encoded password from
     * storage
     */
    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        return encodedPassword.equals(MD5.encrypt(rawPassword.toString()));
    }
}
package com.stu.security.security;

import com.stu.service.base.result.R;
import com.stu.service.base.utils.ResponseUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutHandler;

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

/******************************
 * 用途说明:
 * 作者姓名: Administrator
 * 创建时间: 2022-09-01 9:39
 ******************************/
public class TokenLogoutHandler implements LogoutHandler {

    private AuthenticationManager authenticationManager;
    private TokenManager tokenManager;
    private RedisTemplate redisTemplate;

    public TokenLogoutHandler(TokenManager tokenManager, RedisTemplate redisTemplate) {
        this.tokenManager = tokenManager;
        this.redisTemplate = redisTemplate;

    }

    @Override
    public void logout(HttpServletRequest request,
                       HttpServletResponse response,
                       Authentication authentication) {
        String token = request.getHeader("token");
        if (token != null) {
            tokenManager.removeToken(token);
            String userName = tokenManager.getUserFromToken(token);
            redisTemplate.delete(userName);
            ResponseUtil.out(response, R.ok());
        }

    }
}
package com.stu.security.security;

import io.jsonwebtoken.CompressionCodecs;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;

import java.util.Date;

/******************************
 * 用途说明:
 * 作者姓名: Administrator
 * 创建时间: 2022-09-01 9:40
 ******************************/
@Component
public class TokenManager {

    private long tokenExpiration = 24*60*60*1000;
    private String tokenSignKey = "123456";

    public String createToken(String username) {
        String token = Jwts.builder().setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
                .signWith(SignatureAlgorithm.HS512, tokenSignKey).compressWith(CompressionCodecs.GZIP).compact();
        return token;
    }

    public String getUserFromToken(String token) {
        String user = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token).getBody().getSubject();
        return user;
    }

    public void removeToken(String token) {
        //jwttoken无需删除,客户端扔掉即可。
    }
}
package com.stu.security.security;

import com.stu.service.base.result.R;
import com.stu.service.base.utils.ResponseUtil;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/******************************
 * 用途说明:
 * 作者姓名: Administrator
 * 创建时间: 2022-09-01 9:40
 ******************************/
public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest httpServletRequest,
                         HttpServletResponse httpServletResponse,
                         AuthenticationException e) throws IOException, ServletException {
        ResponseUtil.out(httpServletResponse, R.error());

    }
}

6、源码获取

源码获取,扫描下面的图片关注公众号

回复:在线教育

不是评论区回复,不是评论区回复,不是评论区回复,扫码关注公众号:技术从南指到北,回复  在线教育 

 

标签:return,SpringBoot,service,stu,强烈建议,源码,import,com,public
From: https://www.cnblogs.com/konglxblog/p/17229662.html

相关文章