avatar

目录
牛客网后端项目实战(十二):显示登录信息
  • 拦截器示例
    • 定义拦截器,实现HandlerInterceptor
    • 配置拦截器,为它指定拦截、排除的路径
  • 拦截器应用
    • 在请求开始时查询登录用户
    • 在本次请求中持有用户数据
    • 在模板视图上显示用户数据
    • 在请求结束时清理用户数据

拦截器示例

当用户登录过后,之后的请求都应该以登录态去访问,也就是每次带上ticket,例如网站首页,登录和未登录的显示应该不同,如果我们按照正常逻辑,每个请求都得判断登录态,处理相关逻辑。而使用拦截器,则可以拦截浏览器的请求,再对齐进行统一的处理。

定义拦截器

首先再controller包下新建一个Interceptor包,在包下新建一个AlphaInterceptor做演示。

首先实现HandlerInterceptor接口,我们可以ctrl加鼠标左键查看HandlerInterceptor类。可以看到主要有三个方法。

我们重写这三个方法,并记录日志来检查是否正确拦截。preHandle在controller之前执行,postHandle在controller之后,afterCompletion在模板引擎之后。

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.neu.langsam.community.controller.interceptor;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
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());
return true;
}

//在Controller之后执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
logger.debug("postHandle: "+handler.toString());

}

//在TemplateEngine之后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
logger.debug("afterCompletion: "+handler.toString());
}
}

配置拦截器

定义好拦截器过后,我们还需要一个配置类对它进行配置。在config包下新建WebMvcConfig类,并实现WebMvcConfigurer接口。

注入拦截器,重写添加拦截器方法。

  • .excludePathPatterns把静态资源排除在外,不进行拦截
  • .addPathPatterns添加拦截路径
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.neu.langsam.community.config;


import com.neu.langsam.community.controller.interceptor.AlphaInterceptor;
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;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

@Autowired
private AlphaInterceptor alphaInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(alphaInterceptor)
.excludePathPatterns("/**/*.css","/**/*.js","/**/*.png","/**/*.jpg","/**/*.jpeg")
.addPathPatterns("/register","/login");
}
}

然后启动项目,查看拦截是否成功。可以看到控制台输出的日志。

拦截器应用

用户登录后拿到ticket存到cookie,之后每次请求都带上ticket,拦截器拦截请求,用ticket查询login_ticket,再查询user,将user添加到model中,模板引擎根据有无user及user的值去渲染页面。

封装两个小工具

我们要从cookie中拿到ticket的值,先封装一个cookieUtil。

比较简单,直接写静态方法,不交给容器管理。传入request和我们想要的cookie的值。首先判断空值的情况,然后遍历cookie,找到我们想要的那个。

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.neu.langsam.community.util;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

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;
}

}

浏览器同时处理多个用户请求,对每个用户的user对象要做一个存储,可以使用session,但是session依赖servlet api,我们想要在方法里随用随取,anywhere!为了解决这个问题,我们就要采取一种新的方法来存储用户信息——ThreadLocal。

ThreadLocal,顾名思义,就是本地线程,可是这个名字实在容易让人误解,因为其实它是本地线程局部变量的意思,首先我们要知道,我们每个请求都会对应一个线程,这个ThreadLocal就是这个线程使用过程中的一个变量,该变量为其所属线程所有,各个线程互不影响。

我们封装一个工具HostHolder,用来持有用户信息,代替session对象。主要有添加查询和删除方法。

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.neu.langsam.community.util;


import com.neu.langsam.community.entity.User;
import org.springframework.stereotype.Component;

/**
* 持有用户信息,用于代替session对象
*/
@Component
public class HostHolder {

private ThreadLocal users=new ThreadLocal<>();

public void setUsers(User user){
users.set(user);
}

public User getUser(){
return users.get();
}

public void clear(){
users.remove();
}

}

在请求开始时查询登录用户,在本次请求中持有用户数据

做好准备工作,开始正式写登录拦截器,在Interceptor包下新建LoginTicketInterceptor。

首先重写preHandler方法,利用前面封装的cookieUtil工具得到ticket,查到用户,并用hostHolder持有用户。

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

logger.debug("preHandle: "+handler.toString());
//从cookies中获取凭证
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.setUsers(user);
}
}

return true;
}

在模板视图上显示用户数据

然后重写postHandle方法,在modelAndView上加上user。

java
1
2
3
4
5
6
7
8
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
logger.debug("postHandle: "+handler.toString());
User user=hostHolder.getUser();
if (user!=null&&modelAndView!=null){
modelAndView.addObject("loginUser",user);
}
}

在请求结束时清理用户数据

请求结束后,清理掉不需要的用户数据。

java
1
2
3
4
5
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
logger.debug("afterHandle: "+handler.toString());
hostHolder.clear();
}

配置

添加这个拦截器,因为拦截所有路径,就不加addPathPatterns了。

java
1
2
3
4
5
6
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginTicketInterceptor)
.excludePathPatterns("/**/*.css","/**/*.js","/**/*.png","/**/*.jpg","/**/*.jpeg");

}

启动项目,访问首页,登录

文章作者: langsam
文章链接: https://langsam1998.github.io/2020/03/29/20200328-%E7%89%9B%E5%AE%A2%E7%BD%91%E5%90%8E%E7%AB%AF%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98%EF%BC%88%E5%8D%81%E4%BA%8C%EF%BC%89%EF%BC%9A%E6%98%BE%E7%A4%BA%E7%99%BB%E5%BD%95%E4%BF%A1%E6%81%AF/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 平儿的博客
打赏
  • 微信
    微信
  • 支付寶
    支付寶

评论