学习内容
通过实现HandlerMethodArgumentResolver接口,快速获取基于token登录的用户信息。这里简单介绍一下我一般是如何基于token来登录的。
- 用户登录成功后,通过一些算法(例如:雪花算法)生成一个唯一的字符串token,然后使用Redis存储用户的信息,以token为key,用户的登录信息为value,并将token传给客户端或者直接将token存储在cookie里面。
- 当用户请求需要登录的页面时,获取token,然后拿着该token去Redis中获取用户信息。
- 如果不存在该token,则说明当前用户未登录,直接抛出异常。
编写代码
- 新建LoginUser实体类,用于存储登录用户信息。
public class LoginUser {
private Long id;
private String username;
// 省略get、set方法
}
- 新建LoginUserArgumentResolver类,用于获取用户信息
@Component
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> clazz = parameter.getParameterType();
return clazz==LoginUser.class;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
String token = request.getParameter("token");
// 然后根据token获取用户登录信息,这里省略获取用户信息的过程,随便填写一些数据
LoginUser loginUser = new LoginUser();
loginUser.setId(1L);
loginUser.setUsername("lizhencheng");
return loginUser;
}
}
- 新建一个配置类
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginUserArgumentResolver loginUserArgumentResolver;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(loginUserArgumentResolver);
}
}
- 编写一个Controller查看效果
@RestController
public class IndexController {
@GetMapping("/getLoginUserInfo")
public Object getLoginUserInfo(LoginUser loginUser) {
return loginUser;
}
}
在浏览器输入获取用户信息的地址
可以看到,我们只需要将LoginUser以参数的方式注入到每个方法中就可以获取到用户的信息。
改造代码
前面我们把获取用户信息的代码放在了LoginUserArgumentResolver中,而我们获取用户信息的时候还需要做一些权限的认证,一般使用过滤器或者拦截器来做权限校验,我这里就使用拦截器来处理了。
- 新建UserContext类,存放登录的用户信息
public class UserContext {
private static ThreadLocal<LoginUser> userHolder = new ThreadLocal<LoginUser>();
public static void setUser(LoginUser loginUser) {
userHolder.set(loginUser);
}
public static LoginUser getUser() {
return userHolder.get();
}
}
- 新建拦截器LoginInterceptor
@Service
public class LoginInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
LoginUser loginUser = getUser(request, response);
UserContext.setUser(loginUser);
}
return super.preHandle(request, response, handler);
}
private LoginUser getUser(HttpServletRequest request, HttpServletResponse response) {
String token = request.getParameter("token");
// 然后根据token获取用户登录信息,这里省略获取用户信息的过程
LoginUser loginUser = new LoginUser();
loginUser.setId(1L);
loginUser.setUsername("lizhencheng");
// 如果这里校验用户信息失败,则直接抛出异常
return loginUser;
}
}
- 修改LoginUserArgumentResolver中的代码
@Component
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> clazz = parameter.getParameterType();
return clazz==LoginUser.class;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
return UserContext.getUser();
}
}
- 修改WebConfig,注入拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Autowired
private LoginUserArgumentResolver loginUserArgumentResolver;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(loginUserArgumentResolver);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor).addPathPatterns("/**"); //配置拦截规则
}
}
到这里代码就修改完成了,一般我们会在拦截器中校验用户的登录信息,如果校验失败,则抛出异常。
代码地址:https://github.com/923226145/SpringBoot-learning/tree/master/springboot-chapter6