前言
我们做项目都有这样类似的情景:用户登录成功过后,前端会得到一个 Token(一个用户一个token)
后面假设后端的接口逻辑需要用到这个 token 去交互一个用户身份,然后完成逻辑。
当然我们可以封装写好一个方法,通过传参 token 再加上我们自己的一些代码逻辑去拿到这个用户的身份。不过我们作为优质的程序员,还是尽量精简自己的代码。
能否有一个方法,在我们进行参数绑定的时候,就注入我们封装的 User 这个实体中呢?
于是:Spring 提供了这么一个接口: HandlerMethodArgumentResolver
看一下这个东西的位置:
HandlerMethodArgumentResolver是什么?
翻译是处理方法参数的解析器。可以通过它,在前端只传一个 token 的时候交换到用户信息,封装到
User 实体里面
看一下这个接口的源码:
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter var1);
@Nullable
Object resolveArgument(MethodParameter var1, @Nullable ModelAndViewContainer var2, NativeWebRequest var3, @Nullable WebDataBinderFactory var4) throws Exception;
}
接口里面只提供了两个抽象方法:
boolean supportsParameter(MethodParameter var1)
是否支持此方法参数的解析,如果返回false,则不调用resolveArgument方法
Object resolveArgument(MethodParameter var1, @Nullable ModelAndViewContainer var2, NativeWebRequest var3, @Nullable WebDataBinderFactory var4) throws Exception;
解析参数的方法,如果前端将 token 放入请求头,则我们从NativeWebRequest中获取 token 参数。该方法的返回值将自动注入到被注解标记的 User 实体中。
怎么使用?
步骤一:
需要先写一个自定义注解(可以用 @CurrentUser ),后面就可以通过这个自定义的注解标识,被它注解过的参数的值都需要由方法参数解析器CurrentUserMethodArgumentResolver 来“注入”
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentUser {
}
步骤二:
创建参数解析器类,实现HandlerMethodArgumentResolver接口,重写接口的两个方法:
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import edp.core.annotation.CurrentUser;
import edp.core.consts.Consts;
import edp.davinci.model.User;
/**
* @CurrentUser 注解 解析器
*/
public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().isAssignableFrom(User.class)
&& parameter.hasParameterAnnotation(CurrentUser.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
//写点儿伪代码...
1. //从请求头中获取sessionId
String token = webRequest.getHeader("token");
2. //根据token从通过自己的逻辑获取到用户信息(你可以通过Redis 获取)
3. //记得最后返回拿到的这个 user 对象
}
在上面的 supportsParameter() 方法中,我们指定了用哪个实体来装我们的用户信息,而且也指定了用哪个注解来标识
步骤三:(也是最后一步)
上面都准备好之后,其实我们发现这个东西还没交给Spring.
定义MVC配置类,实现 WebMvcConfigurer 这个接口,在 addArgumentResolvers 方法中,添加自定义参数解析器,如下:
/**
* WebMvcConfigurer加入自定义的参数解析器
*/
@Configuration
public class WebForMallMvcConfig implements WebMvcConfigurer {
/**
* 允许访问的域
*/
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 仅允许指定域名使用
if (!StringUtils.isBlank(allowCorsOrigin)) {
String[] origin = allowCorsOrigin.split(",");
for (String s : origin) {
corsConfiguration.addAllowedOrigin(s);
}
}
// 2允许任何头
corsConfiguration.addAllowedHeader("*");
// 3允许任何方法(post、get等)
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setAllowCredentials(true);
return corsConfiguration;
}
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig());
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<CorsFilter>(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(currentUserMethodArgumentResolver());
}
/**
* 用户参数解析
* @return CurrentUserMethodArgumentResolver
*/
@Bean
public CurrentUserMethodArgumentResolver currentUserMethodArgumentResolver() {
return new CurrentUserMethodArgumentResolver ();
}
}
Over了…