前言
- 之前 SpringMVC 中已经有过关于 拦截器的讲解和配置,(尚硅谷)2021 版 SpringMVC 教程笔记,这里简单带过。
1、显示登录信息
1.1 拦截器示例
- 定义拦截器,实现 HandlerInterceptor;
@Component
public class HelloInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(HelloInterceptor.class);
// 在Controller之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.debug("preHandle: " + handler.toString());
return true;
}
// 在Controller之后执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.debug("postHandle: " + handler.toString());
}
// 在TemplateEngine之后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.debug("afterCompletion: " + handler.toString());
}
}
- 配置拦截器,为它指定拦截、排除的路径;
- 配置 MVC 的时候要实现 WebMvcConfigurer 这个类才可以;
- 将拦截器注入;
- 重写
addInterceptors
方法
- 将拦截器添加进来:
registry.addInterceptor
- 排除所有静态资源:
registry.excludePathPatterns
- 设置拦截哪些路径:
registry.addPathPatterns
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private HelloInterceptor helloInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(helloInterceptor)
.excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")
.addPathPatterns("/register", "/login");
}
}
测试一下是否拦截成功,index 页面不拦截,登录、注册页面被拦截:
2.2 拦截器应用(使用线程隔离批量处理用户请求)
- 在请求开始时查询登录用户
- 在本次请求中持有用户数据
- 在模板视图上显示用户数据
- 在请求结束时清理用户数据
2.2.1 新建 LoginTicketInterceptor 拦截器
- 将从 cookie 中获取特定信息的方法包装成一个工具类;
public class CookieUtil {
public static String getValue(HttpServletRequest request, String name) {
if (request == null || name == null) {
throw new IllegalArgumentException("参数为空!");
}
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(name)) {
return cookie.getValue();
}
}
}
return null;
}
}
request.getCookies();
返回的是所有 cookie 信息,我们要从中取我们想要的信息。
- 实现 prehandle 方法:
- 通过 cookie 得到 ticket 信息,用户登录之后会重定向到首页,这时我们可以从第二次请求中获取用户信息;
- 在 prehandle 中只能通过请求 request 来获取 cookie;
- 如果 ticket 不为空,调用 UserService 层去获取该凭证对应的所有登录信息(UserService 层要加一个方法);
- 检查凭证是否有效:LoginTicket 不为空,状态码为0,且有效时长没有超时的情况是有效的,调用 UserService 层查询 user 数据;
- 本次请求中持有用户:
//用户登录成功之后重定向到首页,这时我们可以获取用户信息,让线程持有当前用户
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从cookie中获取凭证
String ticket = CookieUtil.getValue(request, "ticket");
if (ticket != null) {
// 查询凭证
LoginTicket loginTicket = userService.findLoginTicket(ticket);
// 检查凭证是否有效
if (loginTicket != null && loginTicket.getStatus() == 0 && loginTicket.getExpired().after(new Date())) {
// 根据凭证查询用户
User user = userService.findUserById(loginTicket.getUserId());
// 在本次请求中持有用户
hostHolder.setUser(user);
}
}
return true;
}
解释:
- 线程隔离存放用户信息,实现 HostHolde r类,使用 ThreadLocal 模拟 session 存放用户信息;
- ThreadLocal 以线程为 key 实现存取值;
- 将 HostHolder 注入,调用其中的 setUser 方法,将用户信息存到某一线程对应的map容器中;
/**
* 持有用户信息,用于代替session对象.
*/
@Component
public class HostHolder {
private ThreadLocal<User> users = new ThreadLocal<>();
public void setUser(User user) {
users.set(user);
}
public User getUser() {
return users.get();
}
public void clear() {
users.remove();
}
}
- 实现 postHandler 方法:
- 调用 HostHolder 取 user 值,将当前用户存放在模板引擎中,用来显示用户信息;
//将持有的用户信息保存到模板引擎中
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
User user = hostHolder.getUser();
if (user != null && modelAndView != null) {
modelAndView.addObject("loginUser", user);
}
}
- 实现afterCompletion方法:
- 清掉当前用户占用的线程
//模板渲染结束后,即当前请求结束后清理县城内存放的用户
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
hostHolder.clear();
}
代码总结:
@Component
public class LoginTicketInterceptor implements HandlerInterceptor {
@Autowired
private UserService userService;
@Autowired
private HostHolder hostHolder;
//用户登录成功之后重定向到首页,这时我们可以获取用户信息,让线程持有当前用户
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从cookie中获取凭证
String ticket = CookieUtil.getValue(request, "ticket");
if (ticket != null) {
// 查询凭证
LoginTicket loginTicket = userService.findLoginTicket(ticket);
// 检查凭证是否有效
if (loginTicket != null && loginTicket.getStatus() == 0 && loginTicket.getExpired().after(new Date())) {
// 根据凭证查询用户
User user = userService.findUserById(loginTicket.getUserId());
// 在本次请求中持有用户
hostHolder.setUser(user);
}
}
return true;
}
//将持有的用户信息保存到模板引擎中
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
User user = hostHolder.getUser();
if (user != null && modelAndView != null) {
modelAndView.addObject("loginUser", user);
}
}
//模板渲染结束后,即当前请求结束后清理县城内存放的用户
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
hostHolder.clear();
}
}
2.2.2 UserService 层
- 实现通过 Ticket 查询用户登录凭证信息;
@Override
public LoginTicket findLoginTicket(String ticket) {
return loginTicketMapper.selectByTicket(ticket);
}
2.2.3 WebMvcConfig 中配置拦截器
- 添加当前拦截器,同时排除所有静态页面(减少拦截器拦截的请求数量,静态页面也会发请求):
@Autowired
private LoginTicketInterceptor loginTicketInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginTicketInterceptor)
.excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
}
2.2.4 修改 index.html 页面
- 消息:登录状态下显示,
th:if="${loginUser!=null}"
- 登录、注册:登录了不显示,
th:if="${loginUser==null}"
- 用户头像、用户名动态显示:
th:src="${loginUser.headerUrl}"
,th:utext="${loginUser.username}"
- 修改退出登录超链接:
th:href="@{/logout}"
<ul class="navbar-nav mr-auto">
<li class="nav-item ml-3 btn-group-vertical">
<a class="nav-link" th:href="@{/index}">首页</a>
</li>
<li class="nav-item ml-3 btn-group-vertical" th:if="${loginUser!=null}">
<a class="nav-link position-relative" href="site/letter.html">消息<span class="badge badge-danger">12</span></a>
</li>
<li class="nav-item ml-3 btn-group-vertical" th:if="${loginUser==null}">
<a class="nav-link" th:href="@{/register}">注册</a>
</li>
<li class="nav-item ml-3 btn-group-vertical" th:if="${loginUser==null}">
<a class="nav-link" th:href="@{/login}">登录</a>
</li>
<li class="nav-item ml-3 btn-group-vertical dropdown" th:if="${loginUser!=null}">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<img th:src="${loginUser.headerUrl}" class="rounded-circle" style="width:30px;"/>
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item text-center" href="site/profile.html">个人主页</a>
<a class="dropdown-item text-center" href="site/setting.html">账号设置</a>
<a class="dropdown-item text-center" th:href="@{/logout}">退出登录</a>
<div class="dropdown-divider"></div>
<span class="dropdown-item text-center text-secondary" th:utext="${loginUser.username}">nowcoder</span>
</div>
</li>
</ul>
2.2.5 测试页面
- 登陆之前:
- 登录之后:
- 点击退出登录
2、账号设置
2.1 上传文件
在 Controller 中处理上传文件的步骤:
- 判断控制和文件类型是否正确;
- 随机生成文件名 + 后缀;
- 确定文件上传目录,并判断该目录是否存在,不存在则创建目录;
- 最终确定文件上传路径为 上传目录 + / + 文件名;
- 利用 transferTo 方法上传文件。
2.2 新建 UserController 类
- 实现 getSettingPage() 方法;
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping(value = "/setting",method = RequestMethod.GET)
public String getSettingPage(){
return "site/setting";
}
}
2.3 修改 setting.html 页面
- 引入 thymeleaf 文件
- 静态资源路径修改
- 头部代码复用:
th:replace="index::header"
2.4 修改 index.html 页面
- 将账号设置的超链接进行修改:
<a class="dropdown-item text-center" th:href="@{/user/setting}">账号设置</a>
2.5 测试页面
2.6 配置资源上传的路径(上传头像)
community.path.upload=d:/work/data/upload
- 我们只在 Controller 中处理上传文件的逻辑,而不调用 service 层,因为 MultipartFile 是表现层 SpringMVC 的一个组件,不要在 service 层调用;
2.7 UserService 层
- 实现更新用户头像方法,即根据用户的 userId 更新用户头像的 url 地址;
@Override
public int updateHeader(int userId, String headerUrl) {
return userMapper.updateHeader(userId,headerUrl);
}
2.8 UserController 层(客户端到服务器)
-
判断文件是否为空,或者文件格式是否正确;
-
生成随机文件名: 使用生成随机字符串工具类生成 随机字符+后缀 作为文件名
-
确定文件目录是否为空,为空则创建目录;
-
确定文件存放的位置:新建一个 file,其中是上传路径+/+文件名字
-
存储文件:使用 transferTo(File file) 方法传到服务器中;
-
更新当前用户的头像的路径(web访问路径):
http://localhost:8080/community/user/header/xxx.png
, 调用 UserService 层更新数据库中用户头像的 url 地址; -
重定向到首页。
@RequestMapping(value = "/setting", method = RequestMethod.POST)
public String uploadHeader(
@RequestParam(value = "headerImage") MultipartFile photo, Model model) {
//空值处理
if (photo == null || photo.isEmpty()) {
model.addAttribute("error", "您还没有选择图片!");
return "/site/setting";
}
//文件检测是否合格
String fileName = photo.getName();
String suffix = fileName.substring(fileName.lastIndexOf('.') + 1);
if (StringUtils.isBlank(suffix)) {
model.addAttribute("error", "文件的格式不正确!");
return "/site/setting";
}
//生成随机文件名
fileName = CommunityUtil.generateUUID() + suffix;
//确认文件上传路径目录是否为空
File dir = new File(uploadPath);
if (!dir.exists()){
dir.mkdir();
}
//确定图片在服务器中的存放路径
File dest = new File(uploadPath + File.separator + fileName);
try {
//上传图片
photo.transferTo(dest);
} catch (IOException e) {
logger.error("上传文件失败: " + e.getMessage());
throw new RuntimeException("上传文件失败,服务器发生异常!", e);
}
// 更新当前用户的头像的路径(web访问路径)
// http://localhost:8080/community/user/header/xxx.png
User user = hostHolder.getUser();
String headerUrl = domain + contextPath + "/user/header/" + fileName;
userService.updateHeader(user.getId(), headerUrl);
//重定向到首页
return "redirect:/index";
}
2.9 UserController 层:获取头像(服务器响应给客户端)
- 实现获取头像方法,从图片在服务器的路径中读取图片(字节输入流),使用 response 获取字节输出流向客户端写文件;
- 设置相应类型为:
setContentType=("image/"+suffix);
; - 手动关闭我们自己创建的输入流(java7 中在 try 后面的小括号中写上需要关闭的资源,前提是要有 close 方法);通过 response 获取到的输出流,SpringMVC 会自动帮我们进行关闭操作。
//给客户端显示新头像
@RequestMapping(path = "/header/{fileName}", method = RequestMethod.GET)
public void getHeader(@PathVariable("fileName") String fileName, HttpServletResponse response) {
// 服务器存放路径
fileName = uploadPath + File.separator + fileName;
// 文件后缀(要作为给web的响应内容,所以不可以带.)
String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
// 响应图片
response.setContentType("image/" + suffix);
try (
//通过输入流读取文件信息
FileInputStream fis = new FileInputStream(fileName);
//通过输出流写到网页上
OutputStream os = response.getOutputStream();
) {
//图片可能比较大,缓冲输出,声明一个缓冲区,游标
byte[] buffer = new byte[1024];
int b = 0;
while ((b = fis.read(buffer)) != -1) {
os.write(buffer, 0, b);
}
} catch (IOException e) {
logger.error("读取头像失败: " + e.getMessage());
}
}
2.10 处理 settings.html 页面
- 上传头像的表单修改
- 手动处理如果没有文件为空的错误信息显示
<!-- 上传头像 -->
<h6 class="text-left text-info border-bottom pb-2">上传头像</h6>
<form class="mt-5" th:action="@{/user/setting}" enctype="multipart/form-data" method="post">
<div class="form-group row mt-4">
<label for="head-image" class="col-sm-2 col-form-label text-right">选择头像:</label>
<div class="col-sm-10">
<div class="custom-file">
<input type="file" th:class="|custom-file-input ${error!=null?'is-invalid':''}|"
name="headerImage"
id="head-image" lang="es" required="">
<label class="custom-file-label" for="head-image" data-browse="文件">选择一张图片</label>
<div class="invalid-feedback" th:text="${error}">
请上传图片!
</div>
</div>
</div>
</div>
<div class="form-group row mt-4">
<div class="col-sm-2"></div>
<div class="col-sm-10 text-center">
<button type="submit" class="btn btn-info text-white form-control">立即上传</button>
</div>
</div>
</form>
2.11 测试页面
- 不上传头像(前端逻辑也过不去)
- 上传不带文件后缀的文件:
- 正确上传头像:
2.10 错误排查
- 必须在请求的 form 表单中设置上传格式为:
enctype="multipart/form-data"
,否则的话报错;
Current request is not a multipart request
-
获取上传的图片的文件名字必须使用
photo.getOriginalFilename();
方法,这样才是获取你上传的文件的名字,否则的话是不对的; -
记得在 UserController 上面加上
@RequestMapping("/user")
,代表在上下文路径下多一层目录,同时表单请求和 index.html 文件中的账号设置请求都要在请求中添加/user
这一层路径:
<form class="mt-5" th:action="@{/user/setting}" enctype="multipart/form-data" method="post">
- 上传不带文件后缀的文件时报错,表示文件没有后缀名的时候
lastIndexOf
这个方法默认为 -1 ,这时使用substring
方法来进行字符串截取就会报索引越界异常:
java.lang.StringIndexOutOfBoundsException: begin -1, end 23, length 23
将 UserController 修改如下:
int separator = 0;
String suffix = fileName.substring(separator = fileName.lastIndexOf('.')==-1?fileName.length():separator);
3、账号设置(修改密码)
3.1 UserService 层
- 实现修改密码功能:
- 空值处理:输入旧密码不能为空,输入的新密码不能为空,输入的新密码的确认密码不能为空;
- 验证状态:
- 判断两次输入的新密码是否一致,验证输入的旧密码是否正确;
- 将用户输入的旧密码使用 MD5 加密算法进行加密得到加密后的密码,将这个密码和当前用户的密码进行比较,如果不一致,代表输入的密码错误
- 使登录凭证失效:
- 调用 DAO 层修改登录凭证状态码为 1 ;
@Override
public Map<String, Object> updatePassword(User user,String oldPassword, String newPassword, String confirmPassword,String ticket) {
Map<String, Object> map = new HashMap<>();
//空值处理
if (StringUtils.isBlank(user.getPassword())) {
map.put("oldPasswordMsg", "请输入您原来的密码!");
return map;
}
if (StringUtils.isBlank(newPassword)) {
map.put("newPasswordMsg", "请输入您的新密码!");
return map;
}
if (StringUtils.isBlank(confirmPassword)) {
map.put("confirmPasswordMsg", "请再次输入您的新密码!");
return map;
}
//两次输入的新密码是否一致
if (!newPassword.equals(confirmPassword)) {
map.put("confirmPasswordMsg", "两次输入密码不一致!");
return map;
}
//验证旧密码是否正确,注册的时候在末尾加盐,这里也一样
oldPassword = CommunityUtil.md5(oldPassword + user.getSalt());
if (!user.getPassword().equals(oldPassword)){
map.put("oldPasswordMsg", "您输入的密码不正确!");
return map;
}
//修改密码
newPassword = CommunityUtil.md5(newPassword + user.getSalt());
userMapper.updatePassword(user.getId(),newPassword);
//修改用户登录状态,修改密码之后让用户重新登陆,所以要将原来的登录状态改为退出
loginTicketMapper.updateStatus(ticket, 1);
return map;
}
3.2 UserController 层
- 实现修改密码的功能:
- 从当前线程获取用户信息
- 调用 UserService 层进行密码的校验;
- 根据返回的 map 集合信息判断要跳转的页面,并且显示错误信息,或者修改成功的信息。
//修改密码
@RequestMapping(path = "/update", method = RequestMethod.POST)
public String updatePassword(Model model, String oldPassword,
String newPassword, String confirmPassword,
@CookieValue("ticket") String ticket
) {
User user = hostHolder.getUser();
Map<String, Object> map = userService.updatePassword(user, oldPassword, newPassword, confirmPassword, ticket);
if (map == null || map.isEmpty()) {
model.addAttribute("msg", "密码修改成功,请重新登录!");
model.addAttribute("target", "/login");
return "site/operate-result";
}else {
model.addAttribute("oldPasswordMsg",map.get("oldPasswordMsg"));
model.addAttribute("newPasswordMsg",map.get("newPasswordMsg"));
model.addAttribute("confirmPasswordMsg",map.get("confirmPasswordMsg"));
return "/site/setting";
}
}
3.3 修改 setting.html 页面:
- 首先是前端对两次输入密码不一致的判定,这里参考 register.html 注册页面的验证方式,新增了一个 setting.js 文件,实现验证两次密码不一致时的错误信息显示:
$(function(){
$("form").submit(check_data);
$("input").focus(clear_error);
});
function check_data() {
var pwd1 = $("#new-password").val();
var pwd2 = $("#confirm-password").val();
if(pwd1 != pwd2) {
$("#confirm-password").addClass("is-invalid");
return false;
}
return true;
}
function clear_error() {
$(this).removeClass("is-invalid");
}
- 在 setting.html 最后引入该文件:
<script th:src="@{/js/setting.js}"></script>
-
修改 form 表单的方法和行为:
th:action="@{/user/update}" method="post"
; -
修改所有输入文本框的 name 属性,必须和 UserController 中形参定义的参数名称一致,否则接收不到 form 表单提交的值;
-
错误信息展示:
- 用户输入的值还显示在页面上,可以从 request 中取值:
th:value="${param.oldPassword}"
; - 显示错误信息:
th:text="${oldPasswordMsg}"
- 显示错误信息样式:
th:class="|form-control ${oldPasswordMsg!=null?oldPasswordMsg}|"
- 用户输入的值还显示在页面上,可以从 request 中取值:
<!-- 修改密码 -->
<h6 class="text-left text-info border-bottom pb-2 mt-5">修改密码</h6>
<form class="mt-5" th:action="@{/user/update}" method="post">
<div class="form-group row mt-4">
<label for="old-password" class="col-sm-2 col-form-label text-right">原密码:</label>
<div class="col-sm-10">
<input type="password" th:class="|form-control ${oldPasswordMsg!=null?oldPasswordMsg}|"
name="oldPassword" th:value="${param.oldPassword}"
id="old-password" placeholder="请输入原始密码!" required>
<div class="invalid-feedback" th:text="${oldPasswordMsg}">
密码长度不能小于8位!
</div>
</div>
</div>
<div class="form-group row mt-4">
<label for="new-password" class="col-sm-2 col-form-label text-right">新密码:</label>
<div class="col-sm-10">
<input type="password" class="form-control"
name="newPassword" th:value="${param.newPassword}"
id="new-password" placeholder="请输入新的密码!" required>
<div class="invalid-feedback" th:text="${newPasswordMsg}">
密码长度不能小于8位!
</div>
</div>
</div>
<div class="form-group row mt-4">
<label for="confirm-password" class="col-sm-2 col-form-label text-right">确认密码:</label>
<div class="col-sm-10">
<input type="password" class="form-control"
name="confirmPassword" th:value="${param.confirmPassword}"
id="confirm-password" placeholder="再次输入新密码!" required>
<div class="invalid-feedback" th:text="${confirmPasswordMsg}">
两次输入的密码不一致!
</div>
</div>
</div>
<div class="form-group row mt-4">
<div class="col-sm-2"></div>
<div class="col-sm-10 text-center">
<button type="submit" class="btn btn-info text-white form-control">立即保存</button>
</div>
</div>
</form>
3.4 测试页面
- 输入为空值的时候(前端实现了不允许为空值的情况):
- 输入新旧密码不一致的时候:
- 输入旧密码不正确的时候(虽然跳回到了设置页面,但是没有提示错误信息,检查页面接收到的响应代码没有问题,不知道为什么不显示):
下面这个是上传头像文件格式有误时候的前端代码,页面提示错误信息显示是正常的(费解!!!!):
- 输入正确情况:
并且登录凭证状态修改为1,即失效了。
3.5 错误排查
- 旧密码输入不正确的时候跳回到设置页面,没有显示错误信息(问题未解决!!!!!!!!!!!)
4、检查登录状态
- 在用户没有登录的情况下,拦截一些请求,放置知道访问路径的人可以通过手动修改访问路径去访问一下页面;
-
常用的元注解(主要定义前两个就可以了):
@Target
:定义在类、方法、属性等上面
@Retention
:编译时有效or运行时有效
@Document
:生成文档的时候要不要带这个注解
@Inherited
:子类要不要继承父类的这个注解 -
如何读取注解:
Method.getDeclaredAnnotations ()
:获取方法上的所有注解
Method.getAnnotation (Class<T> annotationClass)
:尝试获取某个类型的注解
4.1 在 annotation 包下新建 LoginRequired 类
- 其中定义一个登录之后才能访问的页面的注解
- 给所有登录后才能访问的方法,加上这个自定义注解(所有关于用户设置的,都要在方法上方加上这个注解,但是获取用户头像不用这个注解,因为在首页的时候要显示所有帖子的用户头像)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {
}
4.2 新建 LoginRequiredInterceptor 类
- 注入 Hostholder,获取当前是否有用户信息
- 如果拦截到的是一个方法的话,将他转型
- 从方法上面取对应的自定义注解类型的注解
- 如果获取到了该注解,但是获取到的用户为空,我们就进行拦截,即返回false,同时强制重定向到登录页面
@Component
public class LoginRequiredInterceptor implements HandlerInterceptor {
@Autowired
private HostHolder hostHolder;
//在请求前拦截
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
LoginRequired loginRequired = method.getAnnotation(LoginRequired.class);
if (loginRequired != null && hostHolder.getUser() == null) {
response.sendRedirect(request.getContextPath() + "/login");
return false;
}
}
return true;
}
}
4.3 配置拦截器:
- 将当前拦截器注入
- 排除对静态资源的拦截
@Autowired
private LoginRequiredInterceptor loginRequiredInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginRequiredInterceptor)
.excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
}
4.4 测试页面
- 当我试图访问修改用户信息界面时,自动跳转到了登陆页面:
总结
- 修改密码的错误信息回显没有效果。可能是前端哪里出问题了。