一. ?? 研究背景及意義
旅游作為一種經(jīng)濟(jì)活動(dòng),已經(jīng)成為世界經(jīng)濟(jì)中不可或缺的一部分。旅游業(yè)的快速發(fā)展,使得旅游產(chǎn)品和服務(wù)的供應(yīng)和需求日益增加,其中旅游網(wǎng)站起著至關(guān)重要的作用。近年來(lái),隨著移動(dòng)互聯(lián)網(wǎng)的普及和技術(shù)的進(jìn)步,旅游網(wǎng)站不斷創(chuàng)新和發(fā)展,為旅游市場(chǎng)的發(fā)展貢獻(xiàn)了巨大的力量。
旅游網(wǎng)站的研究背景在于,通過(guò)對(duì)旅游網(wǎng)站的市場(chǎng)現(xiàn)狀、用戶需求和技術(shù)創(chuàng)新等方面進(jìn)行研究,以更好地了解旅游網(wǎng)站的發(fā)展趨勢(shì)和用戶特征,進(jìn)而為旅游網(wǎng)站的設(shè)計(jì)和優(yōu)化提供依據(jù),提升旅游網(wǎng)站的服務(wù)質(zhì)量和用戶體驗(yàn)。
旅游網(wǎng)站的研究意義在于,為旅游網(wǎng)站的設(shè)計(jì)和優(yōu)化提供了思路和方法,以滿足用戶多樣化的需求。同時(shí),可以幫助企業(yè)更好地把握市場(chǎng)的機(jī)遇和挑戰(zhàn),提升旅游產(chǎn)品和服務(wù)的質(zhì)量和效益。此外,隨著旅游業(yè)的不斷發(fā)展,旅游網(wǎng)站的研究也為旅游業(yè)的升級(jí)和轉(zhuǎn)型提供了有價(jià)值的參考和借鑒。綜上所述,旅游網(wǎng)站的研究具有十分重要和現(xiàn)實(shí)的意義。
二. ?? 技術(shù)棧系列及主要功能
Ⅰ. 技術(shù)棧
- Springboot
- SpringMVC
- MySQL
- Mybatis-Plus
- SpringSecurity
- Thymeleaf
- AdminLTE2
- lombok、ajax、logback
Ⅱ. 主要功能
本系統(tǒng)基于以上技術(shù)來(lái)實(shí)現(xiàn)一個(gè)完善的旅游網(wǎng)后臺(tái)管理系統(tǒng),具有以下主要功能:
- 管理員管理
- 角色管理
- 權(quán)限管理
- 認(rèn)證和授權(quán)
- 產(chǎn)品類型管理
- 旅游產(chǎn)品管理
三. ?? 項(xiàng)目搭建
Ⅰ. 后端模塊
該項(xiàng)目和平常項(xiàng)目差不多,后端模塊按平常Springboot項(xiàng)目結(jié)構(gòu)搭建即可,如下:
Ⅱ. 前端模塊
在項(xiàng)目中,我們使用AdminLTE框架作為管理員端頁(yè)面,使用自己編寫的網(wǎng)頁(yè)作為用戶端頁(yè)面。
tips:
AdminLTE是一款建立在bootstrap和jquery之上的開源的模板主題工具,它提供了一系列響應(yīng)的、 可重復(fù)使用的組件,并內(nèi)置了多個(gè)模板頁(yè)面;同時(shí)自適應(yīng)多種屏幕分辨率,兼容PC和移動(dòng)端。通過(guò)AdminLTE,我們可以快速的創(chuàng)建一個(gè)響應(yīng)式的Html5網(wǎng)站。AdminLTE框架在網(wǎng)頁(yè)架構(gòu)與設(shè)計(jì)上,有很大的輔助作用,尤其是前端架構(gòu)設(shè)計(jì)師,用好AdminLTE 不但美觀,而且可以免去寫很大CSS與JS的工作量。
使用AdminLTE非常簡(jiǎn)單,只需要根據(jù)需求將需要的組件復(fù)制到我們的頁(yè)面中即可。
四. ?? 主要實(shí)現(xiàn)過(guò)程
主要挑幾個(gè)核心部分來(lái)展開描述:
Ⅰ. 認(rèn)證和授權(quán)
1. SpringSecurity配置類
眾所周知,SpringSecurity遵循RBAC原則,即角色,權(quán)限,用戶表,在我們添加完這個(gè)表的實(shí)體類以及一些常規(guī)CRUD操作后,就可以開始編寫系統(tǒng)的認(rèn)證和授權(quán)了。
如何來(lái)編寫一個(gè)權(quán)限表?詳情請(qǐng)看獅子前面的文章:點(diǎn)擊
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) // 開啟注解-鑒權(quán)配置
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") //登錄提交路徑,提交后執(zhí)行認(rèn)證邏輯
.successHandler(new MyLoginSuccessHandler()) //登錄成功跳轉(zhuǎn)路徑
.failureHandler(new MyLoginFailureHandler()); //登錄失敗跳轉(zhuǎn)路徑
// 權(quán)限攔截配置
http.authorizeRequests()
.antMatchers("/backstage/admin/login").permitAll() //登錄不需要認(rèn)證
.antMatchers("/backstage/admin_fail").permitAll() //登錄失敗不需要認(rèn)證
.antMatchers("/backstage/admin_login").permitAll() //登錄頁(yè)不需要認(rèn)證
.antMatchers("/**/*.css","/**/*.js").permitAll() //靜態(tài)資源放行
.antMatchers("/backstage/**").authenticated(); //剩下的都要認(rèn)證
// 退出登錄配置
http.logout()
.logoutUrl("/backstage/admin/logout") //退出登錄的路徑
.logoutSuccessUrl("/backstage/admin_login") //退出登錄成功后跳轉(zhuǎn)的頁(yè)面
.clearAuthentication(true) //退出成功后清除認(rèn)證信息
.invalidateHttpSession(true); //退出成功后清除session
// 異常處理
http.exceptionHandling()
.accessDeniedHandler(new MyAccessDeniedHandler()); //權(quán)限不足處理器
// 關(guān)閉csrf防護(hù)
http.csrf().disable();
// 開啟跨域訪問(wèn)
http.cors();
}
// 密碼加密器
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
}
security的配置類寫法很類似,咱們分塊來(lái)實(shí)現(xiàn):
自定義表單登錄塊——>權(quán)限攔截配置——>退出登錄配置——>異常處理——>關(guān)閉csrf防護(hù)——> 開啟跨域訪問(wèn)——>密碼加密器
根據(jù)自己需要來(lái)編寫相應(yīng)模塊,詳細(xì)目錄如下:
2. 自定義邏輯認(rèn)證
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;
// 自定義認(rèn)證邏輯
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 1.認(rèn)證
Admin admin = adminService.findByAdminName(username);
if (admin == null) {
throw new UsernameNotFoundException("用戶不存在");
}
// admin的狀態(tài)是不可登錄則報(bào)錯(cuò)(自定義)
if (!admin.isStatus()){
throw new UsernameNotFoundException("用戶不可用");
}
// 2.授權(quán)
List<Permission> permissions = adminService.findAllPermission(username);
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
for (Permission permission : permissions) {
grantedAuthorities.add(new SimpleGrantedAuthority(permission.getPermissionDesc()));
}
// 3.封裝為UserDetails對(duì)象
UserDetails userDetails = User.withUsername(admin.getUsername())
.password(admin.getPassword())
.authorities(grantedAuthorities)
.build();
// 4.返回封裝好的UserDetails對(duì)象
return userDetails;
}
}
Ⅱ. 富文本編輯器——wangEditor
在項(xiàng)目中,我們使用中國(guó)人開發(fā)的一個(gè)富文本編輯器——wangEditor,官網(wǎng)地
址:https://www.wangeditor.com/
使用步驟如下:
- 在前端頁(yè)面中引入 wangEditor.js
- 在頁(yè)面中加入 wangEditor 插件
- 編寫實(shí)體類返回值
wangEditor要求上傳圖片的返回值必須按照它的要求,所以我們創(chuàng)建一個(gè)實(shí)體類,用于返回上傳的結(jié)果。
import lombok.Data;
@Data
public class WangEditorResult {
private int errno;
private String[] data;
}
- 編寫上傳控制器,接收富文本編輯器上傳的圖片
@RequestMapping(value = "/upload")
@ResponseBody
public WangEditorResult upload(HttpServletRequest request, MultipartFile file) throws Exception {
// 創(chuàng)建文件夾,存放上傳文件。
//1.設(shè)置上傳文件夾的真實(shí)路徑
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;
// 創(chuàng)建空文件
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;
}
- 在yml中配置上傳文件的大小
# 上傳文件
servlet:
multipart:
max-file-size: 10MB # 最大單個(gè)文件
max-request-size: 10MB # 一次請(qǐng)求最大上傳
這樣,一個(gè)編輯器就完成了?。?!
Ⅲ. 編寫AOP實(shí)現(xiàn)日志記錄功能
1. 編寫logback.xml文件
在后臺(tái)代碼運(yùn)行的過(guò)程中,我們要對(duì)每一次操作進(jìn)行日志記錄,一方面通過(guò)日志可以發(fā)現(xiàn)代碼的缺陷,另一方面可以追蹤內(nèi)部人員的操作記錄。
SpringBoot默認(rèn)使用Logback組件作為日志管理,首先在 /resources 下添加Logback配置文件 logback.xml
在這里插入代碼片<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--定義日志文件的存儲(chǔ)地址-->
<property name="LOG_HOME" value="${catalina.base}/logs/"/>
<!-- 控制臺(tái)輸出 -->
<appender name="Stdout" class="ch.qos.logback.core.ConsoleAppender">
<!-- 日志輸出編碼 -->
<layout class="ch.qos.logback.classic.PatternLayout">
<!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級(jí)別從左顯示5個(gè)字符寬度%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表示時(shí)間,%thread表示線程名,%-5level:級(jí)別從左顯示5個(gè)字符寬度%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>
<!-- 日志輸出級(jí)別 -->
<root level="info">
<appender-ref ref="Stdout"/>
<appender-ref ref="RollingFile"/>
</root>
</configuration>
寫法很固定,理解即可?。?!
2. 編寫日志記錄實(shí)體類
import lombok.Data;
import java.util.Date;
@Data
public class Log {
private String url; // 訪問(wèn)的路徑
private Date visitTime; // 訪問(wèn)時(shí)間
private String username; // 訪問(wèn)者
private String ip; // 訪問(wèn)ip
private int executionTime; // 訪問(wèn)時(shí)長(zhǎng)
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);
/**
* 切點(diǎn),以backstage的所有controller方法作為切點(diǎn)
*/
@Pointcut("execution(* com.lion.controller.backstage.*.*(..))")
public void pointCut(){}
/**
* 前置通知
* @param joinPoint
*/
@Before("pointCut()")
public void doBefore(JoinPoint joinPoint) {
// 記錄訪問(wèn)時(shí)間
Date date = new Date();
request.setAttribute("visitTime",date);
}
/**
* 后置通知
*/
@After("pointCut()")
public void doAfter(){
Log log = new Log();
Date visitTime = (Date) request.getAttribute("visitTime"); // 訪問(wèn)時(shí)間
Date now = new Date();
int executionTime = (int)(now.getTime() - visitTime.getTime()); // 訪問(wèn)時(shí)長(zhǎng)
String ip = request.getRemoteAddr(); // 訪問(wèn)ip
String url = request.getRequestURI();// 訪問(wèn)路徑
// 拿到security中的User對(duì)象
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"); // 訪問(wèn)時(shí)間
Date now = new Date();
int executionTime = (int) (now.getTime() - visitTime.getTime()); // 訪問(wèn)時(shí)長(zhǎng)
String ip = request.getRemoteAddr(); // 訪問(wèn)ip
String url = request.getRequestURI(); // 訪問(wèn)路徑
// 拿到Security中的User對(duì)象
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為切點(diǎn),編寫前置通知,后置通知和異常通知,記錄用戶每一次操作的具體內(nèi)容。
五. ?? 實(shí)現(xiàn)效果
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-440021.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-440021.html
到了這里,關(guān)于基于Springboot的旅游網(wǎng)管理系統(tǒng)后臺(tái)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!