11.16-spring-HandlerInterceptor-interface-发布

api-diagram
  1. ![image.png](https://img-blog.csdnimg.cn/img_convert/04e082ccbcca9e63e429e1933403e336.png#averageHue=#3d4144&clientId=u0b753806-6d1c-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=415&id=uc4c77c36&margin=[object Object]&name=image.png&originHeight=415&originWidth=395&originalType=binary&ratio=1&rotation=0&showTitle=false&size=9967&status=done&style=none&taskId=uac872966-6bdb-4c65-92b6-581c9a878ce&title=&width=395)
api-summary
  1. Workflow interface that allows for customized handler execution chains. Applications can register any number of existing or custom interceptors for certain groups of handlers, to add common preprocessing behavior without needing to modify each handler implementation.
  2. A HandlerInterceptor gets called before the appropriate HandlerAdapter triggers the execution of the handler itself. This mechanism can be used for a large field of preprocessing aspects, e.g. for authorization checks, or common handler behavior like locale or theme changes. Its main purpose is to allow for factoring out repetitive handler code.
  3. In an asynchronous processing scenario, the handler may be executed in a separate thread while the main thread exits without rendering or invoking the postHandle and afterCompletion callbacks. When concurrent handler execution completes, the request is dispatched back in order to proceed with rendering the model and all methods of this contract are invoked again. For further options and details see org.springframework.web.servlet.AsyncHandlerInterceptor
  4. Typically an interceptor chain is defined per HandlerMapping bean, sharing its granularity. To be able to apply a certain interceptor chain to a group of handlers, one needs to map the desired handlers via one HandlerMapping bean. The interceptors themselves are defined as beans in the application context, referenced by the mapping bean definition via its “interceptors” property (in XML: a of ).
  5. HandlerInterceptor is basically similar to a Servlet Filter, but in contrast to the latter it just allows custom pre-processing with the option of prohibiting the execution of the handler itself, and custom post-processing. Filters are more powerful, for example they allow for exchanging the request and response objects that are handed down the chain. Note that a filter gets configured in web.xml, a HandlerInterceptor in the application context.
  6. As a basic guideline, fine-grained handler-related preprocessing tasks are candidates for HandlerInterceptor implementations, especially factored-out common handler code and authorization checks. On the other hand, a Filter is well-suited for request content and view content handling, like multipart forms and GZIP compression. This typically shows when one needs to map the filter to certain content types (e.g. images), or to all requests.
generalization-class
package com.yc.plugin.starter.context.handle;

import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.web.servlet.HandlerInterceptor;

import com.yc.framework.starter.kernel.constants.BaseConstants.RequestHeaderKey;
import com.yc.framework.starter.tools.utils.JsonUtil;
import com.yc.plugin.starter.authority.core.model.LoginUser;
import com.yc.plugin.starter.context.autoconfigure.PluginContextProperties;
import com.yc.plugin.starter.context.constants.PluginContextConst;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

/**
 *
 * Http请求头透传
 *
 * @author hetianchang, joy.zhou
 * @version 1.0
 * @date 2020/10/29 14:33
 */
@Slf4j
public class RequestHeaderTransparentInterceptor implements HandlerInterceptor {
  01 基本属性设置 见附件
  @Autowired
  private PluginContextProperties pluginContextProperties;
  02 前置处理 判断是否开启拦截
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    02-01 注入请求头上下文
    if (pluginContextProperties.getUserEnabled()) {
      this.injectUserContext(request);
    }
    02-02 注入用户上下文
    if (pluginContextProperties.getHttpHeaderEnabled()) {
      this.injectHttpHeaderContext(request);
    }
    return true;
  }
  03 后置处理,本次请求完成,清除认证信息
  @Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    UserContext.resetRequestAttributes();
    HttpHeaderContext.clearHttpHeadContext();
  }


  /**
   * 注入请求头上下文
   * @param request 请求
   */ 
  04 02-01实现 注入请求上下文
  private void injectHttpHeaderContext(HttpServletRequest request) {
	04-01 注入认证信息
    this.injectAuthorizationContext(request);
    Enumeration<String> headerNames = request.getHeaderNames();
    if (headerNames == null) {
      return;
    }
    while (headerNames.hasMoreElements()) {
      String key = headerNames.nextElement();
      if (key.startsWith(PluginContextConst.PROJECT_HEADER_PREFIX)) {
        if (log.isDebugEnabled()) {
          if(key.equals(RequestHeaderKey.YC_PRINCIPAL)) {
            log.debug("注入请求头:{} - {}", key, URLDecoder.decode(request.getHeader(key),StandardCharsets.UTF_8));
          }else{
            log.debug("注入请求头:{} - {}", key, request.getHeader(key));
          }
        }
        HttpHeaderContext.setContext(key, request.getHeader(key));
      }
    }
  }

  /**
   * 注入认证信息
   * @param request 请求
   */
  05 04-01实现 注入认证信息实现
  private void injectAuthorizationContext(HttpServletRequest request) {
	  05-01 获取认证请求头
    String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
    if (StrUtil.isNotBlank(authorization)) {
      log.debug("注入认证信息: {}", authorization);
		05-02 注入认证信息 见附件
      HttpHeaderContext.setContext(HttpHeaders.AUTHORIZATION, authorization);
    }
  }


  /**
   * 注入用户上下文
   *
   * 获取网关传递下来的请求头
   *
   * @param request 请求
   */
  06 02-02 实现
  private void injectUserContext(HttpServletRequest request) {
    String header = request.getHeader(RequestHeaderKey.YC_PRINCIPAL);
    if (StrUtil.isNotBlank(header)) {
		06-01 查询当前用户上下文信息是否存在 见附件
      LoginUser user = UserContext.current();
      if (user != null) {
        log.debug("已注入用户上下文:{}", user);
        return;
      }
      user = JsonUtil.toObject(URLDecoder.decode(header, StandardCharsets.UTF_8), LoginUser.class);
      if (user != null) {
        log.debug("注入用户上下文: {}", user);
		  06-02 注入用户上下文 见附件
        UserContext.setRequestAttributes(user, true);
      }
    }
  }
}

package com.yc.plugin.starter.context.autoconfigure;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 *
 * 请求上下文配置
 *
 * @author joy.zhou
 * @date 2020-11-05 11:22:12
 */
@Data
@ConfigurationProperties(prefix = PluginContextProperties.PRE_FIX)
public class PluginContextProperties {

  static final String PRE_FIX = "yc.plugin.context";

  /**
   * 是否启用
   */
  private Boolean enabled;

  /**
   * 不透传的请求
   */
  private String[] excludes;

  /**
   * 是否注入请求上下文
   */
  private Boolean httpHeaderEnabled = true;

  /**
   * 是否注入用户上下文
   */
  private Boolean userEnabled = true;
}

package com.yc.plugin.starter.context.handle;

import static com.yc.plugin.starter.context.constants.PluginContextConst.HTTP_HEADER_CONTEXT;

import java.util.HashMap;
import java.util.Map;

import org.springframework.core.NamedInheritableThreadLocal;

import cn.hutool.core.convert.Convert;

/**
 * @author joy.zhou
 * @version 1.0
 * @date 2020/11/12 9:35
 */
public class HttpHeaderContext {
  03 02-01赋值的本地线程定义
  private static final ThreadLocal<Map<String, String>> HTTP_HEAD_CONTEXT = new NamedInheritableThreadLocal<>(
      HTTP_HEADER_CONTEXT);

  /**
   * 设置请求头信息
   *
   * @param key key
   * @param value value
   */
	 01 05-02调用
  static void setContext(String key, String value) {
	01-01 调用本地getHttpHeadContext方法
    getHttpHeadContext().put(key, value);
  }

  /**
   * 获取请求头信息
   * @param name header key
   */
  public static String getHeader(String name) {
    return getHttpHeadContext().get(name);
  }

  /**
   * 获取请求头信息
   * @param name header key
   * @param defaultValue 默认值
   */
  public static String getHeader(String name, String defaultValue) {
    return getHttpHeadContext().getOrDefault(name, defaultValue);
  }

  /**
   * 获取请求头信息
   * @param name header key
   * @param cls 类型
   */
  public static <T> T getHeader(String name, Class<T> cls) {
    return Convert.convert(cls, getHttpHeadContext().get(name));
  }

  /**
   * 获取请求头信息
   * @param name header key
   * @param cls 类型
   * @param defaultValue 默认值
   */
  public static <T> T getHeader(String name, Class<T> cls, T defaultValue) {
    return Convert.convert(cls, getHttpHeadContext().get(name), defaultValue);
  }
  02 01-01本地方法实现
  private static Map<String, String> getHttpHeadContext() {
	  02-01 给本地线程HTTP_HEAD_CONTEXT赋值
    if (HTTP_HEAD_CONTEXT.get() == null) {
      synchronized (HTTP_HEAD_CONTEXT) {
        if (HTTP_HEAD_CONTEXT.get() != null) {
          return HTTP_HEAD_CONTEXT.get();
        }
        HTTP_HEAD_CONTEXT.set(new HashMap<>(0));
      }
    }
    return HTTP_HEAD_CONTEXT.get();
  }

  /**
   * 清除请求头信息
   * @author hetianchang
   * @date 2020/8/24
   */
  static void clearHttpHeadContext() {
    HTTP_HEAD_CONTEXT.remove();
  }
}

package com.yc.plugin.starter.context.handle;

import javax.annotation.Nullable;

import org.springframework.core.NamedInheritableThreadLocal;
import org.springframework.core.NamedThreadLocal;
import org.springframework.http.HttpStatus;

import com.yc.framework.starter.kernel.exception.AuthenticationException;
import com.yc.plugin.starter.authority.core.model.LoginUser;


/**
 * 用户上下文
 * @author joy.zhou
 * @date 2020-11-05 11:22:12
 */
public class UserContext {
  private static final ThreadLocal<LoginUser> REQUEST_ATTRIBUTES_HOLDER = new NamedThreadLocal<>(
      "UserInfo attributes");

  private static final ThreadLocal<LoginUser> INHERITABLE_REQUEST_ATTRIBUTES_HOLDER = new NamedInheritableThreadLocal<>(
      "UserInfo context");

  static void resetRequestAttributes() {
    REQUEST_ATTRIBUTES_HOLDER.remove();
    INHERITABLE_REQUEST_ATTRIBUTES_HOLDER.remove();
  }
  02 06-02调用 注入本地线程-用户信息
  static void setRequestAttributes(@Nullable LoginUser loginUser, boolean inheritable) {
    if (loginUser == null) {
      resetRequestAttributes();
    } else if (inheritable) {
      INHERITABLE_REQUEST_ATTRIBUTES_HOLDER.set(loginUser);
      REQUEST_ATTRIBUTES_HOLDER.remove();
    } else {
      REQUEST_ATTRIBUTES_HOLDER.set(loginUser);
      INHERITABLE_REQUEST_ATTRIBUTES_HOLDER.remove();
    }
  }
  01 06-01调用 查询本地线程用户信息是否存在
  @Nullable
  static LoginUser current() {
    LoginUser attributes = REQUEST_ATTRIBUTES_HOLDER.get();
    if (attributes == null) {
      attributes = INHERITABLE_REQUEST_ATTRIBUTES_HOLDER.get();
    }
    return attributes;
  }

  static LoginUser me() throws IllegalStateException {
    LoginUser attributes = current();
    if (attributes == null) {
      throw new AuthenticationException("未找到当前用户上下文信息", HttpStatus.UNAUTHORIZED.value());
    }
    // 走fallback
    if (attributes.getId() == null) {
      throw new AuthenticationException("未找到当前用户上下文信息", HttpStatus.UNAUTHORIZED.value());
    }
    return attributes;
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值