SpringBoot用户鉴权以及统一获取用户信息

目录

背景

解决方案

方法一:手动调用方法进行解析

方法二:拦截器+方法参数解析器

步骤

WebMvcConfigurer配置权限拦截器和方法参数解析器

自定义HandlerInterceptor进行权限校验,token解析

自定义@CurrentUser注解,并加入到controller接口中

自定义HandlerMethodArgumentResolver,将request中的User传给@CurrentUser


背景

        前端调用后端Controller方法时,进入Controller方法后,经常需要获取当前登录用户的信息,便于一些后续的用户操作(比如保存时需要自动填入当前登录用户的用户名)。

        通常的做法是,前端将token信息放入请求头中,后端拿到请求头中的token后,再将token解析成用户信息。

解决方案

token解析工具类方法

        校验token有效性:boolean isValid = tokenUtils.isValid(token);

        解析token为User:User user = tokenUtils.getUserInfoByToken(token);

方法一:手动调用方法进行解析

        直接在需要使用用户信息的地方调用token解析方法获取User对象。

方法二:拦截器+方法参数解析器

WebMvcConfigurer + HandlerInterceptor + HandlerMethodArgumentResolver

步骤

  1. 自定义WebMvcConfigurer配置权限拦截器HandlerInterceptor 及方法参数解析器HandlerMethodArgumentResolver
  2. 自定义权限拦截器AuthInterceptor拦截所有请求,校验token有效性,并将token解析为User,放到request中。
  3. 自定义参数注解@CurrentUser,将注解添加到controller的方法参数中。
  4. 自定义方法参数解析器CurrentUserMethodArgumentResolver,取出request中的User对象,并将User对象赋值给@CurrentUser注解的参数user。

WebMvcConfigurer配置权限拦截器和方法参数解析器

        Spring内部的一种配置方法,通过用java代码代替xml配置Bean,可以通过实现WebMvcConfigurer接口自定义一些MVC相关的Handler,Interceptor等。

@Configuration
public class SecurityAutoConfiguration implements WebMvcConfigurer{

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // addInterceptor:添加一个实现HandlerInterceptor了接口的拦截器实例
        // addPathPatterns:用于设置拦截器的过滤路径规则
        registry.addInterceptor(getAuthInterceptor()).addPathPatterns("/**");
    }

    @Bean
    public AuthInterceptor getAuthInterceptor() {
        // 声明AuthInterceptor拦截器Bean,需实现HandlerInterceptor接口
        return new AuthInterceptor();
    }

    @Bean
    public CurrentUserMethodArgumentResolver currentUserMethodArgumentResolver() {
        // 声明自定义方法参数解析器
        return new CurrentUserMethodArgumentResolver();
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        // 添加CurrentUserMethodArgumentResolver参数解析器
        argumentResolvers.add(currentUserMethodArgumentResolver());
    }

}

自定义HandlerInterceptor进行权限校验,token解析

public class AuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String tokenId = request.getHeader(CommonDefs.AUTHORIZATION);
        if (!StringUtils.hasLength(tokenId)) {
            return false;
        }

        // 校验token是否有效
        boolean isValid = tokenUtils.isValid(token);
        if (!isValid) {
            return false;
        }
        // 转换token为User实例
        User user = tokenUtils.getUserInfoByToken(token);
        if (user == null) {
            return false;
        }
        // 将用户信息user保存到request属性中
        request.setAttribute("current_user", user);
        return true;
    }
}

自定义@CurrentUser注解,并加入到controller接口中

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CurrentUser {
    String value() default "current_user";
}
@GetMapping("/getUserInfo")
@ApiOperation(value = "查询用户信息")
public Result<?> getUserInfo(@ApiIgnore @CurrentUser User user) {
    // 通过@CurrentUser修饰,user即为当前登录用户信息
    return Result.ok(user);
}

自定义HandlerMethodArgumentResolver,将request中的User传给@CurrentUser

// Controller参数解析器
public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver {
    public CurrentUserMethodArgumentResolver() {
    }

    @Override
    // 判断Controller层中的参数,是否满足条件,满足条件则执行resolveArgument方法,不满足则跳过
    public boolean supportsParameter(MethodParameter parameter) {
        // 如果controller中的参数有CurrentUser注解修饰,则执行resolveArgument方法
        return parameter.hasParameterAnnotation(CurrentUser.class);
    }

    @Override
    // Controller中的参数满足supportsParameter()条件时执行
    // 将返回值赋值给Controller层中的这个参数
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        // 获取CurrentUser注解
        CurrentUser currentUserAnnotation = (CurrentUser)parameter.getParameterAnnotation(CurrentUser.class);
        // 将request请求中的"current_user"属性赋值给当前参数
        // "current_user"保存的是当前登录用户的信息
        return webRequest.getAttribute(currentUserAnnotation.value(), 0);
    }
}