当前位置: 首页 > news >正文

SpringBoot前后台交互 -- 登录功能实现(拦截器+异常捕获器)

1. 登录

1.1 前端

        实现登录功能,需要前端向后端发送请求,前端请求如下:

methods: {submitForm() {console.log(this.ruleForm)axios({method:"post",url:"/auth/login",data:this.ruleForm}).then(res =>{alert(res.data)if (res.data.code=="200"){// 登录成功提示this.$message.success(res.data.msg)// 存储用户名到CookieCookies.set("empname",this.ruleForm.username)// 两秒后跳转到员工页面setTimeout(function (){location.href = "emp.html"},2000)}else{// 登录失败提示this.$message.error(res.data.msg)}}).catch(res =>{// 网络请求异常处理})},resetForm(formName) {this.$refs[formName].resetFields();}}
  • emp.html页面
/*在页面被加载的时候就去请求后台*/mounted() {console.log("init....")/*发起请求 获取数据 把数据交给渲染层展示*/this.init()/*获取用户的登录信息*/this.username = Cookies.get("empname")/*定时器获取在线人数*/var _this = this// setInterval(function (){//     axios.get("emp/getCount").then(resp=>{//         console.log("在线人数:"+resp.data)//         _this.personcount = resp.data//     })// },5000)},

        前端发送请求之后需要后端接受请求,进行响应

1.2 后端

1.2.1 Emps实体类接收

package com.gaohe.ssm1.pojo;import lombok.Data;
import org.springframework.context.annotation.PropertySource;@Data
public class Emps {private int id;private String username;private String password;private String addr;private int age;private String phone;}

        但是我们会发现前端页面的参数是这样的:

        单靠Emps是无法接收number字段的,所以在这里我们可以创建一个vo类,继承Emps类,使之可以同时接收username、password和number字段

package com.gaohe.ssm1.vo;import com.gaohe.ssm1.pojo.Emps;
import lombok.Data;public class LoginVo extends Emps {private boolean number;public boolean getNumber() {return number;}public void setNumber(boolean number) {this.number = number;}
}

1.2.2 Controller层

        主要实现以下几个功能:

  • 根据用户名查询数据库用户信息
  • 根据查到的用户信息进行登陆判断
  • 若number为true,需要将用户名和密码存入session,方便前端保存cookies
package com.gaohe.ssm1.controller;import com.gaohe.ssm1.common.R;
import com.gaohe.ssm1.pojo.Emps;
import com.gaohe.ssm1.service.EmpsService;
import com.gaohe.ssm1.vo.LoginVo;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/auth")
public class AuthController {@Autowiredprivate EmpsService empsService;@PostMapping("/login")public R login(@RequestBody LoginVo loginVo, HttpSession session, HttpServletResponse response){
//        接收参数
//        System.out.println(loginVo.getNumber());String username = loginVo.getUsername();String password = loginVo.getPassword();Emps emp1 = empsService.findByUserName(username);if (emp1 == null){return R.fail("用户名不存在");}if (!emp1.getPassword().equals(password)){
//            System.out.println(password);
//            System.out.println(emp1.getPassword());return R.fail("密码输入错误");}boolean number = loginVo.getNumber();if (number){// 创建两个 Cookie,用于保存用户名和密码Cookie username1 = new Cookie("username1", emp1.getUsername());Cookie pass1 = new Cookie("pass1", emp1.getPassword());// 设置 Cookie 的最大生存时间为 7 天(单位:秒)username1.setMaxAge(60 * 60 * 24 * 7);pass1.setMaxAge(60 * 60 * 24 * 7);// 设置 Cookie 的路径为根路径,确保在整个应用中可用username1.setPath("/");pass1.setPath("/");// 将 Cookie 添加到响应对象中response.addCookie(username1);response.addCookie(pass1);}session.setAttribute("username",emp1.getUsername());return R.success("登录成功");}
}

1.2.3 mapper层

package com.gaohe.ssm1.mapper;import com.gaohe.ssm1.pojo.Emps;
import org.apache.ibatis.annotations.*;import java.util.List;@Mapper
public interface EmpsMapper {//    查询所有@Select("select * from emps")public List<Emps> list();//    通过id查询@Select("select * from emps where id = #{id}")public Emps findById(int id);//    通过name查询@Select("select * from emps where username = #{username}")public Emps findByUserName(String username);//    新增@Insert("insert into emps(id,username,password,addr,age,phone) " +"values (null ,#{username},#{password},#{addr},#{age},#{phone})")public int save(Emps emps);//    修改@Update("update emps set username=#{username},password = #{password}," +"addr=#{addr},age=#{age},phone=#{phone} where id = #{id}")public int update(Emps emps);//    删除@Delete("delete from emps where id = #{id}")public int delete(int id);
}

1.2.4 service层

package com.gaohe.ssm1.service;import com.gaohe.ssm1.pojo.Emps;import java.util.List;public interface EmpsService {//    查询所有public List<Emps> list();//    通过id查询public Emps findById(int id);public Emps findByUserName(String username);//    新增public int save(Emps emps);//    修改public int update(Emps emps);//    删除public int delete(int id);
}

1.2.5 实现类

@Overridepublic Emps findByUserName(String username) {return empsMapper.findByUserName(username);}

2. SpringMvc拦截器

2.1 概念

        我们登录网站时经常会看到登陆成功后可以查询到页面信息,进行一系列操作,如果我们跳过登录直接访问网站是不可以的,这可以极大的保护信息安全,实现拦截可以用到springMvc拦截

        Spring MVC拦截器(Interceptor)是基于Java反射机制和动态代理实现的,用于在请求处理的不同阶段进行拦截和处理的一种机制。它类似于Servlet中的Filter,但提供了更精细的控制能力。

拦截器的主要特点:

  • 基于AOP思想实现
  • 与Spring框架深度集成
  • 可以获取Spring容器中的Bean
  • 针对Handler(Controller方法)进行拦截

作用

  • 预处理(PreHandle)
    • 在Controller方法执行前拦截
    •  常用于:权限验证、参数校验、日志记录
  • 后处理(PostHandle)
    • 在Controller方法执行后,视图渲染前拦截
    • 常用于:修改ModelAndView、记录响应数据
  • 完成后处理(AfterCompletion)
    • 在整个请求完成后拦截(视图渲染完成)
    • 常用于:资源清理、性能监控

与Filter的区别

1. 拦截器是Spring MVC框架层面的,Filter是Servlet规范层面的
2. 拦截器可以获取Spring容器中的Bean,Filter不能
3. 拦截器可以针对具体Controller方法,Filter只能针对URL
4. 拦截器有更精细的生命周期控制(pre/post/complete)

        拦截器通过实现HandlerInterceptor接口或继承HandlerInterceptorAdapter类来创建,然后在Spring MVC配置中注册

2.2 实战

  • LoginInterceptor 声明拦截器,扫描加载bean
package com.gaohe.ssm1.interceptor;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.security.auth.login.LoginException;/*** 登录拦截器,用于处理用户登录状态的验证* 该拦截器会在请求到达Controller之前进行预处理*/
@Component
public class LoginInterceptor implements HandlerInterceptor {
//    前置拦截@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle login interceptor");
//        1.获取路径 2.静态资源放行
//        3.登录就放行Object username = request.getSession().getAttribute("username");if (username != null){return true;}
//        4.没有登录 拦截response.getWriter().write("notlogin");true 拦截放行 false 拦截return false;//    throw new LoginException("notlogin");}//    后置拦截@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle login interceptor");}//    最终拦截@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion login interceptor");}
}
  • 添加拦截器设定拦截访问路径
package com.gaohe.ssm1.config;import com.gaohe.ssm1.interceptor.LoginInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
@EnableWebMvc
@Slf4j
public class SpringMvc implements WebMvcConfigurer {@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {
//        拦截下路径/pages/** 放行到 /pages/registry.addResourceHandler("/pages/**").addResourceLocations("classpath:/pages/");
//        打印日志log.info("静态资源已经放行");}//    拦截器配置@Autowiredprivate LoginInterceptor loginInterceptor;//    拦截路径配置@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/emps");}
}
  • 前端请求拦截

        如果响应notlogin,前端则进行页面跳转,跳转到登录页面进行登录


axios.interceptors.request.use(req => {return req;
})
axios.interceptors.response.use(resp => {if(resp.data == "notlogin") {alert("用户未登录")setTimeout(function () {window.location.href = "/pages/login.html"}, 2000)}return resp;
})

3. 异常捕获器

3.1 概念

        Spring MVC异常捕获器是Spring框架提供的一套统一异常处理机制,主要用于集中处理Controller层抛出的各种异常。核心注解是@ExceptionHandler,它允许开发者在Controller内部或通过@ControllerAdvice全局定义异常处理方法。

3.2 主要组件

  1. @ExceptionHandler - 标注在方法上,定义处理特定异常的逻辑

  2. @ControllerAdvice - 配合@ExceptionHandler实现全局异常处理

  3. ResponseEntityExceptionHandler - Spring提供的默认异常处理基类

3.3 实战

        那我们项目中比较常见的sql异常为例

  • 定义全局异常处理器

        @RestControllerAdvice 是 Spring 框架中的一个组合注解,结合了 @ControllerAdvice 和 @ResponseBody 的功能,用于全局处理控制器(Controller)中抛出的异常,并统一返回结构化的响应数据。

        @ExceptionHandler 是 Spring 框架中的一个注解,用于处理控制器(Controller)中抛出的异常。它通常用在方法上,表示该方法用于捕获和处理当前 Controller 中发生的特定异常。但是只能处理当前 Controller 类中抛出的异常。

package com.gaohe.ssm1.handler;import com.gaohe.ssm1.common.R;
import com.mysql.cj.jdbc.exceptions.MysqlDataTruncation;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;import java.sql.SQLIntegrityConstraintViolationException;/*** 全局异常处理器,用于捕获和处理数据库相关异常。* 结合Spring的@RestControllerAdvice注解,实现全局异常统一响应格式。*/
@RestControllerAdvice
public class SQLHandler {
//    同名异常@ExceptionHandler(SQLIntegrityConstraintViolationException.class)public R ex2(SQLIntegrityConstraintViolationException e){String msg = e.getMessage();if (msg.contains("Duplicate entry")){String[] split = msg.split(" ");msg= split[2]+"用户名已存在";}return R.fail(msg);}//    字段过长 异常@ExceptionHandler(MysqlDataTruncation.class)public R ex1(MysqlDataTruncation e){String msg = e.getMessage();if (msg.contains("Data too long")) {String[] split = msg.split(" ");if (split.length > 8) { // 确保数组长度足够避免越界msg = split[8] + "输入过长";}}return R.fail(e.getMessage());}}
  • 前端接收到后端异常信息,渲染到页面

  • 执行优先级

  1. 优先匹配当前Controller中的@ExceptionHandler

  2. 其次匹配@ControllerAdvice中定义的处理器

  3. 最后是Spring默认的异常处理机制

4.优化

        学习异常捕获器之后,我们可以将SpringMvc拦截器的代码进行优化,创建一个登录的异常捕获类和异常捕获器用来捕获未登录的异常,从而给前端以响应,优化代码如下:

  • LoginException登录异常类
package com.gaohe.ssm1.common;public class LoginException extends RuntimeException{public LoginException(String message) {super(message);}
}
  •  LoginHandler登陆异常捕获器
package com.gaohe.ssm1.handler;import com.gaohe.ssm1.common.LoginException;
import com.gaohe.ssm1.common.R;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvice
public class LoginHandler {@ExceptionHandler(LoginException.class)public R ex(LoginException e){System.out.println(e.getMessage());return R.fail(e.getMessage());}
}
  •  LoginInterceptor登录拦截器,如果验证不通过则抛出异常
package com.gaohe.ssm1.interceptor;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.security.auth.login.LoginException;/*** 登录拦截器,用于处理用户登录状态的验证* 该拦截器会在请求到达Controller之前进行预处理*/
@Component
public class LoginInterceptor implements HandlerInterceptor {
//    前置拦截@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle login interceptor");
//        1.获取路径 2.静态资源放行
//        3.登录就放行Object username = request.getSession().getAttribute("username");if (username != null){return true;}
//        4.没有登录 拦截
//        response.getWriter().write("notlogin");true 拦截放行 false 拦截
//        return false;throw new LoginException("notlogin");}//    后置拦截@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle login interceptor");}//    最终拦截@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion login interceptor");}
}
  •  前端拦截器给用户做出回应

axios.interceptors.request.use(req => {return req;
})
axios.interceptors.response.use(resp => {if(resp.data == "notlogin") {alert("用户未登录")setTimeout(function () {window.location.href = "/pages/login.html"}, 2000)}return resp;
})

 大致流程给大家画了个图,方便大家理解

相关文章:

  • SpringCloud系列 - Nacos 配置中心(二)
  • 美食推荐系统微信小程序
  • 咖啡豆缺陷检测:用YOLOv8+TensorFlow实现工业级质检系统
  • 力扣HOT100之贪心算法:45. 跳跃游戏 II
  • 3 Studying《深入理解Android卷(邓凡平)》2
  • 考试中关于机动车安全技术检验标准(如 GB 7258、GB 21861 等)的考核重点有哪些?
  • 物联网配置记录
  • 鸿蒙开发-封装一个顶部标题栏
  • FastAPI系列20:fastapi-amis-admin,即开即用的后台框架(2)
  • BUG调试案例十四:TL431/TL432电路发热问题案例
  • SLAM3R:基于单目视频的实时密集3D场景重建
  • UE5 学习系列(六)导入资产包
  • Pandas:让数据起舞的Python魔法手册
  • SQL进阶之旅 Day 25:高并发环境下的SQL优化
  • 基于 WebWorker 的 WebAssembly 图像处理吞吐量分析
  • 深入理解TCP以及三次握手与四次挥手
  • Kotlin 中的 Object
  • [Java恶补day22] 240. 搜索二维矩阵Ⅱ
  • 1.SDH概述、STM-N帧结构
  • 【Dv3Admin】系统视图用户登录API文件解析
  • 怎么用腾讯云服务器做网站/今日国内新闻最新消息10条
  • 汨罗网站建设/全球搜索引擎
  • 大连seo快速排名/如何优化搜索引擎的搜索功能
  • 搭建网站 赚钱/seo sem论坛
  • 上海做哪些行业赚钱/最好的网站优化公司
  • 资料代做网站/洛阳搜索引擎优化