说到Java web,那么在web端获取登陆用户信息几乎是所有系统都需要实现的功能,下面我们就来讲一下几种获取登陆用户信息的方法。
从session中获取
这是我们刚刚接触Java web时就了解的一种解决方案,配置一个登陆过滤器或者登陆拦截器,在用户登陆的时候将用户信息放到session中,然后控制层在session中取出用户信息,这是最简单也是最常见的一种解决方案。
随着用户量的增大,单部署的服务往往不能满足我们的需求,这时候往往我们会引入集群来分摊服务器的压力。但是引入集群后,从session中存取用户信息就会变得不那么靠谱了。
大家都知道session是存在服务端的,那么如果用户在集群机器A中登陆,用户信息就存储在了机器A上,此时如果用户新的请求被分发到集群机器B上,就无法获取用户信息了。
这是集群情况下用户信息被存储在集群中的单个服务器上导致的,解决方案主要有两种:
- 让用户后续的所有请求都分发到用户登陆的那台机器
- 把用户信息存储在缓存中间件中,而不是集群中的每个服务器上
第一种方案可以在代理服务器(apache,nginx等)上配置session黏着,这样就能保证用户后续请求都会被路由到同一台机器上,这就解决了集群情况下拿不到用户信息的问题了。
当然,对ip进行hash算法使某一个用户的请求永远随机到某一台机器上也是一种思路。
从缓存中间件获取
从缓存中间件获取,就是把用户信息存储在缓存中间件中,控制层直接从缓存中间件中获取用户信息。
用户在登陆成功后,会生成一个token,然后以token为键,用户信息为值存储在缓存中间件中,并且把token发送到页面端。
页面端向服务端发送请求的时候带上这个token,服务端控制层可以根据这个token从缓存中间件中获取用户信息。
在集群情况下,用户信息还是存储在一个独立出来的缓存服务器上,这样就可以避免获取不到用户信息的问题了。
以上两种方法就是获取登陆用户信息的两种主要解决方案了,这里再从代码层面上给出一个小分享。
不论是从session中获取还是从缓存中间件中获取用户信息,不可避免的是在控制层需要显式的去获取用户
User user = (User)session.get("login_user");
或者
User user = (User)redisService.get(token);
几乎每个控制层方法都需要执行上面的语句获取一下用户信息,着就会显得很罗嗦,所以下面给出一个小分享来使用注解和HandlerMethodArgumentResolver结合优化控制层代码。
我们先来了解一下HandlerMethodArgumentResolver, SpringMvc中的HandlerAdapter会检测所有的 HandlerMethodArgumentResolver,对参数进行处理和加工。
这样,我们就可以自定义一个HandlerMethodArgumentResolver,读取请求中的token,根据token获取到用户信息,将用户信息通过自定义注解的方式传递到控制层。
没错,这个小分享是基于SpringMVC的,下面给出详细代码。
- 首先自定义一个注解,用来传递用户信息
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Logined {
}
- 自定义一个HandlerMethodArgumentResolver,用来获取用户信息
public class LoginedArgumentResolver implements HandlerMethodArgumentResolver {
private RedisService<String, Object> redisService;
public LoginedArgumentResolver(RedisService<String, Object> redisService) {
this.redisService = redisService;
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
if (parameter.getParameterAnnotation(Logined.class) != null
&& parameter.getParameterType() == LoginUser.class) {
// 如果该参数注解有@Logined才会执行下面的resolveArgument方法
return true;
}
return false;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
String accessToken = request.getHeader(LocalConstant.ACCESS_TOKEN_KEY);
LoginUser loginUser = (LoginUser)redisService.get(LocalConstant.REDIS_LOGIN_USER_KEY + accessToken);
return loginUser;
}
}
- 控制层获取用户信息
@GetMapping("/getLoginUser")
public LoginUser addCouple(@Logined LoginUser loginUser){
return loginUser;
}
通过上面的Logined注解和LoginedArgumentResolver结合,在控制层中需要获取用户信息,若需要在参数中添加@Logined就可以了,是不是简单了很多呢,希望今天的分享可以给大家带来帮助。