目录
- 前言
- 后台系统登录功能
- 需求分析
- 代码实现
- 实体类
- Mapper层
- Service层
- Controller层
- 总结
- 后台系统退出功能
- 需求分析
- 代码实现
- 总结
- 后台登录优化
- 需求分析
- 代码实现
- 方法一:过滤器
- 方法二:拦截器
- 总结
前言
所有的命名要符合开发规范,本项目中不再解释命名对应的是哪一个模块,以及变量名的意思
后台系统登录功能
需求分析
后台系统登录主要需要实现以下功能
- 前端传递用户名密码,后端可以接收
- 后端对用户名密码在数据库中进行校验,发送校验结果给前端
- 后端发送数据格式需要包含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 {@Autowiredprivate EmployeeMapper employeeMapper;@Overridepublic 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,失败的编码为0public 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 {@Autowiredprivate 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的时候为0Integer 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 = "登录成功";}//只需要穿是否查询成功的结果就可以,不需要传输datareturn new Result(employee1,code,msg);}
}
总结
完成登录功能后,就可以使用账号密码,进行用户登录,并跳转到后台的界面,用户id也被储存到session中。方便之后的使用。 一定要注意Result类中要注入get和set方法,否则会报错
后台系统退出功能
需求分析
后台退出系统主要需要实现以下功能
- 点击右侧的退出按扭发送请求到后端,删除对应session,然后退出到登录页面
代码实现
EmployeeController.java
/*** 员工退出* @param request* @return*/@PostMapping("/logout")public Result logout(HttpServletRequest request){//删除sessionrequest.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();@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//强制类型转换HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;//1、获取本次请求的URIString 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){//匹配成功返回truereturn true;}}//匹配失败返回falsereturn 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 {@Overridepublic 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;}}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic 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 {@Autowiredprivate LoginInterceptor loginInterceptor;/*** 加入拦截路径和放行的路径* @param registry*/@Overridepublic 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);}
}
总结
添加拦截器或过滤器后,当前端发送请求时,会在拦截器或过滤器中进行处理,通过一系列判断,来决定是否放行这个请求。一般来说如果完成登录则不拦截请求,如果未登录就访问,则跳转回登录界面。