1. 登录
1.1 前端
实现登录功能,需要前端向后端发送请求,前端请求如下:
javascript
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)
// 存储用户名到Cookie
Cookies.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页面
javascript
/*在页面被加载的时候就去请求后台*/
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实体类接收
java
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字段
java
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
java
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 {
@Autowired
private 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层
java
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层
java
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 实现类
java
@Override
public 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的区别
-
拦截器是Spring MVC框架层面的,Filter是Servlet规范层面的
-
拦截器可以获取Spring容器中的Bean,Filter不能
-
拦截器可以针对具体Controller方法,Filter只能针对URL
-
拦截器有更精细的生命周期控制(pre/post/complete)
拦截器通过实现HandlerInterceptor接口或继承HandlerInterceptorAdapter类来创建,然后在Spring MVC配置中注册
2.2 实战
- LoginInterceptor 声明拦截器,扫描加载bean
java
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 {
// 前置拦截
@Override
public 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");
}
// 后置拦截
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle login interceptor");
}
// 最终拦截
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion login interceptor");
}
}
- 添加拦截器设定拦截访问路径
java
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 {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 拦截下路径/pages/** 放行到 /pages/
registry.addResourceHandler("/pages/**")
.addResourceLocations("classpath:/pages/");
// 打印日志
log.info("静态资源已经放行");
}
// 拦截器配置
@Autowired
private LoginInterceptor loginInterceptor;
// 拦截路径配置
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor).addPathPatterns("/emps");
}
}
- 前端请求拦截
如果响应notlogin,前端则进行页面跳转,跳转到登录页面进行登录
java
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 主要组件
-
@ExceptionHandler - 标注在方法上,定义处理特定异常的逻辑
-
@ControllerAdvice - 配合
@ExceptionHandler
实现全局异常处理 -
ResponseEntityExceptionHandler - Spring提供的默认异常处理基类
3.3 实战
那我们项目中比较常见的sql异常为例
定义全局异常处理器

@RestControllerAdvice是 Spring 框架中的一个组合注解,结合了 @ControllerAdvice 和 @ResponseBody 的功能,用于全局处理控制器(Controller)中抛出的异常,并统一返回结构化的响应数据。
@ExceptionHandler 是 Spring 框架中的一个注解,用于处理控制器(Controller)中抛出的异常。它通常用在方法上,表示该方法用于捕获和处理当前 Controller 中发生的特定异常。但是只能处理当前 Controller 类中抛出的异常。
java
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());
}
}
前端接收到后端异常信息,渲染到页面

- 执行优先级
-
优先匹配当前Controller中的
@ExceptionHandler
-
其次匹配
@ControllerAdvice
中定义的处理器 -
最后是Spring默认的异常处理机制
4.优化
学习异常捕获器之后,我们可以将SpringMvc拦截器的代码进行优化,创建一个登录的异常捕获类和异常捕获器用来捕获未登录的异常,从而给前端以响应,优化代码如下:
- LoginException登录异常类
java
package com.gaohe.ssm1.common;
public class LoginException extends RuntimeException{
public LoginException(String message) {
super(message);
}
}
- LoginHandler登陆异常捕获器
java
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登录拦截器,如果验证不通过则抛出异常
java
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 {
// 前置拦截
@Override
public 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");
}
// 后置拦截
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle login interceptor");
}
// 最终拦截
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion login interceptor");
}
}
- 前端拦截器给用户做出回应
javascript
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;
})
大致流程给大家画了个图,方便大家理解
