今日目标:实现用户登录功能,实现用户退出功能,实现添加用户功能,实现拦截器。
用户登录功能
1、根据页面提交的用户名username查询数据库
2、如果没有查询到则返回登录失败结果
3、密码比对,如果不一致则返回登录失败结果
4、查看员工状态,如果为已禁用状态,则返回员工已禁用结果
5、登录成功,将员工id存入Session并返回登录成功结果
代码部分:
@RequestMapping(value = "/login",method = RequestMethod.POST)
public R login(HttpServletRequest request ,@RequestBody Employee employee){
// 1,、根据页面提交的用户名username查询数据库
QueryWrapper<Employee> queryWrapper = new QueryWrapper<>();
Employee user = employeeService.getOne(queryWrapper.eq("username",employee.getUsername()));
// 2、如果没有查询到则返回登录失败结果
if (Objects.isNull(user)){
return R.error("登录失败,用户不存在!");
// 3、密码比对,如果不一致则返回登录失败结果
} else if (!Objects.equals(Md5Util.Md5(employee.getPassword()),user.getPassword())) {
return R.error("登录失败,账号或密码错误!");
// 4、查看员工状态,如果为已禁用状态,则返回员工已禁用结果
} else if (user.getStatus() != 1) {
return R.error("账户异常提醒!");
}else {
// 5、登录成功,将员工id存入Session并返回登录成功结果
request.getSession().setAttribute("employee", user);
return R.success(user);
}
}
知识点:
- HttpServletRequest request :它的作用是用来把员工信息user存储在当前会话中。作用是维护用户的登录状态,可以在用户访问不同页面时保持数据的持续性。
- QueryWrapper 是 MyBatis-Plus 框架中的一个查询构造器工具,用于构建数据库查询条件。
用户退出功能
代码部分:
@RequestMapping(value = "/logout",method = RequestMethod.POST)
public R login(HttpServletRequest request){
request.getSession().removeAttribute("employee");
return R.success("退出成功!");
}
退出功能是绑定前端logout点击事件,通过清除会话信息达到退出的功能。
用户拦截器
前情提要:在实现登录功能的时候,当发现只要copy网址*“http://localhost:8080/backend/index.html”* 就可以直接访问带有数据的页面,这里是有很大的安全隐患的,因此,为了杜绝这种现象,我们在这里设置一个拦截器。目的是为了在未登录的状态下无法访问到带有数据的页面。
代码逻辑:
1、获取本次请求的URI。
2、判断本次请求是否需要处理。
3、如果不需要处理,则直接放行。
4、判断登录状态,如果已登录,则直接放行。
5、如果未登录则返回未登录结果。
代码实现:
@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 curUrl = request.getRequestURI();
String[] urlList = new String[] {
"/employee/login",
"/employee/logout",
"/backend/**",
"/front/**"
};
// 2、判断本次请求是否需要处理
boolean check = check(curUrl, urlList);
// 3、如果不需要处理,则直接放行
if (check){
//放行
filterChain.doFilter(request,response);
return;
}
// 4、判断登录状态,如果已登录,则直接放行
Employee employee = (Employee) request.getSession().getAttribute("employee");
if (employee != null) {
filterChain.doFilter(request,response);
return;
}
// 5、如果未登录则返回未登录结果 response.getWriter().write(JSONUtil.toJsonStr(R.error("NOTLOGIN")));
return;
}
private boolean check(String curUrl, String[] urlList) {
for (String url: urlList) {
if (PATH_MATCHER.match(url, curUrl)){
return true;
}
}
return false;
}
}
知识点:
- @WebFilter 注解:将过滤器与 Web 应用程序关联,并配置它要处理的请求。
- AntPathMatcher:它是Spring框架中用来匹配和处理URL路径的工具类。它通常用于 URL 路径的模式匹配,允许你使用通配符和模式来匹配 URL。
新增用户功能
代码逻辑:
1、先判断用户名是否重复。
2,密码初始化:123456。
3、将传入的数据保存到数据库。
代码实现:
@PostMapping
public R<String> save(HttpServletRequest request ,@RequestBody Employee employee) {
// //1,先判断用户名是否重复
// LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
// queryWrapper.eq(Employee::getUsername, employee.getUsername());
// Employee employee1 = employeeService.getOne(queryWrapper);
// if (employee1 != null) {
// return R.error(employee.getUsername() + "用户名已存在!");
// }
//2,密码初始化:123456
String password = Md5Util.Md5("123456");
employee.setPassword(password);
employee.setCreateTime(new Date());
employee.setUpdateTime(new Date());
Employee em = (Employee)request.getSession().getAttribute("employee");
employee.setCreateUser(em.getId());
employee.setUpdateUser(em.getId());
//3,将传入的数据保存到数据库
employeeService.save(employee);
return R.success("用户添加成功!");
}
代码部分的第一个逻辑模块实现的是一般形式对于重复用户名的过滤。当然视频上是通过异常来进行处理的,原因是username设置了unique字段,所以自动被识别为索引(后面会扩充一下知识点),若重复则会报错。
索引:
异常处理代码实现:
@ControllerAdvice(annotations = {RestController.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(value = SQLIntegrityConstraintViolationException.class)
public R<String> SQLIntegrityConstraintViolationException(Exception ex){
log.info(ex.getCause().getMessage());
if (ex.getMessage().contains("Duplicate entry")){
String[] message = ex.getCause().getMessage().split(" ");
String key = message[2];
return R.error(key + "已经存在!");
}
return R.error("服务器异常");
}
}
知识点:
- ControllerAdvice :它允许你为整个应用程序或特定控制器类提供全局性的异常处理、数据绑定和其他控制器建议。此处是指定为含有@RestController的类进行异常处理。
知识回顾:
1,索引
首先讲一下索引,索引可以看作一本目录,它的底层是B+树,通过把数据信息存到叶子节点,而其他节点则存储的是可以查找到该数据信息的一些标记,所以这些标记必须是唯一的。
一般情况下主键id默认为该表索引,通过主键id查询的时候,会把数据建立在一些通过id大小划分的B+树上,对当前id大小和节点比较,若当前id大于大于某一节点,则所查询的id一定不存在于当前节点的左树,所以只需要查询右树,也就是二分查找,所以在查询的时候会大大节省事件。
在一般的unique设置的字段也会自动添加为索引。也可以手动添加索引。如下:
ALTER TABLE table_name ADD FULLTEXT INDEX index_name (column1, column2, ...);
当然,多个索引的时候,又要通过哪个索引来建立查询目录呢?
这里是遵循最左匹配原则,具体来说,当你在查询中使用了复合索引的一部分列作为查询条件时,只有从索引的最左边列开始到你使用的列,才能加速查询。如果你跳过了最左边的列,索引将不会生效。这是因为数据库引擎会依赖索引的排序和组织来快速定位数据,而如果你跳过了前面的列,数据库引擎就无法有效地使用索引。
索引缺点: 这玩意有好的一面当然也有不好的一面。
1,建立目录的时候,它会使用很多额外的存储。
2,增删改的时候也需要对索引进行维护。
所以呢?
当然是避免!
1,在建立表的时候对于需要大量查询的字段设置索引。
2,对于那些定义为text,image的列别用,因为它们的每个数据量庞大。
关于索引的分类:
- 复合索引:一个索引包含多个列,在数据库操作期间,复合索引比单值索引所需要的开销更小。
- 唯一索引:索引列的值必须唯一,但允许有空值。
- 单列索引:一个索引只包含单个列,一个表可以有多个单列索引。
- 主键索引:设定为主键后数据库会自动建立索引,innodb为聚簇索引。
- 聚簇索引:索引项的排序方式和表中数据记录排序方式一致的索引。
- 非聚簇索引:与聚集索引相反,索引顺序与物理存储顺序不一致。
索引也是SQL优化的一种方式,若是再去讲SQL优化,又是很多知识点,嗯…那就再讲讲!
2、SQL优化
- SQL语句尽量避免使用 **select ***询。
- SQL语句关键字大写,官方的SQL一般都大写。
- 使用union all代替union。
- 批量操作,比如正常的业务逻辑中,insert的时候一般传入的是一个类型,而不是针对每个字段的插入,可以防止频繁访问数据库。
- 多用limit,不需要全局查询的时候,做一个限制。
- in中值太多,若查询的时候遍历in中的所有数据,这肯定会浪费资源。
- 增量查询。
- 高效分页,也是limit来分页查询。
- 提升group by的效率。
- 索引优化。
- …还有很多。
总结一下:敲代码十分钟,写博客两年半。其实我个人认为总结知识点比较重要一点,当然还有许多没有整理的知识点,比如那三个存储引擎,聚簇索引和非聚簇索引讲的也不太细等等。
八股文这种东西背的话容易临场紧张,然后忘掉,只记得头和尾,所以建议大家还是应该去实际操作一下。
本来是昨天整理的,但是JDG输了比赛。没心情整理,所以留到了今天。