HandlerMethodArgumentResolver用于统一获取当前登录用户 HandlerMethodArgumentResolver用于统一获取当前登录用户

HandlerMethodArgumentResolver用于统一获取当前登录用户

一、最原始直接

二、AOP

三、拦截器+方法参数解析器

 3.1 自定义权限拦截器

 3.2 自定义参数注解

 3.3 自定义方法参数解析器

 3.4 配置MVC


  • 环境:SpringBoot 2.0.4.RELEASE
  • 需求:很多Controller方法,刚进来要先获取当前登录用户的信息,以便做后续的用户相关操作。
  • 准备工作:前端每次请求都传token,后端封装一方法tokenUtils.getUserByToken(token),根据token解析得到currentUserInfo。

这是一个常见的业务需求,为实现这个需求,有以下几种解决方案:

一、最原始直接

即,每个Controller开始,先调用tokenUtils.getUserByToken(token),不够优雅。

二、AOP

AOP可以解决很多切面类问题,思路同Spring AOP来自定义注解实现审计或日志记录(完整代码),将currentUser放到request里;比起拦截器稍重。

三、拦截器+方法参数解析器

使用mvc拦截器HandlerInterceptor+方法参数解析器HandlerMethodArgumentResolver最合适。

SpringMVC提供了mvc拦截器HandlerInterceptor,包含以下3个方法:

  • preHandle
  • postHandle
  • afterCompletion

HandlerInterceptor经常被用来解决拦截事件,如用户鉴权等。另外,Spring也向我们提供了多种解析器Resolver,如用来统一处理异常的HandlerExceptionResolver,以及今天的主角HandlerMethodArgumentResolverHandlerMethodArgumentResolver是用来处理方法参数的解析器,包含以下2个方法:

  • supportsParameter(满足某种要求,返回true,方可进入resolveArgument做参数处理)
  • resolveArgument

 知识储备已到位,接下来着手实现,主要分为三步走:

  1. 自定义权限拦截器AuthenticationInterceptor拦截所有request请求,并将token解析为currentUser,最终放到request中;
  2. 自定义参数注解@CurrentUser,添加至controller的方法参数user之上;
  3. 自定义方法参数解析器CurrentUserMethodArgumentResolver,取出request中的user,并赋值给添加了@CurrentUser注解的参数user。

 3.1 自定义权限拦截器

自定义权限拦截器AuthenticationInterceptor,需实现HandlerInterceptor。在preHandle中调用tokenUtils.getUserByToken(token),获取到当前用户,最后塞进request中,如下:


  
  
  1. import javax.servlet.http.HttpServletRequest;
  2. import javax.servlet.http.HttpServletResponse;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.web.servlet.HandlerInterceptor;
  5. import org.springframework.web.servlet.ModelAndView;
  6. import edp.core.utils.TokenUtils;
  7. import edp.davinci.core.common.Constants;
  8. import edp.davinci.model.User;
  9. public class AuthenticationInterceptor implements HandlerInterceptor {
  10. @Autowired
  11. private TokenUtils tokenUtils;
  12. @Override
  13. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  14. String token = request.getHeader( "Authorization");
  15. User user = tokenUtils.getUserByToken(token);
  16. request.setAttribute(Constants.CURRENT_USER, user);
  17. return true;
  18. }
  19. @Override
  20. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  21. }
  22. @Override
  23. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  24. }
  25. }

 3.2 自定义参数注解

自定义方法参数上使用的注解@CurrentUser,代表被它注解过的参数的值都需要由方法参数解析器CurrentUserMethodArgumentResolver来“注入”,如下:


  
  
  1. import java.lang.annotation.ElementType;
  2. import java.lang.annotation.Retention;
  3. import java.lang.annotation.RetentionPolicy;
  4. import java.lang.annotation.Target;
  5. /**
  6. * 自定义 当前用户 注解
  7. * 注解 参数
  8. * 此注解在验证token通过后,获取当前token包含用户
  9. */
  10. @Target({ElementType.PARAMETER})
  11. @Retention(RetentionPolicy.RUNTIME)
  12. public @interface CurrentUser {
  13. }

给各Controller类的参数添加此注解,如下:

public ResponseEntity getDownloadRecordPage(@CurrentUser User user, HttpServletRequest request){...}
  
  

 3.3 自定义方法参数解析器

自定义方法参数解析器CurrentUserMethodArgumentResolver,需实现HandlerMethodArgumentResolver


  
  
  1. import org.springframework.core.MethodParameter;
  2. import org.springframework.web.bind.support.WebDataBinderFactory;
  3. import org.springframework.web.context.request.NativeWebRequest;
  4. import org.springframework.web.context.request.RequestAttributes;
  5. import org.springframework.web.method.support.HandlerMethodArgumentResolver;
  6. import org.springframework.web.method.support.ModelAndViewContainer;
  7. import edp.core.annotation.CurrentUser;
  8. import edp.core.consts.Consts;
  9. import edp.davinci.model.User;
  10. /**
  11. * @CurrentUser 注解 解析器
  12. */
  13. public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver {
  14. @Override
  15. public boolean supportsParameter(MethodParameter parameter) {
  16. return parameter.getParameterType().isAssignableFrom(User.class)
  17. && parameter.hasParameterAnnotation(CurrentUser.class);
  18. }
  19. @Override
  20. public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
  21. return (User) webRequest.getAttribute(Consts.CURRENT_USER, RequestAttributes.SCOPE_REQUEST);
  22. }
  23. }

As we all know,拦截器定义好以后,在SpringMVC项目中,需要去SpringMVC的配置文件springmvc.xml添加该拦截器;但是在SpringBoot中,省去了很多配置文件,取而代之的是被注解@Configuration标识的配置类,SpringMVC配置文件对应的配置类需继承WebMvcConfigurationSupport。同理,解析器定义好以后,也需被添加到SpringMVC的配置文件或配置类中。最后,额外的一步,配置mvc。

 3.4 配置MVC

定义MVC配置类,需继承WebMvcConfigurationSupport。分别在addInterceptors和addArgumentResolvers方法中,添加自定义的拦截器和参数解析器,如下:


  
  
  1. import static edp.core.consts.Consts.EMPTY;
  2. import java.math.BigInteger;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import org.springframework.beans.factory.annotation.Value;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.http.MediaType;
  9. import org.springframework.http.converter.HttpMessageConverter;
  10. import org.springframework.web.method.support.HandlerMethodArgumentResolver;
  11. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  12. import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
  13. import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
  14. import com.alibaba.fastjson.serializer.SerializerFeature;
  15. import com.alibaba.fastjson.serializer.ValueFilter;
  16. import com.alibaba.fastjson.support.config.FastJsonConfig;
  17. import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
  18. import edp.davinci.core.common.Constants;
  19. import edp.davinci.core.inteceptor.AuthenticationInterceptor;
  20. import edp.davinci.core.inteceptor.CurrentUserMethodArgumentResolver;
  21. @Configuration
  22. public class WebMvcConfig extends WebMvcConfigurationSupport {
  23. @Value( "${file.userfiles-path}")
  24. private String filePath;
  25. /**
  26. * 登录校验拦截器
  27. *
  28. * @return
  29. */
  30. @Bean
  31. public AuthenticationInterceptor loginRequiredInterceptor() {
  32. return new AuthenticationInterceptor();
  33. }
  34. /**
  35. * CurrentUser 注解参数解析器
  36. *
  37. * @return
  38. */
  39. @Bean
  40. public CurrentUserMethodArgumentResolver currentUserMethodArgumentResolver() {
  41. return new CurrentUserMethodArgumentResolver();
  42. }
  43. /**
  44. * 参数解析器
  45. *
  46. * @param argumentResolvers
  47. */
  48. @Override
  49. protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
  50. argumentResolvers.add(currentUserMethodArgumentResolver());
  51. super.addArgumentResolvers(argumentResolvers);
  52. }
  53. @Override
  54. protected void addInterceptors(InterceptorRegistry registry) {
  55. registry.addInterceptor(loginRequiredInterceptor())
  56. .addPathPatterns(Constants.BASE_API_PATH + "/**")
  57. .excludePathPatterns(Constants.BASE_API_PATH + "/login");
  58. super.addInterceptors(registry);
  59. }
  60. @Override
  61. public void addResourceHandlers(ResourceHandlerRegistry registry) {
  62. registry.addResourceHandler( "/**")
  63. .addResourceLocations( "classpath:/META-INF/resources/")
  64. .addResourceLocations( "classpath:/static/page/")
  65. .addResourceLocations( "classpath:/static/templates/")
  66. .addResourceLocations( "file:" + filePath);
  67. }
  68. @Override
  69. protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
  70. FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
  71. FastJsonConfig fastJsonConfig = new FastJsonConfig();
  72. fastJsonConfig.setSerializerFeatures(SerializerFeature.QuoteFieldNames,
  73. SerializerFeature.WriteEnumUsingToString,
  74. SerializerFeature.WriteMapNullValue,
  75. SerializerFeature.WriteDateUseDateFormat,
  76. SerializerFeature.DisableCircularReferenceDetect);
  77. fastJsonConfig.setSerializeFilters((ValueFilter) (o, s, source) -> {
  78. if ( null != source && (source instanceof Long || source instanceof BigInteger) && source.toString().length() > 15) {
  79. return source.toString();
  80. } else {
  81. return null == source ? EMPTY : source;
  82. }
  83. });
  84. //处理中文乱码问题
  85. List<MediaType> fastMediaTypes = new ArrayList<>();
  86. fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
  87. fastConverter.setSupportedMediaTypes(fastMediaTypes);
  88. fastConverter.setFastJsonConfig(fastJsonConfig);
  89. converters.add(fastConverter);
  90. }
  91. }

以上。

猜你喜欢

转载自blog.csdn.net/weixin_41722928/article/details/106860723