• 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>
  • 相关阅读:
    CF235D
    模拟赛 circle 题解
    平面图总结
    kd 树总结
    思维题
    luogu P1600 天天爱跑步
    UOJ #42. 【清华集训2014】Sum
    FZOJ 4344 连通性
    平衡树
    计蒜客 T3225 Darko 的生成树
  • 原文地址:https://www.cnblogs.com/codinghard/p/14833506.html
Copyright © 2020-2023  润新知