目录
完善登录功能
问题分析
前面已经完成了后台系统的员工登录功能开发,但是还存在一一个问题:用户如果不登录,直接访问系统首页面,照样可以正常访问。
这种设计并不合理,我们希望看到的效果应该是,只有登录成功后才可以访问系统中的页面,如果没有登录则跳转到登录页面。
那么,具体应该怎么实现呢?
答案就是使用过滤器或者拦截器,在过滤器或者拦截器中判断用户是否已经完成登录,如果没有登录则跳转到登录页面。
实现步骤:
- 创建自定义过滤器LoginCheckFilter
- 在启动类.上加入注解@ServletComponentScan(开启组件扫描,开启过滤器)
- 完善过滤器的处理逻辑
过滤器的处理逻辑:
代码实现
- 创建过滤器
package com.zqf.reggie.filter;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
//设置过滤器名称和拦截路径
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
log.info("拦截到请求:{}",request.getRequestURI());
filterChain.doFilter(request,response);//放行
}
}
- 添加扫描组件的注解
package com.zqf.reggie;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@Slf4j
@SpringBootApplication
@ServletComponentScan//扫描Servlet组件
public class ReggieApplication {
public static void main(String[] args) {
SpringApplication.run(ReggieApplication.class,args);
log.info("项目启动成功...");
}
}
- 实现过滤器的实现逻辑
package com.zqf.reggie.filter;
import com.alibaba.fastjson.JSON;
import com.zqf.reggie.common.R;
import lombok.extern.slf4j.Slf4j;
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;
@Slf4j
//设置过滤器名称和拦截路径
@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();
log.info("拦截到请求:{}",requestURI);
//不用处理的请求路径
String[] urls = new String[]{
"/employee/login",
"/employee/login",
"/backend/**", //静态资源
"/front/**"
};
//2、判断本次请求是否需要处理
boolean check = check(urls, requestURI);
//3、如果不需要处理,则直接放行
if (check){
log.info("本次请求不需要处理:{}",requestURI);
filterChain.doFilter(request,response);//放行
return;
}
//4、判断登录状态,如果已登录,则直接放行
Object employee = request.getSession().getAttribute("employee");
if (employee != null){
log.info("用户已登录,id为:{}",employee);
filterChain.doFilter(request,response);//放行
return;
}
/*
// 响应拦截器
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
}
},如果重复跳转login,请在urls放行路径中,添加 /employee/page,因为进入index时候,会自动发起/employee/page请求
*/
//5、如果未登录则返回未登录结果 通过输出流的方式向客户端页面响应数据
log.info("用户未登录");
response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
return;
}
public boolean check(String[] urls,String requesrURI){
for (String url : urls){
boolean match = PATH_MATCHER.match(url, requesrURI);
if (match){
return true;
}
}
return false;
}
}
关于PATH_MATCHER的细节:Spring 概念模型 : PathMatcher 路径匹配器
新增员工
需求分析
后台系统中可以管理员工信息,通过新增员工来添加后台系统用户。点击[添加员工]按钮跳转到新增页面。
数据模型
employee表中username唯一,status默认为1,表示状态正常
代码开发
1、页面发送ajax请求,将新增员工页面中输入的数据以json的形式提交到服务端
2、服务端Controller接收页面提交的数据并调用Service将数据进行保存
3、Service调 用Mapper操作数据库,保存数据
@PostMapping//无需加路径了
public R<String> save(@RequestBody Employee employee){
log.info("新增员工信息:{}",employee.toString());
return null;
}
新增员工很多属性为空,可以设置MD5加密初始密码和其他属性
补充:fill自动填充:8.使用fill完成字段自动填充
@PostMapping//无需加路径了
public R<String> save(HttpServletRequest request,@RequestBody Employee employee){
log.info("新增员工信息:{}",employee.toString());
//设置初始密码123456, 需要进行md5加密处理
employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
//设置其他属性
employee.setCreateTime(LocalDateTime.now());
employee.setUpdateTime(LocalDateTime.now());
Long empId = (Long)request.getSession().getAttribute("employee");
//设置创建人,即先获取当前登录用户的id
employee.setCreateUser(empId);
employee.setUpdateUser(empId);
employeeService.save(employee);
return R.success("新增员工成功!");
}
功能测试
添加成功:
小问题:
当再次输入相同用户名时, sql报错,因为username加了唯一约束
编写全局异常处理器
当要处理的代码段很多很明显try catch不好用
//拦截那些类上面加了@RestController和@Controller注解的controller
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody //将结果封装为json
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
log.error(ex.getMessage());
return R.error("失败");
}
}
现在@Controller出现异常就会被拦截到,如果是SQLIntegrityConstraintViolationException类异常就会被分到这个函数进行处理。
结果
完善全局异常处理器并测试
现在就要取出001
//拦截那些类上面加了@RestController和@Controller注解的controller
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody //将结果封装为json
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
log.error(ex.getMessage());
//这样我们就知道这样违反了唯一约束
if (ex.getMessage().contains("Duplicate entry")){
String[] split = ex. getMessage().split( " ") ;
String msg = split[2] + "已存在";
return R.error(msg);
}
return R.error("失败");
}
}
结果
小结
员工信息分页查询
需求分析
梳理程序执行过程&代码开发
查看前端的请求路径及其参数
代码开发
- 1.配置mybatis分页插件(通过拦截器的方式将插件配置进来)
package com.zqf.reggie.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}
- 2.创建controller接收查询参数(分页相关数据)
前端需要传入这两个数据,所以R的泛型需要填入Page,因为这个类型含有这两个参数
现在考虑该传入哪些参数?考虑前端会传来哪些数据那些参数:page,pageSize,name
现在再来考虑路径问题,前端采用get请求方式,并且路径为page,所以需要加上注解@GetMapping(“/page”)
@GetMapping("/page")
public R<Page> page(int page, int pageSize, String name){
log.info("page = {},pageSize = {},name = {}",page,pageSize,name);
return null;
}
结果
- 3.构造分页构造器,调用service相关方法
service中page方法已经在内部将各种条件封装到pageinfo里面了。
@GetMapping("/page")
public R<Page> page(int page,int pageSize,String name){
log.info("page = {},pageSize = {},name = {}" ,page,pageSize,name);
//构造分页构造器
Page pageInfo = new Page(page,pageSize);
//构造条件构造器
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper();
//添加过滤条件
queryWrapper.like(StringUtils.isNotEmpty(name),Employee::getName,name);
//添加排序条件
queryWrapper.orderByDesc(Employee::getUpdateTime);
//执行查询
employeeService.page(pageInfo,queryWrapper);
return R.success(pageInfo);
}
功能测试
没有limit应该是拦截器没写好,没加@Configuation注解
加上之后:
package com.zqf.reggie.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}
结果
启用/禁用员工账号
需求分析
分析页面按钮动态显示效果
页面中是怎么做到只有管理员admin能够看到启用、禁用按钮的?
获取当前user,判断是否为admin,并动态显示启用/禁用
分析页面ajax请求发送过程
代码开发和功能测试
启用、禁用员工账号,本质上就是一个更新操作,也就是对status状态字段进行操作
在Controller中创建update方法,此方法是一个通用的修改员工信息的方法
确认路径和请求方式
/**
* 根据id修改员工信息
* @param employee
* @return
*/
@PutMapping
public R<String> update(@RequestBody Employee employee){
log.info(employee.toString());//查看参数是否能传进来
return null;
}
说明客户端和服务端能够正常请求处理,参数是能够正常传递的
设置参数属性之后
/**
* 根据id修改员工信息
* @param employee
* @return
*/
@PutMapping
public R<String> update(HttpServletRequest request,@RequestBody Employee employee){
log.info(employee.toString());//查看参数是否能传进来
Long empId = (Long) request.getSession().getAttribute("employee");
employee.setUpdateTime(LocalDateTime.now());
employee.setUpdateUser(empId);
/*
status在前端传过来的参数里,即在employee里
*/
employeeService.updateById(employee);
return R.success("修改成功");
}
But出现了问题
原因:js对于long类型只能精确到前16位,后面则做了四舍五入,所以
代码修复配置消息转换器
1.在commns目录下添加类,这个类可以将java对象转换为json,也可以将json转换为 对象
package com.zqf.reggie.common;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
/**
* 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
* 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
* 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
*/
public class JacksonObjectMapper extends ObjectMapper {
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
public JacksonObjectMapper() {
super();
//收到未知属性时不报异常
this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
//反序列化时,属性不存在的兼容处理
this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
SimpleModule simpleModule = new SimpleModule()
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
.addSerializer(BigInteger.class, ToStringSerializer.instance)
.addSerializer(Long.class, ToStringSerializer.instance)
.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
//注册功能模块 例如,可以添加自定义序列化器和反序列化器
this.registerModule(simpleModule);
}
}
2.扩展消息转换器
将controller方法中的返回结果(R对象),转换为json,然后再通过输出流的方式响应给页面,所以页面上展示的都是json数据。
/**
* 扩展mvc框架的消息转换器
* @param converters
*/
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//创建消息转换器
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
//设置对象转换器,底层使用Jackson将Java对象转为json
messageConverter.setObjectMapper(new JacksonObjectMapper());
//将上面的消息转换器对象追加到avc框架的转换器集合
converters.add(0,messageConverter); //转换器是有顺序的,要让转换器优先使用我们自己设置的转换器,所以设置索引为0
}
当执行add方法之后,我们的转换器已添加,其余8个事mvc自带的消息转换器
可以看到此时的id已经加上双引号,时间的格式也有所改变
编辑员工信息
需求分析
执行流程
信息回显需要查询数据库
页面效果分析
钩子函数,渲染页面时自动执行
获取参数用来查询回显
如果获取的id有值得话那就是编辑选项而不是add,因为这两个功能都在同一个页面进去
当服务端响应过来之后,就会调用回调函数,如果res.code = 1(成功的状态码)就数据回显
controller添加查询信息
@GetMapping("/{id}") //注意id是个路径变量 通过以下方式获取
public R<Employee> getById(@PathVariable Long id){
log.info("根据id查询信息");
Employee byId = employeeService.getById(id);
if (byId!=null){
return R.success(byId);//确保一定是查出来的
}
return R.error("没有查询到员工信息");
}
效果
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/99436.html