拦截器使用

1.定义拦截器,实现HandlerInterceptor接口
2.配置拦截器,为它指定拦截、排除的路径

拦截器应用

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

定义拦截器LoginTicketInterceptor

@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工具类,遍历cookie[],获取相应的value
        String ticket = CookieUtil.getValue(request, "ticket");
        if (ticket != null) {
            //查询凭证
            LoginTicket loginTicket = userService.selectTicket(ticket);
            if (loginTicket != null && loginTicket.getStatus() == 0 && loginTicket.getExpired().after(new Date())) {
                //根据凭证查询用户
                User user = userService.findUserById(loginTicket.getUserId());
                //在本次请求中持有用户,多线程状态下ThreadLocal每个用户单独持有一个线程
                hostHolder.setUsers(user);
                //构建用户认证的结果,并存入SecurityContext,便于Security进行授权
                Authentication authentication = new UsernamePasswordAuthenticationToken(
                        user, user.getPassword(), userService.getAuthorities(user.getId()));

                SecurityContextHolder.setContext(new SecurityContextImpl(authentication));
            }
        }
        return true;
    }

    //在模板之前调用
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable 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, @Nullable Exception ex) throws Exception {
        hostHolder.clear();
        SecurityContextHolder.clearContext();
    }
}

HostHolder

@Component
public class HostHolder {
    private ThreadLocal<User> users = new ThreadLocal<>();

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

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

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

在config中配置Interceptor

@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");
    }
}

前台页面需要加入判断th:if="${loginUser!=null}",当用户没登陆的时候显示“登录”,登陆后隐藏按钮,并显示用户相关信息

<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" th:href="@{/letter/list}">消息<span class="badge badge-danger" th:text="${allUnreadCount!=0?allUnreadCount:''}">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" th:href="@{|/user/profile/${loginUser.id}|}">个人主页</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>

显示状态
未登录
图片说明

登陆后
图片说明