前言
java项目的权限认证系统大家都熟悉,之前本人也用到过几种认证token的方式,其中JWT是我觉得最神秘的一种,因为我一直没搞明白,在认证完成后,访问业务接口时,接口是怎么把请求头中的token转换为接口中的用户信息的,直到HandlerMethodArgumentResolver这个接口进入我的视野。
一、HandlerMethodArgumentResolver的作用及其中的方法解释:
HandlerMethodArgumentResolver是个接口,这个接口中有两个方法:
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
看方法名应该大致能猜到:
supportsParameter这个方法返回值是boolean,它的作用是判断Controller层中的参数,是否满足条件,满足条件则执行resolveArgument方法,不满足则跳过。
resolveArgument方法,它只有在supportsParameter方法返回true的情况下才会被调用。用于处理一些业务,将返回值赋值给Controller层中的这个参数。
我们可以将HandlerMethodArgumentResolver理解为是一个参数解析器,我们可以通过写一个类实现HandlerMethodArgumentResolver接口来实现对Controller层中方法参数的修改。
二、项目实现
1、比如,我们现在用JWT方式登陆后需要通过下面的接口获取当前登录人的信息和权限菜单等详细信息。
@GetMapping("/routes")
@ApiOperationSupport(order = 7)
@ApiOperation(value = "前端菜单数据", notes = "前端菜单数据")
public R<List<MenuVO>> routes(HttpServletRequest request) {
Claims claims = getClaims(request);
if (claims == null) {
return null;
} else {
Long userId = claims.get("user_id");
String roleId = claims.get("role_id");
String deptId = claims.get("dept_id");
String account = claims.get("account");
String roleName = claims.get("role_name");
String userName = claims.get("user_name");
User user = new User();
user.setUserId(userId);
user.setTenantId(tenantId);
user.setAccount(account);
user.setRoleId(roleId);
user.setDeptId(deptId);
user.setRoleName(roleName);
user.setUserName(userName);
}
List<MenuVO> list = menuService.routes((user == null || user.getUserId() == 0L) ? null : user.getRoleId());
return R.data(list);
}
但是,在项目中肯定每个接口需要这样获取参数,这样的话代码太臃肿了。我们可以稍微改造一下,用HandlerMethodArgumentResolver这个接口:
2、首先写个实现类实现这个接口中的方法:
public class TokenArgumentResolver implements HandlerMethodArgumentResolver {
private static final Logger log = LoggerFactory.getLogger(TokenArgumentResolver.class);
public TokenArgumentResolver() {
}
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.getParameterType().equals(User.class);
}
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) {
return SecureUtil.getUser();
}
}
然后将这个实现类添加到项目的拦截其中:
@Configuration(
proxyBeanMethods = false
)
@EnableCaching
@Order(-2147483648)
public class MyWebMvcConfiguration implements WebMvcConfigurer {
private static final Logger log = LoggerFactory.getLogger(BladeWebMvcConfiguration.class);
public MyWebMvcConfiguration() {
}
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new TokenArgumentResolver());
}
}
这样在项目接口请求时会进入到我们自定义的TokenArgumentResolver类中进行解析接口类和参数信息。
然后,我们的接口就可以这样写啦:
@GetMapping("/routes")
@ApiOperationSupport(order = 7)
@ApiOperation(value = "前端菜单数据", notes = "前端菜单数据")
public R<List<MenuVO>> routes(User user) {
List<MenuVO> list = menuService.routes((user == null || user.getUserId() == 0L) ? null : user.getRoleId());
return R.data(list);
}
这样就不用每个接口都解析请求头中的token了,可以拿到token对应的用户信息