讨论区交流平台项目 - 显示登陆信息

功能分析

我们在每个页面的头部都要把登陆用户的头像显示出来,另外在下拉栏也要显示登陆用户的名字,除此以外在登陆与否的情况下头部显示的内容也不一样,比如你没有登陆就显示首页、注册、登陆这几个功能入口,登陆了就显示首页、消息、用户头像等功能入口。
而我们每个页面头部都需要显示登陆信息,也就是每个请求过来都需要做同样的处理。传统的方法可能就是将这个功能进行封装,然后每个请求执行之前都调用一下,但是这样导致的结果就是代码的耦合度较高,以后再更改就不很方便。因此我们引用拦截器这个思想,它能够通过配置拦截浏览器发送过来的访问请求,拦截到这些请求之后可以在请求的开发、结束的部分加入一些代码以解决很多请求共有的业务。
而对于我们显示登陆信息来说,拦截器发挥的作用应该是如下的:

  1. 在请求开始时查询登陆用户。
  2. 在本次请求中持有用户的数据。
  3. 在模板视图中显示用户数据。
  4. 在请求结束时清理用户数据。

开发步骤

业务层

我们在查询登录凭证是否有效的时候需要根据 ticket 查询出对应的额 LoginTicket,因此我们需要提供对应的业务方法。
UserService 类:

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

工具类

因为我们在查询登陆用户的时候,需要从 Cookie 中提取出来对应的 ticket 来判断用户登录状态,在 preHandle 中实现该逻辑,在之前呢我们是通过 @CookieValue 这种形式来获取的,但是我们实现接口是没有办法自己加参数的,但是 preHandle 里面有 HttpServletRequest 参数,而 Cookie 就是在 HttpServletRequest 参数中的,因此我们封装一个工具实现从 HttpServletRequest 中获取 ticket 值。
CookieUtil 工具类:

package com.spring.community2.util;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
/**
 * @ClassName CookieUtil
 * @Author ruizhou
 * @Date 2020/5/22 22:15
 **/
public class CookieUtil {
    public static String getValue(HttpServletRequest httpServletRequest, String name) {
        if (httpServletRequest == null || name == null) {
            throw new IllegalArgumentException("参数为空!");
        }
        Cookie[] cookies = httpServletRequest.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(name)) {
                    return cookie.getValue();
                }
            }
        }
        return null;
    }
}

而且在本次请求中持有用户数据是一个需要注意的地方,因为浏览器访问服务器是多对一的方式,一个服务器能处理多个请求,当浏览器访问服务器的时候,服务器会创建一个独立的线程来解决这个请求,是一个多线程的情形,因此引用 ThreadLocal。因此我们需要将 User 存到 ThreadLocal 里,保证线程隔离。
HostHolder 工具类:

package com.spring.community2.util;
import com.spring.community2.entity.User;
import org.springframework.stereotype.Component;
/**
 * @ClassName HostHolder
 * @Author ruizhou
 * @Date 2020/5/22 22:19
 **/
@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();
    }
}

定义拦截器

利用 SpringBoot 定义拦截器要实现 HandlerInterceptor 接口,这个接口下面有三个方法 preHandle、postHandle、afterCompletion 分别代表在 Controller 之前执行、在 Controller 之后执行、在 TemplateEngine 之后执行。
LoginTicketInterceptor 拦截器:

package com.spring.community2.controller.interceptor;
import com.spring.community2.entity.LoginTicket;
import com.spring.community2.entity.User;
import com.spring.community2.service.UserService;
import com.spring.community2.util.CookieUtil;
import com.spring.community2.util.HostHolder;
import org.apache.tomcat.util.net.openssl.ciphers.Authentication;
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;
/**
 * @ClassName LoginTicketInterceptor
 * @Author ruizhou
 * @Date 2020/5/22 22:13
 **/
@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();
    }
}

拦截器配置

需要进行对应的配置,SpringBoot 容器才能知道拦截器拦截哪些请求。因为我们要实现一个配置类,和往常在配置类中定义 Bean 不一样,这个是实现接口,重写 addInterceptors 方法,然后将我们写好的拦截器注入进来,然后配置拦截的请求路径,我们这里所有的路径都要拦截,同时所有的静态的文件都不拦截。
WebMvcConfig 配置类:

package com.spring.community2.config;
import com.spring.community2.controller.interceptor.LoginRequiredInterceptor;
import com.spring.community2.controller.interceptor.LoginTicketInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
 * @ClassName WebMvcConfig
 * @Author ruizhou
 * @Date 2020/5/22 21:55
 **/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Autowired
    private LoginTicketInterceptor loginTicketInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginTicketInterceptor)
                .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
    }
}

实现效果

登录后显示登陆信息。在这里插入图片描述
未登录时显示注册、登录。在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值