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
步骤
- 自定义WebMvcConfigurer配置权限拦截器HandlerInterceptor 及方法参数解析器HandlerMethodArgumentResolver。
- 自定义权限拦截器AuthInterceptor拦截所有请求,校验token有效性,并将token解析为User,放到request中。
- 自定义参数注解@CurrentUser,将注解添加到controller的方法参数中。
- 自定义方法参数解析器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);
}
}