开发语言 | Java |
---|---|
开发工具 | IDEA |
数据库 | MYSQL/8.0 |
使用框架 | springboot+vue |
用到的技术栈 | MybatisPlus,lombok,fastjson,druid数据库 |
JDK版本 | jdk1.8 |
创建数据库:reggie,并执行sql文件导入11张表,每张表对应信息如下:
创建maven项目,并命名为reggie_take_out。检查项目设置中的maven仓库是否正确以及Runner中JRE是否正确,最后检查项目设置中的JKD是否正确。详细步骤见下图:
pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.wang</groupId>
<artifactId>reggie_take_out</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
</properties>
<!--配置依赖-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.23</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.5</version>
</plugin>
</plugins>
</build>
</project>
application.yml配置文件:
server:
port: 8080
spring:
application:
name: reggie_take_out
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: root
mybatis-plus:
configuration:
#在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: ASSIGN_ID
将依赖注入后,我们需要写一个springboot的项目启动类ReggieApplication,代码如下:
@Slf4j//日志注解
@SpringBootApplication
public class ReggieApplication {
public static void main(String[] args) {
SpringApplication.run(ReggieApplication.class,args);
log.info("项目启动成功。。。");
}
}
这一步,一个空空的springboot就搭建完成了,大家可以点击启动类,看看项目能不能正常运行。可以的话,就可以进行下一步操作了!!
1,将该项目对应的静态资源(静态资源的获取方式见文末)
赋值到resource目录下面。又因为正常情况下只有在资源resource的目录static和templates下的静态资源,项目才可以直接访问到,所以这里我们需要写一个能够让我们访问静态资源的配置类,进行资源映射。
2,创建config目录,存放我们的配置类,并编写WebMvcConfig.java文件:
@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
/**
*@Author wjq
*@Date 2022/6/23
*@Version v1.0
*@Description 设置静态资源映射
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
log.info("开始静态资源映射。。。");
}
}
到这一步项目的基本大框架就已经搭建好了,接下来就要进行具体功能的需求分析和开发了。
按照视频讲解,完整功能开发一般要分为需求分析—>代码开发—>功能测试,三个步骤。但是我们在学习的过程中,功能测试其实就是查看代码是否能实现对应的功能并且无Bug。因此本文不再赘述这一步骤。
1,查看登录页面 s r c / m a i n / r e s o u r c e s / b a c k e n d / p a g e / l o g i n / l o g i n . h t m l src/main/resources/backend/page/login/login.html src/main/resources/backend/page/login/login.html,获取登录页面需要信息,以及执行流程。
2,查看登录所用到的信息
3,对数据Employee的模型进行分析,查看我们登录用到的信息。
导入实体类Employee,与表Employee进行映射。(登录必须要有用户,对应的就是数据表中的表一Employee)
使用MybatisPlus,编写mapper文件EmployeeMapper。
@Mapper
public interface EmployeeMapper extends BaseMapper<Employee> {
}
使用MybatisPlus,编写service接口以及接口实现类。
导入最终结果返回类R。
/**
*@Author wjq
*@Date 2022/6/23
*@Version v1.0
*@Description 通用返回结果类,服务端相应的数据最终都会封装成该类
*/
@Data
public class R<T> {
private Integer code; //编码:1成功,0和其它数字为失败
private String msg; //错误信息
private T data; //数据
private Map map = new HashMap(); //动态数据
public static <T> R<T> success(T object) {
R<T> r = new R<T>();
r.data = object;
r.code = 1;
return r;
}
public static <T> R<T> error(String msg) {
R r = new R();
r.msg = msg;
r.code = 0;
return r;
}
public R<T> add(String key, Object value) {
this.map.put(key, value);
return this;
}
}
编写Controller文件,在controller类中编写登录方法,登录方法的处理逻辑。
a, 将页面提交的密码password进行md5加密处理
b,根据页面提交的用户名username查询数据库
c,如果没有查询到则返回登录失败结果
d, 密码比对,如果不一致则返回登录失败结果
e, 查看员工状态,如果为已禁用状态,则返回员工已禁用结果
f, 登录成功,将员工id存入Session并返回登录成功结果
@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
/**
*@Author wjq
*@Date 2022/6/24
*@Version v1.0
*@Description 管理元登录功能
*/
@PostMapping("/login")
public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee){
//1、将页面提交的密码password进行md5加密处理
String password = employee.getPassword();
password = DigestUtils.md5DigestAsHex(password.getBytes());
//2、根据页面提交的用户名username查询数据库
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Employee::getUsername,employee.getUsername());
Employee emp = employeeService.getOne(queryWrapper);
//3、如果没有查询到则返回登录失败结果
if(emp == null){
return R.error("登录失败");
}
//4、密码比对,如果不一致则返回登录失败结果
if(!emp.getPassword().equals(password)){
return R.error("登录失败");
}
//5、查看员工状态,如果为已禁用状态,则返回员工已禁用结果
if(emp.getStatus() == 0){
return R.error("账号已禁用");
}
//6、登录成功,将员工id存入Session并返回登录成功结果
request.getSession().setAttribute("employee",emp.getId());
return R.success(emp);
}
}
1,登录上去后,点击退出图标,要返回到登录页面。
2,打开 s r c / m a i n / r e s o u r c e s / b a c k e n d / i n d e x . h t m l src/main/resources/backend/index.html src/main/resources/backend/index.html文件,看到推出按键会触发方法logout。
<img src="images/icons/btn_close@2x.png" class="outLogin" alt="退出" @click="logout" />
3,分析logout方法,其中会调用函数logoutApi()。打开该函数发现其会post到 / e m p l o y e e / l o g o u t /employee/logout /employee/logout,因此我们只要在controller中编写退出方法,并返回一个通用类R就行了。
logout() {
logoutApi().then((res)=>{
if(res.code === 1){
localStorage.removeItem('userInfo')
window.location.href = '/backend/page/login/login.html'
}
})
},
function logoutApi(){
return $axios({
'url': '/employee/logout',
'method': 'post',
})
}
1,清理Session中保存的当前登录员工的id。
2,返回通用类R。
/**
* 员工退出
* @param request
* @return
*/
@PostMapping("/logout")
public R<String> logout(HttpServletRequest request){
//清理Session中保存的当前登录员工的id
request.getSession().removeAttribute("employee");
return R.success("退出成功");
}
1,当我们直接访问 h t t p : / / l o c a l h o s t : 8080 / b a c k e n d / i n d e x . h t m l http://localhost:8080/backend/index.html http://localhost:8080/backend/index.html页面时,不需要账号和密码就可以直接登录上去,这是不符合实际需求的。实际需求是:只有用户输入正确账号和密码才能完成登录!
2,实现该功能有两种方式:用过滤器和拦截器,在过滤器和拦截器中判断用户是否已经完成登录,如果没有登录则跳转到登录页面。
如下图展示:第一次我们用账号密码登录属于正常需求。但是当我们推出了,直接访问index页面,竟然也能直接访问,这就是很严重的问题了。
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
}
}
在启动类上加入注解@ServletComponentScan,相当于打开过滤器的开关。
完善过滤器的处理逻辑
a, 获取本次请求的URI。
b,定义不需要处理的请求路径,并放在String数组中。
c,判断本次请求是否需要处理,在函数check中实现,利用了AntPathMatcher
类。如果不需要处理,则直接放行。
//路径匹配器,支持通配符
public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
public boolean check(String[] urls,String requestURI){
for (String url : urls) {
boolean match = PATH_MATCHER.match(url, requestURI);
if(match){
return true;
}
}
return false;
}
d,判断登录状态,如果已登录,则直接放行。
e,如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据。
4,在我们写完过滤器后,前端页面中,通过文件 j r e q u e s t . j s jrequest.js jrequest.js中的下面方法来响应过滤器,阅读代码可以发现,其最后仍需要获取一个通用类型R,这正好对应了步骤3中流程e,需要我们向客户端即前端页面响应数据。
service.interceptors.response.use(res => {
if (res.data.code === 0 && res.data.msg === 'NOTLOGIN') {// 返回登录页面
console.log('---/backend/page/login/login.html---')
localStorage.removeItem('userInfo')
window.top.location.href = '/backend/page/login/login.html'
} else {
return res.data
}
},
整合后的过滤器代码,LoginCheckFilter类如下:
/**
* 检查用户是否已经完成登录*/
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter {
//路径匹配器,支持通配符
public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//1、获取本次请求的URI
String requestURI = request.getRequestURI();// /backend/index.html
log.info("拦截到请求:{}",requestURI);
//定义不需要处理的请求路径
String[] urls = new String[]{
"/employee/login",
"/employee/logout",
"/backend/**",
"/front/**"
};
//2、判断本次请求是否需要处理
boolean check = check(urls, requestURI);
//3、如果不需要处理,则直接放行
if(check){
log.info("本次请求{}不需要处理",requestURI);
filterChain.doFilter(request,response);
return;
}
//4、判断登录状态,如果已登录,则直接放行
if(request.getSession().getAttribute("employee") != null){
log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("employee"));
filterChain.doFilter(request,response);
return;
}
log.info("用户未登录");
//5、如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据
response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
return;
}
/**
* 路径匹配,检查本次请求是否需要放行
* @param urls
* @param requestURI
* @return*/
public boolean check(String[] urls,String requestURI){
for (String url : urls) {
boolean match = PATH_MATCHER.match(url, requestURI);
if(match){
return true;
}
}
return false;
}
}
到此,项目的整体框架以及基础的登录推出功能已经完成了,看看自己搭建的项目的详细目录,对每个模块以及其作用是不是一目了然,接下来的功能开发也是按照上述的流程逐步完成。
注意:本博客为跟随《黑马程序员》学习,并搭建项目的详细流程记录。文中所需要的数据库sql文件以及静态资源文件和详细代码,都可以通过关注公众号黑马程序员,回复”瑞吉外卖“获得。