Java Web 项目学习(二) 显示登录信息

 拦截器

拦截器的作用:以低耦合度,解决共有的问题。 可以拦截浏览器访问过来的部分或全部请求,在这些请求的开始或者结束部分插入代码,从而可以批量的解决多个请求共有的业务。

示例

  • 定义拦截器interceptor实现HandlerInterceptor接口。在controller 下新建interceptor包。建AlphaInterceptor.java。
    • HandlerInterceptor 有三个实现方法。preHandle,postHandle,afterCompletion。
    • 代码
      @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; //返回false 不执行controller了就
          }
      
          //在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());
          }
      }
  • 对AlphaInterceptor拦截器进行配置。指定拦截、排除的路径 Config包下 WebMvcConfig

    通常写配置类是声明第三方的类,作为Bean。拦截器配置器要求实现 WebMvcConfigurer 接口。

    /**
     * 通常写配置类是声明第三方的类,作为Bean
     * 拦截器配置器要求实现 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") //排除访问静态资源,所有目录下的cs文件
                    .addPathPatterns("/register","/login"); //拦截指定路径及其下的页面
        }
    }

拦截器应用

每一次请求都需要完成以下这种操作。因此写拦截器完成这部分工作

 首先要Controller实现HandelInterceptor接口

  • 在请求开始之前查询登录用户

    //获取ticket,在處理所有請求之前,contrller之前
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            //无法直接使用@CookieValue()注解了,但是可以从request中获取Cookie。比较麻烦,因此进行封装在CookieUtil中。
            //从Cookie中获取凭证
    
            String ticket =CookieUtil.getValue(request,"ticket");
            if(ticket !=null){
                //登录了,通过ticket查询 到UserID(在LoginTicket中)
                LoginTicket loginTicket =userService.findLoginTicket(ticket);
                //查看凭证是否有效??
                if(loginTicket !=null && loginTicket.getStatus()==0 && loginTicket.getExpired().after(new Date())){
                    //根据凭证查询用户。
                    User user = userService.findUserById(loginTicket.getUserId()) ;
                    //在本次请求中持有用户 (要考虑多线程的情况)
                    //考虑线程的隔离,ThreadLocal。 HostHolder封装使用
                    hostHolder.setUser(user);
                }
            }
            return true;
        }


    CookieUtil

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


    HostHolder
    线程的隔离 有一个工具类 ThreadLocal。 通过获取当前线程做一个map,每次取得时候从map中根据当前线程取。通过这种方式实现线程隔离。其get方法如下。

    public T get() {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
            return setInitialValue();
        }

    因此我们针对User对ThreadLocal进行封装。HostHolder

    /**
     * 主要目的是起到一个容器的作用
     * 持有用户信息,用于代替session对象
     */
    @Component
    public class HostHolder {
        private ThreadLocal<User> users = new ThreadLocal<User>();
    public void setUser (User user){ users.set(user); }
    public User getUser(){ return users.get(); } //清理 public void clear(){ users.remove(); } }
  • 在本次请求中持有用户数据(存一下)

    //在模板之前,将信息加入model中,用以显示
        @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();
        }

对LoginTicketIntercepter拦截器进行配置。 WebMvcConfig

/**
 * 通常写配置类是声明第三方的类,作为Bean
 * 拦截器配置器要求实现 WebMvcConfigurer 接口
 *
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Autowired
    private AlphaInterceptor alphaInterceptor;
    @Autowired
    private LoginTicketIntercepter loginTicketIntercepter;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(alphaInterceptor) //拦截一切请求
                .excludePathPatterns("/*/*.css","/*/*.js","/*/*.png","/*/*.jpg","/*/*.jpeg") //排除访问静态资源,所有目录下的cs文件
                .addPathPatterns("/register","/login"); //拦截指定路径及其下的页面


        registry.addInterceptor(loginTicketIntercepter)
                .excludePathPatterns("/*/*.css","/*/*.js","/*/*.png","/*/*.jpg","/*/*.jpeg");
    }
}

在对应模板上进行修改

改写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>
                        </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="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>
原文地址:https://www.cnblogs.com/codinghard/p/14833506.html