基于Springboot的旅游网管理系统后台

news2024/11/15 22:34:33

目录

  • 一. 🦁 研究背景及意义
  • 二. 🦁 技术栈系列及主要功能
    • Ⅰ. 技术栈
    • Ⅱ. 主要功能
  • 三. 🦁 项目搭建
    • Ⅰ. 后端模块
    • Ⅱ. 前端模块
  • 四. 🦁 主要实现过程
    • Ⅰ. 认证和授权
      • 1. SpringSecurity配置类
      • 2. 自定义逻辑认证
    • Ⅱ. 富文本编辑器——wangEditor
    • Ⅲ. 编写AOP实现日志记录功能
      • 1. 编写logback.xml文件
      • 2. 编写日志记录实体类
      • 3. 编写日志AOP类
  • 五. 🦁 实现效果

一. 🦁 研究背景及意义

旅游作为一种经济活动,已经成为世界经济中不可或缺的一部分。旅游业的快速发展,使得旅游产品和服务的供应和需求日益增加,其中旅游网站起着至关重要的作用。近年来,随着移动互联网的普及和技术的进步,旅游网站不断创新和发展,为旅游市场的发展贡献了巨大的力量。

旅游网站的研究背景在于,通过对旅游网站的市场现状、用户需求和技术创新等方面进行研究,以更好地了解旅游网站的发展趋势和用户特征,进而为旅游网站的设计和优化提供依据,提升旅游网站的服务质量和用户体验。

旅游网站的研究意义在于,为旅游网站的设计和优化提供了思路和方法,以满足用户多样化的需求。同时,可以帮助企业更好地把握市场的机遇和挑战,提升旅游产品和服务的质量和效益。此外,随着旅游业的不断发展,旅游网站的研究也为旅游业的升级和转型提供了有价值的参考和借鉴。综上所述,旅游网站的研究具有十分重要和现实的意义。

二. 🦁 技术栈系列及主要功能

Ⅰ. 技术栈

  • Springboot
  • SpringMVC
  • MySQL
  • Mybatis-Plus
  • SpringSecurity
  • Thymeleaf
  • AdminLTE2
  • lombok、ajax、logback

Ⅱ. 主要功能

本系统基于以上技术来实现一个完善的旅游网后台管理系统,具有以下主要功能:

  • 管理员管理
  • 角色管理
  • 权限管理
  • 认证和授权
  • 产品类型管理
  • 旅游产品管理

三. 🦁 项目搭建

Ⅰ. 后端模块

该项目和平常项目差不多,后端模块按平常Springboot项目结构搭建即可,如下:
在这里插入图片描述

Ⅱ. 前端模块

在项目中,我们使用AdminLTE框架作为管理员端页面,使用自己编写的网页作为用户端页面。

tips:
AdminLTE是一款建立在bootstrap和jquery之上的开源的模板主题工具,它提供了一系列响应的、 可重复使用的组件,并内置了多个模板页面;同时自适应多种屏幕分辨率,兼容PC和移动端。通过AdminLTE,我们可以快速的创建一个响应式的Html5网站。AdminLTE框架在网页架构与设计上,有很大的辅助作用,尤其是前端架构设计师,用好AdminLTE 不但美观,而且可以免去写很大CSS与JS的工作量。

使用AdminLTE非常简单,只需要根据需求将需要的组件复制到我们的页面中即可。
在这里插入图片描述

四. 🦁 主要实现过程

主要挑几个核心部分来展开描述:

Ⅰ. 认证和授权

1. SpringSecurity配置类

众所周知,SpringSecurity遵循RBAC原则,即角色,权限,用户表,在我们添加完这个表的实体类以及一些常规CRUD操作后,就可以开始编写系统的认证和授权了。
如何来编写一个权限表?详情请看狮子前面的文章:点击

import com.lion.security.handler.MyAccessDeniedHandler;
import com.lion.security.handler.MyLoginFailureHandler;
import com.lion.security.handler.MyLoginSuccessHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)     // 开启注解-鉴权配置
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // Spring Security配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .loginPage("/backstage/admin_login")         //自定义表单登录
                .usernameParameter("username")          //用户名
                .passwordParameter("password")          //密码
                .loginProcessingUrl("/backstage/admin/login")   //登录提交路径,提交后执行认证逻辑
                .successHandler(new MyLoginSuccessHandler())    //登录成功跳转路径
                .failureHandler(new MyLoginFailureHandler());    //登录失败跳转路径
//          权限拦截配置
        http.authorizeRequests()
                .antMatchers("/backstage/admin/login").permitAll()      //登录不需要认证
                .antMatchers("/backstage/admin_fail").permitAll()       //登录失败不需要认证
                .antMatchers("/backstage/admin_login").permitAll()      //登录页不需要认证
                .antMatchers("/**/*.css","/**/*.js").permitAll()        //静态资源放行
                .antMatchers("/backstage/**").authenticated();          //剩下的都要认证

//        退出登录配置
        http.logout()
                .logoutUrl("/backstage/admin/logout")       //退出登录的路径
                .logoutSuccessUrl("/backstage/admin_login") //退出登录成功后跳转的页面
                .clearAuthentication(true)                  //退出成功后清除认证信息
                .invalidateHttpSession(true);                //退出成功后清除session
//            异常处理
        http.exceptionHandling()
                .accessDeniedHandler(new MyAccessDeniedHandler());  //权限不足处理器

//        关闭csrf防护
        http.csrf().disable();
//        开启跨域访问
        http.cors();

    }
//    密码加密器
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

security的配置类写法很类似,咱们分块来实现:

自定义表单登录块——>权限拦截配置——>退出登录配置——>异常处理——>关闭csrf防护——> 开启跨域访问——>密码加密器
根据自己需要来编写相应模块,详细目录如下:
在这里插入图片描述

2. 自定义逻辑认证

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lion.domain.Admin;
import com.lion.domain.Permission;
import com.lion.mapper.AdminMapper;
import com.lion.service.AdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

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

@Service
public class MyUserDetailService implements UserDetailsService {
    @Autowired
    private AdminService adminService;


    // 自定义认证逻辑
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 1.认证
        Admin admin = adminService.findByAdminName(username);
        if (admin == null) {
            throw new UsernameNotFoundException("用户不存在");
        }
//        admin的状态是不可登录则报错(自定义)
        if (!admin.isStatus()){
            throw new UsernameNotFoundException("用户不可用");
        }


        // 2.授权
        List<Permission> permissions = adminService.findAllPermission(username);
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        for (Permission permission : permissions) {
            grantedAuthorities.add(new SimpleGrantedAuthority(permission.getPermissionDesc()));
        }


        // 3.封装为UserDetails对象
        UserDetails userDetails = User.withUsername(admin.getUsername())
                .password(admin.getPassword())
                .authorities(grantedAuthorities)
                .build();


        // 4.返回封装好的UserDetails对象
        return userDetails;
    }
}

Ⅱ. 富文本编辑器——wangEditor

在项目中,我们使用中国人开发的一个富文本编辑器——wangEditor,官网地
址:https://www.wangeditor.com/
使用步骤如下:

  1. 在前端页面中引入 wangEditor.js
  2. 在页面中加入 wangEditor 插件
  3. 编写实体类返回值

wangEditor要求上传图片的返回值必须按照它的要求,所以我们创建一个实体类,用于返回上传的结果。

import lombok.Data;

@Data
public class WangEditorResult {
    private int errno;
    private String[] data;
}

  1. 编写上传控制器,接收富文本编辑器上传的图片
@RequestMapping(value = "/upload")
    @ResponseBody
    public WangEditorResult upload(HttpServletRequest request, MultipartFile file) throws Exception {
        // 创建文件夹,存放上传文件。
        //1.设置上传文件夹的真实路径
        String realPath = ResourceUtils.getURL("classpath:").getPath()+"/static/upload/";;
        //2.判断该文件夹是否存在,如果不存在,新建文件夹
        File dir = new File(realPath);
        if (!dir.exists()){
            dir.mkdirs();
        }
        // 拿到上传文件名
        String filename = file.getOriginalFilename();
        filename = UUID.randomUUID()+filename;
        // 创建空文件
        File newFile = new File(dir, filename);
        // 将上传的文件写到空文件中
        file.transferTo(newFile);


        WangEditorResult wangEditorResult = new WangEditorResult();
        String[] data = {"/upload/"+filename};
        wangEditorResult.setErrno(0);
        wangEditorResult.setData(data);
        return wangEditorResult;
    }
  1. 在yml中配置上传文件的大小
# 上传文件
  servlet:
    multipart:
      max-file-size: 10MB # 最大单个文件
      max-request-size: 10MB # 一次请求最大上传

这样,一个编辑器就完成了!!!
在这里插入图片描述

Ⅲ. 编写AOP实现日志记录功能

1. 编写logback.xml文件

在后台代码运行的过程中,我们要对每一次操作进行日志记录,一方面通过日志可以发现代码的缺陷,另一方面可以追踪内部人员的操作记录。
SpringBoot默认使用Logback组件作为日志管理,首先在 /resources 下添加Logback配置文件 logback.xml

在这里插入代码片<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <!--定义日志文件的存储地址-->
    <property name="LOG_HOME" value="${catalina.base}/logs/"/>


    <!-- 控制台输出 -->
    <appender name="Stdout" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 日志输出编码 -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            </pattern>
        </layout>
    </appender>


    <!-- 按照每天生成日志文件 -->
    <appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/server.%d{yy99-MM-dd}.log</FileNamePattern>
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <!--格式化输出:%d表示时间,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            </pattern>
        </layout>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>


    <!-- 日志输出级别 -->
    <root level="info">
        <appender-ref ref="Stdout"/>
        <appender-ref ref="RollingFile"/>
    </root>
</configuration>

写法很固定,理解即可!!!

2. 编写日志记录实体类

import lombok.Data;
import java.util.Date;

@Data
public class Log {
    private String url; // 访问的路径
    private Date visitTime; // 访问时间
    private String username; // 访问者
    private String ip; // 访问ip
    private int executionTime; // 访问时长
    private String exceptionMessage; // 异常信息
}

3. 编写日志AOP类

import com.lion.bean.Log;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

@Component
@Aspect
public class LogAop {
    @Autowired
    private HttpServletRequest request;
    @Autowired
    private final static Logger logger = LoggerFactory.getLogger(LogAop.class);


    /**
     * 切点,以backstage的所有controller方法作为切点
     */
    @Pointcut("execution(* com.lion.controller.backstage.*.*(..))")
    public void pointCut(){}

    /**
     * 前置通知
     * @param joinPoint
     */
    @Before("pointCut()")
    public void doBefore(JoinPoint joinPoint) {
        // 记录访问时间
        Date date = new Date();
        request.setAttribute("visitTime",date);
    }

    /**
     * 后置通知
     */
    @After("pointCut()")
    public void doAfter(){
        Log log = new Log();

        Date visitTime = (Date) request.getAttribute("visitTime"); // 访问时间
        Date now = new Date();
        int executionTime = (int)(now.getTime() - visitTime.getTime()); // 访问时长
        String ip = request.getRemoteAddr(); // 访问ip
        String url = request.getRequestURI();// 访问路径
        // 拿到security中的User对象
        Object user = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (user instanceof User){
            String username = ((User)user).getUsername();
            log.setUsername(username);
        }
        log.setExecutionTime(executionTime);
        log.setUrl(url);
        log.setIp(ip);
        log.setVisitTime(visitTime);


        logger.info(log.toString());
    }

    /**
     * 异常通知
     * @param ex
     */
    @AfterThrowing(pointcut = "pointCut()",throwing = "ex")
    public void afterThrowing(Throwable ex){
        Log log = new Log();


        Date visitTime = (Date) request.getAttribute("visitTime"); // 访问时间
        Date now = new Date();
        int executionTime = (int) (now.getTime() - visitTime.getTime()); // 访问时长
        String ip = request.getRemoteAddr(); // 访问ip
        String url = request.getRequestURI(); // 访问路径
        // 拿到Security中的User对象
        Object user = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (user instanceof User){
            String username = ((User) user).getUsername();
            log.setUsername(username);
        }
        log.setExecutionTime(executionTime);
        log.setUrl(url);
        log.setIp(ip);
        log.setVisitTime(visitTime);


        // 异常信息
        String exMessage = ex.getMessage();
        log.setExceptionMessage(exMessage);

        logger.info(log.toString());
    }
}

以Controller为切点,编写前置通知,后置通知和异常通知,记录用户每一次操作的具体内容。

五. 🦁 实现效果

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/488021.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

边无际CEO陈永立:让ChatGPT通过物联网看到世界

如何利用ChatGPT及其插件&#xff0c;将AI与物联网设备相连&#xff1f; 通过Shifu Plugin获取摄像头的实时画面 我们都认识到&#xff0c;ChatGPT引发了一连串的技术变革。作为一个对AI和物联网技术充满热情的开发者&#xff0c;获得ChatGPT插件的早期访问权限&#xff0c;是…

Table 表格 + Pagination 分页

<el-table>属性 v-loading 加载动画 data 显示的数据 border 是否带有纵向边框 fit 列的宽度是否自撑开 highlight-current-row 是否要高亮当前行 row-style 行的 style 的回调方法&#xff0c;也可以使用一个固定的 Object 为所有行设置一样的 Style…

Android - 深色主题

一、简介 我们一直以来使用的操作系统都是以浅色主题为主的&#xff0c;这种主题模式在白天或者是光线充足的 情况下使用起来没有任何问题&#xff0c;可是在夜晚灯光关闭的情况下使用就会显得非常刺眼。 于是&#xff0c;许多应用程序为了能够让用户在光线昏暗的环境下更加舒…

mysql使用innodb引擎,请简述mysql索引的最左前缀如何优化orderby语句。

关键点 如果排序字段不在索引列上&#xff0c;filesort有两种算法&#xff1a; mysql就要启动双路排序和单路排序无过滤不索引order by非最左 filesort顺序错 filesort方向反 filesort 答案 首先要对sql进行分析检查必要的查询字段&#xff0c;过滤字段&#xff0c;排序字段…

JeecgBoot免费低代码平台—多租户SAAS解决方案

JeecgBoot免费低代码平台&#xff0c;提供一键切换多租户模式机制&#xff01;快速实现全系统的saas租户方案&#xff0c;通过租户ID进行数据隔离。 租户设计思路 1、开启全系统租户隔离 开启方法 将 org.jeecg.config.mybatis.MybatisPlusSaasConfig#OPEN_SYSTEM_TENANT_CO…

Selenium:浏览器及鼠标、键盘事件

目录 一、控制浏览器 二、鼠标事件 三、键盘事件 一、控制浏览器 webdriver主要提供操作页面上各种元素的方法&#xff0c;但它也提供操作浏览器的一些方法&#xff0c;例如控制浏览器大小、前进和后退等。 1、控制浏览器窗口大小 1 # 控制浏览器大小 2 from selenium imp…

2023哪款蓝牙耳机性价比高?200左右高性价比蓝牙耳机推荐

现如今的蓝牙耳机越来越多&#xff0c;人们在选择时不免纠结&#xff0c;不知道选什么蓝牙耳机比较好&#xff1f;针对这个问题&#xff0c;我来给大家推荐几款性价比高的蓝牙耳机&#xff0c;一起来看看吧。 一、南卡小音舱Lite2蓝牙耳机 参考价&#xff1a;299 蓝牙版本&am…

常见提高SQL执行效率示例

1、减少与数据库的交互&#xff1a; 当传参是一个list集合时&#xff0c;通过遍历查询数据库会造成数据库压力过大&#xff0c;这样查询效率就比较低&#xff0c;应该减少与数据库的交互来提高查询效率&#xff1a;(这里的objId为“ljydl”,"ljtpfl") QueryWrapper&…

JavaScript全解析——本地存储的概念、用法详解

本地存储概念&#xff1a; 就是浏览器给我们提供的可以让我们在浏览器上保存一些数据 常用的本地存储 localStorage sessionStorage localStorage 特点: 1.长期存储,除非手动删除否则会一直保存在浏览器中&#xff0c;清除缓存或者卸载浏览器也就没有了 2.可以跨页面通讯,…

如何设计出结构清晰布局漂亮的SWC结构图

我们都知道AUTOSAR的设计理念是自顶向下的设计,并且现在使用的很多AUTOSAR工具链对于SWC的设计都支持模型设计,以Vector Developer工具为例,假设我们不调整Composition,其结构图显示将会比较乱,比如如下这个样子,结构图中ports关系不清晰,位置重叠,布局不够清晰,有些朋…

二维码在设备管理中的应用

对设备进行巡检是工厂日常管理中必不可少的工作之一&#xff0c;从前很多工厂使用纸质记录打钩的方式进行设备巡检&#xff0c;但纸质巡检单不易保存也不方便进行查阅或汇总&#xff0c;除此之外光靠打钩很难保证巡检的真实性&#xff0c;不能起到防假检的功能。 为了让设备巡…

vue3+Typescript实现路由标签页和面包屑

文章目录 在vue中实现路由标签页和面包屑需求分析最终效果实现步骤第一步&#xff1a;创建路由标签组件和面包屑第二步&#xff1a;创建路由标签数组第三步&#xff1a;监听路由变化并自动添加新标签第四步&#xff1a;处理关闭标签事件第五步&#xff1a;当前路由标题同步面包…

Oracle RAC 19c集群搭建指南:实现高可用、高性能的数据库集群(超详细)

Oracle RAC 19c集群搭建指南&#xff1a;实现高可用、高性能的数据库集群&#xff08;超详细&#xff09; 1.新建节点 因为rac集群使用的是共享盘&#xff08;esxi设置共享盘步骤如下&#xff09;&#xff1a; 首先添加一个iSCSI控制器SCSI总线共享选择虚拟添加硬盘&#xf…

java:fastjson,jackson自定义反序列化器设计暨jackson反序列化时出现StackOverflowError异常的原因分析

问题描述 如下是一个Java Bean类&#xff0c;这是我的sql2java工具根据数据库表自动生成的对应表记录的Java类。 与之对应的数据库表定义如下&#xff1a; CREATE TABLE IF NOT EXISTS dc_device_channel (device_id int NOT NULL COMMENT XNAME:设备IDX,sid …

一种具备过载检测和打嗝式保护功能的MBUS主机电路

关于MBUS电路原理和简化电路见本篇文章&#xff0c; MBUS主机端简化版电路设计_Tech-Wang的博客-CSDN博客https://blog.csdn.net/jsf120/article/details/123378064?spm1001.2014.3001.5502 上篇中的电路已经验证通过并在产品中实际应用&#xff0c;本篇在此基础上增加了打嗝…

itop-3568开发板驱动学习笔记(25)设备树(四)GPIO 实例分析

《【北京迅为】itop-3568开发板驱动开发指南.pdf》 学习笔记 文章目录 GPIO 控制器必要属性其他属性 指定 GPIO 引脚 和时钟类似&#xff0c;GPIO 在设备树中也存在两层定义&#xff0c;首先是 GPIO 控制器&#xff0c;这部分由芯片原厂工程师编写&#xff0c;相当于 GPIO 底层…

恺撒密码太简单?这样一改,秒变地狱级难度(37)

小朋友们好&#xff0c;大朋友们好&#xff01; 我是猫妹&#xff0c;一名爱上Python编程的小学生。 欢迎和猫妹一起&#xff0c;趣味学Python。 今日主题 看过上一篇文章的朋友&#xff0c;可能觉得这样加密&#xff0c;未免太简单了吧&#xff1f; 一共26个字母&#xf…

【java】一文让你了解透彻Java中的IO模型

文章目录 前言一、什么是IO二、常见的IO模型BIO&#xff08;Blocking IO&#xff09;如何优化NIO的面世 三、NIO&#xff08;Non-blocking/New IO&#xff09;同步非阻塞IO模型IO多路复用模型Java中的NIO 四、AIO&#xff08;Asynchronous IO&#xff09;总结 前言 本文只是说…

不要一昧的给孩子做骨密度检查,这些情况才要做

国内&#xff0c;骨密度检测应用非常广泛。也成了很多医院儿科常规的体检项目之一。而在儿科领域&#xff0c;对骨密度检查的必要性、临床参考意义一直颇有争议。 有专家说&#xff0c;“孩子需要及时检测骨密度&#xff0c;但是要用科学有效的检测方式……只有双能X线的检测方…

BetaFlight统一硬件配置文件研读

BetaFlight统一硬件配置文件研读 1. 源由2. 分析2.1 硬件SOC2.2 统一配置文件2.3 cli命令2.4 板级配置主要命令2.4.1 board_name2.4.2 manufacturer_id2.4.3 resource2.4.4 timer2.4.5 dma2.4.6 serial2.4.7 set2.4.8 feature 3. 实例研读3.1 C遗留配置3.2 BoardName - AOCODAR…