目录
前言
所有的命名要符合开发规范,本项目中不再解释命名对应的是哪一个模块,以及变量名的意思
后台系统登录功能
需求分析
后台系统登录主要需要实现以下功能
- 前端传递用户名密码,后端可以接收
- 后端对用户名密码在数据库中进行校验,发送校验结果给前端
- 后端发送数据格式需要包含data(处理结果),code(处理结果编码),msg(操作成功或失败的提示)
- 将员工id放入session,方便之后进行权限验证,防止直接通过网页访问页面跳过登录
- 员工登录成功后,页面跳转到后台系统首页面(backend/index.html),显示当前登录用户的姓名
代码实现
建议把静态资源backend目录下的js下的request.js目录进行修改
(function (win) {
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL: '/',
// 超时
timeout: 1000000
})
将timeout尽量改大,这样方便进行调试,不会超时
实体类
创建entity包并在其中创建Employee实体类
如果druid版本太低,时间类不建议使用localDate,改用Date类
package com.cjgn.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.time.LocalDateTime;
//使用lombok自动生成实体类代码
@Data
//员工实体类
public class Employee {
private Long id;
//姓名
private String name;
//用户名
private String username;
private String password;
private String phone;
private String sex;
//身份证
private String idNumber;
private Integer status;
//如果druid版本太低改用Date
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableField(fill = FieldFill.INSERT)
private Long createUser;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;
}
Mapper层
package com.cjgn.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cjgn.entity.Employee;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface EmployeeMapper extends BaseMapper<Employee> {
}
Service层
EmployeeService.java
package com.cjgn.service;
import com.cjgn.entity.Employee;
public interface EmployeeService {
/**
* 通过用户名密码查询
* @param employee
* @return
*/
public Employee getByEmployee(Employee employee);
}
EmployeeServiceImpl.java
package com.cjgn.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cjgn.entity.Employee;
import com.cjgn.mapper.EmployeeMapper;
import com.cjgn.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeMapper employeeMapper;
@Override
public Employee getByEmployee(Employee employee) {
//1、将页面提交的密码password进行md5处理
String password = employee.getPassword();
password = DigestUtils.md5DigestAsHex(password.getBytes());
String username = employee.getUsername();
//2、根据用户名密码去数据库查询
LambdaQueryWrapper<Employee> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(null != username, Employee::getUsername, username)
.eq(null!=password,Employee::getPassword,password);
Employee employee1 = employeeMapper.selectOne(wrapper);
return employee1;
}
}
Controller层
Result.java
记得注入get和set方法
package com.cjgn.contorller;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
//通用结果类
@Data
public class Result {
//数据
private Object data;
//传递消息
private String msg;
//结果代码
private Integer code;
//动态数据
private Map map = new HashMap();
//三个值都有的构造方法
public Result(Object data, Integer code,String msg) {
this.data = data;
this.msg = msg;
this.code = code;
}
//只有消息和编码的构造方法,用于失败的时候用
public Result(Integer code,String msg) {
this.msg = msg;
this.code = code;
}
//无参构造方法
public Result() {
}
public Result add(String key,Object value){
this.map.put(key,value);
return this;
}
}
Code.java
package com.cjgn.contorller;
public class Code {
//根据前端的代码,成功的编码为1,失败的编码为0
public static final Integer OK = 1;
public static final Integer ERR = 0;
}
EmployeeController.java
package com.cjgn.contorller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cjgn.entity.Employee;
import com.cjgn.service.EmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
@RestController
@RequestMapping("/employee")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
/**
* 员工登录功能
* 使用的post请求
* @param request
* @param employee
* @return
*/
@PostMapping("/login")
public Result Login(HttpServletRequest request,@RequestBody Employee employee){
Employee employee1 = employeeService.getByEmployee(employee);
//判断对象是否为空,空:查询失败,非空:查询成功
//code:非空为1,空或者status为0的时候为0
Integer code;
String msg;
//查询为空
if(null==employee1){
code = Code.ERR;
msg = "登录失败";
}
//账号已禁用
else if(employee1.getStatus()==0){
code = Code.ERR;
msg = "登录失败";
}
//查询不为空且账号启用则登录成功
else {
//设置session,把员工的id存入
request.getSession().setAttribute("employee",employee1.getId());
code = Code.OK;
msg = "登录成功";
}
//只需要穿是否查询成功的结果就可以,不需要传输data
return new Result(employee1,code,msg);
}
}
总结
完成登录功能后,就可以使用账号密码,进行用户登录,并跳转到后台的界面,用户id也被储存到session中。方便之后的使用。 一定要注意Result类中要注入get和set方法,否则会报错
后台系统退出功能
需求分析
后台退出系统主要需要实现以下功能
- 点击右侧的退出按扭发送请求到后端,删除对应session,然后退出到登录页面
代码实现
EmployeeController.java
/**
* 员工退出
* @param request
* @return
*/
@PostMapping("/logout")
public Result logout(HttpServletRequest request){
//删除session
request.getSession().removeAttribute("employee");
//返回前端数据
return new Result(Code.OK,"退出成功");
}
总结
退出功能相对简单,只需对前端发送的ajax请求进行处理,删除登录设置的session,然后返回前端一个退出成功的数据,就可以实现退出和页面的跳转。(这里的页面跳转由前端执行)
后台登录优化
需求分析
- 问题:之前的登录系统存在一个漏洞,用户如果直接通过url去访问index.html主页是可以访问的,显然这不是我们想要的效果。
- 优化:使用过滤器或拦截器,判断用户是否进行登录,如果没有登录就跳转到登录界面
- 效果:用户只有登录后才能访问主页,否则会跳转回登录界面
代码实现
首先在pom文件中导入fastjson依赖
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
这里采用两种不同的方法分别实现
方法一:过滤器
- 在springboot的运行类中添加@ServletComponentScan注解
- 创建filter包并在其下创建类LoginCheckFilter
LoginCheckFilter.java
package com.cjgn.filter;
import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.fastjson.JSON;
import com.cjgn.contorller.Code;
import com.cjgn.contorller.Result;
import org.springframework.util.AntPathMatcher;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**拦截器类
* @author DELL
*/
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
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();
System.out.println("本次拦截路径"+requestURI);
//定义不需要处理的路径
String[] urls = new String[]{
"/employee/login",
"/employee/logout",
"/backend/**",
"/front/**"
};
//2、判断本次请求是否需要处理
boolean check = this.check(urls, requestURI);
//3、不需要处理直接放行
if (check){
System.out.println("不需要处理");
filterChain.doFilter(servletRequest,servletResponse);
}
//4、判断登录状态,如果已经登录则放行
else if (request.getSession().getAttribute("employee")!=null){
System.out.println("已登录");
filterChain.doFilter(servletRequest,servletResponse);
}
//5、如果没有登录则跳转到登录页面
else {
//使用response传回json数据
response.getWriter().write(JSON.toJSONString(new Result(Code.ERR,"NOTLOGIN")));
System.out.println("未登录");
}
}
/**
* 进行路径匹配,检查本次请求是否需要放心
* @param requestURI
* @return
*/
public boolean check(String[] urls,String requestURI){
//进行逐个匹配
for (String url : urls) {
boolean match = PATH_MATCHER.match(url, requestURI);
if(match){
//匹配成功返回true
return true;
}
}
//匹配失败返回false
return false;
}
}
方法二:拦截器
- 在controller包下创建LoginInterceptor类
- 创建config包并在其中创建WebMvcConfig类
LoginInterceptor.java
package com.cjgn.contorller;
import com.alibaba.fastjson.JSON;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 使用拦截器实现过滤器功能
*/
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
System.out.println("本次拦截路径" + requestURI);
//4、判断登录状态,如果已经登录则放行
if (request.getSession().getAttribute("employee") != null) {
System.out.println("已登录");
return true;
}
//5、如果没有登录则跳转到登录页面
else {
//使用response传回json数据
response.getWriter().write(JSON.toJSONString(new Result(Code.ERR, "NOTLOGIN")));
System.out.println("未登录");
return false;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
WebMvcConfig.java
要注意这个类最好去实现WebMvcConfigurer接口。最好不要继承WebMvcConfigurationSupport类,虽然功能大致相同,但采用继承的话可能导致静态资源被SpringMvc拦截,即使放在static目录下也没用。
package com.cjgn.comfig;
import com.cjgn.contorller.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
/**
* 加入拦截路径和放行的路径
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
//使用列表去装放行的路径
List<String> urls = new ArrayList<>();
urls.add( "/employee/login");
urls.add( "/employee/logout");
urls.add( "/backend/**");
urls.add( "/front/**");
urls.add("/error");
registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns(urls);
}
}
总结
添加拦截器或过滤器后,当前端发送请求时,会在拦截器或过滤器中进行处理,通过一系列判断,来决定是否放行这个请求。一般来说如果完成登录则不拦截请求,如果未登录就访问,则跳转回登录界面。