api-diagram
![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
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. 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. 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 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 ). 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. 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 ;
@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 ( ) ;
}
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) ) ;
}
}
}
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) ;
}
}
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 ;
@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 ;
public class HttpHeaderContext {
03 02 - 01 赋值的本地线程定义
private static final ThreadLocal < Map < String , String > > HTTP_HEAD_CONTEXT = new NamedInheritableThreadLocal < > (
HTTP_HEADER_CONTEXT ) ;
01 05 - 02 调用
static void setContext ( String key, String value) {
01 - 01 调用本地getHttpHeadContext方法
getHttpHeadContext ( ) . put ( key, value) ;
}
public static String getHeader ( String name) {
return getHttpHeadContext ( ) . get ( name) ;
}
public static String getHeader ( String name, String defaultValue) {
return getHttpHeadContext ( ) . getOrDefault ( name, defaultValue) ;
}
public static < T > T getHeader ( String name, Class < T > cls) {
return Convert . convert ( cls, getHttpHeadContext ( ) . get ( name) ) ;
}
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 ( ) ;
}
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 ;
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 ( ) ) ;
}
if ( attributes. getId ( ) == null ) {
throw new AuthenticationException ( "未找到当前用户上下文信息" , HttpStatus . UNAUTHORIZED . value ( ) ) ;
}
return attributes;
}
}