2.27显示登录信息

用户登录成功后,需要把用户的头像在这个位置显示
在这里插入图片描述
此外,在这里 要显示用户的名字
在这里插入图片描述
这些内容统称为 登录信息

要实现此功能,需要利用spring的拦截器

拦截器可以拦截浏览器的请求,在请求的开始和结束 插入一些代码,从而能够批量解决多个请求共有的业务。

本文主要内容:

在这里插入图片描述
在controller下面新建一个包interceptor(拦截器),新建一个类——拦截器AlphaInterceptor,其是接口HandlerInterceptor的实现类

package com.nowcoder.community.controller.interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class AlphaInterceptor implements HandlerInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(AlphaInterceptor.class);//实例化 日志组件, 日志起到调试作用。

    // 在Controller之前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.debug("preHandle: " + handler.toString());//日志设置为debug级别。handler.toString()就是可视化handler
        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 {//Exception ex是异常对象,返回异常信息的时候会用到
        logger.debug("afterCompletion: " + handler.toString());
    }
}

然后在config下 新建一个配置类WebMvcConfig

重新启动项目,打开浏览器,访问控制台看看有没有拦截器

可以看到按顺序执行了 之前定义的三个方法。
在这里插入图片描述
另外,因为object是拦截的目标,也打印了
在这里插入图片描述
可以通过设置断点,判断 preHandle是否在Controller之前执行
postHandle是否在Controller之后执行。
在这里插入图片描述
通过以上 小例子,我们了解了 拦截器是怎么用的:
写一个类,简单配置后可以实现这样很强大的功能。

每次请求都要按以下流程:

在这里插入图片描述
下面通过代码实现 此过程

在interceptor新建一个拦截器 LoginTicketInterceptor

package com.nowcoder.community.controller.interceptor;

import com.nowcoder.community.entity.LoginTicket;
import com.nowcoder.community.entity.User;
import com.nowcoder.community.service.UserService;
import com.nowcoder.community.util.CookieUtil;
import com.nowcoder.community.util.HostHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

@Component
public class LoginTicketInterceptor implements HandlerInterceptor {

    @Autowired
    private UserService userService;

    @Autowired
    private HostHolder hostHolder;

    @Override//重写preHandle方法
    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())) {//loginTicket.getExpired().after(new Date())表示超时时间晚于当前时间

                // 根据凭证查询用户,查到用户后,搬到模板上。
                User user = userService.findUserById(loginTicket.getUserId());
                // 在本次请求中持有用户,将用户 先暂存。存用户 要考虑到多线程的情况。浏览器访问服务器是多对一的,即一个服务器能处理多对请求,是并发的方式,每个浏览器访问服务器。服务器都会创建一个独立的线程来解决请求,所以服务器处理请求是在 一个多线程的环境。要考虑线程的隔离。需要使用ThreadLocal。
                hostHolder.setUser(user);//因此在hostHolder里面初始化并使用ThreadLocal
            }
        }

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        User user = hostHolder.getUser();//得到当前线程持有的User,
        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();//整个请求执行完后
    }
}

在util包下,新建一个类CookieUtil

package com.nowcoder.community.util;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
public class CookieUtil {
    public static String getValue(HttpServletRequest request, String name) {//从cookie中取出值String
        if (request == null || name == null) {
            throw new IllegalArgumentException("参数为空!");
        }
        Cookie[] cookies = request.getCookies();//从request中取出cookies,能得到所有cookies对象,得到的是一个数组。
        if (cookies != null) {
            for (Cookie cookie : cookies) {//遍历cookies
                if (cookie.getName().equals(name)) {//判断cookie的name 是否等于传入的参数。
                    return cookie.getValue();//如果是的话 返回要找的数据。
                }
            }
        }
        return null;
    }
}

在userService类中添加findLoginTicket方法:

 public LoginTicket findLoginTicket(String ticket) {
        return loginTicketMapper.selectByTicket(ticket);
    }

在在util包下,再次新建一个类HostHolder
在HostHolder里面初始化并使用了ThreadLocal,

package com.nowcoder.community.util;
import com.nowcoder.community.entity.User;
import org.springframework.stereotype.Component;
/**
 * 持有用户信息,用于代替session对象.
 */
@Component//放进容器的注解
public class HostHolder {
    private ThreadLocal<User> users = new ThreadLocal<>();//初始化ThreadLocal
    public void setUser(User user) {//存值
        users.set(user);
    }
    public User getUser() {
        return users.get();
    }//取值
    public void clear() {//请求结束,把ThreadLocal里面的User 清除掉,不然只存进去,不清理,会占用太多内存
        users.remove();
    }
}

关于ThreadLocal讲解如下:
https://blog.csdn.net/Mr_zhang66/article/details/116356313

写好LoginTicketInterceptor后,需要在WebMvcConfig进行配置

@Autowired
private LoginTicketInterceptor loginTicketInterceptor;
registry.addInterceptor(loginTicketInterceptor)
        .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");

之后改写templates下 index 中 header部分

<!-- 功能 -->
	<div class="collapse navbar-collapse" id="navbarSupportedContent">
		<ul class="navbar-nav mr-auto">
			<li class="nav-item ml-3 btn-group-vertical">
				<a class="nav-link" th:href="@{/index}">首页</a> <!-- 首页的访问路径 @{/index} -->
			</li>
			<li class="nav-item ml-3 btn-group-vertical" th:if="${loginUser!=null}"><!--没有登录,则看不到任何消息;登录了,即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}"><!-- 和 消息正好相反 ;-没有登录即 loginUser==null 才会显示-->
				<a class="nav-link" th:href="@{/register}">注册</a>     <!-- 注册的访问路径 "@{/register}"-->
			</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;"/><!--img th:src="${loginUser.headerUrl}" 是当前登录的用户头像 -->

				</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" th:href="@{/user/setting}">账号设置</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>

运行:
登录之前:
在这里插入图片描述
登录成功以后:
在这里插入图片描述

总之,preHandle在请求开始之初,通过凭证找到用户,并且把用户暂存到HostHolder。

什么时候需要去用user?
在模板引擎之前,

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值